Skip to content

Commit

Permalink
Add split for device description
Browse files Browse the repository at this point in the history
  • Loading branch information
beque committed Apr 26, 2024
1 parent f67eb88 commit a8a4c81
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 6 deletions.
20 changes: 20 additions & 0 deletions app/api/chemotion/device_description_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ def device_description_with_entity(device_description)
end
end

# split device description into sub device description
namespace :sub_device_descriptions do
params do
requires :ui_state, type: Hash, desc: 'Selected device descriptions from the UI'
end
post do
ui_state = params[:ui_state]
col_id = ui_state[:currentCollectionId]
element_params = ui_state[:device_description]
device_description_ids =
DeviceDescription.for_user(current_user.id)
.for_ui_state_with_collection(element_params, CollectionsDeviceDescription, col_id)
DeviceDescription.where(id: device_description_ids).each do |device_description|
device_description.create_sub_device_description(current_user, col_id)
end

{} # JS layer does not use the reply
end
end

# return serialized device description by id
params do
requires :id, type: Integer
Expand Down
37 changes: 36 additions & 1 deletion app/models/device_description.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@
# deleted_at :datetime
# created_by :integer
# ontologies :jsonb
# ancestry :string
#
# Indexes
#
# index_device_descriptions_on_device_id (device_id)
#
class DeviceDescription < ApplicationRecord
attr_accessor :collection_id
attr_accessor :collection_id, :is_split

include ElementUIStateScopes
# include PgSearch::Model
Expand All @@ -73,6 +74,7 @@ class DeviceDescription < ApplicationRecord

has_many :comments, as: :commentable, inverse_of: :commentable, dependent: :destroy
has_one :container, as: :containable, inverse_of: :containable, dependent: :nullify
has_ancestry orphan_strategy: :adopt

accepts_nested_attributes_for :collections_device_descriptions

Expand All @@ -85,10 +87,43 @@ def analyses
end

def set_short_label
return if is_split == true

prefix = 'Dev'
counter = creator.increment_counter 'device_descriptions' # rubocop:disable Rails/SkipsModelValidations
user_label = creator.name_abbreviation

update(short_label: "#{user_label}-#{prefix}#{counter}")
end

