commit
4451c4decd
18
Gemfile.lock
18
Gemfile.lock
|
@ -83,7 +83,7 @@ GEM
|
|||
url
|
||||
coderay (1.1.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
crass (1.0.5)
|
||||
crass (1.0.6)
|
||||
database_cleaner (1.7.0)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
|
@ -109,8 +109,8 @@ GEM
|
|||
factory_bot_rails (5.1.1)
|
||||
factory_bot (~> 5.1.0)
|
||||
railties (>= 4.2.0)
|
||||
faker (2.10.0)
|
||||
i18n (>= 1.6, < 1.8)
|
||||
faker (2.10.1)
|
||||
i18n (>= 1.6, < 2)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday_middleware (0.13.1)
|
||||
|
@ -148,7 +148,7 @@ GEM
|
|||
hashdiff (1.0.0)
|
||||
hashie (3.6.0)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.7.0)
|
||||
i18n (1.8.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.3.0)
|
||||
jsonapi-renderer (0.2.2)
|
||||
|
@ -165,7 +165,7 @@ GEM
|
|||
mimemagic (0.3.3)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.13.0)
|
||||
minitest (5.14.0)
|
||||
multi_json (1.13.1)
|
||||
multipart-post (2.1.1)
|
||||
mutations (0.9.0)
|
||||
|
@ -178,7 +178,7 @@ GEM
|
|||
passenger (6.0.4)
|
||||
rack
|
||||
rake (>= 0.8.1)
|
||||
pg (1.2.1)
|
||||
pg (1.2.2)
|
||||
pry (0.12.2)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
|
@ -190,7 +190,7 @@ GEM
|
|||
faraday_middleware (~> 0.13.0)
|
||||
hashie (~> 3.6)
|
||||
multi_json (~> 1.13.1)
|
||||
rack (2.0.8)
|
||||
rack (2.1.1)
|
||||
rack-attack (6.2.2)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-cors (1.1.1)
|
||||
|
@ -240,7 +240,7 @@ GEM
|
|||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
retriable (3.1.2)
|
||||
rollbar (2.23.1)
|
||||
rollbar (2.23.2)
|
||||
rspec (3.9.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
|
@ -265,7 +265,7 @@ GEM
|
|||
scenic (1.5.1)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
secure_headers (6.1.1)
|
||||
secure_headers (6.1.2)
|
||||
signet (0.12.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
|
|
|
@ -529,7 +529,7 @@ module CeleryScriptSettingsBag
|
|||
resource_id = n.args.fetch(:point_group_id).value
|
||||
check_resource_type(n, "PointGroup", resource_id, Device.current)
|
||||
end,
|
||||
},
|
||||
}
|
||||
}.map { |(name, list)| Corpus.node(name, **list) }
|
||||
|
||||
HASH = Corpus.as_json
|
||||
|
|
|
@ -3,9 +3,17 @@ class PointGroup < ApplicationRecord
|
|||
%w(xy_ascending xy_descending yx_ascending yx_descending random).sort
|
||||
BAD_SORT = "%{value} is not valid. Valid options are: " +
|
||||
SORT_TYPES.map(&:inspect).join(", ")
|
||||
DEFAULT_CRITERIA = {
|
||||
day: { op: "<", days: 0 },
|
||||
string_eq: {},
|
||||
number_eq: {},
|
||||
number_lt: {},
|
||||
number_gt: {},
|
||||
}
|
||||
|
||||
belongs_to :device
|
||||
has_many :point_group_items, dependent: :destroy
|
||||
validates_inclusion_of :sort_type, in: SORT_TYPES,
|
||||
message: BAD_SORT
|
||||
serialize :criteria
|
||||
end
|
||||
|
|
|
@ -8,10 +8,13 @@ module PointGroups
|
|||
array :point_ids, class: Integer
|
||||
end
|
||||
|
||||
criteria
|
||||
|
||||
optional do
|
||||
string :sort_type
|
||||
end
|
||||
|
||||
|
||||
def validate
|
||||
validate_point_ids
|
||||
validate_sort_type
|
||||
|
@ -20,14 +23,21 @@ module PointGroups
|
|||
def execute
|
||||
PointGroup.transaction do
|
||||
PointGroupItem.transaction do
|
||||
pg = PointGroup.new(name: name, device: device)
|
||||
point_ids.uniq.map do |id|
|
||||
pg.point_group_items << PointGroupItem.new(point_id: id)
|
||||
end
|
||||
pg = PointGroup.new(name: name,
|
||||
device: device,
|
||||
criteria: PointGroup::DEFAULT_CRITERIA.merge(criteria || {})
|
||||
)
|
||||
add_point_group_items(pg)
|
||||
pg.save!
|
||||
pg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_point_group_items(pg)
|
||||
point_ids.uniq.map do |id|
|
||||
pg.point_group_items << PointGroupItem.new(point_id: id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
module PointGroups
|
||||
module ClassLevelHelpers
|
||||
def criteria
|
||||
self.optional do
|
||||
hash :criteria do
|
||||
hash(:day) do
|
||||
string :op, in: [">", "<"]
|
||||
integer :days
|
||||
end
|
||||
hash(:string_eq) { array :*, class: String }
|
||||
hash(:number_eq) { array :*, class: Integer }
|
||||
hash(:number_lt) { integer :* }
|
||||
hash(:number_gt) { integer :* }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Helpers
|
||||
BAD_POINT_IDS = "The group contains invalid points."
|
||||
def self.included(base)
|
||||
base.extend PointGroups::ClassLevelHelpers
|
||||
end
|
||||
|
||||
def points
|
||||
@points ||= Point.where(id: point_ids, device: device)
|
||||
|
|
|
@ -8,6 +8,8 @@ module PointGroups
|
|||
model :point_group, class: PointGroup
|
||||
end
|
||||
|
||||
criteria
|
||||
|
||||
optional do
|
||||
string :name
|
||||
array :point_ids, class: Integer
|
||||
|
@ -34,7 +36,9 @@ module PointGroups
|
|||
private
|
||||
|
||||
def update_attributes
|
||||
@update_attributes ||= inputs.except(*BLACKLISTED_FIELDS)
|
||||
@update_attributes ||= inputs
|
||||
.except(*BLACKLISTED_FIELDS)
|
||||
.merge(criteria: criteria || point_group.criteria)
|
||||
end
|
||||
|
||||
def maybe_reconcile_points
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class PointGroupSerializer < ApplicationSerializer
|
||||
attributes :name, :point_ids, :sort_type
|
||||
attributes :name, :point_ids, :sort_type, :criteria
|
||||
|
||||
def point_ids
|
||||
object.point_group_items.pluck(:point_id)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddCriteriaToPointGroups < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :point_groups, :criteria, :text, limit: 1000
|
||||
end
|
||||
end
|
|
@ -1185,7 +1185,8 @@ CREATE TABLE public.point_groups (
|
|||
device_id bigint NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
sort_type character varying(20) DEFAULT 'xy_ascending'::character varying
|
||||
sort_type character varying(20) DEFAULT 'xy_ascending'::character varying,
|
||||
criteria text
|
||||
);
|
||||
|
||||
|
||||
|
@ -1406,7 +1407,7 @@ CREATE VIEW public.resource_update_steps AS
|
|||
edge_nodes.kind,
|
||||
edge_nodes.value
|
||||
FROM public.edge_nodes
|
||||
WHERE (((edge_nodes.kind)::text = 'resource_type'::text) AND ((edge_nodes.value)::text = ANY ((ARRAY['"GenericPointer"'::character varying, '"ToolSlot"'::character varying, '"Plant"'::character varying])::text[])))
|
||||
WHERE (((edge_nodes.kind)::text = 'resource_type'::text) AND ((edge_nodes.value)::text = ANY (ARRAY[('"GenericPointer"'::character varying)::text, ('"ToolSlot"'::character varying)::text, ('"Plant"'::character varying)::text])))
|
||||
), resource_id AS (
|
||||
SELECT edge_nodes.primary_node_id,
|
||||
edge_nodes.kind,
|
||||
|
@ -1686,7 +1687,8 @@ CREATE TABLE public.users (
|
|||
agreed_to_terms_at timestamp without time zone,
|
||||
confirmation_sent_at timestamp without time zone,
|
||||
unconfirmed_email character varying,
|
||||
inactivity_warning_sent_at timestamp without time zone
|
||||
inactivity_warning_sent_at timestamp without time zone,
|
||||
inactivity_warning_count integer
|
||||
);
|
||||
|
||||
|
||||
|
@ -3445,6 +3447,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20191119204916'),
|
||||
('20191203163621'),
|
||||
('20191219212755'),
|
||||
('20191220010646');
|
||||
('20191220010646'),
|
||||
('20200116140201');
|
||||
|
||||
|
||||
|
|
|
@ -475,6 +475,13 @@ export function fakePointGroup(): TaggedPointGroup {
|
|||
return fakeResource("PointGroup", {
|
||||
name: "Fake",
|
||||
sort_type: "xy_ascending",
|
||||
point_ids: []
|
||||
point_ids: [],
|
||||
criteria: {
|
||||
day: { op: ">", days: 0 },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,11 +34,18 @@ describe("group action creators and thunks", () => {
|
|||
|
||||
const thunk = createGroup({ points, name: "Name123" });
|
||||
await thunk(dispatch, () => fakeS as Everything);
|
||||
expect(init).toHaveBeenCalledWith("PointGroup", {
|
||||
expect(init).toHaveBeenCalledWith("PointGroup", expect.objectContaining({
|
||||
name: "Name123",
|
||||
point_ids: [1, 2],
|
||||
sort_type: "xy_ascending"
|
||||
});
|
||||
sort_type: "xy_ascending",
|
||||
criteria: {
|
||||
day: { days: 0, op: ">" },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {},
|
||||
},
|
||||
}));
|
||||
expect(save).toHaveBeenCalledWith("???");
|
||||
expect(history.push)
|
||||
.toHaveBeenCalledWith("/app/designer/groups/323232332");
|
||||
|
|
|
@ -69,6 +69,13 @@ describe("<GroupDetailActive/>", () => {
|
|||
name: "XYZ",
|
||||
point_ids: [1],
|
||||
sort_type: "xy_ascending",
|
||||
criteria: {
|
||||
day: { days: 0, op: ">" },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {},
|
||||
}
|
||||
},
|
||||
kind: "PointGroup",
|
||||
specialStatus: "DIRTY",
|
||||
|
|
|
@ -69,7 +69,18 @@ describe("<PointGroupItem/>", () => {
|
|||
i.click();
|
||||
expect(i.props.dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(overwrite).toHaveBeenCalledWith({
|
||||
body: { name: "Fake", point_ids: [], sort_type: "xy_ascending" },
|
||||
body: {
|
||||
name: "Fake",
|
||||
point_ids: [],
|
||||
sort_type: "xy_ascending",
|
||||
criteria: {
|
||||
day: { days: 0, op: ">" },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {},
|
||||
}
|
||||
},
|
||||
kind: "PointGroup",
|
||||
specialStatus: "",
|
||||
uuid: expect.any(String),
|
||||
|
@ -77,6 +88,13 @@ describe("<PointGroupItem/>", () => {
|
|||
name: "Fake",
|
||||
point_ids: [],
|
||||
sort_type: "xy_ascending",
|
||||
criteria: {
|
||||
day: { days: 0, op: ">" },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {},
|
||||
}
|
||||
});
|
||||
expect(setHoveredPlant).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
|
|
@ -22,8 +22,18 @@ export const createGroup = ({ points, name }: CreateGroupProps) => {
|
|||
.map(x => references[x])
|
||||
.map(x => x ? x.body.id : undefined);
|
||||
const point_ids = betterCompact(possiblyNil);
|
||||
const group: PointGroup =
|
||||
({ name: name || UNTITLED(), point_ids, sort_type: "xy_ascending" });
|
||||
const group: PointGroup = {
|
||||
name: name || UNTITLED(),
|
||||
point_ids,
|
||||
sort_type: "xy_ascending",
|
||||
criteria: {
|
||||
day: { op: ">", days: 0 },
|
||||
number_eq: {},
|
||||
number_gt: {},
|
||||
number_lt: {},
|
||||
string_eq: {}
|
||||
}
|
||||
};
|
||||
const action = init("PointGroup", group);
|
||||
dispatch(action);
|
||||
return dispatch(save(action.payload.uuid)).then(() => {
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
"coveralls": "3.0.9",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.2",
|
||||
"farmbot": "9.0.1-rc0",
|
||||
"farmbot": "9.0.1-rc1",
|
||||
"i18next": "19.0.3",
|
||||
"install": "0.13.0",
|
||||
"lodash": "4.17.15",
|
||||
|
|
|
@ -47,4 +47,45 @@ describe Api::PointGroupsController do
|
|||
expect(response.status).to eq(422)
|
||||
expect(json[:sort_type]).to include(PointGroup::BAD_SORT.split("}").last)
|
||||
end
|
||||
|
||||
it "adds criteria to a group" do
|
||||
sign_in user
|
||||
payload = {
|
||||
name: "Criteria group",
|
||||
point_ids: point_ids,
|
||||
criteria: {
|
||||
string_eq: {
|
||||
openfarm_slug: ["carrot"],
|
||||
},
|
||||
number_eq: {
|
||||
z: [24, 25, 26],
|
||||
},
|
||||
number_lt: {
|
||||
x: 4,
|
||||
y: 4,
|
||||
},
|
||||
number_gt: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
},
|
||||
day: {
|
||||
op: "<",
|
||||
days: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
post :create, body: payload.to_json, format: :json
|
||||
expect(response.status).to eq(200)
|
||||
hash = json[:criteria]
|
||||
expect(hash).to be_kind_of(Hash)
|
||||
expect(hash.dig(:number_eq, :z)).to eq([24, 25, 26])
|
||||
expect(hash.dig(:number_lt, :x)).to eq(4)
|
||||
expect(hash.dig(:number_lt, :y)).to eq(4)
|
||||
expect(hash.dig(:number_gt, :x)).to eq(1)
|
||||
expect(hash.dig(:number_gt, :y)).to eq(1)
|
||||
expect(hash.dig(:day, :op)).to eq("<")
|
||||
expect(hash.dig(:day, :days)).to eq(0)
|
||||
expect(hash.dig(:string_eq, :openfarm_slug)).to eq(["carrot"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,4 +44,44 @@ describe Api::PointGroupsController do
|
|||
json2 = JSON.parse(call1.first, symbolize_names: true).fetch(:body)
|
||||
expect(json).to eq(json2)
|
||||
end
|
||||
|
||||
it "updates criteria of a group" do
|
||||
sign_in user
|
||||
initial_params = {
|
||||
device: device,
|
||||
name: "XYZ",
|
||||
point_ids: [],
|
||||
criteria: {
|
||||
string_eq: { openfarm_slug: ["carrot"] },
|
||||
number_eq: { z: [24, 25, 26] },
|
||||
number_lt: { x: 4, y: 4 },
|
||||
number_gt: { x: 1, y: 1 },
|
||||
day: { op: "<", days: 0 },
|
||||
},
|
||||
}
|
||||
pg = PointGroups::Create.run!(initial_params)
|
||||
payload = {
|
||||
point_ids: [],
|
||||
criteria: {
|
||||
string_eq: { name: ["carrot"] },
|
||||
number_eq: { x: [42, 52, 62] },
|
||||
number_lt: { y: 8 },
|
||||
number_gt: { z: 2 },
|
||||
day: { op: ">", days: 10 },
|
||||
},
|
||||
}
|
||||
put :update, body: payload.to_json, format: :json, params: { id: pg.id }
|
||||
expect(response.status).to eq(200)
|
||||
expect(json.dig(:criteria, :day, :days)).to eq(10)
|
||||
expect(json.dig(:criteria, :day, :op)).to eq(">")
|
||||
expect(json.dig(:criteria, :number_eq, :x)).to eq([42, 52, 62])
|
||||
expect(json.dig(:criteria, :number_eq, :z)).to eq(nil)
|
||||
expect(json.dig(:criteria, :number_gt, :x)).to eq(nil)
|
||||
expect(json.dig(:criteria, :number_gt, :y)).to eq(nil)
|
||||
expect(json.dig(:criteria, :number_gt, :z)).to eq(2)
|
||||
expect(json.dig(:criteria, :number_lt, :x)).to eq(nil)
|
||||
expect(json.dig(:criteria, :number_lt, :y)).to eq(8)
|
||||
expect(json.dig(:criteria, :string_eq, :name)).to eq(["carrot"])
|
||||
expect(json.dig(:criteria, :string_eq, :openfarm_slug)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue