diff --git a/app/services/team_importer.rb b/app/services/team_importer.rb index 0eb484b8c..0535f98eb 100644 --- a/app/services/team_importer.rb +++ b/app/services/team_importer.rb @@ -168,6 +168,11 @@ class TeamImporter ActiveRecord::Base.no_touching do experiment = create_experiment(experiment_json, project, user_id) + experiment.my_modules.each do |my_module| + my_module.nr_of_assigned_samples = 0 + my_module.save(touch: false) + end + # Create connections for modules experiment_json['my_modules'].each do |my_module_json| create_task_connections(my_module_json['outputs']) diff --git a/spec/services/model_importers/team_importer_spec.rb b/spec/services/model_importers/team_importer_spec.rb new file mode 100644 index 000000000..4ca8e32ae --- /dev/null +++ b/spec/services/model_importers/team_importer_spec.rb @@ -0,0 +1,399 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe TeamImporter do + describe '#import_template_experiment_from_dir' do + context 'successful import of all different elements with given json' do + # Constants + TEMPLATE_DIR = "#{Rails.root}/spec/services/model_importers/" \ + "test_experiment_data" + USER_ID = 2 + PROJECT_ID = 2 + + before :all do + time = Time.new(2015, 8, 1, 14, 35, 0) + + # Create 2 users and 2 projects to check for a different ID (id = 2) + # when importing to avoid any defaults + create :user + @user = create :user + @team = create :team + create :project, name: 'Temp project', visibility: 1, + team: @team, archived: false, created_at: time + + @project = create :project, name: 'Project', visibility: 1, team: @team, + archived: false, created_at: time + + # Reassign if multiple tests are run + PROJECT_ID = @project.id + USER_ID = @user.id + + @team_importer = TeamImporter.new + @exp = @team_importer.import_experiment_template_from_dir(TEMPLATE_DIR, + PROJECT_ID, + USER_ID) + end + + describe 'Experiment variables' do + it { expect(@exp.project.id).to eq PROJECT_ID } + it { expect(@exp.name).to eq 'Experiment export' } + it { expect(@exp.description).to eq 'My description' } + + it { expect(@exp.created_at).to eq '2019-01-21T13:27:53.342Z'.to_time } + it { expect(@exp.created_by.id).to eq USER_ID } + it { expect(@exp.last_modified_by.id).to eq USER_ID } + + it { expect(@exp.archived).to eq false } + it { expect(@exp.archived_by_id).to be_nil } + it { expect(@exp.archived_on).to be_nil } + + it { expect(@exp.restored_by_id).to be_nil } + it { expect(@exp.restored_on).to be_nil } + + it do + expect(@exp.workflowimg_updated_at).to eq( + '2019-01-21T13:31:04.682Z'.to_time + ) + end + it { expect(@exp.workflowimg_file_size).to eq 4581 } + end + + describe 'Module groups' do + # Module groups + it { expect(@exp.my_module_groups.count).to eq 2 } + it do + expect(@exp.my_module_groups.pluck(:created_by_id)).to all eq( + USER_ID + ) + end + it do + expect(@exp.my_module_groups.pluck(:created_at)).to( + match_array(['2019-01-21T13:32:46.449Z'.to_time, + '2019-01-21T13:32:46.460Z'.to_time]) + ) + end + it do + expect(@exp.my_module_groups.pluck(:updated_at)).to( + match_array(['2019-01-21T13:32:46.449Z'.to_time, + '2019-01-21T13:32:46.460Z'.to_time]) + ) + end + it do + expect(@exp.my_modules.where(my_module_group: nil).count).to eq 2 + end + it { expect(@exp.archived_modules.count).to eq 1 } + end + + describe 'Connections' do + it 'creates all connections between modules' do + expect(Connection.all.count).to eq 3 + + # Load known modules + exp_design = @exp.my_modules.find_by(name: 'Experiment design') + rna = @exp.my_modules.find_by( + name: 'RNA quality & quantity - BIOANALYSER' + ) + reverse = @exp.my_modules.find_by( + name: 'Reverse transcription' + ) + + task1 = @exp.my_modules.find_by(name: 'Workflow 2 - task 1') + task2 = @exp.my_modules.find_by(name: 'Workflow 2 - task 2') + + # Check connections + expect(exp_design.outputs.first.input_id).to eq( + rna.id + ) + expect(rna.outputs.first.input_id).to eq( + reverse.id + ) + expect(task1.outputs.first.input_id).to eq( + task2.id + ) + + # Others should be empty + expect(task2.outputs).to be_empty + expect(reverse.outputs).to be_empty + end + end + + describe 'Modules' do + it 'imports all the modules' do + team_json = JSON.parse(File.read("#{TEMPLATE_DIR}/experiment.json")) + team_json['my_modules'].each do |my_module| + json_module = my_module.dig('my_module') + + # Find DB mapped module + name = json_module.dig('name') + db_module = @exp.my_modules.find_by(name: name) + + expect(db_module.description).to eq json_module['description'] + expect(db_module.created_at).to eq json_module['created_at'].to_time + expect(db_module.created_by_id).to eq USER_ID + + expect(db_module.updated_at).to eq json_module['updated_at'].to_time + expect(db_module.last_modified_by_id).to eq USER_ID + + expect(db_module.archived).to eq json_module['archived'] + + expect(db_module.state).to eq json_module['state'] + if json_module['completed_on'] + expect(db_module.completed_on).to eq( + json_module['completed_on'].to_time + ) + end + + if json_module['due_date'] + expect(db_module.due_date).to eq( + json_module['due_date'].to_time + ) + end + + # Coordinates and workflow + expect(db_module.x).to eq json_module['x'] + expect(db_module.y).to eq json_module['y'] + expect(db_module.workflow_order).to eq( + json_module['workflow_order'] + ) + + # Check for lonely tasks + if json_module['my_module_group_id'].nil? + expect(db_module.my_module_group_id).to be_nil + end + + expect(db_module.nr_of_assigned_samples).to be_zero + + # Check if callbacks for protocols are turned off + expect(db_module.protocols.count).to eq 1 + + # Although rows are present in JSON, they shouldn't be imported + expect(db_module.repository_rows.count).to be_zero + + # No tags should be imported + expect(db_module.tags.count).to be_zero + + ########### + # Protocols + ########### + db_protocol = db_module.protocols.first + json_protocol = my_module['protocols'][0]['protocol'] + + # Protocol object + expect(db_protocol.name).to eq json_protocol['name'] + expect(db_protocol.description).to eq json_protocol['description'] + expect(db_protocol.created_at).to eq( + json_protocol['created_at'].to_time + ) + expect(db_protocol.updated_at).to eq( + json_protocol['updated_at'].to_time + ) + expect(db_protocol.added_by_id).to eq( + json_protocol.dig('added_by_id') + ) + expect(db_protocol.archived_by_id).to be_nil + expect(db_protocol.archived_on).to be_nil + expect(db_protocol.restored_by_id).to be_nil + expect(db_protocol.restored_on).to be_nil + expect(db_protocol.authors).to eq json_protocol['authors'] + expect(db_protocol.parent_id).to eq json_protocol['parent_id'] + expect(db_protocol.parent_updated_at).to eq( + json_protocol['parent_updated_at'] + ) + expect(db_protocol.protocol_type).to eq( + json_protocol['protocol_type'] + ) + expect(db_protocol.team_id).to eq @team.id + expect(db_protocol.nr_of_linked_children).to eq( + json_protocol.dig('nr_of_linked_children') + ) + + ####### + # STEPS + ####### + json_steps = my_module.dig('protocols')[0].dig('steps') + json_steps.each do |json_step| + json_step_obj = json_step.dig('step') + db_step = db_protocol.steps.find_by(name: json_step_obj['name']) + + # Step object + expect(db_step.description).to eq json_step_obj['description'] + expect(db_step.updated_at).to eq( + json_step_obj['updated_at'].to_time + ) + expect(db_step.position).to eq json_step_obj['position'] + expect(db_step.last_modified_by_id).to eq USER_ID + expect(db_step.completed).to eq json_step_obj['completed'] + + if json_step_obj['completed'] + expect(db_step.completed_on).to eq( + json_step_obj['completed_on'].to_time + ) + end + + # Checklists + expect(db_step.checklists.count).to eq( + json_step['checklists'].count + ) + json_step['checklists'].each do |checklist| + json_checklist = checklist['checklist'] + json_items = checklist['checklist_items'] + db_checklist = db_step.checklists.find_by( + name: json_checklist['name'] + ) + + # Checklist object + expect(db_checklist.created_at).to eq( + json_checklist['created_at'].to_time + ) + expect(db_checklist.updated_at).to eq( + json_checklist['updated_at'].to_time + ) + expect(db_checklist.created_by_id).to eq USER_ID + expect(db_checklist.last_modified_by_id).to eq USER_ID + + expect(db_checklist.checklist_items.count).to eq( + json_items.count + ) + + # Checklist items + json_items.each do |json_item| + db_item = db_checklist.checklist_items.find_by( + text: json_item['text'] + ) + expect(db_item.checked).to eq(json_item['checked']) + expect(db_item.position).to eq(json_item['position']) + expect(db_item.created_at).to eq( + json_item['created_at'].to_time + ) + expect(db_item.updated_at).to eq( + json_item['updated_at'].to_time + ) + expect(db_item.created_by_id).to eq USER_ID + expect(db_item.last_modified_by_id).to eq USER_ID + end + end + + # Step assets + expect(db_step.assets.count).to eq( + json_step['assets'].count + ) + + expect(db_step.step_assets.count).to eq( + json_step['step_assets'].count + ) + + json_step['assets'].each do |json_asset| + db_asset = db_step.assets.find_by( + file_file_name: json_asset['file_file_name'] + ) + + # Basic fields + expect(db_asset.created_at).to eq( + json_asset['created_at'].to_time + ) + expect(db_asset.created_by_id).to eq USER_ID + expect(db_asset.last_modified_by_id).to eq USER_ID + expect(db_asset.team_id). to eq @team.id + + # Other fields + expect(db_asset.estimated_size).to eq( + json_asset['estimated_size'] + ) + expect(db_asset.file_content_type).to eq( + json_asset['file_content_type'] + ) + expect(db_asset.file_file_size).to eq( + json_asset['file_file_size'] + ) + expect(db_asset.file_updated_at).to be_within(10.seconds) + .of(Time.now) + expect(db_asset.lock).to eq( + json_asset['lock'] + ) + expect(db_asset.lock_ttl).to eq( + json_asset['lock_ttl'] + ) + expect(db_asset.version).to eq( + json_asset['version'] + ) + end + + # Tables + expect(db_step.step_tables.count).to eq( + json_step['step_tables'].count + ) + expect(db_step.tables.count).to eq( + json_step['tables'].count + ) + + json_step['tables'].each do |json_table| + db_table = db_step.tables.find_by( + contents: Base64.decode64(json_table['contents']) + ) + + # Basic fields + expect(db_table.created_at).to eq( + json_table['created_at'].to_time + ) + expect(db_table.updated_at).to eq( + json_table['updated_at'].to_time + ) + expect(db_table.created_by_id).to eq USER_ID + expect(db_table.last_modified_by_id).to eq USER_ID + expect(db_table.team_id). to eq @team.id + + # Other fields + expect(db_table.name).to eq(json_table['name']) + expect(db_table.data_vector).to eq( + Base64.decode64(json_table['data_vector']) + ) + end + + # Step comments + expect(db_step.step_comments.count).to eq( + json_step['step_comments'].count + ) + json_step['step_comments'].each do |json_comment| + db_comment = db_step.step_comments.find_by( + message: json_comment['message'] + ) + + expect(db_comment.created_at).to eq( + json_comment['created_at'].to_time + ) + expect(db_comment.updated_at).to eq( + json_comment['updated_at'].to_time + ) + expect(db_comment.last_modified_by_id).to eq USER_ID + end + end + + # Task comments + expect(db_module.task_comments.count).to eq( + my_module['task_comments'].count + ) + my_module['task_comments'].each do |json_comment| + db_comment = db_module.task_comments.find_by( + message: json_comment['message'] + ) + + expect(db_comment.created_at).to eq( + json_comment['created_at'].to_time + ) + expect(db_comment.updated_at).to eq( + json_comment['updated_at'].to_time + ) + expect(db_comment.last_modified_by_id).to eq USER_ID + end + # + # User assigns to the the module + unless my_module['user_my_modules'].empty? + expect(db_module.user_my_modules.first.user_id).to eq USER_ID + end + end + end + end + end + end +end diff --git a/spec/services/model_importers/test_experiment_data/assets/1/samples.txt b/spec/services/model_importers/test_experiment_data/assets/1/samples.txt new file mode 100644 index 000000000..ea92e297a --- /dev/null +++ b/spec/services/model_importers/test_experiment_data/assets/1/samples.txt @@ -0,0 +1,4 @@ +Sample name Sample type +sample6 sample +sample7 sample +sample8 sample diff --git a/spec/services/model_importers/test_experiment_data/assets/21/100gen_line_bending.png b/spec/services/model_importers/test_experiment_data/assets/21/100gen_line_bending.png new file mode 100644 index 000000000..7f34f776f Binary files /dev/null and b/spec/services/model_importers/test_experiment_data/assets/21/100gen_line_bending.png differ diff --git a/spec/services/model_importers/test_experiment_data/assets/8/G2938-90034_KitRNA6000Nano_ebook.pdf b/spec/services/model_importers/test_experiment_data/assets/8/G2938-90034_KitRNA6000Nano_ebook.pdf new file mode 100644 index 000000000..5ed7248a5 Binary files /dev/null and b/spec/services/model_importers/test_experiment_data/assets/8/G2938-90034_KitRNA6000Nano_ebook.pdf differ diff --git a/spec/services/model_importers/test_experiment_data/assets/9/Bioanalyser_result.JPG b/spec/services/model_importers/test_experiment_data/assets/9/Bioanalyser_result.JPG new file mode 100644 index 000000000..63a42ac59 Binary files /dev/null and b/spec/services/model_importers/test_experiment_data/assets/9/Bioanalyser_result.JPG differ diff --git a/spec/services/model_importers/test_experiment_data/experiment.json b/spec/services/model_importers/test_experiment_data/experiment.json new file mode 100644 index 000000000..1f85e2c00 --- /dev/null +++ b/spec/services/model_importers/test_experiment_data/experiment.json @@ -0,0 +1,1170 @@ +{ + "experiment": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "created_at": "2019-01-21T13:27:53.342Z", + "created_by_id": 1, + "description": "My description", + "id": 3, + "last_modified_by_id": 1, + "name": "Experiment export", + "project_id": 1, + "restored_by_id": null, + "restored_on": null, + "updated_at": "2019-01-21T13:32:46.473Z", + "workflowimg_content_type": "image/png", + "workflowimg_file_name": "wimg20190121-1-111oj8z.png", + "workflowimg_file_size": 4581, + "workflowimg_updated_at": "2019-01-21T13:31:04.682Z" + }, + "my_module_groups": [ + { + "created_at": "2019-01-21T13:32:46.449Z", + "created_by_id": 1, + "experiment_id": 3, + "id": 13, + "updated_at": "2019-01-21T13:32:46.449Z" + }, + { + "created_at": "2019-01-21T13:32:46.460Z", + "created_by_id": 1, + "experiment_id": 3, + "id": 14, + "updated_at": "2019-01-21T13:32:46.460Z" + } + ], + "my_modules": [ + { + "my_module": { + "archived": true, + "archived_by_id": 1, + "archived_on": "2019-01-21T13:32:46.000Z", + "completed_on": null, + "created_at": "2019-01-17T09:42:11.511Z", + "created_by_id": 1, + "description": null, + "due_date": "2019-01-28T12:44:56.555Z", + "experiment_id": 3, + "id": 9, + "last_modified_by_id": 1, + "my_module_group_id": null, + "name": "Data analysis - Pfaffl method", + "nr_of_assigned_samples": 0, + "restored_by_id": 1, + "restored_on": "2019-01-21T13:31:37.000Z", + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.278Z", + "workflow_order": -1, + "x": 64, + "y": 46 + }, + "my_module_repository_rows": [], + "my_module_tags": [], + "outputs": [], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T12:44:56.564Z", + "description": null, + "id": 9, + "my_module_id": 9, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T13:31:55.946Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [], + "checklists": [], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-21T13:31:55.921Z", + "description": "", + "id": 21, + "last_modified_by_id": 1, + "name": "Temp step", + "position": 0, + "protocol_id": 9, + "updated_at": "2019-01-21T13:31:55.921Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [], + "step_tables": [], + "tables": [] + } + ] + } + ], + "results": [], + "task_comments": [], + "user_my_modules": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-17T09:42:54.874Z", + "id": 9, + "my_module_id": 9, + "updated_at": "2019-01-21T12:44:56.591Z", + "user_id": 1 + } + ] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-21T13:01:34.508Z", + "created_by_id": 1, + "description": null, + "due_date": null, + "experiment_id": 3, + "id": 12, + "last_modified_by_id": 1, + "my_module_group_id": null, + "name": "Lonely task", + "nr_of_assigned_samples": 0, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:28:43.805Z", + "workflow_order": -1, + "x": 64, + "y": 46 + }, + "my_module_repository_rows": [], + "my_module_tags": [], + "outputs": [], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T13:01:34.544Z", + "description": null, + "id": 12, + "my_module_id": 12, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T13:04:30.641Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [], + "checklists": [ + { + "checklist": { + "created_at": "2019-01-21T13:04:30.562Z", + "created_by_id": null, + "id": 2, + "last_modified_by_id": null, + "name": "Guide", + "step_id": 18, + "updated_at": "2019-01-21T13:04:30.562Z" + }, + "checklist_items": [ + { + "checked": false, + "checklist_id": 2, + "created_at": "2019-01-21T13:04:30.565Z", + "created_by_id": null, + "id": 8, + "last_modified_by_id": null, + "position": 0, + "text": "Wolf", + "updated_at": "2019-01-21T13:04:30.565Z" + }, + { + "checked": true, + "checklist_id": 2, + "created_at": "2019-01-21T13:04:30.568Z", + "created_by_id": null, + "id": 9, + "last_modified_by_id": null, + "position": 1, + "text": "Cat", + "updated_at": "2019-01-21T13:04:32.708Z" + }, + { + "checked": false, + "checklist_id": 2, + "created_at": "2019-01-21T13:04:30.570Z", + "created_by_id": null, + "id": 10, + "last_modified_by_id": null, + "position": 2, + "text": "Bull", + "updated_at": "2019-01-21T13:04:30.570Z" + } + ] + } + ], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-21T13:04:30.558Z", + "description": "", + "id": 18, + "last_modified_by_id": 1, + "name": "Lonelier step", + "position": 0, + "protocol_id": 12, + "updated_at": "2019-01-21T13:04:30.564Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [], + "step_tables": [], + "tables": [] + } + ] + } + ], + "results": [], + "task_comments": [], + "user_my_modules": [] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-21T13:01:34.493Z", + "created_by_id": 1, + "description": null, + "due_date": null, + "experiment_id": 3, + "id": 11, + "last_modified_by_id": 1, + "my_module_group_id": 13, + "name": "Workflow 2 - task 2", + "nr_of_assigned_samples": 0, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.452Z", + "workflow_order": 1, + "x": 0, + "y": 46 + }, + "my_module_repository_rows": [], + "my_module_tags": [], + "outputs": [], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T13:01:34.502Z", + "description": null, + "id": 11, + "my_module_id": 11, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T13:09:36.370Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [ + { + "created_at": "2019-01-21T13:09:35.615Z", + "created_by_id": 1, + "estimated_size": 17799, + "file_content_type": "image/png", + "file_file_name": "100gen_line_bending.png", + "file_file_size": 16181, + "file_present": true, + "file_processing": false, + "file_updated_at": "2019-01-21T13:10:05.392Z", + "id": 21, + "last_modified_by_id": null, + "lock": null, + "lock_ttl": null, + "team_id": 1, + "updated_at": "2019-01-21T13:10:06.290Z", + "version": 1 + } + ], + "checklists": [], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-21T13:09:35.466Z", + "description": "", + "id": 20, + "last_modified_by_id": 1, + "name": "Workflow 2 step", + "position": 0, + "protocol_id": 11, + "updated_at": "2019-01-21T13:10:06.292Z", + "user_id": 1 + }, + "step_assets": [ + { + "asset_id": 21, + "id": 9, + "step_id": 20 + } + ], + "step_comments": [], + "step_tables": [], + "tables": [] + } + ] + } + ], + "results": [], + "task_comments": [], + "user_my_modules": [] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-21T13:01:34.470Z", + "created_by_id": 1, + "description": null, + "due_date": null, + "experiment_id": 3, + "id": 10, + "last_modified_by_id": 1, + "my_module_group_id": 13, + "name": "Workflow 2 - task 1", + "nr_of_assigned_samples": 0, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.456Z", + "workflow_order": 0, + "x": 0, + "y": 29 + }, + "my_module_repository_rows": [], + "my_module_tags": [], + "outputs": [ + { + "id": 34, + "input_id": 11, + "output_id": 10 + } + ], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T13:01:34.485Z", + "description": null, + "id": 10, + "my_module_id": 10, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T13:05:20.594Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [], + "checklists": [], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-21T13:05:20.547Z", + "description": "", + "id": 19, + "last_modified_by_id": 1, + "name": "USB step", + "position": 0, + "protocol_id": 10, + "updated_at": "2019-01-21T13:05:36.010Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [ + { + "associated_id": 19, + "created_at": "2019-01-21T13:05:36.007Z", + "id": 10, + "last_modified_by_id": null, + "message": "Comment 12", + "updated_at": "2019-01-21T13:05:36.007Z", + "user_id": 1 + } + ], + "step_tables": [ + { + "id": 2, + "step_id": 19, + "table_id": 5 + } + ], + "tables": [ + { + "contents": "eyJkYXRhIjpbWyIxIixudWxsLG51bGwsbnVsbCxudWxsXSxbbnVsbCwiMiIs\nbnVsbCxudWxsLG51bGxdLFtudWxsLG51bGwsIjMiLG51bGwsbnVsbF0sW251\nbGwsbnVsbCxudWxsLCI0IixudWxsXSxbbnVsbCxudWxsLG51bGwsbnVsbCwi\nNSJdXX0=\n", + "created_at": "2019-01-21T13:05:20.550Z", + "created_by_id": 1, + "data_vector": "JzEnOjEgJzInOjcgJzMnOjEzICc0JzoxOSAnNSc6MjUgJ251bGwnOjIsMyw0\nLDUsNiw4LDksMTAsMTEsMTIsMTQsMTUsMTYsMTcsMTgsMjAsMjEsMjIsMjMs\nMjQ=\n", + "id": 5, + "last_modified_by_id": null, + "name": "USB table", + "team_id": 1, + "updated_at": "2019-01-21T13:05:20.550Z" + } + ] + } + ] + } + ], + "results": [], + "task_comments": [], + "user_my_modules": [] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-19T07:51:02.803Z", + "created_by_id": 1, + "description": null, + "due_date": "2019-01-28T12:44:56.002Z", + "experiment_id": 3, + "id": 1, + "last_modified_by_id": null, + "my_module_group_id": 14, + "name": "Experiment design", + "nr_of_assigned_samples": 0, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.464Z", + "workflow_order": 2, + "x": 0, + "y": 0 + }, + "my_module_repository_rows": [], + "my_module_tags": [ + { + "created_by_id": null, + "id": 1, + "my_module_id": 1, + "tag_id": 1 + } + ], + "outputs": [ + { + "id": 32, + "input_id": 4, + "output_id": 1 + } + ], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T12:44:56.065Z", + "description": null, + "id": 1, + "my_module_id": 1, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T12:44:56.065Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [], + "checklists": [], + "step": { + "completed": true, + "completed_on": "2019-01-19T10:56:48.558Z", + "created_at": "2019-01-19T08:52:33.490Z", + "description": "Compare response of PVYNTN, cab4 and PR1 genes in mock/virus inoculated potatoes & in time", + "id": 1, + "last_modified_by_id": null, + "name": "Gene expression", + "position": 0, + "protocol_id": 1, + "updated_at": "2019-01-21T12:59:03.007Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [ + { + "associated_id": 1, + "created_at": "2019-01-21T12:59:03.005Z", + "id": 9, + "last_modified_by_id": null, + "message": "Very nice job!", + "updated_at": "2019-01-21T12:59:03.005Z", + "user_id": 1 + } + ], + "step_tables": [], + "tables": [] + } + ] + } + ], + "results": [ + { + "asset": null, + "result": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "created_at": "2019-01-20T00:57:52.635Z", + "id": 1, + "last_modified_by_id": null, + "my_module_id": 1, + "name": "Experimental design", + "restored_by_id": null, + "restored_on": null, + "updated_at": "2019-01-21T12:44:58.101Z", + "user_id": 1 + }, + "result_comments": [], + "result_text": null, + "table": { + "contents": "eyJkYXRhIjpbWyIiLCIiLCIiLG51bGwsbnVsbF0sWyIiLCIiLCIiLG51bGws\nbnVsbF0sWyJncm91cC90aW1lIiwiMSBkcGkiLCI2IGRwaSIsIiIsIiJdLFsi\nUFZZTlROIiwiMSIsIjEiLCIiLCIiXSxbIm1vY2siLCIxIiwiMSIsIiIsIiJd\nLFtudWxsLG51bGwsbnVsbCxudWxsLG51bGxdXX0=\n", + "created_at": "2019-01-21T12:44:58.086Z", + "created_by_id": 1, + "data_vector": "JzEnOjYsMTEsMTIsMTQsMTUgJzYnOjggJ2RwaSc6Nyw5ICdncm91cC90aW1l\nJzo1ICdtb2NrJzoxMyAnbnVsbCc6MSwyLDMsNCwxNiwxNywxOCwxOSwyMCAn\ncHZ5bnRuJzoxMA==\n", + "id": 1, + "last_modified_by_id": null, + "name": "", + "team_id": 1, + "updated_at": "2019-01-21T12:44:58.093Z" + } + }, + { + "asset": { + "created_at": "2019-01-21T12:45:29.138Z", + "created_by_id": 1, + "estimated_size": 232, + "file_content_type": "text/plain", + "file_file_name": "samples.txt", + "file_file_size": 69, + "file_present": true, + "file_processing": false, + "file_updated_at": "2019-01-21T12:45:28.889Z", + "id": 1, + "last_modified_by_id": 1, + "lock": null, + "lock_ttl": null, + "team_id": 1, + "updated_at": "2019-01-21T13:06:01.610Z", + "version": 1 + }, + "result": { + "archived": true, + "archived_by_id": 1, + "archived_on": "2019-01-21T13:15:54.000Z", + "created_at": "2019-01-20T18:09:01.268Z", + "id": 6, + "last_modified_by_id": 1, + "my_module_id": 1, + "name": "sF", + "restored_by_id": null, + "restored_on": null, + "updated_at": "2019-01-21T13:15:54.487Z", + "user_id": 1 + }, + "result_comments": [], + "result_text": null, + "table": {} + } + ], + "task_comments": [ + { + "associated_id": 1, + "created_at": "2019-01-20T01:27:40.727Z", + "id": 2, + "last_modified_by_id": null, + "message": "We should have a meeting to discuss sampling parametrs soon.", + "updated_at": "2019-01-21T12:44:57.607Z", + "user_id": 1 + }, + { + "associated_id": 1, + "created_at": "2019-01-19T11:47:29.757Z", + "id": 3, + "last_modified_by_id": null, + "message": "I agree.", + "updated_at": "2019-01-21T12:44:57.620Z", + "user_id": 1 + } + ], + "user_my_modules": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-19T07:52:25.851Z", + "id": 1, + "my_module_id": 1, + "updated_at": "2019-01-21T12:44:56.118Z", + "user_id": 1 + } + ] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-17T08:25:30.434Z", + "created_by_id": 1, + "description": null, + "due_date": "2019-03-25T12:44:56.326Z", + "experiment_id": 3, + "id": 5, + "last_modified_by_id": null, + "my_module_group_id": 14, + "name": "Reverse transcription", + "nr_of_assigned_samples": 4, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.467Z", + "workflow_order": 4, + "x": 33, + "y": 16 + }, + "my_module_repository_rows": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.146Z", + "id": 16, + "my_module_id": 5, + "repository_row_id": 1, + "updated_at": "2019-01-21T12:44:57.146Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.169Z", + "id": 17, + "my_module_id": 5, + "repository_row_id": 2, + "updated_at": "2019-01-21T12:44:57.169Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.184Z", + "id": 18, + "my_module_id": 5, + "repository_row_id": 3, + "updated_at": "2019-01-21T12:44:57.184Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.201Z", + "id": 19, + "my_module_id": 5, + "repository_row_id": 4, + "updated_at": "2019-01-21T12:44:57.201Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.217Z", + "id": 20, + "my_module_id": 5, + "repository_row_id": 5, + "updated_at": "2019-01-21T12:44:57.217Z" + } + ], + "my_module_tags": [ + { + "created_by_id": null, + "id": 5, + "my_module_id": 5, + "tag_id": 2 + } + ], + "outputs": [], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T12:44:56.334Z", + "description": null, + "id": 5, + "my_module_id": 5, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T12:57:52.015Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [], + "checklists": [ + { + "checklist": { + "created_at": "2019-01-21T12:44:58.537Z", + "created_by_id": null, + "id": 1, + "last_modified_by_id": null, + "name": "Mastermix", + "step_id": 9, + "updated_at": "2019-01-21T12:44:58.537Z" + }, + "checklist_items": [ + { + "checked": true, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.540Z", + "created_by_id": null, + "id": 1, + "last_modified_by_id": null, + "position": null, + "text": "RT buffer", + "updated_at": "2019-01-21T12:50:40.690Z" + }, + { + "checked": true, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.542Z", + "created_by_id": null, + "id": 2, + "last_modified_by_id": null, + "position": null, + "text": "dNTP mix", + "updated_at": "2019-01-21T12:50:41.287Z" + }, + { + "checked": false, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.543Z", + "created_by_id": null, + "id": 3, + "last_modified_by_id": null, + "position": null, + "text": "Random Primers", + "updated_at": "2019-01-21T12:44:58.543Z" + }, + { + "checked": false, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.545Z", + "created_by_id": null, + "id": 4, + "last_modified_by_id": null, + "position": null, + "text": "RNase inhibitor", + "updated_at": "2019-01-21T12:44:58.545Z" + }, + { + "checked": false, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.547Z", + "created_by_id": null, + "id": 5, + "last_modified_by_id": null, + "position": null, + "text": "Reverse transcriptase", + "updated_at": "2019-01-21T12:44:58.547Z" + }, + { + "checked": false, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.548Z", + "created_by_id": null, + "id": 6, + "last_modified_by_id": null, + "position": null, + "text": "Optional: Luciferase mRNA (denatured)", + "updated_at": "2019-01-21T12:44:58.548Z" + }, + { + "checked": false, + "checklist_id": 1, + "created_at": "2019-01-21T12:44:58.550Z", + "created_by_id": null, + "id": 7, + "last_modified_by_id": null, + "position": null, + "text": "H2O to 12.5 ul", + "updated_at": "2019-01-21T12:44:58.550Z" + } + ] + } + ], + "step": { + "completed": true, + "completed_on": "2019-01-17T13:52:32.426Z", + "created_at": "2019-01-17T13:15:20.691Z", + "description": "High Capacity cDNA Reverse Transcription Kit (Applied Biosystems)", + "id": 9, + "last_modified_by_id": null, + "name": "Prepare mastermix for RT", + "position": 1, + "protocol_id": 5, + "updated_at": "2019-01-21T12:44:58.539Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [], + "step_tables": [], + "tables": [] + }, + { + "assets": [], + "checklists": [], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-17T12:58:06.802Z", + "description": "25\u00b0C for 10 min 37\u00b0C for 2 h", + "id": 10, + "last_modified_by_id": null, + "name": "RT reaction", + "position": 2, + "protocol_id": 5, + "updated_at": "2019-01-21T12:44:58.483Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [], + "step_tables": [], + "tables": [] + }, + { + "assets": [], + "checklists": [], + "step": { + "completed": true, + "completed_on": "2019-01-17T17:37:06.427Z", + "created_at": "2019-01-17T09:35:43.770Z", + "description": "\n
1 ug of RNA denature at 80\u00b0C for 5 min --> ice
", + "id": 8, + "last_modified_by_id": 1, + "name": "RNA denaturation", + "position": 0, + "protocol_id": 5, + "updated_at": "2019-01-21T12:57:51.929Z", + "user_id": 1 + }, + "step_assets": [], + "step_comments": [], + "step_tables": [ + { + "id": 1, + "step_id": 8, + "table_id": 4 + } + ], + "tables": [ + { + "contents": "eyJkYXRhIjpbWyIxMSIsIjIyIiwiMzMiLCI0NCIsIjU1Il0sW251bGwsbnVs\nbCxudWxsLG51bGwsbnVsbF0sW251bGwsbnVsbCxudWxsLG51bGwsbnVsbF0s\nW251bGwsbnVsbCxudWxsLG51bGwsbnVsbF0sW251bGwsbnVsbCxudWxsLG51\nbGwsIjY3OCJdXX0=\n", + "created_at": "2019-01-21T12:57:51.923Z", + "created_by_id": 1, + "data_vector": "JzExJzoxICcyMic6MiAnMzMnOjMgJzQ0Jzo0ICc1NSc6NSAnNjc4JzoyNSAn\nbnVsbCc6Niw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwxOSwy\nMCwyMSwyMiwyMywyNA==\n", + "id": 4, + "last_modified_by_id": null, + "name": "", + "team_id": 1, + "updated_at": "2019-01-21T12:57:51.923Z" + } + ] + } + ] + } + ], + "results": [], + "task_comments": [ + { + "associated_id": 5, + "created_at": "2019-01-17T18:29:15.198Z", + "id": 6, + "last_modified_by_id": null, + "message": "Please show Steve the RT procedure.", + "updated_at": "2019-01-21T12:44:57.665Z", + "user_id": 1 + } + ], + "user_my_modules": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-17T08:27:25.032Z", + "id": 5, + "my_module_id": 5, + "updated_at": "2019-01-21T12:44:56.361Z", + "user_id": 1 + } + ] + }, + { + "my_module": { + "archived": false, + "archived_by_id": null, + "archived_on": null, + "completed_on": null, + "created_at": "2019-01-18T16:17:06.765Z", + "created_by_id": 1, + "description": null, + "due_date": "2019-03-11T12:44:56.265Z", + "experiment_id": 3, + "id": 4, + "last_modified_by_id": null, + "my_module_group_id": 14, + "name": "RNA quality & quantity - BIOANALYSER", + "nr_of_assigned_samples": 4, + "restored_by_id": null, + "restored_on": null, + "state": "uncompleted", + "updated_at": "2019-01-21T13:32:46.471Z", + "workflow_order": 3, + "x": 33, + "y": 0 + }, + "my_module_repository_rows": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.027Z", + "id": 11, + "my_module_id": 4, + "repository_row_id": 1, + "updated_at": "2019-01-21T12:44:57.027Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.047Z", + "id": 12, + "my_module_id": 4, + "repository_row_id": 2, + "updated_at": "2019-01-21T12:44:57.047Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.062Z", + "id": 13, + "my_module_id": 4, + "repository_row_id": 3, + "updated_at": "2019-01-21T12:44:57.062Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.078Z", + "id": 14, + "my_module_id": 4, + "repository_row_id": 4, + "updated_at": "2019-01-21T12:44:57.078Z" + }, + { + "assigned_by_id": 1, + "created_at": "2019-01-21T12:44:57.093Z", + "id": 15, + "my_module_id": 4, + "repository_row_id": 5, + "updated_at": "2019-01-21T12:44:57.093Z" + } + ], + "my_module_tags": [ + { + "created_by_id": null, + "id": 4, + "my_module_id": 4, + "tag_id": 2 + } + ], + "outputs": [ + { + "id": 33, + "input_id": 5, + "output_id": 4 + } + ], + "protocols": [ + { + "protocol": { + "added_by_id": null, + "archived_by_id": null, + "archived_on": null, + "authors": null, + "created_at": "2019-01-21T12:44:56.272Z", + "description": null, + "id": 4, + "my_module_id": 4, + "name": null, + "nr_of_linked_children": 0, + "parent_id": null, + "parent_updated_at": null, + "protocol_type": "unlinked", + "published_on": null, + "restored_by_id": null, + "restored_on": null, + "team_id": 1, + "updated_at": "2019-01-21T12:44:56.272Z" + }, + "protocol_protocol_keywords": [], + "steps": [ + { + "assets": [ + { + "created_at": "2019-01-21T12:45:32.296Z", + "created_by_id": 1, + "estimated_size": 1129370, + "file_content_type": "application/pdf", + "file_file_name": "G2938-90034_KitRNA6000Nano_ebook.pdf", + "file_file_size": 974068, + "file_present": true, + "file_processing": false, + "file_updated_at": "2019-01-21T12:45:32.237Z", + "id": 8, + "last_modified_by_id": 1, + "lock": null, + "lock_ttl": null, + "team_id": 1, + "updated_at": "2019-01-21T13:06:04.590Z", + "version": 1 + } + ], + "checklists": [], + "step": { + "completed": false, + "completed_on": null, + "created_at": "2019-01-18T16:27:28.209Z", + "description": null, + "id": 7, + "last_modified_by_id": null, + "name": "Use Nano chip for testing RNA integrity", + "position": 0, + "protocol_id": 4, + "updated_at": "2019-01-21T13:06:04.592Z", + "user_id": 1 + }, + "step_assets": [ + { + "asset_id": 8, + "id": 3, + "step_id": 7 + } + ], + "step_comments": [], + "step_tables": [], + "tables": [] + } + ] + } + ], + "results": [ + { + "asset": { + "created_at": "2019-01-21T12:45:32.847Z", + "created_by_id": 1, + "estimated_size": 46131, + "file_content_type": "image/jpeg", + "file_file_name": "Bioanalyser_result.JPG", + "file_file_size": 41938, + "file_present": true, + "file_processing": false, + "file_updated_at": "2019-01-21T12:45:43.655Z", + "id": 9, + "last_modified_by_id": 1, + "lock": null, + "lock_ttl": null, + "team_id": 1, + "updated_at": "2019-01-21T12:45:44.269Z", + "version": 1 + }, + "result": { + "archived": true, + "archived_by_id": 1, + "archived_on": "2019-01-21T13:16:01.000Z", + "created_at": "2019-01-20T13:55:26.927Z", + "id": 11, + "last_modified_by_id": 1, + "my_module_id": 4, + "name": "Result of RNA integrity", + "restored_by_id": null, + "restored_on": null, + "updated_at": "2019-01-21T13:16:01.326Z", + "user_id": 1 + }, + "result_comments": [], + "result_text": null, + "table": {} + } + ], + "task_comments": [], + "user_my_modules": [ + { + "assigned_by_id": 1, + "created_at": "2019-01-18T16:18:49.946Z", + "id": 4, + "my_module_id": 4, + "updated_at": "2019-01-21T12:44:56.301Z", + "user_id": 1 + } + ] + } + ] +}