def counter_for_split_short_label
element_children = children.with_deleted.order('created_at')
last_child_label = element_children.where('short_label LIKE ?', "#{short_label}-%").last&.short_label
last_child_counter = (last_child_label&.match(/^#{short_label}-(\d+)/) && ::Regexp.last_match(1).to_i) || 0

[last_child_counter, element_children.count].max
end

def all_collections(user, collection_ids)
Collection.where(id: collection_ids) | Collection.where(user_id: user, label: 'All', is_locked: true)
end

def create_sub_device_description(user, collection_ids)
device_description = dup
segments = device_description.segments

device_description.is_split = true
device_description.short_label = "#{short_label}-#{counter_for_split_short_label + 1}"
device_description.parent = self
device_description.created_by = user.id
device_description.container = Container.create_root_container
device_description.attachments = []
device_description.segments = []
device_description.collections << all_collections(user, collection_ids)
device_description.save!

device_description.save_segments(segments: segments, current_user_id: user.id) if segments

device_description.reload
end
end
26 changes: 26 additions & 0 deletions app/packs/src/components/contextActions/CreateButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@ export default class CreateButton extends React.Component {
ClipboardActions.fetchDeviceDescriptionsByUIState(params, 'copy_device_description');
}

noDeviceDescriptionSelected() {
const { device_description } = UIStore.getState()
return device_description.checkedIds.size == 0 && device_description.checkedAll == false
}

splitSelectionAsSubDeviceDescription() {
const uiState = UIStore.getState()
let params = {
ui_state: {
device_description: {
all: uiState.device_description.checkedAll,
included_ids: uiState.device_description.checkedIds,
excluded_ids: uiState.device_description.uncheckedIds,
},
currentCollectionId: uiState.currentCollection.id,
isSync: uiState.isSync,
}
}

ElementActions.splitAsSubDeviceDescription(params);
}

createWellplateFromSamples() {
let uiState = UIStore.getState();
let sampleFilter = this.filterParamsFromUIStateByElementType(uiState, "sample");
Expand Down Expand Up @@ -321,6 +343,10 @@ export default class CreateButton extends React.Component {
>
Split Wellplate
</MenuItem>
<MenuItem onSelect={() => this.splitSelectionAsSubDeviceDescription()}
disabled={this.noDeviceDescriptionSelected() || this.isAllCollection()}>
Split Device Description
</MenuItem>
</SplitButton>
</div>
)
Expand Down
11 changes: 11 additions & 0 deletions app/packs/src/fetchers/DeviceDescriptionFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ export default class DeviceDescriptionFetcher {
.catch(errorMessage => console.log(errorMessage));
}

static splitAsSubDeviceDescription(params) {
return fetch('/api/v1/device_descriptions/sub_device_descriptions/',
{
...this._httpOptions('POST'),
body: JSON.stringify(params)
}
).then(response => response.json())
.then((json) => json)
.catch(errorMessage => console.log(errorMessage));
}

static fetchById(deviceDescriptionId) {
return fetch(
`/api/v1/device_descriptions/${deviceDescriptionId}`,
Expand Down
11 changes: 11 additions & 0 deletions app/packs/src/stores/alt/actions/ElementActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,17 @@ class ElementActions {
return collection_id;
}

splitAsSubDeviceDescription(ui_state) {
return (dispatch) => {
DeviceDescriptionFetcher.splitAsSubDeviceDescription(ui_state)
.then((result) => {
dispatch(ui_state.ui_state);
}).catch((errorMessage) => {
console.log(errorMessage);
});
};
}

// -- DataCite/Radar metadata --

fetchMetadata(collection_id) {
Expand Down
7 changes: 7 additions & 0 deletions app/packs/src/stores/alt/stores/ElementStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class ElementStore {
handleRemoveElementsCollection: ElementActions.removeElementsCollection,
handleSplitAsSubsamples: ElementActions.splitAsSubsamples,
handleSplitAsSubwellplates: ElementActions.splitAsSubwellplates,
handleSplitAsSubDeviceDescription: ElementActions.splitAsSubDeviceDescription,
// formerly from DetailStore
handleSelect: DetailActions.select,
handleClose: DetailActions.close,
Expand Down Expand Up @@ -967,6 +968,12 @@ class ElementStore {
}
}

handleSplitAsSubDeviceDescription(ui_state) {
ElementActions.fetchDeviceDescriptionsByCollectionId(
ui_state.currentCollectionId, {}, ui_state.isSync
);
}

// -- Reactions --

handleFetchReactionById(result) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddAncestryToDeviceDescriptions < ActiveRecord::Migration[6.1]
def change
add_column :device_descriptions, :ancestry, :string
add_index :device_descriptions, :ancestry
end
end
18 changes: 17 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_04_17_092033) do
ActiveRecord::Schema.define(version: 2024_04_24_181556) do

# These are extensions that must be enabled in order to support this database
enable_extension "hstore"
Expand Down Expand Up @@ -470,6 +470,8 @@
t.datetime "deleted_at"
t.integer "created_by"
t.jsonb "ontologies"
t.string "ancestry"
t.index ["ancestry"], name: "index_device_descriptions_on_ancestry"
t.index ["device_id"], name: "index_device_descriptions_on_device_id"
end

Expand Down Expand Up @@ -530,6 +532,18 @@
t.index ["name_abbreviation"], name: "index_devices_on_name_abbreviation", unique: true, where: "(name_abbreviation IS NOT NULL)"
end

create_table "element_form_types", force: :cascade do |t|
t.string "name"
t.string "description"
t.string "element_type"
t.jsonb "structure", default: {}
t.boolean "enabled"
t.integer "enabled_for"
t.integer "creator_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end

create_table "element_klasses", id: :serial, force: :cascade do |t|
t.string "name"
t.string "label"
Expand Down Expand Up @@ -1214,7 +1228,9 @@
t.jsonb "solvent"
t.boolean "inventory_sample", default: false
t.boolean "dry_solvent", default: false
t.integer "element_form_type_id"
t.index ["deleted_at"], name: "index_samples_on_deleted_at"
t.index ["element_form_type_id"], name: "index_samples_on_element_form_type_id"
t.index ["identifier"], name: "index_samples_on_identifier"
t.index ["inventory_sample"], name: "index_samples_on_inventory_sample"
t.index ["molecule_id"], name: "index_samples_on_sample_id"
Expand Down
72 changes: 68 additions & 4 deletions spec/api/chemotion/device_description_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
let(:device_description) do
create(:device_description, :with_ontologies, collection_id: collection.id, created_by: collection.user_id)
end
let(:device_description2) do
create(:device_description, :with_ontologies, collection_id: collection.id, created_by: collection.user_id)
end
let(:device_description_collection) do
create(:collections_device_description, device_description: device_description, collection: collection)
end
let(:segment_klass) { create(:segment_klass, :with_ontology_properties_template) }

describe 'GET /api/v1/device_descriptions/' do
Expand All @@ -27,12 +33,20 @@
end

describe 'POST /api/v1/device_descriptions' do
let(:device_description_params) { attributes_for(:device_description) }
let(:device_description_params) { attributes_for(:device_description, collection_id: collection.id) }

it 'creates a device description' do
post '/api/v1/device_descriptions', params: device_description_params
context 'when creating a device description' do
it 'returns a device description' do
post '/api/v1/device_descriptions', params: device_description_params

expect(parsed_json_response['device_description']['short_label']).to include('Dev')
expect(parsed_json_response['device_description']['short_label']).to include('Dev')
end

it 'has taggable_data' do
post '/api/v1/device_descriptions', params: device_description_params

expect(parsed_json_response['device_description']['tag']['taggable_data'].size).to be(1)
end
end
end

Expand Down Expand Up @@ -82,6 +96,56 @@
end
end

describe 'POST /api/v1/device_descriptions/ui_state/' do
before do
device_description_collection
end

let(:params) do
{
ui_state: {
all: false,
included_ids: [device_description.id, device_description2.id],
excluded_ids: [],
collection_id: collection.id,
},
limit: 1,
}
end

it 'fetches only one device description' do
post '/api/v1/device_descriptions/ui_state/', params: params, as: :json

expect(parsed_json_response['device_descriptions'].size).to be(1)
end
end

describe 'POST /api/v1/device_descriptions/sub_device_descriptions/' do
before do
device_description_collection
end

let(:params) do
{
ui_state: {
currentCollectionId: collection.id,
device_description: {
all: false,
included_ids: [device_description.id],
excluded_ids: [],
},
isSync: false,
},
}
end

it 'creates a split of selected device description' do
post '/api/v1/device_descriptions/sub_device_descriptions/', params: params, as: :json

expect(device_description.reload.children.size).to be(1)
end
end

describe 'PUT /api/v1/device_descriptions/:id' do
context 'when updating an device description' do
let(:params) do
Expand Down
8 changes: 8 additions & 0 deletions spec/factories/collections_device_descriptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

FactoryBot.define do
factory :collections_device_description do
collection_id { 1 }
device_description_id { 1 }
end
end

0 comments on commit a8a4c81

Please sign in to comment.