From 5685709f48a06f7742ae9c21fa6dcc53b437d0e2 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 29 Apr 2019 08:44:09 +0200 Subject: [PATCH 01/73] Implement API client for protocols.io Closes SCI-3357 --- .../protocols_io/v3/api_client.rb | 67 ++++++++++ .../protocols_io/v3/api_client_spec.rb | 120 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 app/utilities/protocol_importers/protocols_io/v3/api_client.rb create mode 100644 spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb new file mode 100644 index 000000000..7bdfc6db5 --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb @@ -0,0 +1,67 @@ +module ProtocolImporters + module ProtocolsIO + module V3 + class ApiClient + include HTTParty + + base_uri 'https://www.protocols.io/api/v3/' + default_timeout 10 + + def initialize(token = nil) + # Currently we support public tokens only (no token needed for public data) + @auth = { token: token } + + # Set default headers + self.class.headers('Authorization': "Bearer #{@auth[:token]}") if @auth[:token].present? + end + + # Query params available are: + # filter (optional): {public|user_public|user_private|shared_with_user} + # Which type of protocols to filter. + # default is public and requires no auth token. + # user_public requires public token. + # user_private|shared_with_user require private auth token. + # key (optional): string + # Search key to search for in protocol name, description, authors. + # default: '' + # order_field (optional): {activity|date|name|id} + # order by this field. + # default is activity. + # order_dir (optional): {desc|asc} + # Direction of ordering. + # default is desc. + # page_size (optional): int + # Number of items per page. + # Default 10. + # page_id (optional): int (1..n) + # id of page. + # Default is 1. + def protocol_list(query_params = {}) + query = { + filter: :public, + key: '', + order_field: :activity, + order_dir: :desc, + page_size: 10, + page_id: 1 + }.merge!(query_params) + + self.class.get('/protocols', query: query) + end + + # Returns full representation of given protocol ID + def single_protocol(id) + self.class.get("/protocols/#{id}") + end + + # Returns html preview for given protocol + # This endpoint is outside the scope of API but is listed here for the + # sake of clarity + def protocol_html_preview(uri) + self.class.get("https://www.protocols.io/view/#{uri}.html", headers: {}) + end + end + + end + end +end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb new file mode 100644 index 000000000..0d17284e7 --- /dev/null +++ b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::ProtocolsIO::V3::ApiClient do + TOKEN = 'test_token' + + describe '#protocol_list' do + URL = 'https://www.protocols.io/api/v3/protocols' + + let(:stub_protocols) do + stub_request(:get, URL).with(query: hash_including({})) + end + + let(:default_query_params) do + { + filter: :public, + key: '', + order_dir: :desc, + order_field: :activity, + page_id: 1, + page_size: 10 + } + end + + it 'returns 200 on successfull call' do + stub_protocols.to_return(status: 200, body: '[]', headers: {}) + + expect(subject.protocol_list.code).to eq 200 + expect(stub_protocols).to have_been_requested + end + + it 'raises OpenTimeout error on timeout' do + stub_protocols.to_timeout + + expect { subject.protocol_list }.to raise_error(Net::OpenTimeout) + end + + it 'requests server with default query parameters if none are given' do + stub_request(:get, URL).with(query: default_query_params) + + subject.protocol_list + expect(WebMock).to have_requested(:get, URL).with(query: default_query_params) + end + + it 'requests server with given query parameters' do + query = { + filter: :user_public, + key: 'banana', + order_dir: :asc, + order_field: :date, + page_id: 2, + page_size: 15 + } + stub_request(:get, URL).with(query: query) + + subject.protocol_list(query) + expect(WebMock).to have_requested(:get, URL).with(query: query) + end + + it 'should send authorization token if provided on initialization' do + headers = { 'Authorization': "Bearer #{TOKEN}" } + stub_request(:get, URL).with(headers: headers, query: default_query_params) + + ProtocolImporters::ProtocolsIO::V3::ApiClient.new(TOKEN).protocol_list + expect(WebMock).to have_requested(:get, URL).with(headers: headers, query: default_query_params) + end + end + + describe '#single_protocol' do + PROTOCOL_ID = 15 + SINGLE_PROTOCOL_URL = "https://www.protocols.io/api/v3/protocols/#{PROTOCOL_ID}" + + let(:stub_single_protocol) do + stub_request(:get, SINGLE_PROTOCOL_URL) + end + + it 'returns 200 on successfull call' do + stub_single_protocol.to_return(status: 200, body: '[]', headers: {}) + + expect(subject.single_protocol(PROTOCOL_ID).code).to eq 200 + expect(stub_single_protocol).to have_been_requested + end + + it 'raises OpenTimeout error on timeout' do + stub_single_protocol.to_timeout + + expect { subject.single_protocol(PROTOCOL_ID) }.to raise_error(Net::OpenTimeout) + end + + it 'should send authorization token if provided on initialization' do + headers = { 'Authorization': "Bearer #{TOKEN}" } + stub_single_protocol.with(headers: headers) + + ProtocolImporters::ProtocolsIO::V3::ApiClient.new(TOKEN).single_protocol(PROTOCOL_ID) + expect(WebMock).to have_requested(:get, SINGLE_PROTOCOL_URL).with(headers: headers) + end + end + + describe '#protocol_html_preview' do + PROTOCOL_URI = 'Extracting-DNA-from-bananas-esvbee6' + + let(:stub_html_preview) do + stub_request(:get, "https://www.protocols.io/view/#{PROTOCOL_URI}.html") + end + + it 'returns 200 on successfull call' do + stub_html_preview.to_return(status: 200, body: '[]', headers: {}) + + expect(subject.protocol_html_preview(PROTOCOL_URI).code).to eq 200 + expect(stub_html_preview).to have_been_requested + end + + it 'raises OpenTimeout error on timeout' do + stub_html_preview.to_timeout + + expect { subject.protocol_html_preview(PROTOCOL_URI) }.to raise_error(Net::OpenTimeout) + end + end +end From a15b1f6dfaf5e059c24db729cc173db9ad2420d9 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 29 Apr 2019 08:58:43 +0200 Subject: [PATCH 02/73] Add logging for ApiClient and fix hound formatting --- .../protocol_importers/protocols_io/v3/api_client.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb index 7bdfc6db5..da0f92c77 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ProtocolImporters module ProtocolsIO module V3 @@ -6,6 +8,7 @@ module ProtocolImporters base_uri 'https://www.protocols.io/api/v3/' default_timeout 10 + logger Rails.logger, :debug def initialize(token = nil) # Currently we support public tokens only (no token needed for public data) @@ -61,7 +64,6 @@ module ProtocolImporters self.class.get("https://www.protocols.io/view/#{uri}.html", headers: {}) end end - end end end From c1cee07a638aaee03eff7df70a8f18aeb8aae414 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Tue, 30 Apr 2019 10:15:56 +0200 Subject: [PATCH 03/73] Extract API client constants to constants.rb --- .../protocols_io/v3/api_client.rb | 18 ++++++--------- config/initializers/constants.rb | 22 +++++++++++++++++++ .../protocols_io/v3/api_client_spec.rb | 14 ++++-------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb index da0f92c77..a4b907a93 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb @@ -6,9 +6,11 @@ module ProtocolImporters class ApiClient include HTTParty - base_uri 'https://www.protocols.io/api/v3/' - default_timeout 10 - logger Rails.logger, :debug + CONSTANTS = Constants::PROTOCOLS_IO_V3_API + + base_uri CONSTANTS[:base_uri] + default_timeout CONSTANTS[:default_timeout] + logger Rails.logger, CONSTANTS[:debug_level] def initialize(token = nil) # Currently we support public tokens only (no token needed for public data) @@ -40,14 +42,8 @@ module ProtocolImporters # id of page. # Default is 1. def protocol_list(query_params = {}) - query = { - filter: :public, - key: '', - order_field: :activity, - order_dir: :desc, - page_size: 10, - page_id: 1 - }.merge!(query_params) + query = CONSTANTS.dig(:endpoints, :protocols, :default_query_params) + .merge(query_params) self.class.get('/protocols', query: query) end diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 47da80f93..bbeffad44 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -189,6 +189,28 @@ class Constants # Default user picture avatar DEFAULT_AVATAR_URL = '/images/:style/missing.png'.freeze + #============================================================================= + # Protocol importers + #============================================================================= + + PROTOCOLS_IO_V3_API = { + base_uri: 'https://www.protocols.io/api/v3/', + default_timeout: 10, + debug_level: :debug, + endpoints: { + protocols: { + default_query_params: { + filter: :public, + key: '', + order_field: :activity, + order_dir: :desc, + page_size: 10, + page_id: 1 + } + } + } + }.freeze + #============================================================================= # Other #============================================================================= diff --git a/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb index 0d17284e7..1ece0aa23 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb @@ -3,24 +3,18 @@ require 'rails_helper' describe ProtocolImporters::ProtocolsIO::V3::ApiClient do + CONSTANTS = Constants::PROTOCOLS_IO_V3_API TOKEN = 'test_token' describe '#protocol_list' do - URL = 'https://www.protocols.io/api/v3/protocols' + URL = "#{CONSTANTS[:base_uri]}protocols" let(:stub_protocols) do stub_request(:get, URL).with(query: hash_including({})) end let(:default_query_params) do - { - filter: :public, - key: '', - order_dir: :desc, - order_field: :activity, - page_id: 1, - page_size: 10 - } + CONSTANTS.dig(:endpoints, :protocols, :default_query_params) end it 'returns 200 on successfull call' do @@ -69,7 +63,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ApiClient do describe '#single_protocol' do PROTOCOL_ID = 15 - SINGLE_PROTOCOL_URL = "https://www.protocols.io/api/v3/protocols/#{PROTOCOL_ID}" + SINGLE_PROTOCOL_URL = "#{CONSTANTS[:base_uri]}protocols/#{PROTOCOL_ID}" let(:stub_single_protocol) do stub_request(:get, SINGLE_PROTOCOL_URL) From ec49388283fafee36236fd3128ccf41a5935bef0 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Tue, 14 May 2019 15:09:37 +0200 Subject: [PATCH 04/73] Add protocol base normalizer class --- .../protocol_importers/protocol_normalizer.rb | 13 +++++++++++++ .../protocol_normalizer_spec.rb | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 app/utilities/protocol_importers/protocol_normalizer.rb create mode 100644 spec/utilities/protocol_importers/protocol_normalizer_spec.rb diff --git a/app/utilities/protocol_importers/protocol_normalizer.rb b/app/utilities/protocol_importers/protocol_normalizer.rb new file mode 100644 index 000000000..60d8c4610 --- /dev/null +++ b/app/utilities/protocol_importers/protocol_normalizer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ProtocolImporters + class ProtocolNormalizer + def load_all_protocols(_params: {}) + raise NotImplementedError + end + + def load_protocol(_id:, _params: {}) + raise NotImplementedError + end + end +end diff --git a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb new file mode 100644 index 000000000..54eed656e --- /dev/null +++ b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::ProtocolNormalizer do + describe '.load_all_protocols' do + it { expect { subject.load_all_protocols }.to raise_error(NotImplementedError) } + it { expect { subject.load_all_protocols(_params: 'some-params') }.to raise_error(NotImplementedError) } + end + + describe '.load_protocol' do + it { expect { subject.load_protocol(_id: 'random-id') }.to raise_error(NotImplementedError) } + it { expect { subject.load_protocol(_id: 'random-id', _params: 'someparams') }.to raise_error(NotImplementedError) } + end +end From aadf2eecb48fc318b938811105bfd770e0a7c65d Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Thu, 16 May 2019 15:55:04 +0200 Subject: [PATCH 05/73] Basic data normalizer for ProtocolIO --- .../protocols_io/v3/protocol_normalizer.rb | 55 + .../protocols_io/v3/step_components.rb | 30 + config/initializers/constants.rb | 3 +- .../v3/normalized_single_protocol.json | 411 ++ .../protocols_io/v3/single_protocol.json | 4876 +++++++++++++++++ .../v3/protocol_normalizer_spec.rb | 45 + 6 files changed, 5419 insertions(+), 1 deletion(-) create mode 100644 app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb create mode 100644 app/utilities/protocol_importers/protocols_io/v3/step_components.rb create mode 100644 spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json create mode 100644 spec/fixtures/files/protocols_io/v3/single_protocol.json create mode 100644 spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb new file mode 100644 index 000000000..fd4c329f0 --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + class ProtocolNormalizer < ProtocolImporters::ProtocolNormalizer + def load_protocol(id:) + response = ProtocolImporters::ProtocolsIO::V3::ApiClient.new.single_protocol(id) + + normalized_hash = normalize(response) + normalized_hash + end + + private + + def normalize(response) + protocol_hash = response.parsed_response.with_indifferent_access[:protocol] + normalized_data = {} + normalized_data[:uri] = response.request.last_uri.to_s + normalized_data[:source] = Constants::PROTOCOLS_IO_V3_API[:source_id] + normalized_data[:doi] = protocol_hash[:doi] + normalized_data[:published_on] = protocol_hash[:published_on] + normalized_data[:version] = protocol_hash[:version_id] + normalized_data[:source_id] = protocol_hash[:id] + normalized_data[:name] = protocol_hash[:title] + normalized_data[:description] = protocol_hash[:description] + normalized_data[:authors] = protocol_hash[:authors].map { |e| e[:name] }.join(', ') + + normalized_data[:steps] = protocol_hash[:steps].map do |e| + { + source_id: e[:id], + name: StepComponents.name(e[:components]), + description: StepComponents.description(e[:components]), + position: e[:previous_id].nil? ? 0 : nil + } + end + + # set positions + first_step_id = normalized_data[:steps].find { |s| s[:position].zero? }[:source_id] + next_step_id = protocol_hash[:steps].find { |s| s[:previous_id] == first_step_id }.try(:[], :id) + steps = normalized_data[:steps].map { |s| [s[:source_id], s] }.to_h + original_order = protocol_hash[:steps].map { |m| [m[:previous_id], m[:id]] }.to_h + current_position = 0 + while next_step_id + current_position += 1 + steps[next_step_id][:position] = current_position + next_step_id = original_order[next_step_id] + end + + { protocol: normalized_data } + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/step_components.rb b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb new file mode 100644 index 000000000..08c23d9bd --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + class StepComponents + AVAILABLE_COMPONENTS = { + 6 => :title, + 1 => :description + }.freeze + + def self.get_component(id, components) + if AVAILABLE_COMPONENTS.include?(id) + components.find { |o| o[:type_id] == id } + else + raise ArgumentError + end + end + + def self.name(components) + get_component(6, components)[:source][:title] + end + + def self.description(components) + get_component(1, components)[:source][:description] + end + end + end + end +end diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index bbeffad44..2b3c5caea 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -208,7 +208,8 @@ class Constants page_id: 1 } } - } + }, + source_id: 'protocolsio/v3' }.freeze #============================================================================= diff --git a/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json b/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json new file mode 100644 index 000000000..0624f84a2 --- /dev/null +++ b/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json @@ -0,0 +1,411 @@ +{ + "protocol": { + "uri": "https://www.protocols.io/api/v3/protocols/9451", + "source": "protocolsio/v3", + "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", + "published_on": 1516132805, + "version": 0, + "source_id": 9451, + "name": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", + "authors": "Peter J. Skene, Steven Henikoff", + "steps": [ + { + "source_id": 601564, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 0 + }, + { + "source_id": 601565, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 1 + }, + { + "source_id": 601566, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eResuspend in 1.5 ml room temperature Wash buffer by gently pipetting and transfer if necessary to a 2 ml tube. (wash 1/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 2 + }, + { + "source_id": 601567, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 1/2)\u003c/div\u003e\u003c/div\u003e", + "position": 3 + }, + { + "source_id": 601568, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAgain, resuspend in 1.5 ml room temperature Wash buffer by gently pipetting. Centrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 2/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 4 + }, + { + "source_id": 601569, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eResuspend in 1 ml room temperature Wash buffer by gently pipetting.\u003c/div\u003e\u003c/div\u003e", + "position": 5 + }, + { + "source_id": 601570, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhile gently vortexing the cells at room temperature, add the bead slurry.\u003c/div\u003e\u003c/div\u003e", + "position": 6 + }, + { + "source_id": 601571, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRotate 5-10 min at room temperature.\u003c/div\u003e\u003c/div\u003e", + "position": 7 + }, + { + "source_id": 601572, + "name": "Binding cells to beads", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDivide into aliquots in 1.5-ml tubes, one for each antibody to be used.\u003c/div\u003e\u003c/div\u003e", + "position": 8 + }, + { + "source_id": 601573, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 9 + }, + { + "source_id": 601574, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Antibody buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 10 + }, + { + "source_id": 601575, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4°C for ~2 hr (or at room temperature for 5-10 min)\u003c/div\u003e\u003c/div\u003e", + "position": 11 + }, + { + "source_id": 601576, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", + "position": 12 + }, + { + "source_id": 601577, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear (~30 s) and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 13 + }, + { + "source_id": 601578, + "name": "Bind (primary) antibodies", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting using a 1 ml tip if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 14 + }, + { + "source_id": 601579, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 15 + }, + { + "source_id": 601580, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 16 + }, + { + "source_id": 601581, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the secondary antibody to a final concentration of 1:100 or to the manufacturer’s recommended concentration for immunofluorescence.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 17 + }, + { + "source_id": 601582, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e", + "position": 18 + }, + { + "source_id": 601583, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", + "position": 19 + }, + { + "source_id": 601584, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 20 + }, + { + "source_id": 601585, + "name": "Bind secondary antibody (as required)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-Wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 21 + }, + { + "source_id": 601586, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 22 + }, + { + "source_id": 601587, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 23 + }, + { + "source_id": 601588, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the pA-MNase to a final concentration of ~700 ng/ml (e.g. 2.5 μL/50 μL of a 1:10 dilution of the 140 μg/ml glycerol stock provided upon request).\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 24 + }, + { + "source_id": 601589, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e", + "position": 25 + }, + { + "source_id": 601590, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", + "position": 26 + }, + { + "source_id": 601591, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 27 + }, + { + "source_id": 601592, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 28 + }, + { + "source_id": 601593, + "name": "Bind Protein A-MNase fusion protein", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRepeat Dig-wash steps 28-29.\u003c/div\u003e\u003c/div\u003e", + "position": 29 + }, + { + "source_id": 601594, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", + "position": 30 + }, + { + "source_id": 601595, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and add 100 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 31 + }, + { + "source_id": 601596, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eInsert tubes into the 1.5 ml wells of a heater block sitting in wet ice to chill down to 0 °C.\u003c/div\u003e\u003c/div\u003e", + "position": 32 + }, + { + "source_id": 601597, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eRemove each tube from the block, mix in 2 μL 100 mM CaCl\u003c/span\u003e\u003cspan style = \":justify;vertical-align:sub;\"\u003e2\u003c/span\u003e\u003cspan style = \":justify;\"\u003e (diluted 1:10 from a 1 M stock) with gentle vortexing and immediately replace the tube in the 0 °C block.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 33 + }, + { + "source_id": 601598, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate at 0 °C for the desired digestion time (default is 30 min).\u003c/div\u003e\u003c/div\u003e", + "position": 34 + }, + { + "source_id": 601599, + "name": "Targeted digestion", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 100 μL 2XSTOP and mix by gentle vortexing. When there are multiple time points, remove 100 μL to 100 μL 2XSTOP and mix by gentle vortexing.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 35 + }, + { + "source_id": 601600, + "name": "Target chromatin release", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate 10 min 37 °C to release CUT\u0026RUN fragments from the insoluble nuclear chromatin.\u003c/div\u003e\u003c/div\u003e", + "position": 36 + }, + { + "source_id": 601601, + "name": "Target chromatin release", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 5 min at 4 °C and 16,000 x g and place on magnet stand.\u003c/div\u003e\u003c/div\u003e", + "position": 37 + }, + { + "source_id": 601602, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace a spin column into a collection tube and add 400 μL Buffer NT1 (from NucleoSpin kit or equivalent).\u003c/div\u003e\u003c/div\u003e", + "position": 38 + }, + { + "source_id": 601603, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eDecant the supernatant cleanly from the pellet and transfer to the NT1 in the spin column pipetting gently up and down to mix.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 39 + }, + { + "source_id": 601604, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e", + "position": 40 + }, + { + "source_id": 601605, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e", + "position": 41 + }, + { + "source_id": 601606, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through and replace in rotor.\u003c/div\u003e\u003c/div\u003e", + "position": 42 + }, + { + "source_id": 601607, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge for 1 min at 11,000 x g. Let dry 5 min.\u003c/div\u003e\u003c/div\u003e", + "position": 43 + }, + { + "source_id": 601608, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace in a fresh tube and add 20-40 μL Buffer NE to membrane.\u003c/div\u003e\u003c/div\u003e", + "position": 44 + }, + { + "source_id": 601609, + "name": "Option A: Fast DNA extraction by spin column", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAfter 1 min, centrifuge for 1 min at 11,000 x g.\u003c/div\u003e\u003c/div\u003e", + "position": 45 + }, + { + "source_id": 601610, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDecant the supernatant cleanly from the pellet and transfer to a fresh 1.5-ml microcentrifuge tube.\u003c/div\u003e\u003c/div\u003e", + "position": 46 + }, + { + "source_id": 601611, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eTo each sample add 2 μL 10% SDS (to 0.1%), and 2.5 μL Proteinase K (20 mg/ml). Mix by inversion and incubate 10 min 70 °C.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 47 + }, + { + "source_id": 601612, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL PCI and mix by full-speed vortexing ~2 s.\u003c/div\u003e\u003c/div\u003e", + "position": 48 + }, + { + "source_id": 601613, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eTransfer to a phase-lock tube (e.g., Qiagen MaXtract), and centrifuge 5 min room temperature at 16,000 x g.\u003c/div\u003e\u003c/div\u003e", + "position": 49 + }, + { + "source_id": 601614, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL chloroform and invert ~10x to mix.\u003c/div\u003e\u003c/div\u003e", + "position": 50 + }, + { + "source_id": 601615, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid by pipetting to a fresh tube containing 2 μL 2 mg/ml glycogen.\u003c/div\u003e\u003c/div\u003e", + "position": 51 + }, + { + "source_id": 601616, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 750 μL 100% ethanol and mix by vortexing or tube inversion.\u003c/div\u003e\u003c/div\u003e", + "position": 52 + }, + { + "source_id": 601617, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eChill on ice and centrifuge 10 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e", + "position": 53 + }, + { + "source_id": 601618, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePour off the liquid and drain on a paper towel.\u003c/div\u003e\u003c/div\u003e", + "position": 54 + }, + { + "source_id": 601619, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRinse the pellet in 1 ml 100% ethanol and centrifuge 1 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e", + "position": 55 + }, + { + "source_id": 601620, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCarefully pour off the liquid and drain on a paper towel. Air dry.\u003c/div\u003e\u003c/div\u003e", + "position": 56 + }, + { + "source_id": 601621, + "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhen the pellet is dry, dissolve in 25-50 μL 1 mM Tris-HCl pH8 0.1 mM EDTA.\u003c/div\u003e\u003c/div\u003e", + "position": 57 + }, + { + "source_id": 601622, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eOptional: Quantify 1-2 μL, for example using fluorescence detection with a Qubit instrument.\u003c/div\u003e\u003c/div\u003e", + "position": 58 + }, + { + "source_id": 601623, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eOptional: Evaluate the presence of cleaved fragments and the size distribution by capillary electrophoresis with fluorescence detection, for example using a Tapestation instrument.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 59 + }, + { + "source_id": 601624, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePrepare barcoded libraries for Illumina sequencing with Tru-Seq adapters using a single-tube protocol, following the manufacturer’s instructions. Rapid PCR cycles favor exponential amplification of the desired CUT\u0026RUN fragments over linear amplification of large DNA fragments that are too long for polymerase to complete.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 60 + }, + { + "source_id": 601625, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eQuantify library yield using dsDNA-specific assay, such as Qubit.\u003c/div\u003e\u003c/div\u003e", + "position": 61 + }, + { + "source_id": 601626, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDetermine the size distribution of libraries by Agilent 4200 TapeStation analysis.\u003c/div\u003e\u003c/div\u003e", + "position": 62 + }, + { + "source_id": 601627, + "name": "Library preparation and sequencing", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePerform paired-end Illumina sequencing on the barcoded libraries following the manufacturer’s instructions.\u003c/div\u003e\u003c/div\u003e", + "position": 63 + }, + { + "source_id": 601628, + "name": "Data processing and analysis", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eWe align paired-end reads using Bowtie2 version 2.2.5 with options: --local --very-sensitive- local --no-unal --no-mixed --no-discordant --phred33 -I 10 -X 700. For mapping spike-in fragments, we also use the --no-overlap --no-dovetail options to avoid cross-mapping of the experimental genome to that of the spike-in DNA.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 64 + }, + { + "source_id": 601629, + "name": "Data processing and analysis", + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "position": 65 + } + ] + } +} diff --git a/spec/fixtures/files/protocols_io/v3/single_protocol.json b/spec/fixtures/files/protocols_io/v3/single_protocol.json new file mode 100644 index 000000000..ba2d9e026 --- /dev/null +++ b/spec/fixtures/files/protocols_io/v3/single_protocol.json @@ -0,0 +1,4876 @@ +{ + "protocol": { + "id": 9451, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", + "doi_status": 2, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 1, + "published_on": 1516132805, + "stats": { + "number_of_views": 17928, + "number_of_steps": 66, + "number_of_bookmarks": 65, + "number_of_comments": 122, + "number_of_exports": 397, + "number_of_runs": 39 + }, + "version_id": 0, + "created_on": 1515173338, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [ + { + "id": 2, + "image": { + "source": "/img/badges/bronze.svg", + "placeholder": "/img/badges/bronze.svg" + }, + "name": "Author" + }, + { + "id": 6, + "image": { + "source": "/img/badges/socialbutterfly.svg", + "placeholder": "/img/badges/socialbutterfly.svg" + }, + "name": "Social butterfly" + } + ], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null, + "public": 1, + "has_versions": 1, + "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", + "total_collections": 0, + "number_of_steps": 66, + "authors": [ + { + "name": "Peter J. Skene", + "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + }, + { + "name": "Steven Henikoff", + "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + } + ], + "versions": [ + { + "id": 9451, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 0, + "published_on": 1516132805, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 122, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 0, + "created_on": 1515173338, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + }, + { + "id": 11190, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-n6wdhfe", + "type_id": 0, + "published_on": 1551890606, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 30, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 1, + "created_on": 1522601124, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + }, + { + "id": 21615, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-zcpf2vn", + "type_id": 0, + "published_on": 1557428439, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 12, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 2, + "created_on": 1553027602, + "categories": null, + "creator": { + "name": "Derek Janssens", + "affiliation": null, + "username": "derek-janssens", + "link": null, + "image": { + "source": "/img/avatars/017.png", + "placeholder": "/img/avatars/017.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + } + ], + "groups": [ + { + "id": 523, + "uri": "hca", + "title": "Human Cell Atlas Method Development Community", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 523, + "uri": "hca", + "title": "Human Cell Atlas Method Development Community", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": "Steven Henikoff", + "affiliation": null, + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": true, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": "Steven Henikoff", + "affiliation": null, + "affiliation_url": null, + "username": "steven-henikoff", + "link": null + }, + "protocol": { + "id": 0, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + } + }, + "created_on": 1516132805, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 1, + "forks_count": { + "private": 27, + "public": 0 + }, + "access": { + "can_view": 0, + "can_remove": 0, + "can_add": 0, + "can_edit": 0, + "can_publish": 0, + "can_get_doi": 0, + "can_share": 0, + "can_move": 0, + "can_move_outside": 0, + "can_transfer": 0, + "can_download": 1, + "is_locked": 0 + }, + "steps": [ + { + "id": 601564, + "guid": "F9349635A9AB4395AD1A410F66F785CF", + "previous_id": null, + "previous_guid": null, + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B4239EB74A144551AD46C1ADC6707144", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "BA370A7410C749C580F428A9A8B515D7", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "7229BD361FC041BD829424FDAF87E6BC", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: All steps prior to the addition of antibody are performed at room temperature to minimize stress on the cells. Because it is crucial that DNA breakage is minimized throughout the protocol, we recommend that cavitation during resuspension and vigorous vortexing be avoided.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054726, + "guid": "DE8D3F46B0F74E7CB0E1AC694CCAE048", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003ePAUSE POINT\u003c/span\u003e\u003cspan\u003e: If necessary, cells can be cryopreserved in 10% DMSO using a Mr. Frosty isopropyl alcohol chamber. We do not recommend flash freezing, as this can cause background DNA breakage that may impact final data quality.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054727, + "guid": "379EA17744A943698115A4AB4A3687E4", + "order_id": 4, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 30 minutes.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601565, + "guid": "533BCACB836840EEA0AFEB15424F1208", + "previous_id": 601564, + "previous_guid": "F9349635A9AB4395AD1A410F66F785CF", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "F96FA71527B64CE0941CBB92181E74B7", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "E8A6A960B6204912A8F6697C44845E78", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "4F5BA212323746F3A4D72FF3CDE649E0", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 180, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601566, + "guid": "C403951BF28442E28A1A161C20E91244", + "previous_id": 601565, + "previous_guid": "533BCACB836840EEA0AFEB15424F1208", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "A9D5244F64C442CF8D92FD2A28161C14", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "A9045D12DBDB45AF8D4E05FF213295D4", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eResuspend in 1.5 ml room temperature Wash buffer by gently pipetting and transfer if necessary to a 2 ml tube. (wash 1/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "877918BD3143485BAEB96361FC0095A3", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Wash buffer" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601567, + "guid": "13B786714C8E493181D5EFD67C04F1DE", + "previous_id": 601566, + "previous_guid": "C403951BF28442E28A1A161C20E91244", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "FDE986CF923E4BA68C88CE17630046A0", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "EB4B814A95024FF5992F64A7CE1647B8", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 1/2)\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "392BFA08D1634BF4B692BEE707A04E68", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 180, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601568, + "guid": "B6E7098700144E9CA5F9E421D6CB32A4", + "previous_id": 601567, + "previous_guid": "13B786714C8E493181D5EFD67C04F1DE", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "6B4E841EBA7245178BA6F92F681A6F56", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "6A6FF684D40A44028B35F98B575A72B4", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAgain, resuspend in 1.5 ml room temperature Wash buffer by gently pipetting. Centrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 2/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "36A3B8F9CE1444C0963D848158B07609", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Wash buffer" + } + }, + { + "id": 1054726, + "guid": "9DAAFC949017409B98FD30AEC82D757D", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 180, + "title": "" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601569, + "guid": "F34684E33271452D8C5FCD6825754E06", + "previous_id": 601568, + "previous_guid": "B6E7098700144E9CA5F9E421D6CB32A4", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "8506A6C09CC04AB1B2D793A2C0397D0F", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "07A07A55F68D4AFB9A1827660138BE72", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eResuspend in 1 ml room temperature Wash buffer by gently pipetting.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "5B844BD52D3143C19113859DE2A68608", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Wash buffer" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601570, + "guid": "58662573D1154AB4B618D743EFCB6789", + "previous_id": 601569, + "previous_guid": "F34684E33271452D8C5FCD6825754E06", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7D0F899F3FF445FBAA314D160E7CFCB2", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "4D9DF4A1C6B84178911ECDC5FF220A5F", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhile gently vortexing the cells at room temperature, add the bead slurry.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601571, + "guid": "36E26513CD5941C5A05E511CB2E5519A", + "previous_id": 601570, + "previous_guid": "58662573D1154AB4B618D743EFCB6789", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "75280EF994BE4CE09A940A06E5FDDDAB", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "EF87593612AD42CC9477542C4D97E7CA", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRotate 5-10 min at room temperature.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "C7CC2DC572594827AA2641FE87E98030", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 600, + "title": "Rotation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601572, + "guid": "B781B8F5D2FA4148A2425991DBB90678", + "previous_id": 601571, + "previous_guid": "36E26513CD5941C5A05E511CB2E5519A", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "69CF276CD0EA4553BB9139053C033BC6", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "436018023BD84B678A0FFF5001B26DDE", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDivide into aliquots in 1.5-ml tubes, one for each antibody to be used.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "D62509DE75DF47B68A99B3D0C0257CC1", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: To evaluate success of the procedure without requiring library preparation, include in parallel a positive control antibody (e.g. α-H3K27me3) and a negative control antibody (e.g. α-rabbit). Do not include a no-antibody control, as the lack of tethering may allow any unbound pA-MN to act as a “time-bomb” and digest accessible DNA, resulting in a background of DNA-accessible sites.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601573, + "guid": "E2CA695B2F84412181172D8EA5D2D3F0", + "previous_id": 601572, + "previous_guid": "B781B8F5D2FA4148A2425991DBB90678", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "2341850E79644B69A03EB01793948C8D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "4840BBA918A54A5CA8A1A9421536D740", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off the liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "0D1C8EC5A8274C4B8A13610FCA9B475D", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Although low-retention pipette tips are preferred for accurate solution transfers, use only conventional (not low-binding) microcentrifuge tubes to avoid loss of beads while decanting.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054726, + "guid": "B65CDC1428324D43A179577505968A13", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section can be 15 min–overnight, with longer incubations providing higher yields.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601574, + "guid": "7DC06660925F4633BE0B963407ED5AD1", + "previous_id": 601573, + "previous_guid": "E2CA695B2F84412181172D8EA5D2D3F0", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "DB3500D3ECA941C6898A7BF2F29A8D0C", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "06F721D573844F73B9A08F69EEBDE50D", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Antibody buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "9C495290326F47FD9F34F6B6C725AB03", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: The presence of EDTA during antibody treatment removes excess divalent cation used to activate the ConA, because carry-over of Ca++ from the beads can prematurely initiate strand cleavage after addition of pA-MN. Chelation of divalent cations when cells are permeabilized also serves to quickly halt metabolic processes and prevent endogenous DNAse activity. Washing out the EDTA before pA-MN addition avoids inactivating the enzyme. Spermidine in the wash buffer is intended to compensate for removal of Mg++, which might otherwise affect chromatin properties.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601575, + "guid": "9814A7B521A64FAAAC16B758C91C5AA9", + "previous_id": 601574, + "previous_guid": "7DC06660925F4633BE0B963407ED5AD1", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "3462666F4222412B8EB20D90EEC83C55", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "FAC824D4C5D34733ACB3E6E8A8AC2544", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4°C for ~2 hr (or at room temperature for 5-10 min)\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "C35777E1CBA24F7799754771FB3551A7", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 7200, + "title": "Tube rotator at 4°C" + } + }, + { + "id": 1054726, + "guid": "16B00C16F4C048818A1007581FA4F574", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003ePAUSE POINT\u003c/span\u003e\u003cspan\u003e Antibody incubation may proceed overnight at 4 °C.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601576, + "guid": "F28A5BD1B5364839A3FADFEBDC1A8E37", + "previous_id": 601575, + "previous_guid": "9814A7B521A64FAAAC16B758C91C5AA9", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "FE83837F0BE6488F943387EF41EEA5FB", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "3328BFC05392482B9EB12E5C3FBD343F", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "594F1DD69A6141B8996B9D07975CB9F6", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: After mixing, but before placing a tube on the magnet stand, a very quick spin on a micro-centrifuge (no more than 100 x g) will minimize carry-over of antibody and pA-MN that could result in overall background cleavages during the digestion step.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601577, + "guid": "3D8757B9EE974E00BD637E8681453972", + "previous_id": 601576, + "previous_guid": "F28A5BD1B5364839A3FADFEBDC1A8E37", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "037AB90AA46249CD90EA28BA730CC904", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "79FFA0D1193943768B15C41477C8E8EC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear (~30 s) and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E1147891F4974E538246DB8053826E98", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 30, + "title": "Magnet stand" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601578, + "guid": "9591637A664740139561134EE158BBC9", + "previous_id": 601577, + "previous_guid": "3D8757B9EE974E00BD637E8681453972", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "860CFC9B66F845A5B8E41A99E43BDC8B", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind (primary) antibodies" + } + }, + { + "id": 1054724, + "guid": "9457B72E38DE4E4D98591079D35167A1", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting using a 1 ml tip if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "23BECB0713B84CEA816F369220032EE5", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Dig-wash buffer" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601579, + "guid": "447CFE23E751440EA778368FEB3F87C4", + "previous_id": 601578, + "previous_guid": "9591637A664740139561134EE158BBC9", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "58657CE22CCB4BE5A63056677A67883D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "F79AAC77550540A2AEDADBF4D49145B1", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "AB1F6945CA47471298C5DD2CC332DB88", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCRITICAL STEP: The binding efficiency of Protein A to the primary antibody depends on host species and IgG isotype. For example, Protein A binds well to rabbit and guinea pig IgG but poorly to mouse and goat IgG, and so for these latter antibodies a secondary antibody, such as rabbit α-mouse is recommended.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054726, + "guid": "95AFDC83212A4B89A1A014501B4169D4", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 15 min to 1.5 hours\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601580, + "guid": "D677EECD1EC640709044078D1B26A7C0", + "previous_id": 601579, + "previous_guid": "447CFE23E751440EA778368FEB3F87C4", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "8C40CF2502A4433EA4E6FE29B1633A64", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "8B51FCDB3F23412D9A459BDFC4C58783", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "C4A802B3B29549B694E1834E9E3954C3", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 50, + "unit": "µl", + "title": "Dig-wash buffer (per sample and/or digestion time point)" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601581, + "guid": "44947D0288034DB9A2E0DCB25D1F9387", + "previous_id": 601580, + "previous_guid": "D677EECD1EC640709044078D1B26A7C0", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "AA3AB0862B0E477BB6B1538A83F56A7D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "C8A586C6907849D8958C433C34620C1B", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the secondary antibody to a final concentration of 1:100 or to the manufacturer’s recommended concentration for immunofluorescence.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601582, + "guid": "2C71FDFD3966418E89282A2E498DA2D7", + "previous_id": 601581, + "previous_guid": "44947D0288034DB9A2E0DCB25D1F9387", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "542FEE6BFDED4272A0D16B559BF6054A", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "D458DC24DD4E419AA6BA400F189C96CC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "F01854534A1A456495DBB43C4DC3E75B", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 3600, + "title": "Tube rotator at 4 °C" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601583, + "guid": "902EB027B7E842B084193355AB786637", + "previous_id": 601582, + "previous_guid": "2C71FDFD3966418E89282A2E498DA2D7", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "C3E46848962E482594D5E6E08A15DBFB", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "80BB9951F8654C9FA8A441AD786A867E", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601584, + "guid": "54FE2501D13B4D8BB18EB9D35D2FA8EF", + "previous_id": 601583, + "previous_guid": "902EB027B7E842B084193355AB786637", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "6A6EE625B5A54B97B44D29C3F7426EFD", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "3B79EB3E49534FE5882962991ED25D38", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601585, + "guid": "1940483B302C40F0B3EB5443F1D48418", + "previous_id": 601584, + "previous_guid": "54FE2501D13B4D8BB18EB9D35D2FA8EF", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "0131A8D952F2459BB7599A7557632D71", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind secondary antibody (as required)" + } + }, + { + "id": 1054724, + "guid": "6CCD3BCC11264591A74FF730AE7B1F57", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-Wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E491A6117E00497A9264F820FF8EC18E", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Dig-Wash buffer" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601586, + "guid": "4191A6159A354E7BAC02E73BDEAD8925", + "previous_id": 601585, + "previous_guid": "1940483B302C40F0B3EB5443F1D48418", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "8B3342A661F144C1BFF13CFAE4A42E77", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "1072337428C44BE492F0EDC365300B9D", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "C99C243EBB684C12B9C9558F3F9118EC", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 15 min - 1.5 hours\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601587, + "guid": "ACDFAA48CC08448DA097F2D350B31E1C", + "previous_id": 601586, + "previous_guid": "4191A6159A354E7BAC02E73BDEAD8925", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "A3247E04A7854689B8D3006E415EA2CF", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "BFE7E50BA51C4550B755C464996FE98A", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "D42DDCCCC45A46D19A97DD9D16F1CEBA", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 50, + "unit": "µl", + "title": "Dig-wash buffer (per sample and/or digestion time point)" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601588, + "guid": "F79CEE8B697646AABB02EE35AE0A886F", + "previous_id": 601587, + "previous_guid": "ACDFAA48CC08448DA097F2D350B31E1C", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "0A411C2E23154C0C96B32E9B3C74F24F", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "E38812006C4C4017BF88857B45E5EBE4", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the pA-MNase to a final concentration of ~700 ng/ml (e.g. 2.5 μL/50 μL of a 1:10 dilution of the 140 μg/ml glycerol stock provided upon request).\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601589, + "guid": "F75C798D1B4E41AF87BEE198CED29B59", + "previous_id": 601588, + "previous_guid": "F79CEE8B697646AABB02EE35AE0A886F", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "2571CDB064C84DA299360D46D9BA236B", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "666D8CF79C874ADEB09E3B95A2D9DE5B", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "717875E7ECF2487CAFE9D8DF0402977D", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 3600, + "title": "Tube rotator at 4 °C" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601590, + "guid": "5C48D1C95CC64BDB82361FD5B1D6512A", + "previous_id": 601589, + "previous_guid": "F75C798D1B4E41AF87BEE198CED29B59", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "567105CDB1F14707AE96B3DAFA4244B9", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "0790EFF9BB9548EE91E10518ECEFDA20", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601591, + "guid": "0B3368083BB94AC6939C68950CDCD02E", + "previous_id": 601590, + "previous_guid": "5C48D1C95CC64BDB82361FD5B1D6512A", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "83AB9CA54C864121B40E328C694D72B6", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "01EFB7830C9C4DBDA8F15B276AA7720E", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601592, + "guid": "8ECBF2C2495B4AA18E5F393B55B82112", + "previous_id": 601591, + "previous_guid": "0B3368083BB94AC6939C68950CDCD02E", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "3D2F6134980A41EFB74B01922D3C2149", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "FE326382C1F54C368EB60BB7A68FB51E", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "96882087A34D4D21A2761301A70E55C6", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "Dig-wash buffer" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601593, + "guid": "9317B32F055E4FA295C502F2F233FB48", + "previous_id": 601592, + "previous_guid": "8ECBF2C2495B4AA18E5F393B55B82112", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "E6C1DBDD01934E838C688507425A33BF", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Bind Protein A-MNase fusion protein" + } + }, + { + "id": 1054724, + "guid": "4867D4ED725E47BE8F5D1E6E117DF6B7", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRepeat Dig-wash steps 28-29.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "4E45FCFD3A38403B8E62DAA850756D57", + "order_id": 2, + "type_id": 22, + "title": "gotostep", + "source": { + "step_guid": "0B3368083BB94AC6939C68950CDCD02E", + "title": "Repeat Dig-wash steps" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601594, + "guid": "E1E60873DF52456DAEC420EC033DE2F4", + "previous_id": 601593, + "previous_guid": "9317B32F055E4FA295C502F2F233FB48", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B36111C478B1407EA298A2838746CB6D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "887F64E715464E2A8D04D4FBF003A675", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "61C10B2F96F14B48B0FF59A768E86E44", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 45 minutes\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601595, + "guid": "BFF4DB1905D04077932EEC44B94EAA0D", + "previous_id": 601594, + "previous_guid": "E1E60873DF52456DAEC420EC033DE2F4", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "05644E9B0F6C45758CEBB8F344B9D0C2", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "CDA1FADE4E9B46F29C7F78799BD628A1", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and add 100 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "7E606AD945824136ADD434F3BFA32F9F", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 100, + "unit": "µl", + "title": "Dig-wash buffer (per sample and/or digestion time point)" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601596, + "guid": "C23AC0CA22F240F7B8C9AA144CB3241E", + "previous_id": 601595, + "previous_guid": "BFF4DB1905D04077932EEC44B94EAA0D", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "A1E4BE65B8224066A3FE359A7E1DE314", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "893F13A565854F139EB9DA24F93383F0", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eInsert tubes into the 1.5 ml wells of a heater block sitting in wet ice to chill down to 0 °C.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E9038CDB84B14624B92E729A5E703F41", + "order_id": 2, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 0, + "unit": "°C", + "title": "" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601597, + "guid": "8BD0574B304D450FBC50034FFBCEB658", + "previous_id": 601596, + "previous_guid": "C23AC0CA22F240F7B8C9AA144CB3241E", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B6A89ECE5EA849BFA9B61CC624FA394D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "7106E2E428B24FF6A654AB3176BE305F", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eRemove each tube from the block, mix in 2 μL 100 mM CaCl\u003c/span\u003e\u003cspan style = \":justify;vertical-align:sub;\"\u003e2\u003c/span\u003e\u003cspan style = \":justify;\"\u003e (diluted 1:10 from a 1 M stock) with gentle vortexing and immediately replace the tube in the 0 °C block.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E1DDDBD9E1C2429D83A15EFDD75BB753", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 2, + "unit": "µl", + "title": "100 mM CaCl2" + } + }, + { + "id": 1054726, + "guid": "B041538D67914F95B489E6698CDC1186", + "order_id": 3, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 0, + "unit": "°C", + "title": "" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601598, + "guid": "6D318018B048416DA9FC2F42C2E6F3CA", + "previous_id": 601597, + "previous_guid": "8BD0574B304D450FBC50034FFBCEB658", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B3F14B93F3CC48D5AEAAA89BEE744234", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "122337F8736B40F89DBCC6A4C750E58B", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate at 0 °C for the desired digestion time (default is 30 min).\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "A7A41B3B3DA74D069F7BBA177C705EF3", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 1800, + "title": "Incubation" + } + }, + { + "id": 1054726, + "guid": "1E9050BDD5C24D06839BD89E3BC677D8", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: MNase binds DNA but only cleaves when Ca++ is present, so that digestion is a zero-order reaction that seems to be less temperature-dependent than the subsequent diffusion of released pA-MNase-bound particles that can digest accessible regions of the genome. Cleavage and release of particles in most of the cell population can be obtained at 0 oC while minimizing background cleavages attributable to diffusion. We have found that digestion at ambient temperature or higher results in unacceptable background cleavage levels.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054727, + "guid": "2D6A506A60794425A179175F23982863", + "order_id": 4, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 0, + "unit": "°C", + "title": "Incubation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601599, + "guid": "4B84A0F7E7704AA6BC0608736CCC76E9", + "previous_id": 601598, + "previous_guid": "6D318018B048416DA9FC2F42C2E6F3CA", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "597AEDF049524E58AEE0F8D8FADC4173", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Targeted digestion" + } + }, + { + "id": 1054724, + "guid": "AAADF06579AF4231894E3B13156B8728", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 100 μL 2XSTOP and mix by gentle vortexing. When there are multiple time points, remove 100 μL to 100 μL 2XSTOP and mix by gentle vortexing.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "A12A6A3FCC294123AB58F9554C83FD75", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 100, + "unit": "µl", + "title": "2XSTOP" + } + }, + { + "id": 1054726, + "guid": "599C738D692E45419389123D3567F819", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Heterologous spike-in DNA should be present in the 2XSTOP to calibrate DNA amounts, for example to compare treatments or digestion time points. This is especially important for CUT\u0026RUN, as there is too little background cleavage for normalization of samples.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601600, + "guid": "C8F46DC9456C4761B2B93D2BFD2B20D6", + "previous_id": 601599, + "previous_guid": "4B84A0F7E7704AA6BC0608736CCC76E9", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "1CD60910082F4F8D8F7BAC15C5378996", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Target chromatin release" + } + }, + { + "id": 1054724, + "guid": "B96477A32E8147F19BA1696491C76E46", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate 10 min 37 °C to release CUT\u0026RUN fragments from the insoluble nuclear chromatin.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "3AC9ABE994D04870A849601C47767C65", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 600, + "title": "Incubation" + } + }, + { + "id": 1054726, + "guid": "AB2D2D887FB64BD9AE4FC36BC1C3895C", + "order_id": 3, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 37, + "unit": "°C", + "title": "Incubation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601601, + "guid": "9DA29E33454047FB8C050EE24DC2DE1B", + "previous_id": 601600, + "previous_guid": "C8F46DC9456C4761B2B93D2BFD2B20D6", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "349B0DF57F0D47A6A15882963E565BF4", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Target chromatin release" + } + }, + { + "id": 1054724, + "guid": "3BC3B1D1BC4B41B48EF642470E12CF04", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 5 min at 4 °C and 16,000 x g and place on magnet stand.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "F45853ECBC76496A8B92430CE17BE24F", + "order_id": 2, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 4, + "unit": "°C", + "title": "Centrifugation" + } + }, + { + "id": 1054726, + "guid": "3CA183D2D99E412DA60E54BBDEF332BA", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 300, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601602, + "guid": "A665FB6F1B224EF896EE45E7F7AB3DA7", + "previous_id": 601601, + "previous_guid": "9DA29E33454047FB8C050EE24DC2DE1B", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7F4B0B90BAFC4DA8A302AC583B68BD53", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "F922DEA9D3554E44A893D049C13FC84B", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace a spin column into a collection tube and add 400 μL Buffer NT1 (from NucleoSpin kit or equivalent).\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "9D3A2B71C6FC4F12A323E6FA6DEFF884", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 400, + "unit": "µl", + "title": "Buffer NT1" + } + }, + { + "id": 1054726, + "guid": "4225349180CE430887B1F1E1DDF39E35", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIf you are performing Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments), please directly proceed to Step 47.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054727, + "guid": "C96435DAB79A4BAF9CA1A8AD0DBBB082", + "order_id": 4, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 20 minutes\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601603, + "guid": "25777B0B8EC44729A3B6B4A09B518931", + "previous_id": 601602, + "previous_guid": "A665FB6F1B224EF896EE45E7F7AB3DA7", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "D96F11F10EAC4AFB8BFFCE40CDB769A0", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "F63B5EAAD3E945EABC268D735448ED6B", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eDecant the supernatant cleanly from the pellet and transfer to the NT1 in the spin column pipetting gently up and down to mix.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601604, + "guid": "58645AB3643C42ABA55E2A7D22412DDB", + "previous_id": 601603, + "previous_guid": "25777B0B8EC44729A3B6B4A09B518931", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "5CE4D2D4643C4992BEE2EE05C2E6B18E", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "B48890940F824809A54BA0B7E27A3661", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "4115311E917343D2A663114FC6E737E1", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 30, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601605, + "guid": "B901E247E4C34304B012D414C703B540", + "previous_id": 601604, + "previous_guid": "58645AB3643C42ABA55E2A7D22412DDB", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "D5147547CDCC485D8025B93AF85C7D91", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "BECFFADC221641A892845E5423C786CC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "DCEE8F8D8D8C436AB997731AC6B042F4", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 700, + "unit": "µl", + "title": "Buffer NT3" + } + }, + { + "id": 1054726, + "guid": "682228D6EAAA41CC808CA2B05467D1A2", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 30, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601606, + "guid": "E8E7A5825F0D4E7989FD5A5F7A662CEE", + "previous_id": 601605, + "previous_guid": "B901E247E4C34304B012D414C703B540", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "D344EF37B5664A4687C8E98AF6D36154", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "1C1BAF4644024AE4BB676D3455155BEB", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through and replace in rotor.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "78FF6F20213E4D2D9E291417B0F1CD73", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 700, + "unit": "µl", + "title": "Buffer NT3" + } + }, + { + "id": 1054726, + "guid": "D8ECF01EF2E741DC9A20ED8973561044", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 30, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601607, + "guid": "ED4075EE53C747698805ACB75D5EA451", + "previous_id": 601606, + "previous_guid": "E8E7A5825F0D4E7989FD5A5F7A662CEE", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "F5830217AEB74ADB991AB1142F102DC0", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "BF1108C352C64B07B04ACFDF9D57E3CD", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge for 1 min at 11,000 x g. Let dry 5 min.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "2E284C497A384FD6B6C9CBD1A2F53DCE", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 60, + "title": "Centrifugation" + } + }, + { + "id": 1054726, + "guid": "5A0C39A9DD82459FBB7A3B6D423BF7E7", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 300, + "title": "Drying" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601608, + "guid": "E0B331A51A5348C0A4008B2828A4A240", + "previous_id": 601607, + "previous_guid": "ED4075EE53C747698805ACB75D5EA451", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "292403FFC1E547D2BC377A41C197E0AB", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "A117D44E312E4F648723E3683AF03412", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace in a fresh tube and add 20-40 μL Buffer NE to membrane.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "966584EAC8494419B5B8A26D18320CD7", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 40, + "unit": "µl", + "title": "Buffer NE" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601609, + "guid": "8FF08B0D5AC14A26AF4CC93CA3A80A3B", + "previous_id": 601608, + "previous_guid": "E0B331A51A5348C0A4008B2828A4A240", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "67D721B6C364480587C61F67F48A281E", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option A: Fast DNA extraction by spin column" + } + }, + { + "id": 1054724, + "guid": "62C40D6BFD0B4A7CB27450D3C54EBBCC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAfter 1 min, centrifuge for 1 min at 11,000 x g.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "B4786BA03DF84C578BDA01C58582EADB", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 60, + "title": "Wait" + } + }, + { + "id": 1054726, + "guid": "95DCECDC0CDC4FE09A2F98AC4ED84489", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 60, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601610, + "guid": "B9DF2E67A33047C280CDB0E7B6B11448", + "previous_id": 601609, + "previous_guid": "8FF08B0D5AC14A26AF4CC93CA3A80A3B", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "6EC4C0C9C7BE4E998A05AB2965CB6096", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "CF27DB48439D436C96C567FAE67138D6", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDecant the supernatant cleanly from the pellet and transfer to a fresh 1.5-ml microcentrifuge tube.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "A0A556F0BE8042698783ED1218D086D5", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIf you are performing Option A: Fast DNA extraction by spin column, please directly proceed to Step 59. \u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054726, + "guid": "B279A7A2D5F24C5782993FC08EA051B7", + "order_id": 3, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 1.5 hours\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601611, + "guid": "46FA11C4A22A475E9E2499A7C0A347D3", + "previous_id": 601610, + "previous_guid": "B9DF2E67A33047C280CDB0E7B6B11448", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B0EE9E3E2B654D1099A0C3B5939E254B", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "4E8F1066FF174DEEB1A3CD9BB1A08ECC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eTo each sample add 2 μL 10% SDS (to 0.1%), and 2.5 μL Proteinase K (20 mg/ml). Mix by inversion and incubate 10 min 70 °C.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "89C222141D84445DA534C1E94DCA4085", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 2, + "unit": "µl", + "title": "10% SDS (to 0.1%)/sample" + } + }, + { + "id": 1054726, + "guid": "09F1B1667E64428FB3BEA0983ED54B93", + "order_id": 3, + "type_id": 3, + "title": "amount", + "source": { + "amount": 2, + "unit": "µl", + "title": "Proteinase K (20 mg/ml)/sample" + } + }, + { + "id": 1054727, + "guid": "2B80F664A4EF4A42AF31FDE635FA4B29", + "order_id": 4, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 70, + "unit": "°C", + "title": "Incubation" + } + }, + { + "id": 1054728, + "guid": "F2234AF0BDE249F9874228800970B775", + "order_id": 5, + "type_id": 4, + "title": "duration", + "source": { + "duration": 600, + "title": "Incubation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601612, + "guid": "FA12B8081A5C4BF59EA5D90B5C2BF9A4", + "previous_id": 601611, + "previous_guid": "46FA11C4A22A475E9E2499A7C0A347D3", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "AB004D393AC2496C9B5B7D8C0FC8C64C", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "10E32980BC8646B9B0E371BD74BE8558", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL PCI and mix by full-speed vortexing ~2 s.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E41B8897FB374D8083CF6A764D080C5A", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 300, + "unit": "µl", + "title": "PCl" + } + }, + { + "id": 1054726, + "guid": "1435EA4D4B4A4AC992DD463D566355E4", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 2, + "title": "Vortexing" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601613, + "guid": "384FB88D80F048C5901158ED7E8BA90B", + "previous_id": 601612, + "previous_guid": "FA12B8081A5C4BF59EA5D90B5C2BF9A4", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "368F44BE9FB8401DB53ABE5E9570F69D", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "2C7B63F456494225B760401717F98438", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eTransfer to a phase-lock tube (e.g., Qiagen MaXtract), and centrifuge 5 min room temperature at 16,000 x g.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "3E3180DAB3BF4D78843ABD8F93C08EB9", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 300, + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601614, + "guid": "38007A622CF946A3B7B15B09B378162F", + "previous_id": 601613, + "previous_guid": "384FB88D80F048C5901158ED7E8BA90B", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "EED94564ADB7444BA241F2D7CA3F41A1", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "EF8A02E2243943DF813D04289D070EDB", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL chloroform and invert ~10x to mix.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "09E20C738AF74304B4AFEE66D9B6F867", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 300, + "unit": "µl", + "title": "Chloroform" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601615, + "guid": "720E2E6FE0324E0BA7381764772C020A", + "previous_id": 601614, + "previous_guid": "38007A622CF946A3B7B15B09B378162F", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "30864A383D764673B612C7CAFAC374EC", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "DEA0F8A7E6BE4241A6B698EB213433C0", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid by pipetting to a fresh tube containing 2 μL 2 mg/ml glycogen.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "5C8C43AA00614010A9176F8A0F0F9BA9", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 2, + "unit": "µl", + "title": "2 mg/ml glycogen" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601616, + "guid": "4C976F0262A6427FA82B35A3DB3131CF", + "previous_id": 601615, + "previous_guid": "720E2E6FE0324E0BA7381764772C020A", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "DFD5BB572A434610B3A1CA7957299F7A", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "CA7CD354E40B400A9312A3AB1419A0D9", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 750 μL 100% ethanol and mix by vortexing or tube inversion.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "E0CE4408F3D943CAB503DC86EABAF992", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 750, + "unit": "µl", + "title": "100% ethanol" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601617, + "guid": "9462096276C84C7D9DAC857F13552DF9", + "previous_id": 601616, + "previous_guid": "4C976F0262A6427FA82B35A3DB3131CF", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7AD34EB1DB784C61989DBA3B703B5399", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "BE91B057559E48289CAAAD4679D1F920", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eChill on ice and centrifuge 10 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "2962671BA9D641928AD3FC211F29F8B7", + "order_id": 2, + "type_id": 4, + "title": "duration", + "source": { + "duration": 600, + "title": "Centrifugation" + } + }, + { + "id": 1054726, + "guid": "0ED3B3704A6B4A4299CFFB84907214D2", + "order_id": 3, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 4, + "unit": "°C", + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601618, + "guid": "9845314177E44EA29C66DEF785D63809", + "previous_id": 601617, + "previous_guid": "9462096276C84C7D9DAC857F13552DF9", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7A15E93E2C6D4BD8821647CF3B9D5404", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "A927939E8DAE402CA604ABD5CC9C30B8", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePour off the liquid and drain on a paper towel.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601619, + "guid": "CBEE67B491E644D5BC80599F1E7E3AF5", + "previous_id": 601618, + "previous_guid": "9845314177E44EA29C66DEF785D63809", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "4B738DA925094D8191FCFBC48AA8B280", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "23036507DFC84EA5951BC5A09F4FE56D", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRinse the pellet in 1 ml 100% ethanol and centrifuge 1 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "C495B77992164E708246F672351804C1", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 1, + "unit": "ml", + "title": "100% ethanol" + } + }, + { + "id": 1054726, + "guid": "F9D9F774B70A42ACA57CD8B762AF9128", + "order_id": 3, + "type_id": 4, + "title": "duration", + "source": { + "duration": 60, + "title": "Centrifugation" + } + }, + { + "id": 1054727, + "guid": "2EB7938E5F264E698BB8BEE2965C5D1B", + "order_id": 4, + "type_id": 24, + "title": "temperature", + "source": { + "concentration": 4, + "unit": "°C", + "title": "Centrifugation" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601620, + "guid": "2C2E2507E3D7415FB974FFFF9AF5F3C6", + "previous_id": 601619, + "previous_guid": "CBEE67B491E644D5BC80599F1E7E3AF5", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "3344A38E21DB4C6094661A2CC862E5E8", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "8BF41171128449E2B304C1209A0E2527", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCarefully pour off the liquid and drain on a paper towel. Air dry.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601621, + "guid": "060192325023468E80E83369843B5F46", + "previous_id": 601620, + "previous_guid": "2C2E2507E3D7415FB974FFFF9AF5F3C6", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "6CAB58A20D6E4D11894E37D533B75EFB", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" + } + }, + { + "id": 1054724, + "guid": "454915A1771B49D0A8016F555DC38351", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhen the pellet is dry, dissolve in 25-50 μL 1 mM Tris-HCl pH8 0.1 mM EDTA.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "8DE7CC3FDBDA46E68D5BE31072755638", + "order_id": 2, + "type_id": 3, + "title": "amount", + "source": { + "amount": 50, + "unit": "µl", + "title": "1 mM Tris-HCl pH8 0.1 mM EDTA" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601622, + "guid": "438E0BA7C3104D2CBEA56CFA4098F872", + "previous_id": 601621, + "previous_guid": "060192325023468E80E83369843B5F46", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7AA8C72D3ED14D209B2B125476C1FE74", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "5E2EE2C3208E4953AD36CF81C39916DC", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eOptional: Quantify 1-2 μL, for example using fluorescence detection with a Qubit instrument.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "7816EEC226314D8BA2F12831FFD26DAC", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 2-4 days.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601623, + "guid": "9512A4A47D0943339895E8489BC854ED", + "previous_id": 601622, + "previous_guid": "438E0BA7C3104D2CBEA56CFA4098F872", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "0EDDEB899C7D47FDBF1F4C961333AC45", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "9D436B82382D43A5A4071E135B6E0CA2", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eOptional: Evaluate the presence of cleaved fragments and the size distribution by capillary electrophoresis with fluorescence detection, for example using a Tapestation instrument.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "0ADBD372327C4532A02E1CEFF1873F00", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Some long undigested DNA will leak through, and this is what will dominate the Qubit fluorescence for CUT\u0026RUN of typical transcription factors. For these, the targeted DNA recovered is too low in amount and too small in size to be detected by gel analysis or even by Tapestation. In such cases it may be necessary to make a PCR-amplified library to quantify by Tapestation or Bioanalyzer analysis.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601624, + "guid": "387E0F06EE80496B89D75A0DBE161F51", + "previous_id": 601623, + "previous_guid": "9512A4A47D0943339895E8489BC854ED", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "93CD46B488144448A7ABB94182D06422", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "141431407878405F8E197D3F304BB705", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePrepare barcoded libraries for Illumina sequencing with Tru-Seq adapters using a single-tube protocol, following the manufacturer’s instructions. Rapid PCR cycles favor exponential amplification of the desired CUT\u0026RUN fragments over linear amplification of large DNA fragments that are too long for polymerase to complete.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "5CB262560CCE48FF83DA097FBA61B296", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: To minimize the contribution of large DNA fragments, PCR cycles should be at least 12-14 cycles, preferably with a 10 s 60°C combined annealing/extension step. Good results have been obtained with the Hyper-prep kit (KAPA Biosystems).\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601625, + "guid": "E08B0B983AD348F4AF9EB6C826990F9E", + "previous_id": 601624, + "previous_guid": "387E0F06EE80496B89D75A0DBE161F51", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B445486E28FA4D12ACE7B4CF96EA129F", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "956DD794E6DE4C2AA0EC3D45D5014938", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eQuantify library yield using dsDNA-specific assay, such as Qubit.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601626, + "guid": "E6081479C9FF429A9270B0CBBE6A34BC", + "previous_id": 601625, + "previous_guid": "E08B0B983AD348F4AF9EB6C826990F9E", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "8D17671E023349C09268C3488EF6F52B", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "245650BBB78740A0B49A12D347E06CBE", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDetermine the size distribution of libraries by Agilent 4200 TapeStation analysis.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601627, + "guid": "F330F44C402A4229A2D2871FFCEF47A2", + "previous_id": 601626, + "previous_guid": "E6081479C9FF429A9270B0CBBE6A34BC", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "14A47C0FF3C145D89627D45AC39A191B", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Library preparation and sequencing" + } + }, + { + "id": 1054724, + "guid": "6454381B7C7E414AB4E0CFF48D67B5D9", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePerform paired-end Illumina sequencing on the barcoded libraries following the manufacturer’s instructions.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "68EAABB78DB54EC9A94A5EAF50107782", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Because of the very low background with CUT\u0026RUN, typically 5 million paired-end reads suffices for transcription factors or nucleosome modifications, even for the human genome. For maximum economy, we mix up to 24 barcoded samples per lane on a 2-lane flow cell, and perform paired-end 25x25 bp sequencing. Single-end sequencing is not recommended for CUT\u0026RUN, as it sacrifices resolution and discrimination between transcription factors and neighboring nucleosomes.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601628, + "guid": "E3BB1069597D48709ED4267F18AD8644", + "previous_id": 601627, + "previous_guid": "F330F44C402A4229A2D2871FFCEF47A2", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "7078902132D143C9BCB6B5261BF01D5F", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Data processing and analysis" + } + }, + { + "id": 1054724, + "guid": "420D85486C4A4AEF9A6A9EBF1DE8726E", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eWe align paired-end reads using Bowtie2 version 2.2.5 with options: --local --very-sensitive- local --no-unal --no-mixed --no-discordant --phred33 -I 10 -X 700. For mapping spike-in fragments, we also use the --no-overlap --no-dovetail options to avoid cross-mapping of the experimental genome to that of the spike-in DNA.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "57597517C87E472C98F17FDBF6ED86FF", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Separation of sequenced fragments into ≤120 bp and ≥150 bp size classes provides mapping of the local vicinity of a DNA-binding protein, but this can vary depending on the steric access to the DNA by the tethered MNase. Single-end sequencing is not recommended for CUT\u0026RUN, as it sacrifices resolution and discrimination between transcription factors and neighboring nucleosomes.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601629, + "guid": "D6C59AD46F7C4763B56FA387E8F6ADF5", + "previous_id": 601628, + "previous_guid": "E3BB1069597D48709ED4267F18AD8644", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "800A2F833FA54D018E46063D640C7700", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Data processing and analysis" + } + }, + { + "id": 1054724, + "guid": "2B004750745847A5A7920C2D58038791", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054725, + "guid": "FE5FC1257E4A47318E51CD88D2D52F47", + "order_id": 2, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\n\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": " ", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + } + ], + "materials": [ + { + "id": 119441, + "mol_weight": 0, + "name": "Cell suspension. We have used human K562 cells, Drosophila S2 cells and dissected Drosophila tissues such as brains and imaginal disks, and spheroplasted yeast.", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119442, + "mol_weight": 0, + "name": "Concanavalin-coated magnetic beads ", + "linfor": null, + "url": "", + "sku": "BP531", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Bangs Laboratories", + "affiliation": null, + "username": null, + "link": "http://www.bangslabs.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 278 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119443, + "mol_weight": 0, + "name": "Antibody to an epitope of interest. For example, rabbit α-CTCF polyclonal antibody (Millipore 07-729) for mapping 1D and 3D interactions by CUT\u0026RUN", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119444, + "mol_weight": 0, + "name": "Positive control antibody to an abundant epitope, e.g. α-H3K27me3 rabbit monoclonal antibody (Cell Signaling Technology, cat. no. 9733)", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119445, + "mol_weight": 0, + "name": "Negative control antibody to an absent epitope, e.g. guinea pig α-rabbit antibody", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119446, + "mol_weight": 0, + "name": "5% Digitonin ", + "linfor": null, + "url": "", + "sku": "300410", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Emd Millipore", + "affiliation": null, + "username": null, + "link": "http://www.emdmillipore.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 8 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119447, + "mol_weight": 0, + "name": "Protein A–Micrococcal Nuclease (pA-MNase) fusion protein (provided in 50% glycerol by the authors upon request). Store at -20 oC.", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119448, + "mol_weight": 0, + "name": "Spike-in DNA (e.g., from Saccharomyces cerevisiae micrococcal nuclease-treated chromatin, provided by authors upon request)", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119449, + "mol_weight": 0, + "name": "Distilled, deionized or RNAse-free H2O (dH2O e.g., Promega, cat. no. P1197)", + "linfor": null, + "url": "", + "sku": "P1197", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Promega", + "affiliation": null, + "username": null, + "link": "http://www.promega.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 23 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119450, + "mol_weight": 0, + "name": "1 M Manganese Chloride (MnCl2)", + "linfor": null, + "url": "", + "sku": "203734", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119451, + "mol_weight": 0, + "name": "1 M Calcium Chloride (CaCl2)", + "linfor": null, + "url": "", + "sku": "BP510", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.fishersci.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 31 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119452, + "mol_weight": 0, + "name": "1 M Potassium Chloride (KCl)", + "linfor": null, + "url": "", + "sku": "P3911", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119453, + "mol_weight": 0, + "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.5 (HEPES (Na ))", + "linfor": null, + "url": "", + "sku": "H3375", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119454, + "mol_weight": 0, + "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.9 (HEPES (K ))", + "linfor": null, + "url": "", + "sku": "H3375", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119455, + "mol_weight": 0, + "name": "5 M Sodium chloride (NaCl)", + "linfor": null, + "url": "", + "sku": "S5150-1L", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119456, + "mol_weight": 0, + "name": "0.5 M Ethylenediaminetetraacetic acid (EDTA)", + "linfor": null, + "url": "", + "sku": "3002E", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Research Organics", + "affiliation": null, + "username": null, + "link": "http://www.sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 279 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119457, + "mol_weight": 0, + "name": "0.2 M Ethylene glycol-bis(β-aminoethyl ether)-N,N,N,N-tetraacetic acid (EGTA)", + "linfor": null, + "url": "", + "sku": "E3889", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119458, + "mol_weight": 0, + "name": "2 M Spermidine ", + "linfor": null, + "url": "", + "sku": "S2501", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119459, + "mol_weight": 0, + "name": "Roche Complete Protease Inhibitor EDTA-Free tablets ", + "linfor": null, + "url": "", + "sku": "5056489001", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119460, + "mol_weight": 0, + "name": "2 mg/ml Glycogen (1:10 dilution)", + "linfor": null, + "url": "", + "sku": "10930193001", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119461, + "mol_weight": 0, + "name": "RNase A, DNase and protease-free (10 mg/ml)", + "linfor": null, + "url": "", + "sku": "EN0531", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Thermo Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.thermofisher.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 95 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119462, + "mol_weight": 0, + "name": "Gel and PCR Clean-up kit ", + "linfor": null, + "url": "", + "sku": "740609.250", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Macherey and Nagel", + "affiliation": null, + "username": null, + "link": "http://www.mn-net.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 133 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119463, + "mol_weight": 0, + "name": "Agencourt AMPure XP magnetic beads ", + "linfor": null, + "url": "", + "sku": "A63880", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Beckman Coulter", + "affiliation": null, + "username": null, + "link": "https://www.beckmancoulter.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 32 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119464, + "mol_weight": 0, + "name": "10% Sodium dodecyl sulfate (SDS)", + "linfor": null, + "url": "", + "sku": "L4509", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119465, + "mol_weight": 0, + "name": "Proteinase K", + "linfor": null, + "url": "", + "sku": "EO0492", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Thermo Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.thermofisher.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 95 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119466, + "mol_weight": 0, + "name": "Phenol-chloroform-isoamyl alcohol 25:24:1 (PCI)", + "linfor": null, + "url": "", + "sku": "15593049", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Invitrogen - Thermo Fisher", + "affiliation": null, + "username": null, + "link": "http://www.thermofisher.com/us/en/home/brands/invitrogen.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 191 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119467, + "mol_weight": 0, + "name": "Chloroform", + "linfor": null, + "url": "", + "sku": "366919-1L", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma", + "affiliation": null, + "username": null, + "link": "www.sigma.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 114 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119468, + "mol_weight": 0, + "name": "1 M Tris-HCl pH 8.0", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119469, + "mol_weight": 0, + "name": "Ethanol ", + "linfor": null, + "url": "", + "sku": "2716", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Decon Labs", + "affiliation": null, + "username": null, + "link": "http://deconlabs.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 280 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119470, + "mol_weight": 0, + "name": "Qubit dsDNA HS kit ", + "linfor": null, + "url": "", + "sku": "Q32851", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Life Technologies", + "affiliation": null, + "username": null, + "link": "http://www.lifetechnologies.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 11 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + } + ], + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", + "changed_on": 1522231932 + }, + "status_code": 0 +} diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb new file mode 100644 index 000000000..2738c93f1 --- /dev/null +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do + let(:response) do + JSON.parse(file_fixture('protocols_io/v3/single_protocol.json').read).to_h.with_indifferent_access + end + + let(:response_without_title) do + res_without_title = response + res_without_title[:protocol].reject! { |a| a == 'title' } + res_without_title + end + + let(:normalized_result) do + JSON.parse(file_fixture('protocols_io/v3/normalized_single_protocol.json').read).to_h.with_indifferent_access + end + + describe '#load_protocol' do + before do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive_message_chain(:single_protocol, :request, :last_uri, :to_s) + .and_return('https://www.protocols.io/api/v3/protocols/9451')) + end + + context 'when have all data' do + it 'should normalize data correctly' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to receive_message_chain(:single_protocol, :parsed_response).and_return(response) + + expect(subject.load_protocol(id: 'id').deep_stringify_keys).to be == normalized_result + end + end + + context 'when do not have name' do + it 'sets nil for name' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to receive_message_chain(:single_protocol, :parsed_response).and_return(response_without_title) + + expect(subject.load_protocol(id: 'id')[:protocol][:name]).to be_nil + end + end + end +end From 6a4deb02013915a2fc83f760a58241ebf8cf0f87 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Tue, 21 May 2019 17:16:30 +0200 Subject: [PATCH 06/73] Add ProtocolIntermediateObject --- .../protocol_intermediate_object.rb | 57 +++++++++++++++++++ .../protocol_intermediate_object_spec.rb | 30 ++++++++++ 2 files changed, 87 insertions(+) create mode 100644 app/utilities/protocol_importers/protocol_intermediate_object.rb create mode 100644 spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb diff --git a/app/utilities/protocol_importers/protocol_intermediate_object.rb b/app/utilities/protocol_importers/protocol_intermediate_object.rb new file mode 100644 index 000000000..62ce77702 --- /dev/null +++ b/app/utilities/protocol_importers/protocol_intermediate_object.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module ProtocolImporters + class ProtocolIntermediateObject + attr_accessor :normalized_response, :user, :team + + def initialize(normalized_json: {}, user:, team:) + @normalized_response = normalized_json[:protocol] if normalized_json + @user = user + @team = team + end + + def import + p = build + p.save if p.valid? + p + end + + def build + p = build_protocol + p.steps << build_steps + p + end + + private + + def build_protocol + Protocol.new(protocol_attributes) + end + + def build_steps + # TODO + # Add: + # - Assets + # - Tables + # - Checklists + + @normalized_response[:steps].map do |s| + Step.new(step_attributes(s)) + end + end + + def protocol_attributes + defaults = { protocol_type: :in_repository_public, added_by: @user, team: @team } + values = %i(name published_on description authors) + p_attrs = @normalized_response.slice(*values).each_with_object({}) do |(k, v), h| + h[k] = k == 'published_on' ? Time.at(v) : v + end + p_attrs.merge!(defaults) + end + + def step_attributes(step_json) + defaults = { user: @user, completed: false } + step_json.slice(:name, :position, :description).merge!(defaults) + end + end +end diff --git a/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb new file mode 100644 index 000000000..78e2b6eb8 --- /dev/null +++ b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::ProtocolIntermediateObject do + subject(:pio) { described_class.new(normalized_json: normalized_result, user: user, team: team) } + let(:invalid_pio) { described_class.new(normalized_json: normalized_result, user: nil, team: team) } + let(:user) { create :user } + let(:team) { create :team } + let(:normalized_result) do + JSON.parse(file_fixture('protocols_io/v3/normalized_single_protocol.json').read).to_h.with_indifferent_access + end + + describe '.build' do + it { expect(subject.build).to be_instance_of(Protocol) } + end + + describe '.import' do + context 'when have valid object' do + it { expect { pio.import }.to change { Protocol.all.count }.by(1) } + it { expect { pio.import }.to change { Step.all.count }.by(66) } + it { expect(pio.import).to be_valid } + end + + context 'when have invalid object' do + it { expect(invalid_pio.import).to be_invalid } + it { expect { invalid_pio.import }.not_to(change { Protocol.all.count }) } + end + end +end From 807104035ca5d48234ab2a9fe17d2e4d5929854e Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Thu, 30 May 2019 08:35:16 +0200 Subject: [PATCH 07/73] Add StepBuilder, TableBuilder, AssetBuilder for PIO, normalized updated --- app/models/step.rb | 2 +- .../protocol_importers/attachments_builder.rb | 17 + .../protocol_description_builder.rb | 21 + .../protocol_intermediate_object.rb | 41 +- .../protocols_io/v3/protocol_normalizer.rb | 12 +- .../protocols_io/v3/step_components.rb | 130 +- .../step_description_builder.rb | 35 + .../protocol_importers/tables_builder.rb | 27 + .../step_description/_details.html.erb | 6 + .../step_description/amount.html.erb | 4 + .../step_description/command.html.erb | 5 + .../step_description/concentration.html.erb | 3 + .../step_description/dataset.html.erb | 5 + .../step_description/duration.html.erb | 4 + .../step_description/gotostep.html.erb | 4 + .../step_description/link.html.erb | 4 + .../step_description/note.html.erb | 4 + .../step_description/reagent.html.erb | 7 + .../step_description/result.html.erb | 3 + .../step_description/software.html.erb | 6 + .../step_description/temperature.html.erb | 3 + .../step_description/warning.html.erb | 3 + .../description_with_body.json | 5 + .../description_with_body_html.json | 5 + .../description_with_extra_content.json | 19 + .../description_with_image.json | 6 + .../v3/normalized_single_protocol.json | 128 + .../protocols_io/v3/single_protocol.json | 1486 +++++ .../step_description_with_components.json | 22 + .../step_with_attachments.json | 18 + .../v3/normalized_single_protocol.json | 411 -- .../protocols_io/v3/single_protocol.json | 4876 ----------------- .../attachments_builder_spec.rb | 27 + .../protocol_description_builder_spec.rb | 48 + .../protocol_intermediate_object_spec.rb | 12 +- .../v3/protocol_normalizer_spec.rb | 6 +- .../step_description_builder_spec.rb | 64 + .../protocol_importers/tables_builder_spec.rb | 34 + 38 files changed, 2194 insertions(+), 5319 deletions(-) create mode 100644 app/utilities/protocol_importers/attachments_builder.rb create mode 100644 app/utilities/protocol_importers/protocol_description_builder.rb create mode 100644 app/utilities/protocol_importers/step_description_builder.rb create mode 100644 app/utilities/protocol_importers/tables_builder.rb create mode 100644 app/views/templates/protocols_import/step_description/_details.html.erb create mode 100644 app/views/templates/protocols_import/step_description/amount.html.erb create mode 100644 app/views/templates/protocols_import/step_description/command.html.erb create mode 100644 app/views/templates/protocols_import/step_description/concentration.html.erb create mode 100644 app/views/templates/protocols_import/step_description/dataset.html.erb create mode 100644 app/views/templates/protocols_import/step_description/duration.html.erb create mode 100644 app/views/templates/protocols_import/step_description/gotostep.html.erb create mode 100644 app/views/templates/protocols_import/step_description/link.html.erb create mode 100644 app/views/templates/protocols_import/step_description/note.html.erb create mode 100644 app/views/templates/protocols_import/step_description/reagent.html.erb create mode 100644 app/views/templates/protocols_import/step_description/result.html.erb create mode 100644 app/views/templates/protocols_import/step_description/software.html.erb create mode 100644 app/views/templates/protocols_import/step_description/temperature.html.erb create mode 100644 app/views/templates/protocols_import/step_description/warning.html.erb create mode 100644 spec/fixtures/files/protocols_importer/description_with_body.json create mode 100644 spec/fixtures/files/protocols_importer/description_with_body_html.json create mode 100644 spec/fixtures/files/protocols_importer/description_with_extra_content.json create mode 100644 spec/fixtures/files/protocols_importer/description_with_image.json create mode 100644 spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json create mode 100644 spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json create mode 100644 spec/fixtures/files/protocols_importer/step_description_with_components.json create mode 100644 spec/fixtures/files/protocols_importer/step_with_attachments.json delete mode 100644 spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json delete mode 100644 spec/fixtures/files/protocols_io/v3/single_protocol.json create mode 100644 spec/utilities/protocol_importers/attachments_builder_spec.rb create mode 100644 spec/utilities/protocol_importers/protocol_description_builder_spec.rb create mode 100644 spec/utilities/protocol_importers/step_description_builder_spec.rb create mode 100644 spec/utilities/protocol_importers/tables_builder_spec.rb diff --git a/app/models/step.rb b/app/models/step.rb index ee5111216..8cdbd35fd 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -131,7 +131,7 @@ class Step < ApplicationRecord end def set_last_modified_by - if @current_user + if @current_user&.is_a?(User) self.tables.each do |t| t.created_by ||= @current_user t.last_modified_by = @current_user if t.changed? diff --git a/app/utilities/protocol_importers/attachments_builder.rb b/app/utilities/protocol_importers/attachments_builder.rb new file mode 100644 index 000000000..7efcb3c6a --- /dev/null +++ b/app/utilities/protocol_importers/attachments_builder.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module ProtocolImporters + module AttachmentsBuilder + def self.generate(step_json) + return [] unless step_json[:attachments]&.any? + + step_json[:attachments].map do |f| + Asset.new(file: URI.parse(f[:url]), + created_by: @user, + last_modified_by: @user, + team: @team, + file_file_name: f[:name]) + end + end + end +end diff --git a/app/utilities/protocol_importers/protocol_description_builder.rb b/app/utilities/protocol_importers/protocol_description_builder.rb new file mode 100644 index 000000000..a0cb4eb03 --- /dev/null +++ b/app/utilities/protocol_importers/protocol_description_builder.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ProtocolImporters + class ProtocolDescriptionBuilder + def self.generate(protocol_json) + return '' unless protocol_json[:description] + + html_desc = '' + html_desc = "

#{remove_html(protocol_json[:description][:body])}

" if protocol_json[:description][:body] + html_desc += "
" if protocol_json[:description][:image] + html_desc += protocol_json[:description][:extra_content]&.map do |i| + "

#{remove_html(i[:title])}:
#{remove_html(i[:body])}

" + end&.join('
').to_s + html_desc + end + + def self.remove_html(string) + ActionView::Base.full_sanitizer.sanitize(string) + end + end +end diff --git a/app/utilities/protocol_importers/protocol_intermediate_object.rb b/app/utilities/protocol_importers/protocol_intermediate_object.rb index 62ce77702..fd3bcf5a5 100644 --- a/app/utilities/protocol_importers/protocol_intermediate_object.rb +++ b/app/utilities/protocol_importers/protocol_intermediate_object.rb @@ -2,48 +2,43 @@ module ProtocolImporters class ProtocolIntermediateObject - attr_accessor :normalized_response, :user, :team + attr_accessor :normalized_protocol_data, :user, :team, :protocol def initialize(normalized_json: {}, user:, team:) - @normalized_response = normalized_json[:protocol] if normalized_json + @normalized_protocol_data = normalized_json.with_indifferent_access[:protocol] if normalized_json @user = user @team = team end def import - p = build - p.save if p.valid? - p + build unless @protocol + @protocol.save + @protocol end def build - p = build_protocol - p.steps << build_steps - p + @protocol = Protocol.new(protocol_attributes) + @protocol.description = ProtocolDescriptionBuilder.generate(@normalized_protocol_data&.reject { |k| k == :steps }) + @protocol.steps << build_steps + @protocol end private - def build_protocol - Protocol.new(protocol_attributes) - end - def build_steps - # TODO - # Add: - # - Assets - # - Tables - # - Checklists - - @normalized_response[:steps].map do |s| - Step.new(step_attributes(s)) + @normalized_protocol_data[:steps].map do |s| + step = Step.new(step_attributes(s)) + step.description = StepDescriptionBuilder.generate(s) + step.assets << AttachmentsBuilder.generate(s) + step.tables << TablesBuilder.extract_tables_from_html_string(s[:description][:body]) + step end end def protocol_attributes defaults = { protocol_type: :in_repository_public, added_by: @user, team: @team } - values = %i(name published_on description authors) - p_attrs = @normalized_response.slice(*values).each_with_object({}) do |(k, v), h| + values = %i(name published_on authors) + p_attrs = @normalized_protocol_data.slice(*values).each_with_object({}) do |(k, v), h| h[k] = k == 'published_on' ? Time.at(v) : v end p_attrs.merge!(defaults) @@ -51,7 +46,7 @@ module ProtocolImporters def step_attributes(step_json) defaults = { user: @user, completed: false } - step_json.slice(:name, :position, :description).merge!(defaults) + step_json.slice(:name, :position).merge!(defaults) end end end diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index fd4c329f0..48a4df86e 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -15,7 +15,8 @@ module ProtocolImporters def normalize(response) protocol_hash = response.parsed_response.with_indifferent_access[:protocol] - normalized_data = {} + normalized_data = Hash.new { |h, k| h[k] = {} } + normalized_data[:uri] = response.request.last_uri.to_s normalized_data[:source] = Constants::PROTOCOLS_IO_V3_API[:source_id] normalized_data[:doi] = protocol_hash[:doi] @@ -23,14 +24,19 @@ module ProtocolImporters normalized_data[:version] = protocol_hash[:version_id] normalized_data[:source_id] = protocol_hash[:id] normalized_data[:name] = protocol_hash[:title] - normalized_data[:description] = protocol_hash[:description] + normalized_data[:description][:body] = protocol_hash[:description] + normalized_data[:description][:image] = protocol_hash[:image][:source] normalized_data[:authors] = protocol_hash[:authors].map { |e| e[:name] }.join(', ') normalized_data[:steps] = protocol_hash[:steps].map do |e| { source_id: e[:id], name: StepComponents.name(e[:components]), - description: StepComponents.description(e[:components]), + attachments: StepComponents.attachments(e[:components]), + description: { + body: StepComponents.description(e[:components]), + components: StepComponents.description_components(e[:components]) + }, position: e[:previous_id].nil? ? 0 : nil } end diff --git a/app/utilities/protocol_importers/protocols_io/v3/step_components.rb b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb index 08c23d9bd..13d988699 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/step_components.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb @@ -6,7 +6,20 @@ module ProtocolImporters class StepComponents AVAILABLE_COMPONENTS = { 6 => :title, - 1 => :description + 1 => :description, + 3 => :amount, + 4 => :duration, + 7 => :link, + 8 => :software, + 9 => :dataset, + 15 => :command, + 17 => :result, + 19 => :safety, + 20 => :reagents, + 22 => :gotostep, + 24 => :temperature, + 25 => :concentration, + 26 => :notes }.freeze def self.get_component(id, components) @@ -24,6 +37,121 @@ module ProtocolImporters def self.description(components) get_component(1, components)[:source][:description] end + + def self.description_components(components) + description_components = components.select { |c| AVAILABLE_COMPONENTS.except(6, 1).include?(c[:type_id]) } + + description_components.map do |dc| + build_desc_component dc + end.compact + end + + def self.attachments(components) + components.select { |c| c[:type_id] == 23 }.map do |cc| + { + url: cc[:source][:source], + name: cc[:source][:name] + } + end + end + + def self.build_desc_component(desc_component) + case AVAILABLE_COMPONENTS[desc_component[:type_id]] + when :amount + { + type: 'amount', + value: desc_component[:source][:amount], + unit: desc_component[:source][:unit], + name: desc_component[:source][:title] + } + when :duration + { + type: 'duration', + value: desc_component[:source][:duration], + name: desc_component[:source][:title] + } + when :link + { + type: 'link', + source: desc_component[:source][:link] + } + when :software + { + type: 'software', + name: desc_component[:source][:name], + source: desc_component[:source][:link], + details: { + repository_link: desc_component[:source][:repository], + developer: desc_component[:source][:developer], + os_name: desc_component[:source][:os_name] + } + } + when :command + { + type: 'command', + software_name: desc_component[:source][:name], + command: desc_component[:source][:command], + details: { + os_name: desc_component[:source][:os_name] + } + } + when :result + { + type: 'result', + body: desc_component[:source][:body] + } + when :safety + { + type: 'warning', + body: desc_component[:source][:body], + details: { + link: desc_component[:source][:link] + } + } + when :reagents + { + type: 'reagent', + name: desc_component[:source][:name], + mol_weight: desc_component[:source][:mol_weight], + details: { + link: desc_component[:source][:vendor][:link], + linear_formula: desc_component[:source][:linfor] + } + } + when :gotostep + { + type: 'gotostep', + value: desc_component[:source][:title], + step_id: desc_component[:source][:step_guid] + } + when :temperature + { + type: 'temperature', + value: desc_component[:source][:temperature], + unit: desc_component[:source][:unit], + name: desc_component[:source][:title] + } + when :concentration + { + type: 'concentration', + value: desc_component[:source][:concentration], + unit: desc_component[:source][:unit], + name: desc_component[:source][:title] + } + when :notes + { + type: 'note', + author: desc_component[:source][:creator][:name], + body: desc_component[:source][:body] + } + when :dataset + { + type: 'dataset', + name: desc_component[:source][:name], + source: desc_component[:source][:link] + } + end + end end end end diff --git a/app/utilities/protocol_importers/step_description_builder.rb b/app/utilities/protocol_importers/step_description_builder.rb new file mode 100644 index 000000000..5714f976e --- /dev/null +++ b/app/utilities/protocol_importers/step_description_builder.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module ProtocolImporters + class StepDescriptionBuilder + def self.generate(step_json) + return '' unless step_json[:description] + + html_description = '' + html_description = "

#{remove_html(step_json[:description][:body])}

" if step_json[:description][:body] + + # Add components from JSON + html_description += step_json[:description][:components]&.inject('') do |html_string, component| + sanitized_component = component.except(:type) + sanitized_component[:body] = remove_html(component[:body]) if component[:body] + + html_string + ApplicationController + .renderer + .render(template: "templates/protocols_import/step_description/#{component[:type]}", + layout: false, + assigns: { item: sanitized_component }) + end.to_s + + # Add extra content from JSON + html_description += step_json[:description][:extra_content]&.map do |i| + "

#{remove_html(i[:title])}:
#{remove_html(i[:body])}

" + end&.join('
').to_s + + html_description + end + + def self.remove_html(string) + ActionView::Base.full_sanitizer.sanitize(string) + end + end +end diff --git a/app/utilities/protocol_importers/tables_builder.rb b/app/utilities/protocol_importers/tables_builder.rb new file mode 100644 index 000000000..242b9a881 --- /dev/null +++ b/app/utilities/protocol_importers/tables_builder.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ProtocolImporters + class TablesBuilder + def self.extract_tables_from_html_string(description_string) + tables = [] + + doc = Nokogiri::HTML(description_string) + tables_nodeset = doc.css('table') + + tables_nodeset.each do |table_node| + rows_nodeset = table_node.css('tr') + + two_d_array = Array.new(rows_nodeset.count) { [] } + + rows_nodeset.each_with_index do |row, i| + row.css('td').each_with_index do |cell, j| + two_d_array[i][j] = cell.inner_html + end + end + + tables << Table.new(contents: { data: two_d_array }.to_json) + end + tables + end + end +end diff --git a/app/views/templates/protocols_import/step_description/_details.html.erb b/app/views/templates/protocols_import/step_description/_details.html.erb new file mode 100644 index 000000000..5b4d4b3f7 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/_details.html.erb @@ -0,0 +1,6 @@ +

+ <%= item[:details]&.any? ? 'Details:' : '' %> + <% item[:details]&.each do |k, v| %> + <%= "#{k.camelize.to_s}: #{v.to_s}" %> + <% end %> +

diff --git a/app/views/templates/protocols_import/step_description/amount.html.erb b/app/views/templates/protocols_import/step_description/amount.html.erb new file mode 100644 index 000000000..a8ecb982f --- /dev/null +++ b/app/views/templates/protocols_import/step_description/amount.html.erb @@ -0,0 +1,4 @@ +

Amount:
+ <%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]} " %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/command.html.erb b/app/views/templates/protocols_import/step_description/command.html.erb new file mode 100644 index 000000000..b0c97c75b --- /dev/null +++ b/app/views/templates/protocols_import/step_description/command.html.erb @@ -0,0 +1,5 @@ +

Command:
+ <%= "Name: #{@item[:name]}" %> + <%= "Command: #{@item[:command]}" %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/concentration.html.erb b/app/views/templates/protocols_import/step_description/concentration.html.erb new file mode 100644 index 000000000..9df661d95 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/concentration.html.erb @@ -0,0 +1,3 @@ +

Concentration:
+<%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]} " %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/dataset.html.erb b/app/views/templates/protocols_import/step_description/dataset.html.erb new file mode 100644 index 000000000..4b8ce3921 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/dataset.html.erb @@ -0,0 +1,5 @@ +

Dataset:
+ <%= "Name: #{@item[:name]}" %> + <%= "Link: #{ActionController::Base.helpers.link_to(@item[:source], @item[:source])}" if @item[:source] %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/duration.html.erb b/app/views/templates/protocols_import/step_description/duration.html.erb new file mode 100644 index 000000000..f0f68e48f --- /dev/null +++ b/app/views/templates/protocols_import/step_description/duration.html.erb @@ -0,0 +1,4 @@ +

Duration:
+ <%= "#{@item[:name]} #{@item[:value]} seconds" %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/gotostep.html.erb b/app/views/templates/protocols_import/step_description/gotostep.html.erb new file mode 100644 index 000000000..fbe41eff8 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/gotostep.html.erb @@ -0,0 +1,4 @@ +

Go to step:
+Step nr: <%= @item[:step_id] %>
+When: <%= @item[:value] %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/link.html.erb b/app/views/templates/protocols_import/step_description/link.html.erb new file mode 100644 index 000000000..13d139192 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/link.html.erb @@ -0,0 +1,4 @@ + +<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/note.html.erb b/app/views/templates/protocols_import/step_description/note.html.erb new file mode 100644 index 000000000..e5e284686 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/note.html.erb @@ -0,0 +1,4 @@ +

Note:
+Author: <%= @item[:author] %>
+<%= @item[:body] %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/reagent.html.erb b/app/views/templates/protocols_import/step_description/reagent.html.erb new file mode 100644 index 000000000..b51b795c0 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/reagent.html.erb @@ -0,0 +1,7 @@ +

Reagent:
+ <%= "Name: #{@item[:name]}" %> + <%= "Name: #{@item[:mol_weight]}" %> + <%= "Link: #{@item[:source]}" if @item[:source] %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> + diff --git a/app/views/templates/protocols_import/step_description/result.html.erb b/app/views/templates/protocols_import/step_description/result.html.erb new file mode 100644 index 000000000..560ac17d6 --- /dev/null +++ b/app/views/templates/protocols_import/step_description/result.html.erb @@ -0,0 +1,3 @@ +

Expected result:
+<%= @item[:body] %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/software.html.erb b/app/views/templates/protocols_import/step_description/software.html.erb new file mode 100644 index 000000000..7abd87f3c --- /dev/null +++ b/app/views/templates/protocols_import/step_description/software.html.erb @@ -0,0 +1,6 @@ +

Software:
+ <%= "Name of software: #{@item[:name]}" %> + <%= "Link: #{ActionController::Base.helpers.link_to(@item[:source], @item[:source])}" %> +

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> + diff --git a/app/views/templates/protocols_import/step_description/temperature.html.erb b/app/views/templates/protocols_import/step_description/temperature.html.erb new file mode 100644 index 000000000..4dadc06eb --- /dev/null +++ b/app/views/templates/protocols_import/step_description/temperature.html.erb @@ -0,0 +1,3 @@ +

Temperature:
+<%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]}" %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/warning.html.erb b/app/views/templates/protocols_import/step_description/warning.html.erb new file mode 100644 index 000000000..141ad719d --- /dev/null +++ b/app/views/templates/protocols_import/step_description/warning.html.erb @@ -0,0 +1,3 @@ +

Warning:
+<%= @item[:body] %>

+<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/spec/fixtures/files/protocols_importer/description_with_body.json b/spec/fixtures/files/protocols_importer/description_with_body.json new file mode 100644 index 000000000..19b62ffab --- /dev/null +++ b/spec/fixtures/files/protocols_importer/description_with_body.json @@ -0,0 +1,5 @@ +{ + "description": { + "body": "original desc" + } +} diff --git a/spec/fixtures/files/protocols_importer/description_with_body_html.json b/spec/fixtures/files/protocols_importer/description_with_body_html.json new file mode 100644 index 000000000..4a91e3c44 --- /dev/null +++ b/spec/fixtures/files/protocols_importer/description_with_body_html.json @@ -0,0 +1,5 @@ +{ + "description": { + "body": "
Text only
" + } +} diff --git a/spec/fixtures/files/protocols_importer/description_with_extra_content.json b/spec/fixtures/files/protocols_importer/description_with_extra_content.json new file mode 100644 index 000000000..ac2aa76a3 --- /dev/null +++ b/spec/fixtures/files/protocols_importer/description_with_extra_content.json @@ -0,0 +1,19 @@ +{ + "description": { + "body": "original desc", + "extra_content": [ + { + "title": "First extra", + "body": "Body of first extra." + }, + { + "title": "Second extra", + "body": "Body of second extra." + }, + { + "title": "Third extra", + "body": "Body of third extra." + } + ] + } +} diff --git a/spec/fixtures/files/protocols_importer/description_with_image.json b/spec/fixtures/files/protocols_importer/description_with_image.json new file mode 100644 index 000000000..364c7e937 --- /dev/null +++ b/spec/fixtures/files/protocols_importer/description_with_image.json @@ -0,0 +1,6 @@ +{ + "description": { + "body": "original desc", + "image": "www.example.com/images/first.jpg" + } +} diff --git a/spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json b/spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json new file mode 100644 index 000000000..8252450f6 --- /dev/null +++ b/spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json @@ -0,0 +1,128 @@ +{ + "protocol": { + "uri": "https://www.protocols.io/api/v3/protocols/9451", + "source": "protocolsio/v3", + "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", + "published_on": 1516132805, + "version": 0, + "source_id": 9451, + "name": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "description": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", + "image": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "authors": "Peter J. Skene, Steven Henikoff", + "steps": [ + { + "source_id": 601564, + "name": "Binding cells to beads", + "description": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", + "components": [ + { + "type": "amount", + "value": 123, + "unit": "µl", + "name": "of MGH" + }, + { + "type": "duration", + "value": 11, + "name": "boil" + }, + { + "type": "link", + "source": "www.google.com/protocols/123" + }, + { + "type": "software", + "name": "RubyMine", + "source": "www.rubymine.com", + "details": { + "repository_link": "www.github.com/rubymine/", + "developer": "JozaHans", + "os_name": "OSX and Linux." + } + }, + { + "type": "dataset", + "name": "WiFi passwords dictionary", + "source": "www.passwords.com/wifi" + }, + { + "type": "command", + "software_name": "Terminal", + "command": "curl", + "details": { + "os_name": "OSX and Linux" + } + }, + { + "type": "result", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is expected result with HTML.\u003c/div\u003e\u003c/div\u003e" + }, + { + "type": "warning", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is WARNING with HTML.\u003c/div\u003e\u003c/div\u003e", + "details": { + "link": "www.warnings.com/be_careful" + } + }, + { + "type": "reagent", + "name": "2 mg Gastrin I, human", + "mol_weight": 0.1, + "details": { + "link": "https://www.biorbyt.com/gastrin-i-human-orb321073", + "linear_formula": "C130H204N38O31S" + } + }, + { + "type": "gotostep", + "value": "In case of someting, go to step", + "step_id": 601565 + }, + { + "type": "temperature", + "value": 55, + "unit": "°C", + "name": "boil" + }, + { + "type": "concentration", + "value": 12, + "unit": "g/L", + "name": "Name of concentration" + }, + { + "type": "note", + "author": "Frank Jones", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is expected result with HTML.\u003c/div\u003e\u003c/div\u003e" + } + ] + }, + "attachments": [ + { + "url": "https://pbs.twimg.com/media/Cwu3zrZWQAA7axs.jpg", + "name": "First file" + }, + { + "url": "http://something.com/wp-content/uploads/2014/11/14506718045_5b3e71dacd_o.jpg", + "name": "Second file" + } + ], + "position": 0 + }, + { + "source_id": 601565, + "name": "Binding cells to beads 2", + "description": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e", + "components": [] + }, + "attachments": [], + "position": 1 + } + ] + } +} diff --git a/spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json b/spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json new file mode 100644 index 000000000..8542d071c --- /dev/null +++ b/spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json @@ -0,0 +1,1486 @@ +{ + "protocol": { + "id": 9451, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", + "doi_status": 2, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 1, + "published_on": 1516132805, + "stats": { + "number_of_views": 17928, + "number_of_steps": 66, + "number_of_bookmarks": 65, + "number_of_comments": 122, + "number_of_exports": 397, + "number_of_runs": 39 + }, + "version_id": 0, + "created_on": 1515173338, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [ + { + "id": 2, + "image": { + "source": "/img/badges/bronze.svg", + "placeholder": "/img/badges/bronze.svg" + }, + "name": "Author" + }, + { + "id": 6, + "image": { + "source": "/img/badges/socialbutterfly.svg", + "placeholder": "/img/badges/socialbutterfly.svg" + }, + "name": "Social butterfly" + } + ], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null, + "public": 1, + "has_versions": 1, + "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", + "total_collections": 0, + "number_of_steps": 66, + "authors": [ + { + "name": "Peter J. Skene", + "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + }, + { + "name": "Steven Henikoff", + "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + } + ], + "versions": [ + { + "id": 9451, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 0, + "published_on": 1516132805, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 122, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 0, + "created_on": 1515173338, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + }, + { + "id": 11190, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-n6wdhfe", + "type_id": 0, + "published_on": 1551890606, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 30, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 1, + "created_on": 1522601124, + "categories": null, + "creator": { + "name": "Steven Henikoff", + "affiliation": "Fred Hutchinson Cancer Research Center", + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + }, + { + "id": 21615, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + }, + "doi": null, + "doi_status": 0, + "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-zcpf2vn", + "type_id": 0, + "published_on": 1557428439, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 12, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "version_id": 2, + "created_on": 1553027602, + "categories": null, + "creator": { + "name": "Derek Janssens", + "affiliation": null, + "username": "derek-janssens", + "link": null, + "image": { + "source": "/img/avatars/017.png", + "placeholder": "/img/avatars/017.png" + }, + "badges": [], + "research_interests": null + }, + "journal": null, + "journal_name": null, + "journal_link": null + } + ], + "groups": [ + { + "id": 523, + "uri": "hca", + "title": "Human Cell Atlas Method Development Community", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 523, + "uri": "hca", + "title": "Human Cell Atlas Method Development Community", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": "Steven Henikoff", + "affiliation": null, + "username": "steven-henikoff", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" + }, + "badges": [], + "research_interests": null + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": true, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": "Steven Henikoff", + "affiliation": null, + "affiliation_url": null, + "username": "steven-henikoff", + "link": null + }, + "protocol": { + "id": 0, + "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + } + }, + "created_on": 1516132805, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 1, + "forks_count": { + "private": 27, + "public": 0 + }, + "access": { + "can_view": 0, + "can_remove": 0, + "can_add": 0, + "can_edit": 0, + "can_publish": 0, + "can_get_doi": 0, + "can_share": 0, + "can_move": 0, + "can_move_outside": 0, + "can_transfer": 0, + "can_download": 1, + "is_locked": 0 + }, + "steps": [ + { + "id": 601564, + "guid": "F9349635A9AB4395AD1A410F66F785CF", + "previous_id": null, + "previous_guid": null, + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "B4239EB74A144551AD46C1ADC6707144", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads" + } + }, + { + "id": 1054724, + "guid": "BA370A7410C749C580F428A9A8B515D7", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054723, + "guid": "9165EC7527BD42D4B49D91025D410E2F", + "order_id": 5, + "type_id": 3, + "title": "amount", + "source": { + "amount": 123, + "unit": "µl", + "title": "of MGH" + } + }, + { + "id": 1054723, + "guid": "53B3062E48EC4B2CACE6F40940F733E0", + "order_id": 9, + "type_id": 4, + "title": "duration", + "source": { + "duration": 11, + "title": "boil" + } + }, + { + "id": 12312312, + "guid": "53B3062E48ECAKCIQCE6F40940F733E0", + "order_id": 10, + "type_id": 7, + "title": "link", + "source": { + "link": "www.google.com/protocols/123" + } + }, + { + "id": 1054723, + "guid": "9F03B1015AC84F9E907B070672FF28E2", + "order_id": 14, + "type_id": 8, + "title": "software", + "source": { + "name": "RubyMine", + "developer": "JozaHans", + "repository": "www.github.com/rubymine/", + "link": "www.rubymine.com", + "os_name": "OSX and Linux.", + "os_version": "os 12.1" + } + }, + { + "id": 1054723, + "guid": "A85D4EBC0F394B38A4C8D65D5CB6877F", + "order_id": 4, + "type_id": 9, + "title": "dataset", + "source": { + "name": "WiFi passwords dictionary", + "link": "www.passwords.com/wifi" + } + }, + { + "id": 1054723, + "guid": "7A73C481C6A941DB918388C5445E57DC", + "order_id": 6, + "type_id": 15, + "title": "command", + "source": { + "name": "Terminal", + "command": "curl", + "os_name": "OSX and Linux" + } + }, + { + "id": 1054723, + "guid": "4FAF665BD86640D5902C91126205E70C", + "order_id": 10, + "type_id": 17, + "title": "result", + "source": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is expected result with HTML.\u003c/div\u003e\u003c/div\u003e" + } + }, + { + "id": 1054723, + "guid": "96A41B1AA3D142A8B44207A966D389C3", + "order_id": 13, + "type_id": 19, + "title": "safety", + "source": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is WARNING with HTML.\u003c/div\u003e\u003c/div\u003e", + "link": "www.warnings.com/be_careful" + } + }, + { + "id": 1054723, + "guid": "7F3C20217A1748849CCBD12FDF505088", + "order_id": 12, + "type_id": 20, + "title": "reagents", + "source": { + "id": 116672, + "mol_weight": 0.1, + "name": "2 mg Gastrin I, human", + "linfor": "C130H204N38O31S", + "url": "", + "sku": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.biorbyt.com/gastrin-i-human-orb321073", + "image": { + "source": "https:\/\/www.protocols.iohttps:\/\/www.protocols.io\/img\/vendors\/1.png", + "placeholder": "https:\/\/www.protocols.iohttps:\/\/www.protocols.io\/img\/vendors\/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 1, + "stats": { + "total_protocols": 0 + } + } + }, + { + "id": 1055261, + "guid": "96A41B1AA3D142A8B44207A966D389C3", + "order_id": 13, + "type_id": 22, + "title": "gotostep", + "source": { + "title": "In case of someting, go to step", + "step_guid": 601565 + } + }, + { + "id": 1054723, + "guid": "F62B273C91EF4D1D841BCDDD87B15C18", + "order_id": 15, + "type_id": 24, + "title": "temperature", + "source": { + "temperature": 55, + "unit": "\u00b0C", + "title": "boil" + } + }, + { + "id": 1054723, + "guid": "61EDE338246C4393A56595D037F2FAE8", + "order_id": 3, + "type_id": 25, + "title": "concentration", + "source": { + "concentration": 12, + "unit": "g/L", + "title": "Name of concentration" + } + }, + { + "id": 1054723, + "guid": "2CCE644F1ED949949A13759539FE768F", + "order_id": 11, + "type_id": 26, + "title": "notes", + "source": { + "id": 0, + "parent_id": 0, + "uri": "", + "title": "", + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min... This is expected result with HTML.\u003c/div\u003e\u003c/div\u003e", + "created_on": 0, + "changed_on": 0, + "creator": { + "name": "Frank Jones", + "affiliation": "", + "username": "", + "link": null, + "image": { + "source": "", + "placeholder": "" + }, + "badges": [], + "research_interests": null + }, + "comments": [] + } + }, + { + "id": 1054723, + "guid": "A85D4EBC0F394B38A4C8D65D5CB6877F", + "order_id": 4, + "type_id": 23, + "title": "file", + "source": { + "name": "First file", + "source": "https://pbs.twimg.com/media/Cwu3zrZWQAA7axs.jpg" + } + }, + { + "id": 1054723, + "guid": "A85D4EBC0F394B38A4C8D65D5CB6877F", + "order_id": 4, + "type_id": 23, + "title": "file", + "source": { + "name": "Second file", + "source": "http://something.com/wp-content/uploads/2014/11/14506718045_5b3e71dacd_o.jpg" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + }, + { + "id": 601565, + "guid": "533BCACB836840EEA0AFEB15424F1208", + "previous_id": 601564, + "previous_guid": "F9349635A9AB4395AD1A410F66F785CF", + "modified_on": 0, + "protocol_id": 0, + "components": [ + { + "id": 1054723, + "guid": "F96FA71527B64CE0941CBB92181E74B7", + "order_id": 1, + "type_id": 6, + "title": "Section", + "source": { + "title": "Binding cells to beads 2" + } + }, + { + "id": 1054724, + "guid": "E8A6A960B6204912A8F6697C44845E78", + "order_id": 1, + "type_id": 1, + "title": "description", + "source": { + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e" + } + } + ], + "cases": null, + "data": null, + "section": null, + "section_color": null, + "duration": 0 + } + ], + "materials": [ + { + "id": 119441, + "mol_weight": 0, + "name": "Cell suspension. We have used human K562 cells, Drosophila S2 cells and dissected Drosophila tissues such as brains and imaginal disks, and spheroplasted yeast.", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119442, + "mol_weight": 0, + "name": "Concanavalin-coated magnetic beads ", + "linfor": null, + "url": "", + "sku": "BP531", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Bangs Laboratories", + "affiliation": null, + "username": null, + "link": "http://www.bangslabs.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 278 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119443, + "mol_weight": 0, + "name": "Antibody to an epitope of interest. For example, rabbit α-CTCF polyclonal antibody (Millipore 07-729) for mapping 1D and 3D interactions by CUT\u0026RUN", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119444, + "mol_weight": 0, + "name": "Positive control antibody to an abundant epitope, e.g. α-H3K27me3 rabbit monoclonal antibody (Cell Signaling Technology, cat. no. 9733)", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119445, + "mol_weight": 0, + "name": "Negative control antibody to an absent epitope, e.g. guinea pig α-rabbit antibody", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119446, + "mol_weight": 0, + "name": "5% Digitonin ", + "linfor": null, + "url": "", + "sku": "300410", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Emd Millipore", + "affiliation": null, + "username": null, + "link": "http://www.emdmillipore.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 8 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119447, + "mol_weight": 0, + "name": "Protein A–Micrococcal Nuclease (pA-MNase) fusion protein (provided in 50% glycerol by the authors upon request). Store at -20 oC.", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119448, + "mol_weight": 0, + "name": "Spike-in DNA (e.g., from Saccharomyces cerevisiae micrococcal nuclease-treated chromatin, provided by authors upon request)", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119449, + "mol_weight": 0, + "name": "Distilled, deionized or RNAse-free H2O (dH2O e.g., Promega, cat. no. P1197)", + "linfor": null, + "url": "", + "sku": "P1197", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Promega", + "affiliation": null, + "username": null, + "link": "http://www.promega.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 23 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119450, + "mol_weight": 0, + "name": "1 M Manganese Chloride (MnCl2)", + "linfor": null, + "url": "", + "sku": "203734", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119451, + "mol_weight": 0, + "name": "1 M Calcium Chloride (CaCl2)", + "linfor": null, + "url": "", + "sku": "BP510", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.fishersci.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 31 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119452, + "mol_weight": 0, + "name": "1 M Potassium Chloride (KCl)", + "linfor": null, + "url": "", + "sku": "P3911", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119453, + "mol_weight": 0, + "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.5 (HEPES (Na ))", + "linfor": null, + "url": "", + "sku": "H3375", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119454, + "mol_weight": 0, + "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.9 (HEPES (K ))", + "linfor": null, + "url": "", + "sku": "H3375", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119455, + "mol_weight": 0, + "name": "5 M Sodium chloride (NaCl)", + "linfor": null, + "url": "", + "sku": "S5150-1L", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119456, + "mol_weight": 0, + "name": "0.5 M Ethylenediaminetetraacetic acid (EDTA)", + "linfor": null, + "url": "", + "sku": "3002E", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Research Organics", + "affiliation": null, + "username": null, + "link": "http://www.sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 279 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119457, + "mol_weight": 0, + "name": "0.2 M Ethylene glycol-bis(β-aminoethyl ether)-N,N,N,N-tetraacetic acid (EGTA)", + "linfor": null, + "url": "", + "sku": "E3889", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119458, + "mol_weight": 0, + "name": "2 M Spermidine ", + "linfor": null, + "url": "", + "sku": "S2501", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119459, + "mol_weight": 0, + "name": "Roche Complete Protease Inhibitor EDTA-Free tablets ", + "linfor": null, + "url": "", + "sku": "5056489001", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119460, + "mol_weight": 0, + "name": "2 mg/ml Glycogen (1:10 dilution)", + "linfor": null, + "url": "", + "sku": "10930193001", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119461, + "mol_weight": 0, + "name": "RNase A, DNase and protease-free (10 mg/ml)", + "linfor": null, + "url": "", + "sku": "EN0531", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Thermo Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.thermofisher.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 95 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119462, + "mol_weight": 0, + "name": "Gel and PCR Clean-up kit ", + "linfor": null, + "url": "", + "sku": "740609.250", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Macherey and Nagel", + "affiliation": null, + "username": null, + "link": "http://www.mn-net.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 133 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119463, + "mol_weight": 0, + "name": "Agencourt AMPure XP magnetic beads ", + "linfor": null, + "url": "", + "sku": "A63880", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Beckman Coulter", + "affiliation": null, + "username": null, + "link": "https://www.beckmancoulter.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 32 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119464, + "mol_weight": 0, + "name": "10% Sodium dodecyl sulfate (SDS)", + "linfor": null, + "url": "", + "sku": "L4509", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma Aldrich", + "affiliation": null, + "username": null, + "link": "http://sigmaaldrich.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 17 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119465, + "mol_weight": 0, + "name": "Proteinase K", + "linfor": null, + "url": "", + "sku": "EO0492", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Thermo Fisher Scientific", + "affiliation": null, + "username": null, + "link": "https://www.thermofisher.com/us/en/home.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 95 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119466, + "mol_weight": 0, + "name": "Phenol-chloroform-isoamyl alcohol 25:24:1 (PCI)", + "linfor": null, + "url": "", + "sku": "15593049", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Invitrogen - Thermo Fisher", + "affiliation": null, + "username": null, + "link": "http://www.thermofisher.com/us/en/home/brands/invitrogen.html", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 191 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119467, + "mol_weight": 0, + "name": "Chloroform", + "linfor": null, + "url": "", + "sku": "366919-1L", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Sigma", + "affiliation": null, + "username": null, + "link": "www.sigma.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 114 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119468, + "mol_weight": 0, + "name": "1 M Tris-HCl pH 8.0", + "linfor": null, + "url": "", + "sku": "", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Contributed by users", + "affiliation": null, + "username": null, + "link": "https://www.protocols.io", + "image": { + "source": "https://www.protocols.io/img/vendors/1.png", + "placeholder": "https://www.protocols.io/img/vendors/1.png" + }, + "badges": [], + "research_interests": null, + "id": 1 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119469, + "mol_weight": 0, + "name": "Ethanol ", + "linfor": null, + "url": "", + "sku": "2716", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Decon Labs", + "affiliation": null, + "username": null, + "link": "http://deconlabs.com/", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 280 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + }, + { + "id": 119470, + "mol_weight": 0, + "name": "Qubit dsDNA HS kit ", + "linfor": null, + "url": "", + "sku": "Q32851", + "cas_number": "", + "rrid": "", + "public": 0, + "vendor": { + "name": "Life Technologies", + "affiliation": null, + "username": null, + "link": "http://www.lifetechnologies.com", + "image": { + "source": "https://www.protocols.io/img/vendors/placeholder.png", + "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" + }, + "badges": [], + "research_interests": null, + "id": 11 + }, + "can_edit": 0, + "stats": { + "total_protocols": 0 + } + } + ], + "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", + "changed_on": 1522231932 + }, + "status_code": 0 +} diff --git a/spec/fixtures/files/protocols_importer/step_description_with_components.json b/spec/fixtures/files/protocols_importer/step_description_with_components.json new file mode 100644 index 000000000..26ab61c2d --- /dev/null +++ b/spec/fixtures/files/protocols_importer/step_description_with_components.json @@ -0,0 +1,22 @@ +{ + "description": { + "body": "original desc", + "components": [ + { + "type": "note", + "author": "First extra", + "body": "Body of first extra." + }, + { + "type": "note", + "author": "Second extra", + "body": "
Text only
" + }, + { + "type": "note", + "author": "Third extra", + "body": "Body of third extra." + } + ] + } +} diff --git a/spec/fixtures/files/protocols_importer/step_with_attachments.json b/spec/fixtures/files/protocols_importer/step_with_attachments.json new file mode 100644 index 000000000..78f42f18c --- /dev/null +++ b/spec/fixtures/files/protocols_importer/step_with_attachments.json @@ -0,0 +1,18 @@ +{ + "source_id": 601564, + "name": "Binding cells to beads", + "position": 0, + "description": { + "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e" + }, + "attachments": [ + { + "url": "https://pbs.twimg.com/media/Cwu3zrZWQAA7axs.jpg", + "name": "First file" + }, + { + "url": "http://something.com/wp-content/uploads/2014/11/14506718045_5b3e71dacd_o.jpg", + "name": "Second file" + } + ] +} diff --git a/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json b/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json deleted file mode 100644 index 0624f84a2..000000000 --- a/spec/fixtures/files/protocols_io/v3/normalized_single_protocol.json +++ /dev/null @@ -1,411 +0,0 @@ -{ - "protocol": { - "uri": "https://www.protocols.io/api/v3/protocols/9451", - "source": "protocolsio/v3", - "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", - "published_on": 1516132805, - "version": 0, - "source_id": 9451, - "name": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", - "authors": "Peter J. Skene, Steven Henikoff", - "steps": [ - { - "source_id": 601564, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 0 - }, - { - "source_id": 601565, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 1 - }, - { - "source_id": 601566, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eResuspend in 1.5 ml room temperature Wash buffer by gently pipetting and transfer if necessary to a 2 ml tube. (wash 1/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 2 - }, - { - "source_id": 601567, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 1/2)\u003c/div\u003e\u003c/div\u003e", - "position": 3 - }, - { - "source_id": 601568, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAgain, resuspend in 1.5 ml room temperature Wash buffer by gently pipetting. Centrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 2/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 4 - }, - { - "source_id": 601569, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eResuspend in 1 ml room temperature Wash buffer by gently pipetting.\u003c/div\u003e\u003c/div\u003e", - "position": 5 - }, - { - "source_id": 601570, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhile gently vortexing the cells at room temperature, add the bead slurry.\u003c/div\u003e\u003c/div\u003e", - "position": 6 - }, - { - "source_id": 601571, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRotate 5-10 min at room temperature.\u003c/div\u003e\u003c/div\u003e", - "position": 7 - }, - { - "source_id": 601572, - "name": "Binding cells to beads", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDivide into aliquots in 1.5-ml tubes, one for each antibody to be used.\u003c/div\u003e\u003c/div\u003e", - "position": 8 - }, - { - "source_id": 601573, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 9 - }, - { - "source_id": 601574, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Antibody buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 10 - }, - { - "source_id": 601575, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4°C for ~2 hr (or at room temperature for 5-10 min)\u003c/div\u003e\u003c/div\u003e", - "position": 11 - }, - { - "source_id": 601576, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", - "position": 12 - }, - { - "source_id": 601577, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear (~30 s) and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 13 - }, - { - "source_id": 601578, - "name": "Bind (primary) antibodies", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting using a 1 ml tip if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 14 - }, - { - "source_id": 601579, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 15 - }, - { - "source_id": 601580, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 16 - }, - { - "source_id": 601581, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the secondary antibody to a final concentration of 1:100 or to the manufacturer’s recommended concentration for immunofluorescence.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 17 - }, - { - "source_id": 601582, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e", - "position": 18 - }, - { - "source_id": 601583, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", - "position": 19 - }, - { - "source_id": 601584, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 20 - }, - { - "source_id": 601585, - "name": "Bind secondary antibody (as required)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-Wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 21 - }, - { - "source_id": 601586, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 22 - }, - { - "source_id": 601587, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 23 - }, - { - "source_id": 601588, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the pA-MNase to a final concentration of ~700 ng/ml (e.g. 2.5 μL/50 μL of a 1:10 dilution of the 140 μg/ml glycerol stock provided upon request).\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 24 - }, - { - "source_id": 601589, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e", - "position": 25 - }, - { - "source_id": 601590, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e", - "position": 26 - }, - { - "source_id": 601591, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 27 - }, - { - "source_id": 601592, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 28 - }, - { - "source_id": 601593, - "name": "Bind Protein A-MNase fusion protein", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRepeat Dig-wash steps 28-29.\u003c/div\u003e\u003c/div\u003e", - "position": 29 - }, - { - "source_id": 601594, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e", - "position": 30 - }, - { - "source_id": 601595, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and add 100 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 31 - }, - { - "source_id": 601596, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eInsert tubes into the 1.5 ml wells of a heater block sitting in wet ice to chill down to 0 °C.\u003c/div\u003e\u003c/div\u003e", - "position": 32 - }, - { - "source_id": 601597, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eRemove each tube from the block, mix in 2 μL 100 mM CaCl\u003c/span\u003e\u003cspan style = \":justify;vertical-align:sub;\"\u003e2\u003c/span\u003e\u003cspan style = \":justify;\"\u003e (diluted 1:10 from a 1 M stock) with gentle vortexing and immediately replace the tube in the 0 °C block.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 33 - }, - { - "source_id": 601598, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate at 0 °C for the desired digestion time (default is 30 min).\u003c/div\u003e\u003c/div\u003e", - "position": 34 - }, - { - "source_id": 601599, - "name": "Targeted digestion", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 100 μL 2XSTOP and mix by gentle vortexing. When there are multiple time points, remove 100 μL to 100 μL 2XSTOP and mix by gentle vortexing.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 35 - }, - { - "source_id": 601600, - "name": "Target chromatin release", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate 10 min 37 °C to release CUT\u0026RUN fragments from the insoluble nuclear chromatin.\u003c/div\u003e\u003c/div\u003e", - "position": 36 - }, - { - "source_id": 601601, - "name": "Target chromatin release", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 5 min at 4 °C and 16,000 x g and place on magnet stand.\u003c/div\u003e\u003c/div\u003e", - "position": 37 - }, - { - "source_id": 601602, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace a spin column into a collection tube and add 400 μL Buffer NT1 (from NucleoSpin kit or equivalent).\u003c/div\u003e\u003c/div\u003e", - "position": 38 - }, - { - "source_id": 601603, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eDecant the supernatant cleanly from the pellet and transfer to the NT1 in the spin column pipetting gently up and down to mix.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 39 - }, - { - "source_id": 601604, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e", - "position": 40 - }, - { - "source_id": 601605, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e", - "position": 41 - }, - { - "source_id": 601606, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through and replace in rotor.\u003c/div\u003e\u003c/div\u003e", - "position": 42 - }, - { - "source_id": 601607, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge for 1 min at 11,000 x g. Let dry 5 min.\u003c/div\u003e\u003c/div\u003e", - "position": 43 - }, - { - "source_id": 601608, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace in a fresh tube and add 20-40 μL Buffer NE to membrane.\u003c/div\u003e\u003c/div\u003e", - "position": 44 - }, - { - "source_id": 601609, - "name": "Option A: Fast DNA extraction by spin column", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAfter 1 min, centrifuge for 1 min at 11,000 x g.\u003c/div\u003e\u003c/div\u003e", - "position": 45 - }, - { - "source_id": 601610, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDecant the supernatant cleanly from the pellet and transfer to a fresh 1.5-ml microcentrifuge tube.\u003c/div\u003e\u003c/div\u003e", - "position": 46 - }, - { - "source_id": 601611, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eTo each sample add 2 μL 10% SDS (to 0.1%), and 2.5 μL Proteinase K (20 mg/ml). Mix by inversion and incubate 10 min 70 °C.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 47 - }, - { - "source_id": 601612, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL PCI and mix by full-speed vortexing ~2 s.\u003c/div\u003e\u003c/div\u003e", - "position": 48 - }, - { - "source_id": 601613, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eTransfer to a phase-lock tube (e.g., Qiagen MaXtract), and centrifuge 5 min room temperature at 16,000 x g.\u003c/div\u003e\u003c/div\u003e", - "position": 49 - }, - { - "source_id": 601614, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL chloroform and invert ~10x to mix.\u003c/div\u003e\u003c/div\u003e", - "position": 50 - }, - { - "source_id": 601615, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid by pipetting to a fresh tube containing 2 μL 2 mg/ml glycogen.\u003c/div\u003e\u003c/div\u003e", - "position": 51 - }, - { - "source_id": 601616, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 750 μL 100% ethanol and mix by vortexing or tube inversion.\u003c/div\u003e\u003c/div\u003e", - "position": 52 - }, - { - "source_id": 601617, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eChill on ice and centrifuge 10 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e", - "position": 53 - }, - { - "source_id": 601618, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePour off the liquid and drain on a paper towel.\u003c/div\u003e\u003c/div\u003e", - "position": 54 - }, - { - "source_id": 601619, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRinse the pellet in 1 ml 100% ethanol and centrifuge 1 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e", - "position": 55 - }, - { - "source_id": 601620, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCarefully pour off the liquid and drain on a paper towel. Air dry.\u003c/div\u003e\u003c/div\u003e", - "position": 56 - }, - { - "source_id": 601621, - "name": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhen the pellet is dry, dissolve in 25-50 μL 1 mM Tris-HCl pH8 0.1 mM EDTA.\u003c/div\u003e\u003c/div\u003e", - "position": 57 - }, - { - "source_id": 601622, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eOptional: Quantify 1-2 μL, for example using fluorescence detection with a Qubit instrument.\u003c/div\u003e\u003c/div\u003e", - "position": 58 - }, - { - "source_id": 601623, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eOptional: Evaluate the presence of cleaved fragments and the size distribution by capillary electrophoresis with fluorescence detection, for example using a Tapestation instrument.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 59 - }, - { - "source_id": 601624, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePrepare barcoded libraries for Illumina sequencing with Tru-Seq adapters using a single-tube protocol, following the manufacturer’s instructions. Rapid PCR cycles favor exponential amplification of the desired CUT\u0026RUN fragments over linear amplification of large DNA fragments that are too long for polymerase to complete.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 60 - }, - { - "source_id": 601625, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eQuantify library yield using dsDNA-specific assay, such as Qubit.\u003c/div\u003e\u003c/div\u003e", - "position": 61 - }, - { - "source_id": 601626, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDetermine the size distribution of libraries by Agilent 4200 TapeStation analysis.\u003c/div\u003e\u003c/div\u003e", - "position": 62 - }, - { - "source_id": 601627, - "name": "Library preparation and sequencing", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePerform paired-end Illumina sequencing on the barcoded libraries following the manufacturer’s instructions.\u003c/div\u003e\u003c/div\u003e", - "position": 63 - }, - { - "source_id": 601628, - "name": "Data processing and analysis", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eWe align paired-end reads using Bowtie2 version 2.2.5 with options: --local --very-sensitive- local --no-unal --no-mixed --no-discordant --phred33 -I 10 -X 700. For mapping spike-in fragments, we also use the --no-overlap --no-dovetail options to avoid cross-mapping of the experimental genome to that of the spike-in DNA.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 64 - }, - { - "source_id": 601629, - "name": "Data processing and analysis", - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "position": 65 - } - ] - } -} diff --git a/spec/fixtures/files/protocols_io/v3/single_protocol.json b/spec/fixtures/files/protocols_io/v3/single_protocol.json deleted file mode 100644 index ba2d9e026..000000000 --- a/spec/fixtures/files/protocols_io/v3/single_protocol.json +++ /dev/null @@ -1,4876 +0,0 @@ -{ - "protocol": { - "id": 9451, - "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" - }, - "doi": "dx.doi.org/10.17504/protocols.io.mgjc3un", - "doi_status": 2, - "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", - "type_id": 1, - "published_on": 1516132805, - "stats": { - "number_of_views": 17928, - "number_of_steps": 66, - "number_of_bookmarks": 65, - "number_of_comments": 122, - "number_of_exports": 397, - "number_of_runs": 39 - }, - "version_id": 0, - "created_on": 1515173338, - "categories": null, - "creator": { - "name": "Steven Henikoff", - "affiliation": "Fred Hutchinson Cancer Research Center", - "username": "steven-henikoff", - "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" - }, - "badges": [ - { - "id": 2, - "image": { - "source": "/img/badges/bronze.svg", - "placeholder": "/img/badges/bronze.svg" - }, - "name": "Author" - }, - { - "id": 6, - "image": { - "source": "/img/badges/socialbutterfly.svg", - "placeholder": "/img/badges/socialbutterfly.svg" - }, - "name": "Social butterfly" - } - ], - "research_interests": null - }, - "journal": null, - "journal_name": null, - "journal_link": null, - "public": 1, - "has_versions": 1, - "link": "https://www.biorxiv.org/content/early/2017/09/24/193219", - "total_collections": 0, - "number_of_steps": 66, - "authors": [ - { - "name": "Peter J. Skene", - "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", - "username": null, - "link": null, - "image": { - "source": null, - "placeholder": null - }, - "badges": [], - "research_interests": null - }, - { - "name": "Steven Henikoff", - "affiliation": "Howard Hughes Medical Institute, Basic Sciences Division, Fred Hutchinson Cancer Research Center, 1100 Fairview Ave N, Seattle, Washington, USA 98109", - "username": null, - "link": null, - "image": { - "source": null, - "placeholder": null - }, - "badges": [], - "research_interests": null - } - ], - "versions": [ - { - "id": 9451, - "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" - }, - "doi": null, - "doi_status": 0, - "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", - "type_id": 0, - "published_on": 1516132805, - "stats": { - "number_of_views": 0, - "number_of_steps": 0, - "number_of_bookmarks": 0, - "number_of_comments": 122, - "number_of_exports": 0, - "number_of_runs": 0 - }, - "version_id": 0, - "created_on": 1515173338, - "categories": null, - "creator": { - "name": "Steven Henikoff", - "affiliation": "Fred Hutchinson Cancer Research Center", - "username": "steven-henikoff", - "link": null, - "image": { - "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" - }, - "badges": [], - "research_interests": null - }, - "journal": null, - "journal_name": null, - "journal_link": null - }, - { - "id": 11190, - "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" - }, - "doi": null, - "doi_status": 0, - "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-n6wdhfe", - "type_id": 0, - "published_on": 1551890606, - "stats": { - "number_of_views": 0, - "number_of_steps": 0, - "number_of_bookmarks": 0, - "number_of_comments": 30, - "number_of_exports": 0, - "number_of_runs": 0 - }, - "version_id": 1, - "created_on": 1522601124, - "categories": null, - "creator": { - "name": "Steven Henikoff", - "affiliation": "Fred Hutchinson Cancer Research Center", - "username": "steven-henikoff", - "link": null, - "image": { - "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" - }, - "badges": [], - "research_interests": null - }, - "journal": null, - "journal_name": null, - "journal_link": null - }, - { - "id": 21615, - "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "title_html": "CUT\u0026amp;RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" - }, - "doi": null, - "doi_status": 0, - "uri": "cut-amp-run-targeted-in-situ-genome-wide-profiling-zcpf2vn", - "type_id": 0, - "published_on": 1557428439, - "stats": { - "number_of_views": 0, - "number_of_steps": 0, - "number_of_bookmarks": 0, - "number_of_comments": 12, - "number_of_exports": 0, - "number_of_runs": 0 - }, - "version_id": 2, - "created_on": 1553027602, - "categories": null, - "creator": { - "name": "Derek Janssens", - "affiliation": null, - "username": "derek-janssens", - "link": null, - "image": { - "source": "/img/avatars/017.png", - "placeholder": "/img/avatars/017.png" - }, - "badges": [], - "research_interests": null - }, - "journal": null, - "journal_name": null, - "journal_link": null - } - ], - "groups": [ - { - "id": 523, - "uri": "hca", - "title": "Human Cell Atlas Method Development Community", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" - }, - "tech_support": { - "email": null, - "phone": null, - "hide_contact": 0, - "use_email": 0 - }, - "is_member": 0, - "request": { - "id": 523, - "uri": "hca", - "title": "Human Cell Atlas Method Development Community", - "image": { - "source": "https://s3.amazonaws.com/pr-journal/psucz36.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/psucz36.png" - }, - "tech_support": { - "email": null, - "phone": null, - "hide_contact": 0, - "use_email": 0 - }, - "is_member": 0, - "description": null, - "research_interests": null, - "website": null, - "location": null, - "affiliation": null, - "status": { - "is_visible": true, - "access_level": 0 - }, - "stats": { - "files": [], - "total_members": 0, - "total_followers": 0, - "total_child_groups": 0, - "total_parent_groups": 0, - "has_collaborations": 0 - }, - "user_status": { - "is_member": false, - "is_confirmed": true, - "is_invited": false, - "is_owner": false, - "is_admin": false, - "is_following": false - }, - "join_link": null, - "token": null, - "owner": { - "name": "Steven Henikoff", - "affiliation": null, - "username": "steven-henikoff", - "link": null, - "image": { - "source": "https://s3.amazonaws.com/pr-journal/s3pitfe.png", - "placeholder": "https://s3.amazonaws.com/pr-journal/s3pitfe.png" - }, - "badges": [], - "research_interests": null - }, - "is_protocol_requested": 0, - "is_group_requested": 0, - "is_my": false, - "is_request": true, - "is_confirmed": 1, - "is_declined": 0, - "requester": { - "name": "Steven Henikoff", - "affiliation": null, - "affiliation_url": null, - "username": "steven-henikoff", - "link": null - }, - "protocol": { - "id": 0, - "title": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "title_html": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", - "image": { - "source": null, - "placeholder": null - }, - "doi": null, - "doi_status": 0, - "uri": "cut-run-targeted-in-situ-genome-wide-profiling-wit-mgjc3un", - "type_id": 1, - "published_on": null, - "stats": { - "number_of_views": 0, - "number_of_steps": 0, - "number_of_bookmarks": 0, - "number_of_comments": 0, - "number_of_exports": 0, - "number_of_runs": 0 - } - }, - "created_on": 1516132805, - "resolve_on": 0, - "resolved_user": { - "name": " ", - "affiliation": null, - "username": null, - "link": null, - "image": { - "source": null, - "placeholder": null - }, - "badges": [], - "research_interests": null - }, - "shared": false - } - } - ], - "has_subprotocols": 0, - "is_subprotocol": 0, - "is_bookmarked": 0, - "can_be_copied": 0, - "can_remove_fork": 1, - "forks_count": { - "private": 27, - "public": 0 - }, - "access": { - "can_view": 0, - "can_remove": 0, - "can_add": 0, - "can_edit": 0, - "can_publish": 0, - "can_get_doi": 0, - "can_share": 0, - "can_move": 0, - "can_move_outside": 0, - "can_transfer": 0, - "can_download": 1, - "is_locked": 0 - }, - "steps": [ - { - "id": 601564, - "guid": "F9349635A9AB4395AD1A410F66F785CF", - "previous_id": null, - "previous_guid": null, - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B4239EB74A144551AD46C1ADC6707144", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "BA370A7410C749C580F428A9A8B515D7", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eHarvest fresh culture(s) at room temperature and count cells. The same protocol can be used for 100 to 250,000 mammalian cells per sample and/or digestion time point.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "7229BD361FC041BD829424FDAF87E6BC", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: All steps prior to the addition of antibody are performed at room temperature to minimize stress on the cells. Because it is crucial that DNA breakage is minimized throughout the protocol, we recommend that cavitation during resuspension and vigorous vortexing be avoided.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054726, - "guid": "DE8D3F46B0F74E7CB0E1AC694CCAE048", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003ePAUSE POINT\u003c/span\u003e\u003cspan\u003e: If necessary, cells can be cryopreserved in 10% DMSO using a Mr. Frosty isopropyl alcohol chamber. We do not recommend flash freezing, as this can cause background DNA breakage that may impact final data quality.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054727, - "guid": "379EA17744A943698115A4AB4A3687E4", - "order_id": 4, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 30 minutes.\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601565, - "guid": "533BCACB836840EEA0AFEB15424F1208", - "previous_id": 601564, - "previous_guid": "F9349635A9AB4395AD1A410F66F785CF", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "F96FA71527B64CE0941CBB92181E74B7", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "E8A6A960B6204912A8F6697C44845E78", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "4F5BA212323746F3A4D72FF3CDE649E0", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 180, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601566, - "guid": "C403951BF28442E28A1A161C20E91244", - "previous_id": 601565, - "previous_guid": "533BCACB836840EEA0AFEB15424F1208", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "A9D5244F64C442CF8D92FD2A28161C14", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "A9045D12DBDB45AF8D4E05FF213295D4", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eResuspend in 1.5 ml room temperature Wash buffer by gently pipetting and transfer if necessary to a 2 ml tube. (wash 1/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "877918BD3143485BAEB96361FC0095A3", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Wash buffer" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601567, - "guid": "13B786714C8E493181D5EFD67C04F1DE", - "previous_id": 601566, - "previous_guid": "C403951BF28442E28A1A161C20E91244", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "FDE986CF923E4BA68C88CE17630046A0", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "EB4B814A95024FF5992F64A7CE1647B8", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 1/2)\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "392BFA08D1634BF4B692BEE707A04E68", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 180, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601568, - "guid": "B6E7098700144E9CA5F9E421D6CB32A4", - "previous_id": 601567, - "previous_guid": "13B786714C8E493181D5EFD67C04F1DE", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "6B4E841EBA7245178BA6F92F681A6F56", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "6A6FF684D40A44028B35F98B575A72B4", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAgain, resuspend in 1.5 ml room temperature Wash buffer by gently pipetting. Centrifuge 3 min 600 x g at room temperature and withdraw liquid. (wash 2/2)\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "36A3B8F9CE1444C0963D848158B07609", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Wash buffer" - } - }, - { - "id": 1054726, - "guid": "9DAAFC949017409B98FD30AEC82D757D", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 180, - "title": "" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601569, - "guid": "F34684E33271452D8C5FCD6825754E06", - "previous_id": 601568, - "previous_guid": "B6E7098700144E9CA5F9E421D6CB32A4", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "8506A6C09CC04AB1B2D793A2C0397D0F", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "07A07A55F68D4AFB9A1827660138BE72", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eResuspend in 1 ml room temperature Wash buffer by gently pipetting.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "5B844BD52D3143C19113859DE2A68608", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Wash buffer" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601570, - "guid": "58662573D1154AB4B618D743EFCB6789", - "previous_id": 601569, - "previous_guid": "F34684E33271452D8C5FCD6825754E06", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7D0F899F3FF445FBAA314D160E7CFCB2", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "4D9DF4A1C6B84178911ECDC5FF220A5F", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhile gently vortexing the cells at room temperature, add the bead slurry.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601571, - "guid": "36E26513CD5941C5A05E511CB2E5519A", - "previous_id": 601570, - "previous_guid": "58662573D1154AB4B618D743EFCB6789", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "75280EF994BE4CE09A940A06E5FDDDAB", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "EF87593612AD42CC9477542C4D97E7CA", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRotate 5-10 min at room temperature.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "C7CC2DC572594827AA2641FE87E98030", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 600, - "title": "Rotation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601572, - "guid": "B781B8F5D2FA4148A2425991DBB90678", - "previous_id": 601571, - "previous_guid": "36E26513CD5941C5A05E511CB2E5519A", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "69CF276CD0EA4553BB9139053C033BC6", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Binding cells to beads" - } - }, - { - "id": 1054724, - "guid": "436018023BD84B678A0FFF5001B26DDE", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDivide into aliquots in 1.5-ml tubes, one for each antibody to be used.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "D62509DE75DF47B68A99B3D0C0257CC1", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: To evaluate success of the procedure without requiring library preparation, include in parallel a positive control antibody (e.g. α-H3K27me3) and a negative control antibody (e.g. α-rabbit). Do not include a no-antibody control, as the lack of tethering may allow any unbound pA-MN to act as a “time-bomb” and digest accessible DNA, resulting in a background of DNA-accessible sites.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601573, - "guid": "E2CA695B2F84412181172D8EA5D2D3F0", - "previous_id": 601572, - "previous_guid": "B781B8F5D2FA4148A2425991DBB90678", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "2341850E79644B69A03EB01793948C8D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "4840BBA918A54A5CA8A1A9421536D740", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off the liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "0D1C8EC5A8274C4B8A13610FCA9B475D", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Although low-retention pipette tips are preferred for accurate solution transfers, use only conventional (not low-binding) microcentrifuge tubes to avoid loss of beads while decanting.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054726, - "guid": "B65CDC1428324D43A179577505968A13", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section can be 15 min–overnight, with longer incubations providing higher yields.\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601574, - "guid": "7DC06660925F4633BE0B963407ED5AD1", - "previous_id": 601573, - "previous_guid": "E2CA695B2F84412181172D8EA5D2D3F0", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "DB3500D3ECA941C6898A7BF2F29A8D0C", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "06F721D573844F73B9A08F69EEBDE50D", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Antibody buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "9C495290326F47FD9F34F6B6C725AB03", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: The presence of EDTA during antibody treatment removes excess divalent cation used to activate the ConA, because carry-over of Ca++ from the beads can prematurely initiate strand cleavage after addition of pA-MN. Chelation of divalent cations when cells are permeabilized also serves to quickly halt metabolic processes and prevent endogenous DNAse activity. Washing out the EDTA before pA-MN addition avoids inactivating the enzyme. Spermidine in the wash buffer is intended to compensate for removal of Mg++, which might otherwise affect chromatin properties.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601575, - "guid": "9814A7B521A64FAAAC16B758C91C5AA9", - "previous_id": 601574, - "previous_guid": "7DC06660925F4633BE0B963407ED5AD1", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "3462666F4222412B8EB20D90EEC83C55", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "FAC824D4C5D34733ACB3E6E8A8AC2544", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4°C for ~2 hr (or at room temperature for 5-10 min)\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "C35777E1CBA24F7799754771FB3551A7", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 7200, - "title": "Tube rotator at 4°C" - } - }, - { - "id": 1054726, - "guid": "16B00C16F4C048818A1007581FA4F574", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003ePAUSE POINT\u003c/span\u003e\u003cspan\u003e Antibody incubation may proceed overnight at 4 °C.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601576, - "guid": "F28A5BD1B5364839A3FADFEBDC1A8E37", - "previous_id": 601575, - "previous_guid": "9814A7B521A64FAAAC16B758C91C5AA9", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "FE83837F0BE6488F943387EF41EEA5FB", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "3328BFC05392482B9EB12E5C3FBD343F", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "594F1DD69A6141B8996B9D07975CB9F6", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: After mixing, but before placing a tube on the magnet stand, a very quick spin on a micro-centrifuge (no more than 100 x g) will minimize carry-over of antibody and pA-MN that could result in overall background cleavages during the digestion step.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601577, - "guid": "3D8757B9EE974E00BD637E8681453972", - "previous_id": 601576, - "previous_guid": "F28A5BD1B5364839A3FADFEBDC1A8E37", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "037AB90AA46249CD90EA28BA730CC904", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "79FFA0D1193943768B15C41477C8E8EC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear (~30 s) and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E1147891F4974E538246DB8053826E98", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 30, - "title": "Magnet stand" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601578, - "guid": "9591637A664740139561134EE158BBC9", - "previous_id": 601577, - "previous_guid": "3D8757B9EE974E00BD637E8681453972", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "860CFC9B66F845A5B8E41A99E43BDC8B", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind (primary) antibodies" - } - }, - { - "id": 1054724, - "guid": "9457B72E38DE4E4D98591079D35167A1", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting using a 1 ml tip if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "23BECB0713B84CEA816F369220032EE5", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Dig-wash buffer" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601579, - "guid": "447CFE23E751440EA778368FEB3F87C4", - "previous_id": 601578, - "previous_guid": "9591637A664740139561134EE158BBC9", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "58657CE22CCB4BE5A63056677A67883D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "F79AAC77550540A2AEDADBF4D49145B1", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "AB1F6945CA47471298C5DD2CC332DB88", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCRITICAL STEP: The binding efficiency of Protein A to the primary antibody depends on host species and IgG isotype. For example, Protein A binds well to rabbit and guinea pig IgG but poorly to mouse and goat IgG, and so for these latter antibodies a secondary antibody, such as rabbit α-mouse is recommended.\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054726, - "guid": "95AFDC83212A4B89A1A014501B4169D4", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 15 min to 1.5 hours\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601580, - "guid": "D677EECD1EC640709044078D1B26A7C0", - "previous_id": 601579, - "previous_guid": "447CFE23E751440EA778368FEB3F87C4", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "8C40CF2502A4433EA4E6FE29B1633A64", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "8B51FCDB3F23412D9A459BDFC4C58783", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "C4A802B3B29549B694E1834E9E3954C3", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 50, - "unit": "µl", - "title": "Dig-wash buffer (per sample and/or digestion time point)" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601581, - "guid": "44947D0288034DB9A2E0DCB25D1F9387", - "previous_id": 601580, - "previous_guid": "D677EECD1EC640709044078D1B26A7C0", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "AA3AB0862B0E477BB6B1538A83F56A7D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "C8A586C6907849D8958C433C34620C1B", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the secondary antibody to a final concentration of 1:100 or to the manufacturer’s recommended concentration for immunofluorescence.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601582, - "guid": "2C71FDFD3966418E89282A2E498DA2D7", - "previous_id": 601581, - "previous_guid": "44947D0288034DB9A2E0DCB25D1F9387", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "542FEE6BFDED4272A0D16B559BF6054A", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "D458DC24DD4E419AA6BA400F189C96CC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "F01854534A1A456495DBB43C4DC3E75B", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 3600, - "title": "Tube rotator at 4 °C" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601583, - "guid": "902EB027B7E842B084193355AB786637", - "previous_id": 601582, - "previous_guid": "2C71FDFD3966418E89282A2E498DA2D7", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "C3E46848962E482594D5E6E08A15DBFB", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "80BB9951F8654C9FA8A441AD786A867E", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601584, - "guid": "54FE2501D13B4D8BB18EB9D35D2FA8EF", - "previous_id": 601583, - "previous_guid": "902EB027B7E842B084193355AB786637", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "6A6EE625B5A54B97B44D29C3F7426EFD", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "3B79EB3E49534FE5882962991ED25D38", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601585, - "guid": "1940483B302C40F0B3EB5443F1D48418", - "previous_id": 601584, - "previous_guid": "54FE2501D13B4D8BB18EB9D35D2FA8EF", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "0131A8D952F2459BB7599A7557632D71", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind secondary antibody (as required)" - } - }, - { - "id": 1054724, - "guid": "6CCD3BCC11264591A74FF730AE7B1F57", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-Wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E491A6117E00497A9264F820FF8EC18E", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Dig-Wash buffer" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601586, - "guid": "4191A6159A354E7BAC02E73BDEAD8925", - "previous_id": 601585, - "previous_guid": "1940483B302C40F0B3EB5443F1D48418", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "8B3342A661F144C1BFF13CFAE4A42E77", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "1072337428C44BE492F0EDC365300B9D", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "C99C243EBB684C12B9C9558F3F9118EC", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 15 min - 1.5 hours\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601587, - "guid": "ACDFAA48CC08448DA097F2D350B31E1C", - "previous_id": 601586, - "previous_guid": "4191A6159A354E7BAC02E73BDEAD8925", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "A3247E04A7854689B8D3006E415EA2CF", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "BFE7E50BA51C4550B755C464996FE98A", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and squirt 50 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "D42DDCCCC45A46D19A97DD9D16F1CEBA", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 50, - "unit": "µl", - "title": "Dig-wash buffer (per sample and/or digestion time point)" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601588, - "guid": "F79CEE8B697646AABB02EE35AE0A886F", - "previous_id": 601587, - "previous_guid": "ACDFAA48CC08448DA097F2D350B31E1C", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "0A411C2E23154C0C96B32E9B3C74F24F", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "E38812006C4C4017BF88857B45E5EBE4", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eMix in the pA-MNase to a final concentration of ~700 ng/ml (e.g. 2.5 μL/50 μL of a 1:10 dilution of the 140 μg/ml glycerol stock provided upon request).\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601589, - "guid": "F75C798D1B4E41AF87BEE198CED29B59", - "previous_id": 601588, - "previous_guid": "F79CEE8B697646AABB02EE35AE0A886F", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "2571CDB064C84DA299360D46D9BA236B", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "666D8CF79C874ADEB09E3B95A2D9DE5B", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the tube rotator at 4 °C for ~1 hr (or at room temperature for 5-10 min).\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "717875E7ECF2487CAFE9D8DF0402977D", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 3600, - "title": "Tube rotator at 4 °C" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601590, - "guid": "5C48D1C95CC64BDB82361FD5B1D6512A", - "previous_id": 601589, - "previous_guid": "F75C798D1B4E41AF87BEE198CED29B59", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "567105CDB1F14707AE96B3DAFA4244B9", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "0790EFF9BB9548EE91E10518ECEFDA20", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601591, - "guid": "0B3368083BB94AC6939C68950CDCD02E", - "previous_id": 601590, - "previous_guid": "5C48D1C95CC64BDB82361FD5B1D6512A", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "83AB9CA54C864121B40E328C694D72B6", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "01EFB7830C9C4DBDA8F15B276AA7720E", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601592, - "guid": "8ECBF2C2495B4AA18E5F393B55B82112", - "previous_id": 601591, - "previous_guid": "0B3368083BB94AC6939C68950CDCD02E", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "3D2F6134980A41EFB74B01922D3C2149", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "FE326382C1F54C368EB60BB7A68FB51E", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 1 ml Dig-wash buffer, mix by inversion, or by gentle pipetting if clumps persist, and remove liquid from the cap and side with a quick pulse on a micro-centrifuge.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "96882087A34D4D21A2761301A70E55C6", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "Dig-wash buffer" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601593, - "guid": "9317B32F055E4FA295C502F2F233FB48", - "previous_id": 601592, - "previous_guid": "8ECBF2C2495B4AA18E5F393B55B82112", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "E6C1DBDD01934E838C688507425A33BF", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Bind Protein A-MNase fusion protein" - } - }, - { - "id": 1054724, - "guid": "4867D4ED725E47BE8F5D1E6E117DF6B7", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRepeat Dig-wash steps 28-29.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "4E45FCFD3A38403B8E62DAA850756D57", - "order_id": 2, - "type_id": 22, - "title": "gotostep", - "source": { - "step_guid": "0B3368083BB94AC6939C68950CDCD02E", - "title": "Repeat Dig-wash steps" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601594, - "guid": "E1E60873DF52456DAEC420EC033DE2F4", - "previous_id": 601593, - "previous_guid": "9317B32F055E4FA295C502F2F233FB48", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B36111C478B1407EA298A2838746CB6D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "887F64E715464E2A8D04D4FBF003A675", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace on the magnet stand to clear and pull off all of the liquid.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "61C10B2F96F14B48B0FF59A768E86E44", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 45 minutes\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601595, - "guid": "BFF4DB1905D04077932EEC44B94EAA0D", - "previous_id": 601594, - "previous_guid": "E1E60873DF52456DAEC420EC033DE2F4", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "05644E9B0F6C45758CEBB8F344B9D0C2", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "CDA1FADE4E9B46F29C7F78799BD628A1", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePlace each tube at a low angle on the vortex mixer set to low (~1100 rpm) and add 100 μL of the Dig-wash buffer (per sample and/or digestion time point) along the side while gently vortexing to allow the solution to dislodge most or all of the beads. Tap to dislodge the remaining beads.\u003c/span\u003e\u003cspan style = \":justify;\"\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "7E606AD945824136ADD434F3BFA32F9F", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 100, - "unit": "µl", - "title": "Dig-wash buffer (per sample and/or digestion time point)" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601596, - "guid": "C23AC0CA22F240F7B8C9AA144CB3241E", - "previous_id": 601595, - "previous_guid": "BFF4DB1905D04077932EEC44B94EAA0D", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "A1E4BE65B8224066A3FE359A7E1DE314", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "893F13A565854F139EB9DA24F93383F0", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eInsert tubes into the 1.5 ml wells of a heater block sitting in wet ice to chill down to 0 °C.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E9038CDB84B14624B92E729A5E703F41", - "order_id": 2, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 0, - "unit": "°C", - "title": "" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601597, - "guid": "8BD0574B304D450FBC50034FFBCEB658", - "previous_id": 601596, - "previous_guid": "C23AC0CA22F240F7B8C9AA144CB3241E", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B6A89ECE5EA849BFA9B61CC624FA394D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "7106E2E428B24FF6A654AB3176BE305F", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eRemove each tube from the block, mix in 2 μL 100 mM CaCl\u003c/span\u003e\u003cspan style = \":justify;vertical-align:sub;\"\u003e2\u003c/span\u003e\u003cspan style = \":justify;\"\u003e (diluted 1:10 from a 1 M stock) with gentle vortexing and immediately replace the tube in the 0 °C block.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E1DDDBD9E1C2429D83A15EFDD75BB753", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 2, - "unit": "µl", - "title": "100 mM CaCl2" - } - }, - { - "id": 1054726, - "guid": "B041538D67914F95B489E6698CDC1186", - "order_id": 3, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 0, - "unit": "°C", - "title": "" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601598, - "guid": "6D318018B048416DA9FC2F42C2E6F3CA", - "previous_id": 601597, - "previous_guid": "8BD0574B304D450FBC50034FFBCEB658", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B3F14B93F3CC48D5AEAAA89BEE744234", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "122337F8736B40F89DBCC6A4C750E58B", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate at 0 °C for the desired digestion time (default is 30 min).\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "A7A41B3B3DA74D069F7BBA177C705EF3", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 1800, - "title": "Incubation" - } - }, - { - "id": 1054726, - "guid": "1E9050BDD5C24D06839BD89E3BC677D8", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: MNase binds DNA but only cleaves when Ca++ is present, so that digestion is a zero-order reaction that seems to be less temperature-dependent than the subsequent diffusion of released pA-MNase-bound particles that can digest accessible regions of the genome. Cleavage and release of particles in most of the cell population can be obtained at 0 oC while minimizing background cleavages attributable to diffusion. We have found that digestion at ambient temperature or higher results in unacceptable background cleavage levels.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054727, - "guid": "2D6A506A60794425A179175F23982863", - "order_id": 4, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 0, - "unit": "°C", - "title": "Incubation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601599, - "guid": "4B84A0F7E7704AA6BC0608736CCC76E9", - "previous_id": 601598, - "previous_guid": "6D318018B048416DA9FC2F42C2E6F3CA", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "597AEDF049524E58AEE0F8D8FADC4173", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Targeted digestion" - } - }, - { - "id": 1054724, - "guid": "AAADF06579AF4231894E3B13156B8728", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eAdd 100 μL 2XSTOP and mix by gentle vortexing. When there are multiple time points, remove 100 μL to 100 μL 2XSTOP and mix by gentle vortexing.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "A12A6A3FCC294123AB58F9554C83FD75", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 100, - "unit": "µl", - "title": "2XSTOP" - } - }, - { - "id": 1054726, - "guid": "599C738D692E45419389123D3567F819", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Heterologous spike-in DNA should be present in the 2XSTOP to calibrate DNA amounts, for example to compare treatments or digestion time points. This is especially important for CUT\u0026RUN, as there is too little background cleavage for normalization of samples.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601600, - "guid": "C8F46DC9456C4761B2B93D2BFD2B20D6", - "previous_id": 601599, - "previous_guid": "4B84A0F7E7704AA6BC0608736CCC76E9", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "1CD60910082F4F8D8F7BAC15C5378996", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Target chromatin release" - } - }, - { - "id": 1054724, - "guid": "B96477A32E8147F19BA1696491C76E46", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIncubate 10 min 37 °C to release CUT\u0026RUN fragments from the insoluble nuclear chromatin.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "3AC9ABE994D04870A849601C47767C65", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 600, - "title": "Incubation" - } - }, - { - "id": 1054726, - "guid": "AB2D2D887FB64BD9AE4FC36BC1C3895C", - "order_id": 3, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 37, - "unit": "°C", - "title": "Incubation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601601, - "guid": "9DA29E33454047FB8C050EE24DC2DE1B", - "previous_id": 601600, - "previous_guid": "C8F46DC9456C4761B2B93D2BFD2B20D6", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "349B0DF57F0D47A6A15882963E565BF4", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Target chromatin release" - } - }, - { - "id": 1054724, - "guid": "3BC3B1D1BC4B41B48EF642470E12CF04", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 5 min at 4 °C and 16,000 x g and place on magnet stand.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "F45853ECBC76496A8B92430CE17BE24F", - "order_id": 2, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 4, - "unit": "°C", - "title": "Centrifugation" - } - }, - { - "id": 1054726, - "guid": "3CA183D2D99E412DA60E54BBDEF332BA", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 300, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601602, - "guid": "A665FB6F1B224EF896EE45E7F7AB3DA7", - "previous_id": 601601, - "previous_guid": "9DA29E33454047FB8C050EE24DC2DE1B", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7F4B0B90BAFC4DA8A302AC583B68BD53", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "F922DEA9D3554E44A893D049C13FC84B", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace a spin column into a collection tube and add 400 μL Buffer NT1 (from NucleoSpin kit or equivalent).\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "9D3A2B71C6FC4F12A323E6FA6DEFF884", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 400, - "unit": "µl", - "title": "Buffer NT1" - } - }, - { - "id": 1054726, - "guid": "4225349180CE430887B1F1E1DDF39E35", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIf you are performing Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments), please directly proceed to Step 47.\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054727, - "guid": "C96435DAB79A4BAF9CA1A8AD0DBBB082", - "order_id": 4, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 20 minutes\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601603, - "guid": "25777B0B8EC44729A3B6B4A09B518931", - "previous_id": 601602, - "previous_guid": "A665FB6F1B224EF896EE45E7F7AB3DA7", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "D96F11F10EAC4AFB8BFFCE40CDB769A0", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "F63B5EAAD3E945EABC268D735448ED6B", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eDecant the supernatant cleanly from the pellet and transfer to the NT1 in the spin column pipetting gently up and down to mix.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601604, - "guid": "58645AB3643C42ABA55E2A7D22412DDB", - "previous_id": 601603, - "previous_guid": "25777B0B8EC44729A3B6B4A09B518931", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "5CE4D2D4643C4992BEE2EE05C2E6B18E", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "B48890940F824809A54BA0B7E27A3661", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "4115311E917343D2A663114FC6E737E1", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 30, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601605, - "guid": "B901E247E4C34304B012D414C703B540", - "previous_id": 601604, - "previous_guid": "58645AB3643C42ABA55E2A7D22412DDB", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "D5147547CDCC485D8025B93AF85C7D91", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "BECFFADC221641A892845E5423C786CC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "DCEE8F8D8D8C436AB997731AC6B042F4", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 700, - "unit": "µl", - "title": "Buffer NT3" - } - }, - { - "id": 1054726, - "guid": "682228D6EAAA41CC808CA2B05467D1A2", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 30, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601606, - "guid": "E8E7A5825F0D4E7989FD5A5F7A662CEE", - "previous_id": 601605, - "previous_guid": "B901E247E4C34304B012D414C703B540", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "D344EF37B5664A4687C8E98AF6D36154", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "1C1BAF4644024AE4BB676D3455155BEB", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 700 μL Buffer NT3. Centrifuge 30 s at 11,000 x g. Discard flow-through and replace in rotor.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "78FF6F20213E4D2D9E291417B0F1CD73", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 700, - "unit": "µl", - "title": "Buffer NT3" - } - }, - { - "id": 1054726, - "guid": "D8ECF01EF2E741DC9A20ED8973561044", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 30, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601607, - "guid": "ED4075EE53C747698805ACB75D5EA451", - "previous_id": 601606, - "previous_guid": "E8E7A5825F0D4E7989FD5A5F7A662CEE", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "F5830217AEB74ADB991AB1142F102DC0", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "BF1108C352C64B07B04ACFDF9D57E3CD", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCentrifuge for 1 min at 11,000 x g. Let dry 5 min.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "2E284C497A384FD6B6C9CBD1A2F53DCE", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 60, - "title": "Centrifugation" - } - }, - { - "id": 1054726, - "guid": "5A0C39A9DD82459FBB7A3B6D423BF7E7", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 300, - "title": "Drying" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601608, - "guid": "E0B331A51A5348C0A4008B2828A4A240", - "previous_id": 601607, - "previous_guid": "ED4075EE53C747698805ACB75D5EA451", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "292403FFC1E547D2BC377A41C197E0AB", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "A117D44E312E4F648723E3683AF03412", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePlace in a fresh tube and add 20-40 μL Buffer NE to membrane.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "966584EAC8494419B5B8A26D18320CD7", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 40, - "unit": "µl", - "title": "Buffer NE" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601609, - "guid": "8FF08B0D5AC14A26AF4CC93CA3A80A3B", - "previous_id": 601608, - "previous_guid": "E0B331A51A5348C0A4008B2828A4A240", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "67D721B6C364480587C61F67F48A281E", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option A: Fast DNA extraction by spin column" - } - }, - { - "id": 1054724, - "guid": "62C40D6BFD0B4A7CB27450D3C54EBBCC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAfter 1 min, centrifuge for 1 min at 11,000 x g.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "B4786BA03DF84C578BDA01C58582EADB", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 60, - "title": "Wait" - } - }, - { - "id": 1054726, - "guid": "95DCECDC0CDC4FE09A2F98AC4ED84489", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 60, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601610, - "guid": "B9DF2E67A33047C280CDB0E7B6B11448", - "previous_id": 601609, - "previous_guid": "8FF08B0D5AC14A26AF4CC93CA3A80A3B", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "6EC4C0C9C7BE4E998A05AB2965CB6096", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "CF27DB48439D436C96C567FAE67138D6", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDecant the supernatant cleanly from the pellet and transfer to a fresh 1.5-ml microcentrifuge tube.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "A0A556F0BE8042698783ED1218D086D5", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eIf you are performing Option A: Fast DNA extraction by spin column, please directly proceed to Step 59. \u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - }, - { - "id": 1054726, - "guid": "B279A7A2D5F24C5782993FC08EA051B7", - "order_id": 3, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is approximately 1.5 hours\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601611, - "guid": "46FA11C4A22A475E9E2499A7C0A347D3", - "previous_id": 601610, - "previous_guid": "B9DF2E67A33047C280CDB0E7B6B11448", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B0EE9E3E2B654D1099A0C3B5939E254B", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "4E8F1066FF174DEEB1A3CD9BB1A08ECC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eTo each sample add 2 μL 10% SDS (to 0.1%), and 2.5 μL Proteinase K (20 mg/ml). Mix by inversion and incubate 10 min 70 °C.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "89C222141D84445DA534C1E94DCA4085", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 2, - "unit": "µl", - "title": "10% SDS (to 0.1%)/sample" - } - }, - { - "id": 1054726, - "guid": "09F1B1667E64428FB3BEA0983ED54B93", - "order_id": 3, - "type_id": 3, - "title": "amount", - "source": { - "amount": 2, - "unit": "µl", - "title": "Proteinase K (20 mg/ml)/sample" - } - }, - { - "id": 1054727, - "guid": "2B80F664A4EF4A42AF31FDE635FA4B29", - "order_id": 4, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 70, - "unit": "°C", - "title": "Incubation" - } - }, - { - "id": 1054728, - "guid": "F2234AF0BDE249F9874228800970B775", - "order_id": 5, - "type_id": 4, - "title": "duration", - "source": { - "duration": 600, - "title": "Incubation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601612, - "guid": "FA12B8081A5C4BF59EA5D90B5C2BF9A4", - "previous_id": 601611, - "previous_guid": "46FA11C4A22A475E9E2499A7C0A347D3", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "AB004D393AC2496C9B5B7D8C0FC8C64C", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "10E32980BC8646B9B0E371BD74BE8558", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL PCI and mix by full-speed vortexing ~2 s.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E41B8897FB374D8083CF6A764D080C5A", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 300, - "unit": "µl", - "title": "PCl" - } - }, - { - "id": 1054726, - "guid": "1435EA4D4B4A4AC992DD463D566355E4", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 2, - "title": "Vortexing" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601613, - "guid": "384FB88D80F048C5901158ED7E8BA90B", - "previous_id": 601612, - "previous_guid": "FA12B8081A5C4BF59EA5D90B5C2BF9A4", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "368F44BE9FB8401DB53ABE5E9570F69D", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "2C7B63F456494225B760401717F98438", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eTransfer to a phase-lock tube (e.g., Qiagen MaXtract), and centrifuge 5 min room temperature at 16,000 x g.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "3E3180DAB3BF4D78843ABD8F93C08EB9", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 300, - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601614, - "guid": "38007A622CF946A3B7B15B09B378162F", - "previous_id": 601613, - "previous_guid": "384FB88D80F048C5901158ED7E8BA90B", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "EED94564ADB7444BA241F2D7CA3F41A1", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "EF8A02E2243943DF813D04289D070EDB", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 300 μL chloroform and invert ~10x to mix.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "09E20C738AF74304B4AFEE66D9B6F867", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 300, - "unit": "µl", - "title": "Chloroform" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601615, - "guid": "720E2E6FE0324E0BA7381764772C020A", - "previous_id": 601614, - "previous_guid": "38007A622CF946A3B7B15B09B378162F", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "30864A383D764673B612C7CAFAC374EC", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "DEA0F8A7E6BE4241A6B698EB213433C0", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRemove liquid by pipetting to a fresh tube containing 2 μL 2 mg/ml glycogen.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "5C8C43AA00614010A9176F8A0F0F9BA9", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 2, - "unit": "µl", - "title": "2 mg/ml glycogen" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601616, - "guid": "4C976F0262A6427FA82B35A3DB3131CF", - "previous_id": 601615, - "previous_guid": "720E2E6FE0324E0BA7381764772C020A", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "DFD5BB572A434610B3A1CA7957299F7A", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "CA7CD354E40B400A9312A3AB1419A0D9", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eAdd 750 μL 100% ethanol and mix by vortexing or tube inversion.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "E0CE4408F3D943CAB503DC86EABAF992", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 750, - "unit": "µl", - "title": "100% ethanol" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601617, - "guid": "9462096276C84C7D9DAC857F13552DF9", - "previous_id": 601616, - "previous_guid": "4C976F0262A6427FA82B35A3DB3131CF", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7AD34EB1DB784C61989DBA3B703B5399", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "BE91B057559E48289CAAAD4679D1F920", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eChill on ice and centrifuge 10 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "2962671BA9D641928AD3FC211F29F8B7", - "order_id": 2, - "type_id": 4, - "title": "duration", - "source": { - "duration": 600, - "title": "Centrifugation" - } - }, - { - "id": 1054726, - "guid": "0ED3B3704A6B4A4299CFFB84907214D2", - "order_id": 3, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 4, - "unit": "°C", - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601618, - "guid": "9845314177E44EA29C66DEF785D63809", - "previous_id": 601617, - "previous_guid": "9462096276C84C7D9DAC857F13552DF9", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7A15E93E2C6D4BD8821647CF3B9D5404", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "A927939E8DAE402CA604ABD5CC9C30B8", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePour off the liquid and drain on a paper towel.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601619, - "guid": "CBEE67B491E644D5BC80599F1E7E3AF5", - "previous_id": 601618, - "previous_guid": "9845314177E44EA29C66DEF785D63809", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "4B738DA925094D8191FCFBC48AA8B280", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "23036507DFC84EA5951BC5A09F4FE56D", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eRinse the pellet in 1 ml 100% ethanol and centrifuge 1 min at 4 °C 16,000 x g.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "C495B77992164E708246F672351804C1", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 1, - "unit": "ml", - "title": "100% ethanol" - } - }, - { - "id": 1054726, - "guid": "F9D9F774B70A42ACA57CD8B762AF9128", - "order_id": 3, - "type_id": 4, - "title": "duration", - "source": { - "duration": 60, - "title": "Centrifugation" - } - }, - { - "id": 1054727, - "guid": "2EB7938E5F264E698BB8BEE2965C5D1B", - "order_id": 4, - "type_id": 24, - "title": "temperature", - "source": { - "concentration": 4, - "unit": "°C", - "title": "Centrifugation" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601620, - "guid": "2C2E2507E3D7415FB974FFFF9AF5F3C6", - "previous_id": 601619, - "previous_guid": "CBEE67B491E644D5BC80599F1E7E3AF5", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "3344A38E21DB4C6094661A2CC862E5E8", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "8BF41171128449E2B304C1209A0E2527", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCarefully pour off the liquid and drain on a paper towel. Air dry.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601621, - "guid": "060192325023468E80E83369843B5F46", - "previous_id": 601620, - "previous_guid": "2C2E2507E3D7415FB974FFFF9AF5F3C6", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "6CAB58A20D6E4D11894E37D533B75EFB", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Option B: Alternate DNA extraction (preferred for quantitative recovery of ≤80 bp fragments)" - } - }, - { - "id": 1054724, - "guid": "454915A1771B49D0A8016F555DC38351", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eWhen the pellet is dry, dissolve in 25-50 μL 1 mM Tris-HCl pH8 0.1 mM EDTA.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "8DE7CC3FDBDA46E68D5BE31072755638", - "order_id": 2, - "type_id": 3, - "title": "amount", - "source": { - "amount": 50, - "unit": "µl", - "title": "1 mM Tris-HCl pH8 0.1 mM EDTA" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601622, - "guid": "438E0BA7C3104D2CBEA56CFA4098F872", - "previous_id": 601621, - "previous_guid": "060192325023468E80E83369843B5F46", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7AA8C72D3ED14D209B2B125476C1FE74", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "5E2EE2C3208E4953AD36CF81C39916DC", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eOptional: Quantify 1-2 μL, for example using fluorescence detection with a Qubit instrument.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "7816EEC226314D8BA2F12831FFD26DAC", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eThis section is 2-4 days.\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601623, - "guid": "9512A4A47D0943339895E8489BC854ED", - "previous_id": 601622, - "previous_guid": "438E0BA7C3104D2CBEA56CFA4098F872", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "0EDDEB899C7D47FDBF1F4C961333AC45", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "9D436B82382D43A5A4071E135B6E0CA2", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eOptional: Evaluate the presence of cleaved fragments and the size distribution by capillary electrophoresis with fluorescence detection, for example using a Tapestation instrument.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "0ADBD372327C4532A02E1CEFF1873F00", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Some long undigested DNA will leak through, and this is what will dominate the Qubit fluorescence for CUT\u0026RUN of typical transcription factors. For these, the targeted DNA recovered is too low in amount and too small in size to be detected by gel analysis or even by Tapestation. In such cases it may be necessary to make a PCR-amplified library to quantify by Tapestation or Bioanalyzer analysis.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601624, - "guid": "387E0F06EE80496B89D75A0DBE161F51", - "previous_id": 601623, - "previous_guid": "9512A4A47D0943339895E8489BC854ED", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "93CD46B488144448A7ABB94182D06422", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "141431407878405F8E197D3F304BB705", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003ePrepare barcoded libraries for Illumina sequencing with Tru-Seq adapters using a single-tube protocol, following the manufacturer’s instructions. Rapid PCR cycles favor exponential amplification of the desired CUT\u0026RUN fragments over linear amplification of large DNA fragments that are too long for polymerase to complete.\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "5CB262560CCE48FF83DA097FBA61B296", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: To minimize the contribution of large DNA fragments, PCR cycles should be at least 12-14 cycles, preferably with a 10 s 60°C combined annealing/extension step. Good results have been obtained with the Hyper-prep kit (KAPA Biosystems).\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601625, - "guid": "E08B0B983AD348F4AF9EB6C826990F9E", - "previous_id": 601624, - "previous_guid": "387E0F06EE80496B89D75A0DBE161F51", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "B445486E28FA4D12ACE7B4CF96EA129F", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "956DD794E6DE4C2AA0EC3D45D5014938", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eQuantify library yield using dsDNA-specific assay, such as Qubit.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601626, - "guid": "E6081479C9FF429A9270B0CBBE6A34BC", - "previous_id": 601625, - "previous_guid": "E08B0B983AD348F4AF9EB6C826990F9E", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "8D17671E023349C09268C3488EF6F52B", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "245650BBB78740A0B49A12D347E06CBE", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eDetermine the size distribution of libraries by Agilent 4200 TapeStation analysis.\u003c/div\u003e\u003c/div\u003e" - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601627, - "guid": "F330F44C402A4229A2D2871FFCEF47A2", - "previous_id": 601626, - "previous_guid": "E6081479C9FF429A9270B0CBBE6A34BC", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "14A47C0FF3C145D89627D45AC39A191B", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Library preparation and sequencing" - } - }, - { - "id": 1054724, - "guid": "6454381B7C7E414AB4E0CFF48D67B5D9", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003ePerform paired-end Illumina sequencing on the barcoded libraries following the manufacturer’s instructions.\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "68EAABB78DB54EC9A94A5EAF50107782", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Because of the very low background with CUT\u0026RUN, typically 5 million paired-end reads suffices for transcription factors or nucleosome modifications, even for the human genome. For maximum economy, we mix up to 24 barcoded samples per lane on a 2-lane flow cell, and perform paired-end 25x25 bp sequencing. Single-end sequencing is not recommended for CUT\u0026RUN, as it sacrifices resolution and discrimination between transcription factors and neighboring nucleosomes.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601628, - "guid": "E3BB1069597D48709ED4267F18AD8644", - "previous_id": 601627, - "previous_guid": "F330F44C402A4229A2D2871FFCEF47A2", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "7078902132D143C9BCB6B5261BF01D5F", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Data processing and analysis" - } - }, - { - "id": 1054724, - "guid": "420D85486C4A4AEF9A6A9EBF1DE8726E", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eWe align paired-end reads using Bowtie2 version 2.2.5 with options: --local --very-sensitive- local --no-unal --no-mixed --no-discordant --phred33 -I 10 -X 700. For mapping spike-in fragments, we also use the --no-overlap --no-dovetail options to avoid cross-mapping of the experimental genome to that of the spike-in DNA.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "57597517C87E472C98F17FDBF6ED86FF", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \"font-weight:bold;\"\u003eCRITICAL STEP\u003c/span\u003e\u003cspan\u003e: Separation of sequenced fragments into ≤120 bp and ≥150 bp size classes provides mapping of the local vicinity of a DNA-binding protein, but this can vary depending on the steric access to the DNA by the tethered MNase. Single-end sequencing is not recommended for CUT\u0026RUN, as it sacrifices resolution and discrimination between transcription factors and neighboring nucleosomes.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - }, - { - "id": 601629, - "guid": "D6C59AD46F7C4763B56FA387E8F6ADF5", - "previous_id": 601628, - "previous_guid": "E3BB1069597D48709ED4267F18AD8644", - "modified_on": 0, - "protocol_id": 0, - "components": [ - { - "id": 1054723, - "guid": "800A2F833FA54D018E46063D640C7700", - "order_id": 1, - "type_id": 6, - "title": "Section", - "source": { - "title": "Data processing and analysis" - } - }, - { - "id": 1054724, - "guid": "2B004750745847A5A7920C2D58038791", - "order_id": 1, - "type_id": 1, - "title": "description", - "source": { - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003e\u003cspan style = \":justify;\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\u003c/span\u003e\u003c/div\u003e\u003c/div\u003e" - } - }, - { - "id": 1054725, - "guid": "FE5FC1257E4A47318E51CD88D2D52F47", - "order_id": 2, - "type_id": 26, - "title": "notes", - "source": { - "id": 0, - "parent_id": 0, - "uri": "", - "title": "", - "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eScripts are available from https://github.com/Henikoff/Cut-and-Run for spike-in calibration and for peak-calling.\n\u003c/div\u003e\u003c/div\u003e", - "created_on": 0, - "changed_on": 0, - "creator": { - "name": " ", - "affiliation": "", - "username": "", - "link": null, - "image": { - "source": "", - "placeholder": "" - }, - "badges": [], - "research_interests": null - }, - "comments": [] - } - } - ], - "cases": null, - "data": null, - "section": null, - "section_color": null, - "duration": 0 - } - ], - "materials": [ - { - "id": 119441, - "mol_weight": 0, - "name": "Cell suspension. We have used human K562 cells, Drosophila S2 cells and dissected Drosophila tissues such as brains and imaginal disks, and spheroplasted yeast.", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119442, - "mol_weight": 0, - "name": "Concanavalin-coated magnetic beads ", - "linfor": null, - "url": "", - "sku": "BP531", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Bangs Laboratories", - "affiliation": null, - "username": null, - "link": "http://www.bangslabs.com/", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 278 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119443, - "mol_weight": 0, - "name": "Antibody to an epitope of interest. For example, rabbit α-CTCF polyclonal antibody (Millipore 07-729) for mapping 1D and 3D interactions by CUT\u0026RUN", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119444, - "mol_weight": 0, - "name": "Positive control antibody to an abundant epitope, e.g. α-H3K27me3 rabbit monoclonal antibody (Cell Signaling Technology, cat. no. 9733)", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119445, - "mol_weight": 0, - "name": "Negative control antibody to an absent epitope, e.g. guinea pig α-rabbit antibody", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119446, - "mol_weight": 0, - "name": "5% Digitonin ", - "linfor": null, - "url": "", - "sku": "300410", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Emd Millipore", - "affiliation": null, - "username": null, - "link": "http://www.emdmillipore.com/", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 8 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119447, - "mol_weight": 0, - "name": "Protein A–Micrococcal Nuclease (pA-MNase) fusion protein (provided in 50% glycerol by the authors upon request). Store at -20 oC.", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119448, - "mol_weight": 0, - "name": "Spike-in DNA (e.g., from Saccharomyces cerevisiae micrococcal nuclease-treated chromatin, provided by authors upon request)", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119449, - "mol_weight": 0, - "name": "Distilled, deionized or RNAse-free H2O (dH2O e.g., Promega, cat. no. P1197)", - "linfor": null, - "url": "", - "sku": "P1197", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Promega", - "affiliation": null, - "username": null, - "link": "http://www.promega.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 23 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119450, - "mol_weight": 0, - "name": "1 M Manganese Chloride (MnCl2)", - "linfor": null, - "url": "", - "sku": "203734", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119451, - "mol_weight": 0, - "name": "1 M Calcium Chloride (CaCl2)", - "linfor": null, - "url": "", - "sku": "BP510", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Fisher Scientific", - "affiliation": null, - "username": null, - "link": "https://www.fishersci.com/us/en/home.html", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 31 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119452, - "mol_weight": 0, - "name": "1 M Potassium Chloride (KCl)", - "linfor": null, - "url": "", - "sku": "P3911", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119453, - "mol_weight": 0, - "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.5 (HEPES (Na ))", - "linfor": null, - "url": "", - "sku": "H3375", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119454, - "mol_weight": 0, - "name": "1 M Hydroxyethyl piperazineethanesulfonic acid pH 7.9 (HEPES (K ))", - "linfor": null, - "url": "", - "sku": "H3375", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119455, - "mol_weight": 0, - "name": "5 M Sodium chloride (NaCl)", - "linfor": null, - "url": "", - "sku": "S5150-1L", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119456, - "mol_weight": 0, - "name": "0.5 M Ethylenediaminetetraacetic acid (EDTA)", - "linfor": null, - "url": "", - "sku": "3002E", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Research Organics", - "affiliation": null, - "username": null, - "link": "http://www.sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 279 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119457, - "mol_weight": 0, - "name": "0.2 M Ethylene glycol-bis(β-aminoethyl ether)-N,N,N,N-tetraacetic acid (EGTA)", - "linfor": null, - "url": "", - "sku": "E3889", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119458, - "mol_weight": 0, - "name": "2 M Spermidine ", - "linfor": null, - "url": "", - "sku": "S2501", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119459, - "mol_weight": 0, - "name": "Roche Complete Protease Inhibitor EDTA-Free tablets ", - "linfor": null, - "url": "", - "sku": "5056489001", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119460, - "mol_weight": 0, - "name": "2 mg/ml Glycogen (1:10 dilution)", - "linfor": null, - "url": "", - "sku": "10930193001", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119461, - "mol_weight": 0, - "name": "RNase A, DNase and protease-free (10 mg/ml)", - "linfor": null, - "url": "", - "sku": "EN0531", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Thermo Fisher Scientific", - "affiliation": null, - "username": null, - "link": "https://www.thermofisher.com/us/en/home.html", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 95 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119462, - "mol_weight": 0, - "name": "Gel and PCR Clean-up kit ", - "linfor": null, - "url": "", - "sku": "740609.250", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Macherey and Nagel", - "affiliation": null, - "username": null, - "link": "http://www.mn-net.com/", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 133 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119463, - "mol_weight": 0, - "name": "Agencourt AMPure XP magnetic beads ", - "linfor": null, - "url": "", - "sku": "A63880", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Beckman Coulter", - "affiliation": null, - "username": null, - "link": "https://www.beckmancoulter.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 32 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119464, - "mol_weight": 0, - "name": "10% Sodium dodecyl sulfate (SDS)", - "linfor": null, - "url": "", - "sku": "L4509", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma Aldrich", - "affiliation": null, - "username": null, - "link": "http://sigmaaldrich.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 17 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119465, - "mol_weight": 0, - "name": "Proteinase K", - "linfor": null, - "url": "", - "sku": "EO0492", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Thermo Fisher Scientific", - "affiliation": null, - "username": null, - "link": "https://www.thermofisher.com/us/en/home.html", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 95 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119466, - "mol_weight": 0, - "name": "Phenol-chloroform-isoamyl alcohol 25:24:1 (PCI)", - "linfor": null, - "url": "", - "sku": "15593049", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Invitrogen - Thermo Fisher", - "affiliation": null, - "username": null, - "link": "http://www.thermofisher.com/us/en/home/brands/invitrogen.html", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 191 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119467, - "mol_weight": 0, - "name": "Chloroform", - "linfor": null, - "url": "", - "sku": "366919-1L", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Sigma", - "affiliation": null, - "username": null, - "link": "www.sigma.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 114 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119468, - "mol_weight": 0, - "name": "1 M Tris-HCl pH 8.0", - "linfor": null, - "url": "", - "sku": "", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Contributed by users", - "affiliation": null, - "username": null, - "link": "https://www.protocols.io", - "image": { - "source": "https://www.protocols.io/img/vendors/1.png", - "placeholder": "https://www.protocols.io/img/vendors/1.png" - }, - "badges": [], - "research_interests": null, - "id": 1 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119469, - "mol_weight": 0, - "name": "Ethanol ", - "linfor": null, - "url": "", - "sku": "2716", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Decon Labs", - "affiliation": null, - "username": null, - "link": "http://deconlabs.com/", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 280 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - }, - { - "id": 119470, - "mol_weight": 0, - "name": "Qubit dsDNA HS kit ", - "linfor": null, - "url": "", - "sku": "Q32851", - "cas_number": "", - "rrid": "", - "public": 0, - "vendor": { - "name": "Life Technologies", - "affiliation": null, - "username": null, - "link": "http://www.lifetechnologies.com", - "image": { - "source": "https://www.protocols.io/img/vendors/placeholder.png", - "placeholder": "https://www.protocols.io/img/vendors/placeholder.png" - }, - "badges": [], - "research_interests": null, - "id": 11 - }, - "can_edit": 0, - "stats": { - "total_protocols": 0 - } - } - ], - "description": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", - "changed_on": 1522231932 - }, - "status_code": 0 -} diff --git a/spec/utilities/protocol_importers/attachments_builder_spec.rb b/spec/utilities/protocol_importers/attachments_builder_spec.rb new file mode 100644 index 000000000..99161d636 --- /dev/null +++ b/spec/utilities/protocol_importers/attachments_builder_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ProtocolImporters::AttachmentsBuilder do + let(:step) do + JSON.parse(file_fixture('protocols_importer/step_with_attachments.json').read).to_h.with_indifferent_access + end + let(:generate_files_from_step) { described_class.generate(step) } + let(:first_file_in_result) { generate_files_from_step.first } + + before do + stub_request(:get, 'https://pbs.twimg.com/media/Cwu3zrZWQAA7axs.jpg').to_return(status: 200, body: '', headers: {}) + stub_request(:get, 'http://something.com/wp-content/uploads/2014/11/14506718045_5b3e71dacd_o.jpg') + .to_return(status: 200, body: '', headers: {}) + end + + describe 'self.build_assets_for_step' do + it 'returns array of Asset instances' do + expect(first_file_in_result).to be_instance_of(Asset) + end + + it 'returns valid table' do + expect(first_file_in_result).to be_valid + end + end +end diff --git a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb new file mode 100644 index 000000000..9b874b817 --- /dev/null +++ b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ProtocolImporters::ProtocolDescriptionBuilder do + let(:description_only) do + JSON.parse(file_fixture('protocols_importer/description_with_body.json').read).to_h.with_indifferent_access + end + let(:description_with_image) do + JSON.parse(file_fixture('protocols_importer/description_with_image.json').read).to_h.with_indifferent_access + end + let(:description_with_extra_content) do + JSON.parse(file_fixture('protocols_importer/description_with_extra_content.json').read).to_h.with_indifferent_access + end + let(:description_with_html) do + JSON.parse(file_fixture('protocols_importer/description_with_body_html.json').read).to_h.with_indifferent_access + end + + describe 'self.generate' do + context 'when description field not exists' do + it 'returns empty string' do + expect(described_class.generate({})).to be == '' + end + end + + context 'when have only description' do + it 'includes paragraph description' do + expect(described_class.generate(description_only)).to include('

original desc

') + end + + it 'strips HTML tags' do + expect(described_class.generate(description_with_html)).to be == '

Text only

' + end + end + + context 'when includes image' do + it 'includes image tag' do + expect(described_class.generate(description_with_image)).to include('').size).to be == 7 + end + end + end +end diff --git a/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb index 78e2b6eb8..92819d584 100644 --- a/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb +++ b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb @@ -8,7 +8,14 @@ describe ProtocolImporters::ProtocolIntermediateObject do let(:user) { create :user } let(:team) { create :team } let(:normalized_result) do - JSON.parse(file_fixture('protocols_io/v3/normalized_single_protocol.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + .to_h.with_indifferent_access + end + + before do + stub_request(:get, 'https://pbs.twimg.com/media/Cwu3zrZWQAA7axs.jpg').to_return(status: 200, body: '', headers: {}) + stub_request(:get, 'http://something.com/wp-content/uploads/2014/11/14506718045_5b3e71dacd_o.jpg') + .to_return(status: 200, body: '', headers: {}) end describe '.build' do @@ -18,7 +25,8 @@ describe ProtocolImporters::ProtocolIntermediateObject do describe '.import' do context 'when have valid object' do it { expect { pio.import }.to change { Protocol.all.count }.by(1) } - it { expect { pio.import }.to change { Step.all.count }.by(66) } + it { expect { pio.import }.to change { Step.all.count }.by(2) } + it { expect { pio.import }.to change { Asset.all.count }.by(2) } it { expect(pio.import).to be_valid } end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index 2738c93f1..86151722d 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -4,7 +4,8 @@ require 'rails_helper' describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do let(:response) do - JSON.parse(file_fixture('protocols_io/v3/single_protocol.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocols_importer/protocols_io/v3/single_protocol.json').read) + .to_h.with_indifferent_access end let(:response_without_title) do @@ -14,7 +15,8 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do end let(:normalized_result) do - JSON.parse(file_fixture('protocols_io/v3/normalized_single_protocol.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + .to_h.with_indifferent_access end describe '#load_protocol' do diff --git a/spec/utilities/protocol_importers/step_description_builder_spec.rb b/spec/utilities/protocol_importers/step_description_builder_spec.rb new file mode 100644 index 000000000..89dfce8b7 --- /dev/null +++ b/spec/utilities/protocol_importers/step_description_builder_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ProtocolImporters::StepDescriptionBuilder do + let(:description_only) do + JSON.parse(file_fixture('protocols_importer/description_with_body.json').read).to_h.with_indifferent_access + end + + let(:description_with_html) do + JSON.parse(file_fixture('protocols_importer/description_with_body_html.json').read).to_h.with_indifferent_access + end + + let(:description_with_components) do + JSON.parse(file_fixture('protocols_importer/step_description_with_components.json').read) + .to_h.with_indifferent_access + end + + let(:description_with_extra_content) do + JSON.parse(file_fixture('protocols_importer/description_with_extra_content.json').read) + .to_h.with_indifferent_access + end + + let(:normalized_json) do + JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + .to_h.with_indifferent_access + end + + let(:step_description_from_normalized_json) { described_class.generate(normalized_json[:protocol][:steps].first) } + + describe 'self.generate' do + context 'when description field not exists' do + it 'returns empty string' do + expect(described_class.generate({})).to be == '' + end + end + + context 'when have only description body' do + it 'includes paragraph description' do + expect(described_class.generate(description_only)).to include('

original desc

') + end + end + + context 'when have components' do + it 'retunrs extra content with title and body' do + expect(step_description_from_normalized_json.scan('step-description-component-').size).to be == 13 + end + + it 'strips HTML tags from body values for component' do + expect(described_class.generate(description_with_components).scan('alert').size).to be == 0 + end + end + + context 'when have extra_fileds' do + it 'add extra fields as paragraphs' do + expect(described_class.generate(description_with_extra_content).scan('

').size).to be == 7 + end + + it 'strips HTML tags for values' do + expect(described_class.generate(description_with_html)).to be == '

Text only

' + end + end + end +end diff --git a/spec/utilities/protocol_importers/tables_builder_spec.rb b/spec/utilities/protocol_importers/tables_builder_spec.rb new file mode 100644 index 000000000..36aee139d --- /dev/null +++ b/spec/utilities/protocol_importers/tables_builder_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ProtocolImporters::TablesBuilder do + # rubocop:disable Metrics/LineLength + let(:description_string) { '
12345678910
abcdefghaa
1112345111
1111111111
asdasdasaasasdsadsaasdas124521
123
' } + # rubocop:enable Metrics/LineLength + + let(:extract_tables_from_string_result) { described_class.extract_tables_from_html_string(description_string) } + let(:first_table_in_result) { extract_tables_from_string_result.first } + + describe '.extract_tables_from_string' do + it 'returns array of Table instances' do + expect(first_table_in_result).to be_instance_of(Table) + end + + it 'returns 2 tables ' do + expect(extract_tables_from_string_result.count).to be == 2 + end + + it 'returns valid table' do + expect(first_table_in_result).to be_valid + end + + it 'returns table with 5 rows' do + expect(JSON.parse(first_table_in_result.contents)['data'].count).to be == 5 + end + + it 'returns table with 10 columns' do + expect(JSON.parse(first_table_in_result.contents)['data'].first.count).to be == 10 + end + end +end From 2bb4f558b7521c8fc8883ab1a69f346e5b33e0b9 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Mon, 3 Jun 2019 12:42:15 +0200 Subject: [PATCH 08/73] View rendering simplified, small improvements --- .../protocol_description_builder.rb | 17 +++---- .../protocol_intermediate_object.rb | 16 +++--- .../protocols_io/v3/protocol_normalizer.rb | 25 +++++----- .../protocols_io/v3/step_components.rb | 7 ++- .../step_description_builder.rb | 31 +++--------- .../templates/_amount.html.erb | 4 ++ .../templates/_command.html.erb | 5 ++ .../templates/_concentration.html.erb | 3 ++ .../templates/_dataset.html.erb | 5 ++ .../templates}/_details.html.erb | 2 +- .../templates/_duration.html.erb | 4 ++ .../templates/_gotostep.html.erb | 4 ++ .../templates/_link.html.erb | 4 ++ .../templates/_note.html.erb | 4 ++ .../templates/_reagent.html.erb | 7 +++ .../templates/_result.html.erb | 3 ++ .../templates/_software.html.erb | 6 +++ .../templates/_temperature.html.erb | 3 ++ .../templates/_warning.html.erb | 3 ++ .../templates/protocol_description.html.erb | 12 +++++ .../templates/step_description.html.erb | 16 ++++++ .../step_description/amount.html.erb | 4 -- .../step_description/command.html.erb | 5 -- .../step_description/concentration.html.erb | 3 -- .../step_description/dataset.html.erb | 5 -- .../step_description/duration.html.erb | 4 -- .../step_description/gotostep.html.erb | 4 -- .../step_description/link.html.erb | 4 -- .../step_description/note.html.erb | 4 -- .../step_description/reagent.html.erb | 7 --- .../step_description/result.html.erb | 3 -- .../step_description/software.html.erb | 6 --- .../step_description/temperature.html.erb | 3 -- .../step_description/warning.html.erb | 3 -- config/locales/protocols/en.yml | 49 +++++++++++++++++++ .../protocol_description_builder_spec.rb | 6 +-- .../step_description_builder_spec.rb | 6 +-- 37 files changed, 179 insertions(+), 118 deletions(-) create mode 100644 app/views/protocol_importers/templates/_amount.html.erb create mode 100644 app/views/protocol_importers/templates/_command.html.erb create mode 100644 app/views/protocol_importers/templates/_concentration.html.erb create mode 100644 app/views/protocol_importers/templates/_dataset.html.erb rename app/views/{templates/protocols_import/step_description => protocol_importers/templates}/_details.html.erb (51%) create mode 100644 app/views/protocol_importers/templates/_duration.html.erb create mode 100644 app/views/protocol_importers/templates/_gotostep.html.erb create mode 100644 app/views/protocol_importers/templates/_link.html.erb create mode 100644 app/views/protocol_importers/templates/_note.html.erb create mode 100644 app/views/protocol_importers/templates/_reagent.html.erb create mode 100644 app/views/protocol_importers/templates/_result.html.erb create mode 100644 app/views/protocol_importers/templates/_software.html.erb create mode 100644 app/views/protocol_importers/templates/_temperature.html.erb create mode 100644 app/views/protocol_importers/templates/_warning.html.erb create mode 100644 app/views/protocol_importers/templates/protocol_description.html.erb create mode 100644 app/views/protocol_importers/templates/step_description.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/amount.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/command.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/concentration.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/dataset.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/duration.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/gotostep.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/link.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/note.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/reagent.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/result.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/software.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/temperature.html.erb delete mode 100644 app/views/templates/protocols_import/step_description/warning.html.erb create mode 100644 config/locales/protocols/en.yml diff --git a/app/utilities/protocol_importers/protocol_description_builder.rb b/app/utilities/protocol_importers/protocol_description_builder.rb index a0cb4eb03..3e96f96b0 100644 --- a/app/utilities/protocol_importers/protocol_description_builder.rb +++ b/app/utilities/protocol_importers/protocol_description_builder.rb @@ -5,17 +5,12 @@ module ProtocolImporters def self.generate(protocol_json) return '' unless protocol_json[:description] - html_desc = '' - html_desc = "

#{remove_html(protocol_json[:description][:body])}

" if protocol_json[:description][:body] - html_desc += "
" if protocol_json[:description][:image] - html_desc += protocol_json[:description][:extra_content]&.map do |i| - "

#{remove_html(i[:title])}:
#{remove_html(i[:body])}

" - end&.join('
').to_s - html_desc - end - - def self.remove_html(string) - ActionView::Base.full_sanitizer.sanitize(string) + html_string = ApplicationController + .renderer + .render(template: 'protocol_importers/templates/protocol_description', + layout: false, + assigns: { description: protocol_json[:description] }) + html_string end end end diff --git a/app/utilities/protocol_importers/protocol_intermediate_object.rb b/app/utilities/protocol_importers/protocol_intermediate_object.rb index fd3bcf5a5..a7e023829 100644 --- a/app/utilities/protocol_importers/protocol_intermediate_object.rb +++ b/app/utilities/protocol_importers/protocol_intermediate_object.rb @@ -18,7 +18,7 @@ module ProtocolImporters def build @protocol = Protocol.new(protocol_attributes) - @protocol.description = ProtocolDescriptionBuilder.generate(@normalized_protocol_data&.reject { |k| k == :steps }) + @protocol.description = ProtocolDescriptionBuilder.generate(@normalized_protocol_data) @protocol.steps << build_steps @protocol end @@ -36,12 +36,14 @@ module ProtocolImporters end def protocol_attributes - defaults = { protocol_type: :in_repository_public, added_by: @user, team: @team } - values = %i(name published_on authors) - p_attrs = @normalized_protocol_data.slice(*values).each_with_object({}) do |(k, v), h| - h[k] = k == 'published_on' ? Time.at(v) : v - end - p_attrs.merge!(defaults) + { + protocol_type: :in_repository_public, + added_by: @user, + team: @team, + name: @normalized_protocol_data[:name], + published_on: Time.at(@normalized_protocol_data[:published_on]), + authors: @normalized_protocol_data[:authors] + } end def step_attributes(step_json) diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index 48a4df86e..70611efa0 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -15,18 +15,21 @@ module ProtocolImporters def normalize(response) protocol_hash = response.parsed_response.with_indifferent_access[:protocol] - normalized_data = Hash.new { |h, k| h[k] = {} } - normalized_data[:uri] = response.request.last_uri.to_s - normalized_data[:source] = Constants::PROTOCOLS_IO_V3_API[:source_id] - normalized_data[:doi] = protocol_hash[:doi] - normalized_data[:published_on] = protocol_hash[:published_on] - normalized_data[:version] = protocol_hash[:version_id] - normalized_data[:source_id] = protocol_hash[:id] - normalized_data[:name] = protocol_hash[:title] - normalized_data[:description][:body] = protocol_hash[:description] - normalized_data[:description][:image] = protocol_hash[:image][:source] - normalized_data[:authors] = protocol_hash[:authors].map { |e| e[:name] }.join(', ') + normalized_data = { + uri: response.request.last_uri.to_s, + source: Constants::PROTOCOLS_IO_V3_API[:source_id], + doi: protocol_hash[:doi], + published_on: protocol_hash[:published_on], + version: protocol_hash[:version_id], + source_id: protocol_hash[:id], + name: protocol_hash[:title], + description: { + body: protocol_hash[:description], + image: protocol_hash[:image][:source] + }, + authors: protocol_hash[:authors].map { |e| e[:name] }.join(', ') + } normalized_data[:steps] = protocol_hash[:steps].map do |e| { diff --git a/app/utilities/protocol_importers/protocols_io/v3/step_components.rb b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb index 13d988699..f8c83547d 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/step_components.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/step_components.rb @@ -5,10 +5,10 @@ module ProtocolImporters module V3 class StepComponents AVAILABLE_COMPONENTS = { - 6 => :title, 1 => :description, 3 => :amount, 4 => :duration, + 6 => :title, 7 => :link, 8 => :software, 9 => :dataset, @@ -17,11 +17,14 @@ module ProtocolImporters 19 => :safety, 20 => :reagents, 22 => :gotostep, + 23 => :file, 24 => :temperature, 25 => :concentration, 26 => :notes }.freeze + DESCRIPTION_COMPONENTS = AVAILABLE_COMPONENTS.slice(3, 4, 7, 8, 9, 15, 17, 19, 20, 22, 24, 25, 26).freeze + def self.get_component(id, components) if AVAILABLE_COMPONENTS.include?(id) components.find { |o| o[:type_id] == id } @@ -39,7 +42,7 @@ module ProtocolImporters end def self.description_components(components) - description_components = components.select { |c| AVAILABLE_COMPONENTS.except(6, 1).include?(c[:type_id]) } + description_components = components.select { |c| DESCRIPTION_COMPONENTS.include?(c[:type_id]) } description_components.map do |dc| build_desc_component dc diff --git a/app/utilities/protocol_importers/step_description_builder.rb b/app/utilities/protocol_importers/step_description_builder.rb index 5714f976e..d510c1372 100644 --- a/app/utilities/protocol_importers/step_description_builder.rb +++ b/app/utilities/protocol_importers/step_description_builder.rb @@ -5,31 +5,12 @@ module ProtocolImporters def self.generate(step_json) return '' unless step_json[:description] - html_description = '' - html_description = "

#{remove_html(step_json[:description][:body])}

" if step_json[:description][:body] - - # Add components from JSON - html_description += step_json[:description][:components]&.inject('') do |html_string, component| - sanitized_component = component.except(:type) - sanitized_component[:body] = remove_html(component[:body]) if component[:body] - - html_string + ApplicationController - .renderer - .render(template: "templates/protocols_import/step_description/#{component[:type]}", - layout: false, - assigns: { item: sanitized_component }) - end.to_s - - # Add extra content from JSON - html_description += step_json[:description][:extra_content]&.map do |i| - "

#{remove_html(i[:title])}:
#{remove_html(i[:body])}

" - end&.join('
').to_s - - html_description - end - - def self.remove_html(string) - ActionView::Base.full_sanitizer.sanitize(string) + html_string = ApplicationController + .renderer + .render(template: 'protocol_importers/templates/step_description', + layout: false, + assigns: { step_description: step_json[:description] }) + html_string end end end diff --git a/app/views/protocol_importers/templates/_amount.html.erb b/app/views/protocol_importers/templates/_amount.html.erb new file mode 100644 index 000000000..063a4952b --- /dev/null +++ b/app/views/protocol_importers/templates/_amount.html.erb @@ -0,0 +1,4 @@ +

<%= t('protocol_importers.templates.amount.title') %>
+ <%= "#{item[:value]} #{item[:unit]} #{item[:name]} " %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_command.html.erb b/app/views/protocol_importers/templates/_command.html.erb new file mode 100644 index 000000000..21ab069e0 --- /dev/null +++ b/app/views/protocol_importers/templates/_command.html.erb @@ -0,0 +1,5 @@ +

<%= t('protocol_importers.templates.command.title') %>
+ <%= "#{t('protocol_importers.templates.command.name')}: #{item[:name]}" %> + <%= "#{t('protocol_importers.templates.command.code')}: #{item[:command]}" %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_concentration.html.erb b/app/views/protocol_importers/templates/_concentration.html.erb new file mode 100644 index 000000000..090d195d5 --- /dev/null +++ b/app/views/protocol_importers/templates/_concentration.html.erb @@ -0,0 +1,3 @@ +

<%= t('protocol_importers.templates.concentration.title') %>
+<%= "#{item[:value]} #{item[:unit]} #{item[:name]} " %>

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_dataset.html.erb b/app/views/protocol_importers/templates/_dataset.html.erb new file mode 100644 index 000000000..0ff510b15 --- /dev/null +++ b/app/views/protocol_importers/templates/_dataset.html.erb @@ -0,0 +1,5 @@ +

<%= t('protocol_importers.templates.dataset.title') %>
+ <%= "#{t('protocol_importers.templates.dataset.name')}: #{item[:name]}" %> + <%= "#{t('protocol_importers.templates.dataset.link')}: #{item[:source]}" if item[:source] %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/templates/protocols_import/step_description/_details.html.erb b/app/views/protocol_importers/templates/_details.html.erb similarity index 51% rename from app/views/templates/protocols_import/step_description/_details.html.erb rename to app/views/protocol_importers/templates/_details.html.erb index 5b4d4b3f7..40fee2b91 100644 --- a/app/views/templates/protocols_import/step_description/_details.html.erb +++ b/app/views/protocol_importers/templates/_details.html.erb @@ -1,5 +1,5 @@

- <%= item[:details]&.any? ? 'Details:' : '' %> + <%= item[:details]&.any? ? "#{t('protocol_importers.templates.details.title') }" : '' %> <% item[:details]&.each do |k, v| %> <%= "#{k.camelize.to_s}: #{v.to_s}" %> <% end %> diff --git a/app/views/protocol_importers/templates/_duration.html.erb b/app/views/protocol_importers/templates/_duration.html.erb new file mode 100644 index 000000000..f446aaca7 --- /dev/null +++ b/app/views/protocol_importers/templates/_duration.html.erb @@ -0,0 +1,4 @@ +

<%= t('protocol_importers.templates.duration.title') %>
+ <%= "#{item[:name]} #{item[:value]} #{t('protocol_importers.templates.duration.unit')}" %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_gotostep.html.erb b/app/views/protocol_importers/templates/_gotostep.html.erb new file mode 100644 index 000000000..56a77ea2c --- /dev/null +++ b/app/views/protocol_importers/templates/_gotostep.html.erb @@ -0,0 +1,4 @@ +

<%= t('protocol_importers.templates.gotostep.title') %>
+ <%= "#{t('protocol_importers.templates.gotostep.number')}: #{item[:step_id]}" %>
+ <%= "#{t('protocol_importers.templates.gotostep.reason')}: #{item[:value]}" %>
+ <%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_link.html.erb b/app/views/protocol_importers/templates/_link.html.erb new file mode 100644 index 000000000..c9aecd6aa --- /dev/null +++ b/app/views/protocol_importers/templates/_link.html.erb @@ -0,0 +1,4 @@ +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_note.html.erb b/app/views/protocol_importers/templates/_note.html.erb new file mode 100644 index 000000000..cda54ec2b --- /dev/null +++ b/app/views/protocol_importers/templates/_note.html.erb @@ -0,0 +1,4 @@ +

<%= t('protocol_importers.templates.note.title') %>
+ <%= "#{t('protocol_importers.templates.note.author')}: #{item[:author]}" %>
+ <%= item[:body] %>

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_reagent.html.erb b/app/views/protocol_importers/templates/_reagent.html.erb new file mode 100644 index 000000000..589e8598a --- /dev/null +++ b/app/views/protocol_importers/templates/_reagent.html.erb @@ -0,0 +1,7 @@ +

<%= t('protocol_importers.templates.reagent.title') %>
+ <%= "#{t('protocol_importers.templates.reagent.name')}: #{item[:name]}" %> + <%= "#{t('protocol_importers.templates.reagent.mol_weight')}: #{item[:mol_weight]}" %> + <%= "#{t('protocol_importers.templates.reagent.link')}: #{item[:source]}" if item[:source] %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> + diff --git a/app/views/protocol_importers/templates/_result.html.erb b/app/views/protocol_importers/templates/_result.html.erb new file mode 100644 index 000000000..ef8450d35 --- /dev/null +++ b/app/views/protocol_importers/templates/_result.html.erb @@ -0,0 +1,3 @@ +

<%= t('protocol_importers.templates.result.title') %>
+<%= item[:body] %>

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_software.html.erb b/app/views/protocol_importers/templates/_software.html.erb new file mode 100644 index 000000000..995d0cc5a --- /dev/null +++ b/app/views/protocol_importers/templates/_software.html.erb @@ -0,0 +1,6 @@ +

<%= t('protocol_importers.templates.software.title') %>
+ <%= "#{t('protocol_importers.templates.software.name')}: #{item[:name]}" %> + <%= "#{t('protocol_importers.templates.software.link')}: #{ActionController::Base.helpers.link_to(item[:source], item[:source])}" %> +

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> + diff --git a/app/views/protocol_importers/templates/_temperature.html.erb b/app/views/protocol_importers/templates/_temperature.html.erb new file mode 100644 index 000000000..22e516d49 --- /dev/null +++ b/app/views/protocol_importers/templates/_temperature.html.erb @@ -0,0 +1,3 @@ +

<%= t('protocol_importers.templates.temperature.title') %>
+<%= "#{item[:value]} #{item[:unit]} #{item[:name]}" %>

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/_warning.html.erb b/app/views/protocol_importers/templates/_warning.html.erb new file mode 100644 index 000000000..8d20de5e2 --- /dev/null +++ b/app/views/protocol_importers/templates/_warning.html.erb @@ -0,0 +1,3 @@ +

<%= t('protocol_importers.templates.warning.title') %>
+<%= item[:body] %>

+<%= render partial: 'protocol_importers/templates/details', locals: { item: item } %> diff --git a/app/views/protocol_importers/templates/protocol_description.html.erb b/app/views/protocol_importers/templates/protocol_description.html.erb new file mode 100644 index 000000000..7f209c203 --- /dev/null +++ b/app/views/protocol_importers/templates/protocol_description.html.erb @@ -0,0 +1,12 @@ +<% if @description[:body] %> +

<%= strip_tags @description[:body] %>

+<% end %> +<% if @description[:image] %> +
+ +<% end %> + +<% @description[:extra_content]&.each do |i| %> + <%= strip_tags i[:title] %>:
+ <%= strip_tags i[:body] %>
+<% end %> diff --git a/app/views/protocol_importers/templates/step_description.html.erb b/app/views/protocol_importers/templates/step_description.html.erb new file mode 100644 index 000000000..133859fb1 --- /dev/null +++ b/app/views/protocol_importers/templates/step_description.html.erb @@ -0,0 +1,16 @@ +<% if @step_description[:body] %> +

<%= strip_tags @step_description[:body] %>

+<% end %> + +<% @step_description[:components]&.each do |component| %> + <% sanitized_component = component.except('type') %> + <% sanitized_component[:body] = strip_tags(component[:body]) if component[:body] %> + <%= render partial: "protocol_importers/templates/#{component[:type]}", locals: { item: sanitized_component } %> +<% end %> + +<% @step_description[:extra_content]&.each do |i| %> + <%= strip_tags i[:title] %>:
+ <%= strip_tags i[:body] %>
+<% end %> + + diff --git a/app/views/templates/protocols_import/step_description/amount.html.erb b/app/views/templates/protocols_import/step_description/amount.html.erb deleted file mode 100644 index a8ecb982f..000000000 --- a/app/views/templates/protocols_import/step_description/amount.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

Amount:
- <%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]} " %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/command.html.erb b/app/views/templates/protocols_import/step_description/command.html.erb deleted file mode 100644 index b0c97c75b..000000000 --- a/app/views/templates/protocols_import/step_description/command.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

Command:
- <%= "Name: #{@item[:name]}" %> - <%= "Command: #{@item[:command]}" %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/concentration.html.erb b/app/views/templates/protocols_import/step_description/concentration.html.erb deleted file mode 100644 index 9df661d95..000000000 --- a/app/views/templates/protocols_import/step_description/concentration.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Concentration:
-<%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]} " %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/dataset.html.erb b/app/views/templates/protocols_import/step_description/dataset.html.erb deleted file mode 100644 index 4b8ce3921..000000000 --- a/app/views/templates/protocols_import/step_description/dataset.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

Dataset:
- <%= "Name: #{@item[:name]}" %> - <%= "Link: #{ActionController::Base.helpers.link_to(@item[:source], @item[:source])}" if @item[:source] %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/duration.html.erb b/app/views/templates/protocols_import/step_description/duration.html.erb deleted file mode 100644 index f0f68e48f..000000000 --- a/app/views/templates/protocols_import/step_description/duration.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

Duration:
- <%= "#{@item[:name]} #{@item[:value]} seconds" %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/gotostep.html.erb b/app/views/templates/protocols_import/step_description/gotostep.html.erb deleted file mode 100644 index fbe41eff8..000000000 --- a/app/views/templates/protocols_import/step_description/gotostep.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

Go to step:
-Step nr: <%= @item[:step_id] %>
-When: <%= @item[:value] %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/link.html.erb b/app/views/templates/protocols_import/step_description/link.html.erb deleted file mode 100644 index 13d139192..000000000 --- a/app/views/templates/protocols_import/step_description/link.html.erb +++ /dev/null @@ -1,4 +0,0 @@ - -<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/note.html.erb b/app/views/templates/protocols_import/step_description/note.html.erb deleted file mode 100644 index e5e284686..000000000 --- a/app/views/templates/protocols_import/step_description/note.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

Note:
-Author: <%= @item[:author] %>
-<%= @item[:body] %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/reagent.html.erb b/app/views/templates/protocols_import/step_description/reagent.html.erb deleted file mode 100644 index b51b795c0..000000000 --- a/app/views/templates/protocols_import/step_description/reagent.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Reagent:
- <%= "Name: #{@item[:name]}" %> - <%= "Name: #{@item[:mol_weight]}" %> - <%= "Link: #{@item[:source]}" if @item[:source] %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> - diff --git a/app/views/templates/protocols_import/step_description/result.html.erb b/app/views/templates/protocols_import/step_description/result.html.erb deleted file mode 100644 index 560ac17d6..000000000 --- a/app/views/templates/protocols_import/step_description/result.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Expected result:
-<%= @item[:body] %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/software.html.erb b/app/views/templates/protocols_import/step_description/software.html.erb deleted file mode 100644 index 7abd87f3c..000000000 --- a/app/views/templates/protocols_import/step_description/software.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Software:
- <%= "Name of software: #{@item[:name]}" %> - <%= "Link: #{ActionController::Base.helpers.link_to(@item[:source], @item[:source])}" %> -

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> - diff --git a/app/views/templates/protocols_import/step_description/temperature.html.erb b/app/views/templates/protocols_import/step_description/temperature.html.erb deleted file mode 100644 index 4dadc06eb..000000000 --- a/app/views/templates/protocols_import/step_description/temperature.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Temperature:
-<%= "#{@item[:value]} #{@item[:unit]} #{@item[:name]}" %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/app/views/templates/protocols_import/step_description/warning.html.erb b/app/views/templates/protocols_import/step_description/warning.html.erb deleted file mode 100644 index 141ad719d..000000000 --- a/app/views/templates/protocols_import/step_description/warning.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Warning:
-<%= @item[:body] %>

-<%= render partial: 'templates/protocols_import/step_description/details', locals: { item: @item } %> diff --git a/config/locales/protocols/en.yml b/config/locales/protocols/en.yml new file mode 100644 index 000000000..5e11cf57c --- /dev/null +++ b/config/locales/protocols/en.yml @@ -0,0 +1,49 @@ +en: + protocol_importers: + templates: + amount: + title: 'Amount' + command: + title: 'Command' + name: 'Name' + code: 'Command' + concentration: + title: 'Concentration' + dataset: + title: 'Dataset' + name: 'Name' + link: 'Link' + details: + title: 'Details' + duration: + title: 'Duration' + unit: 'seconds' + gotostep: + title: 'Go to step' + number: 'Step nr' + reason: 'When' + link: + title: 'Link' + note: + title: 'Note' + author: 'Author' + reagent: + title: 'Reagent' + name: 'Name' + mol_weight: 'Molecular weight' + link: 'Link' + result: + title: 'Expected result' + software: + title: 'Software' + name: 'Name' + link: 'Link' + temperature: + title: 'Temperature' + warning: + title: 'Warning' + + + + + diff --git a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb index 9b874b817..5465df9c0 100644 --- a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb @@ -25,11 +25,11 @@ RSpec.describe ProtocolImporters::ProtocolDescriptionBuilder do context 'when have only description' do it 'includes paragraph description' do - expect(described_class.generate(description_only)).to include('

original desc

') + expect(described_class.generate(description_only)).to include('

original desc

') end it 'strips HTML tags' do - expect(described_class.generate(description_with_html)).to be == '

Text only

' + expect(described_class.generate(description_with_html).scan('script').count).to be == 0 end end @@ -41,7 +41,7 @@ RSpec.describe ProtocolImporters::ProtocolDescriptionBuilder do context 'when have extra content' do it 'add extra fields as paragraphs' do - expect(described_class.generate(description_with_extra_content).scan('

').size).to be == 7 + expect(described_class.generate(description_with_extra_content).scan('
').size).to be == 6 end end end diff --git a/spec/utilities/protocol_importers/step_description_builder_spec.rb b/spec/utilities/protocol_importers/step_description_builder_spec.rb index 89dfce8b7..32badf795 100644 --- a/spec/utilities/protocol_importers/step_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/step_description_builder_spec.rb @@ -37,7 +37,7 @@ RSpec.describe ProtocolImporters::StepDescriptionBuilder do context 'when have only description body' do it 'includes paragraph description' do - expect(described_class.generate(description_only)).to include('

original desc

') + expect(described_class.generate(description_only)).to include('

original desc

') end end @@ -53,11 +53,11 @@ RSpec.describe ProtocolImporters::StepDescriptionBuilder do context 'when have extra_fileds' do it 'add extra fields as paragraphs' do - expect(described_class.generate(description_with_extra_content).scan('

').size).to be == 7 + expect(described_class.generate(description_with_extra_content).scan('
').size).to be == 6 end it 'strips HTML tags for values' do - expect(described_class.generate(description_with_html)).to be == '

Text only

' + expect(described_class.generate(description_with_html).scan('script').count).to be == 0 end end end From a15596a12615ab44bd1e9a97ca99f5ec136190ac Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Thu, 30 May 2019 20:24:11 +0200 Subject: [PATCH 09/73] Add ImportProtocolService --- .../import_protocol_from_client_service.rb | 62 +++++++++++++++++++ config/initializers/constants.rb | 6 ++ ...mport_protocol_from_client_service_spec.rb | 37 +++++++++++ 3 files changed, 105 insertions(+) create mode 100644 app/services/protocols/import_protocol_from_client_service.rb create mode 100644 spec/services/protocols/import_protocol_from_client_service_spec.rb diff --git a/app/services/protocols/import_protocol_from_client_service.rb b/app/services/protocols/import_protocol_from_client_service.rb new file mode 100644 index 000000000..565aa62a0 --- /dev/null +++ b/app/services/protocols/import_protocol_from_client_service.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Protocols + class ImportProtocolFromClientService + extend Service + + attr_reader :errors, :pio_protocol + + def initialize(protocol_client_id:, protocol_source:, user_id:, team_id:) + @id = protocol_client_id + @protocol_source = protocol_source + @user = User.find user_id + @team = Team.find team_id + @errors = {} + end + + def call + return self unless valid? + + # Catch api errors here, json invalid, also wrong/not found normalizer? + normalized_hash = client.load_protocol(id: @id) + # Catch api errors here + + pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, + user: @user, + team: @team) + @pio_protocol = pio.build + + if @pio_protocol.valid? + # catch errors during import here + pio.import + # catch errors during import here + else + # Add AR errors here + # @errors[:protcol] = pio.protocol.errors.to_json...? somehow + @errors[:protocol] = pio.protocol.errors + end + + self + end + + def succeed? + @errors.none? + end + + private + + def valid? + unless @id && @protocol_source && @user && @team + @errors[:invalid_arguments] = { '@id': @id, '@protocol_source': @protocol_source, 'user': @user, 'team': @team } + .map { |key, value| "Can't find #{key.capitalize}" if value.nil? }.compact + return false + end + true + end + + def client + endpoint_name = Constants::PROTOCOLS_ENDPOINTS.dig(*@protocol_source.split('/').map(&:to_sym)) + "ProtocolImporters::#{endpoint_name}::ProtocolNormalizer".constantize.new + end + end +end diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 2b3c5caea..2f5507712 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -193,6 +193,12 @@ class Constants # Protocol importers #============================================================================= + PROTOCOLS_ENDPOINTS = { + protocolsio: { + v3: 'ProtocolsIO::V3' + } + }.freeze + PROTOCOLS_IO_V3_API = { base_uri: 'https://www.protocols.io/api/v3/', default_timeout: 10, diff --git a/spec/services/protocols/import_protocol_from_client_service_spec.rb b/spec/services/protocols/import_protocol_from_client_service_spec.rb new file mode 100644 index 000000000..ccfdcf921 --- /dev/null +++ b/spec/services/protocols/import_protocol_from_client_service_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Protocols::ImportProtocolFromClientService do + let(:user) { create :user } + let(:team) { create :team } + let(:service_call) do + Protocols::ImportProtocolFromClientService + .call(protocol_client_id: 'id', protocol_source: 'protocolsio/v3', user_id: user.id, team_id: team.id) + end + let(:normalized_response) do + JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + .to_h.with_indifferent_access + end + + context 'when have invalid arguments' do + it 'returns an error when can\'t find user' do + allow(User).to receive(:find).and_return(nil) + + expect(service_call.errors).to have_key(:invalid_arguments) + end + end + + context 'when have valid arguments' do + before do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) + .to(receive(:load_protocol).and_return(normalized_response)) + # Do not generate and request real images + allow(ProtocolImporters::AttachmentsBuilder).to(receive(:generate).and_return([])) + end + it 'Adds Protocol record' do + expect { service_call }.to(change { Protocol.all.count }.by(1)) + end + # more tests will be implemented when add error handling to service + end +end From 92c24451d998b6409ec52a99fbb521e1020f393d Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Thu, 30 May 2019 20:26:22 +0200 Subject: [PATCH 10/73] Moved normalized_single_protocol.json --- .../description_with_body.json | 0 .../description_with_body_html.json | 0 .../description_with_extra_content.json | 0 .../description_with_image.json | 0 .../normalized_single_protocol.json | 0 .../protocols_io/v3/single_protocol.json | 0 .../step_description_with_components.json | 0 .../step_with_attachments.json | 0 .../import_protocol_from_client_service_spec.rb | 2 +- .../protocol_importers/attachments_builder_spec.rb | 2 +- .../protocol_description_builder_spec.rb | 8 ++++---- .../protocol_intermediate_object_spec.rb | 2 +- .../protocols_io/v3/protocol_normalizer_spec.rb | 4 ++-- .../step_description_builder_spec.rb | 10 +++++----- 14 files changed, 14 insertions(+), 14 deletions(-) rename spec/fixtures/files/{protocols_importer => protocol_importers}/description_with_body.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/description_with_body_html.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/description_with_extra_content.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/description_with_image.json (100%) rename spec/fixtures/files/{protocols_importer/protocols_io/v3 => protocol_importers}/normalized_single_protocol.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/protocols_io/v3/single_protocol.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/step_description_with_components.json (100%) rename spec/fixtures/files/{protocols_importer => protocol_importers}/step_with_attachments.json (100%) diff --git a/spec/fixtures/files/protocols_importer/description_with_body.json b/spec/fixtures/files/protocol_importers/description_with_body.json similarity index 100% rename from spec/fixtures/files/protocols_importer/description_with_body.json rename to spec/fixtures/files/protocol_importers/description_with_body.json diff --git a/spec/fixtures/files/protocols_importer/description_with_body_html.json b/spec/fixtures/files/protocol_importers/description_with_body_html.json similarity index 100% rename from spec/fixtures/files/protocols_importer/description_with_body_html.json rename to spec/fixtures/files/protocol_importers/description_with_body_html.json diff --git a/spec/fixtures/files/protocols_importer/description_with_extra_content.json b/spec/fixtures/files/protocol_importers/description_with_extra_content.json similarity index 100% rename from spec/fixtures/files/protocols_importer/description_with_extra_content.json rename to spec/fixtures/files/protocol_importers/description_with_extra_content.json diff --git a/spec/fixtures/files/protocols_importer/description_with_image.json b/spec/fixtures/files/protocol_importers/description_with_image.json similarity index 100% rename from spec/fixtures/files/protocols_importer/description_with_image.json rename to spec/fixtures/files/protocol_importers/description_with_image.json diff --git a/spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json b/spec/fixtures/files/protocol_importers/normalized_single_protocol.json similarity index 100% rename from spec/fixtures/files/protocols_importer/protocols_io/v3/normalized_single_protocol.json rename to spec/fixtures/files/protocol_importers/normalized_single_protocol.json diff --git a/spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json b/spec/fixtures/files/protocol_importers/protocols_io/v3/single_protocol.json similarity index 100% rename from spec/fixtures/files/protocols_importer/protocols_io/v3/single_protocol.json rename to spec/fixtures/files/protocol_importers/protocols_io/v3/single_protocol.json diff --git a/spec/fixtures/files/protocols_importer/step_description_with_components.json b/spec/fixtures/files/protocol_importers/step_description_with_components.json similarity index 100% rename from spec/fixtures/files/protocols_importer/step_description_with_components.json rename to spec/fixtures/files/protocol_importers/step_description_with_components.json diff --git a/spec/fixtures/files/protocols_importer/step_with_attachments.json b/spec/fixtures/files/protocol_importers/step_with_attachments.json similarity index 100% rename from spec/fixtures/files/protocols_importer/step_with_attachments.json rename to spec/fixtures/files/protocol_importers/step_with_attachments.json diff --git a/spec/services/protocols/import_protocol_from_client_service_spec.rb b/spec/services/protocols/import_protocol_from_client_service_spec.rb index ccfdcf921..3f77e7321 100644 --- a/spec/services/protocols/import_protocol_from_client_service_spec.rb +++ b/spec/services/protocols/import_protocol_from_client_service_spec.rb @@ -10,7 +10,7 @@ describe Protocols::ImportProtocolFromClientService do .call(protocol_client_id: 'id', protocol_source: 'protocolsio/v3', user_id: user.id, team_id: team.id) end let(:normalized_response) do - JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read) .to_h.with_indifferent_access end diff --git a/spec/utilities/protocol_importers/attachments_builder_spec.rb b/spec/utilities/protocol_importers/attachments_builder_spec.rb index 99161d636..49dbbd156 100644 --- a/spec/utilities/protocol_importers/attachments_builder_spec.rb +++ b/spec/utilities/protocol_importers/attachments_builder_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' RSpec.describe ProtocolImporters::AttachmentsBuilder do let(:step) do - JSON.parse(file_fixture('protocols_importer/step_with_attachments.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/step_with_attachments.json').read).to_h.with_indifferent_access end let(:generate_files_from_step) { described_class.generate(step) } let(:first_file_in_result) { generate_files_from_step.first } diff --git a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb index 5465df9c0..9f8b0130b 100644 --- a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb @@ -4,16 +4,16 @@ require 'rails_helper' RSpec.describe ProtocolImporters::ProtocolDescriptionBuilder do let(:description_only) do - JSON.parse(file_fixture('protocols_importer/description_with_body.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_body.json').read).to_h.with_indifferent_access end let(:description_with_image) do - JSON.parse(file_fixture('protocols_importer/description_with_image.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_image.json').read).to_h.with_indifferent_access end let(:description_with_extra_content) do - JSON.parse(file_fixture('protocols_importer/description_with_extra_content.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_extra_content.json').read).to_h.with_indifferent_access end let(:description_with_html) do - JSON.parse(file_fixture('protocols_importer/description_with_body_html.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_body_html.json').read).to_h.with_indifferent_access end describe 'self.generate' do diff --git a/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb index 92819d584..dd356b002 100644 --- a/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb +++ b/spec/utilities/protocol_importers/protocol_intermediate_object_spec.rb @@ -8,7 +8,7 @@ describe ProtocolImporters::ProtocolIntermediateObject do let(:user) { create :user } let(:team) { create :team } let(:normalized_result) do - JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read) .to_h.with_indifferent_access end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index 86151722d..e42e500a3 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do let(:response) do - JSON.parse(file_fixture('protocols_importer/protocols_io/v3/single_protocol.json').read) + JSON.parse(file_fixture('protocol_importers/protocols_io/v3/single_protocol.json').read) .to_h.with_indifferent_access end @@ -15,7 +15,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do end let(:normalized_result) do - JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read) .to_h.with_indifferent_access end diff --git a/spec/utilities/protocol_importers/step_description_builder_spec.rb b/spec/utilities/protocol_importers/step_description_builder_spec.rb index 32badf795..57037ebcc 100644 --- a/spec/utilities/protocol_importers/step_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/step_description_builder_spec.rb @@ -4,25 +4,25 @@ require 'rails_helper' RSpec.describe ProtocolImporters::StepDescriptionBuilder do let(:description_only) do - JSON.parse(file_fixture('protocols_importer/description_with_body.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_body.json').read).to_h.with_indifferent_access end let(:description_with_html) do - JSON.parse(file_fixture('protocols_importer/description_with_body_html.json').read).to_h.with_indifferent_access + JSON.parse(file_fixture('protocol_importers/description_with_body_html.json').read).to_h.with_indifferent_access end let(:description_with_components) do - JSON.parse(file_fixture('protocols_importer/step_description_with_components.json').read) + JSON.parse(file_fixture('protocol_importers/step_description_with_components.json').read) .to_h.with_indifferent_access end let(:description_with_extra_content) do - JSON.parse(file_fixture('protocols_importer/description_with_extra_content.json').read) + JSON.parse(file_fixture('protocol_importers/description_with_extra_content.json').read) .to_h.with_indifferent_access end let(:normalized_json) do - JSON.parse(file_fixture('protocols_importer/protocols_io/v3/normalized_single_protocol.json').read) + JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read) .to_h.with_indifferent_access end From 1e8627c4505ab1ec4b6a2f4665f8831a5acf31b6 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 10 Jun 2019 09:40:58 +0200 Subject: [PATCH 11/73] Refactor ImportProtocol service to BuildProtocolService Closes SCI-3544 --- .../build_protocol_from_client_service.rb} | 42 +++++++++++-------- .../protocol_importers/protocol_normalizer.rb | 4 +- .../protocols_io/v3/protocol_normalizer.rb | 7 +--- ...uild_protocol_from_client_service_spec.rb} | 7 +--- .../protocol_normalizer_spec.rb | 6 +-- .../v3/protocol_normalizer_spec.rb | 4 +- 6 files changed, 34 insertions(+), 36 deletions(-) rename app/services/{protocols/import_protocol_from_client_service.rb => protocol_importers/build_protocol_from_client_service.rb} (53%) rename spec/services/{protocols/import_protocol_from_client_service_spec.rb => protocol_importers/build_protocol_from_client_service_spec.rb} (83%) diff --git a/app/services/protocols/import_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb similarity index 53% rename from app/services/protocols/import_protocol_from_client_service.rb rename to app/services/protocol_importers/build_protocol_from_client_service.rb index 565aa62a0..9766b0e32 100644 --- a/app/services/protocols/import_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Protocols - class ImportProtocolFromClientService + class BuildProtocolFromClientService extend Service attr_reader :errors, :pio_protocol @@ -17,25 +17,20 @@ module Protocols def call return self unless valid? - # Catch api errors here, json invalid, also wrong/not found normalizer? - normalized_hash = client.load_protocol(id: @id) - # Catch api errors here + # TODO: check for errors + api_response = api_client.single_protocol(id: @id) + normalized_hash = normalizer.load_protocol(api_response) pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, user: @user, team: @team) @pio_protocol = pio.build - - if @pio_protocol.valid? - # catch errors during import here - pio.import - # catch errors during import here - else - # Add AR errors here - # @errors[:protcol] = pio.protocol.errors.to_json...? somehow + unless @pio_protocol.valid? @errors[:protocol] = pio.protocol.errors end - + rescue StandardError => e + @errors[:build_protocol] = e.message + ensure self end @@ -46,16 +41,27 @@ module Protocols private def valid? - unless @id && @protocol_source && @user && @team - @errors[:invalid_arguments] = { '@id': @id, '@protocol_source': @protocol_source, 'user': @user, 'team': @team } - .map { |key, value| "Can't find #{key.capitalize}" if value.nil? }.compact + unless [@id, @protocol_source, @user, @team].all? + @errors[:invalid_arguments] = { + '@id': @id, + '@protocol_source': @protocol_source, + 'user': @user, + 'team': @team + }.map { |key, value| "Can't find #{key.capitalize}" if value.nil? }.compact return false end true end - def client - endpoint_name = Constants::PROTOCOLS_ENDPOINTS.dig(*@protocol_source.split('/').map(&:to_sym)) + def endpoint_name + Constants::PROTOCOLS_ENDPOINTS.dig(*@protocol_source.split('/').map(&:to_sym)) + end + + def api_client + "ProtocolImporters::#{endpoint_name}::ApiClient".constantize.new + end + + def normalizer "ProtocolImporters::#{endpoint_name}::ProtocolNormalizer".constantize.new end end diff --git a/app/utilities/protocol_importers/protocol_normalizer.rb b/app/utilities/protocol_importers/protocol_normalizer.rb index 60d8c4610..696215ef4 100644 --- a/app/utilities/protocol_importers/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocol_normalizer.rb @@ -2,11 +2,11 @@ module ProtocolImporters class ProtocolNormalizer - def load_all_protocols(_params: {}) + def load_all_protocols(api_response) raise NotImplementedError end - def load_protocol(_id:, _params: {}) + def load_protocol(api_response) raise NotImplementedError end end diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index 70611efa0..34325188b 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -4,11 +4,8 @@ module ProtocolImporters module ProtocolsIO module V3 class ProtocolNormalizer < ProtocolImporters::ProtocolNormalizer - def load_protocol(id:) - response = ProtocolImporters::ProtocolsIO::V3::ApiClient.new.single_protocol(id) - - normalized_hash = normalize(response) - normalized_hash + def load_protocol(api_response) + normalize(api_response) end private diff --git a/spec/services/protocols/import_protocol_from_client_service_spec.rb b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb similarity index 83% rename from spec/services/protocols/import_protocol_from_client_service_spec.rb rename to spec/services/protocol_importers/build_protocol_from_client_service_spec.rb index 3f77e7321..cb53431eb 100644 --- a/spec/services/protocols/import_protocol_from_client_service_spec.rb +++ b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb @@ -2,11 +2,11 @@ require 'rails_helper' -describe Protocols::ImportProtocolFromClientService do +describe Protocols::BuildProtocolFromClientService do let(:user) { create :user } let(:team) { create :team } let(:service_call) do - Protocols::ImportProtocolFromClientService + Protocols::BuildProtocolFromClientService .call(protocol_client_id: 'id', protocol_source: 'protocolsio/v3', user_id: user.id, team_id: team.id) end let(:normalized_response) do @@ -29,9 +29,6 @@ describe Protocols::ImportProtocolFromClientService do # Do not generate and request real images allow(ProtocolImporters::AttachmentsBuilder).to(receive(:generate).and_return([])) end - it 'Adds Protocol record' do - expect { service_call }.to(change { Protocol.all.count }.by(1)) - end # more tests will be implemented when add error handling to service end end diff --git a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb index 54eed656e..a22ad921d 100644 --- a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb @@ -4,12 +4,10 @@ require 'rails_helper' describe ProtocolImporters::ProtocolNormalizer do describe '.load_all_protocols' do - it { expect { subject.load_all_protocols }.to raise_error(NotImplementedError) } - it { expect { subject.load_all_protocols(_params: 'some-params') }.to raise_error(NotImplementedError) } + it { expect { subject.load_all_protocols({}) }.to raise_error(NotImplementedError) } end describe '.load_protocol' do - it { expect { subject.load_protocol(_id: 'random-id') }.to raise_error(NotImplementedError) } - it { expect { subject.load_protocol(_id: 'random-id', _params: 'someparams') }.to raise_error(NotImplementedError) } + it { expect { subject.load_protocol({}) }.to raise_error(NotImplementedError) } end end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index e42e500a3..323750761 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -31,7 +31,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) .to receive_message_chain(:single_protocol, :parsed_response).and_return(response) - expect(subject.load_protocol(id: 'id').deep_stringify_keys).to be == normalized_result + expect(subject.load_protocol(response).deep_stringify_keys).to be == normalized_result end end @@ -40,7 +40,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) .to receive_message_chain(:single_protocol, :parsed_response).and_return(response_without_title) - expect(subject.load_protocol(id: 'id')[:protocol][:name]).to be_nil + expect(subject.load_protocol(response)[:protocol][:name]).to be_nil end end end From 640f6c1211a8a9219943582327a7bf9baf8d415f Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 10 Jun 2019 13:08:30 +0200 Subject: [PATCH 12/73] Fix tests after BuildProtocolFromClientService refactor Closes SCI-3544 --- .../build_protocol_from_client_service.rb | 4 ++-- .../protocol_importers/protocol_normalizer.rb | 4 ++-- .../protocols_io/v3/protocol_normalizer.rb | 13 ++++-------- ...build_protocol_from_client_service_spec.rb | 6 +++--- .../protocol_normalizer_spec.rb | 8 +++---- .../v3/protocol_normalizer_spec.rb | 21 ++++++++++--------- 6 files changed, 26 insertions(+), 30 deletions(-) diff --git a/app/services/protocol_importers/build_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb index 9766b0e32..3fbc90ab8 100644 --- a/app/services/protocol_importers/build_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Protocols +module ProtocolImporters class BuildProtocolFromClientService extend Service @@ -19,7 +19,7 @@ module Protocols # TODO: check for errors api_response = api_client.single_protocol(id: @id) - normalized_hash = normalizer.load_protocol(api_response) + normalized_hash = normalizer.load_protocol(api_response.parsed_response) pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, user: @user, diff --git a/app/utilities/protocol_importers/protocol_normalizer.rb b/app/utilities/protocol_importers/protocol_normalizer.rb index 696215ef4..bd98725b8 100644 --- a/app/utilities/protocol_importers/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocol_normalizer.rb @@ -2,11 +2,11 @@ module ProtocolImporters class ProtocolNormalizer - def load_all_protocols(api_response) + def normalize_all_protocols(client_data) raise NotImplementedError end - def load_protocol(api_response) + def normalize_protocol(client_data) raise NotImplementedError end end diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index 34325188b..9a24d71fa 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -4,17 +4,12 @@ module ProtocolImporters module ProtocolsIO module V3 class ProtocolNormalizer < ProtocolImporters::ProtocolNormalizer - def load_protocol(api_response) - normalize(api_response) - end - - private - - def normalize(response) - protocol_hash = response.parsed_response.with_indifferent_access[:protocol] + def normalize_protocol(client_data) + # client_data is HttpParty ApiReponse object + protocol_hash = client_data.parsed_response.with_indifferent_access[:protocol] normalized_data = { - uri: response.request.last_uri.to_s, + uri: client_data.request.last_uri.to_s, source: Constants::PROTOCOLS_IO_V3_API[:source_id], doi: protocol_hash[:doi], published_on: protocol_hash[:published_on], diff --git a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb index cb53431eb..9f0dfd09d 100644 --- a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb +++ b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb @@ -2,11 +2,11 @@ require 'rails_helper' -describe Protocols::BuildProtocolFromClientService do +describe ProtocolImporters::BuildProtocolFromClientService do let(:user) { create :user } let(:team) { create :team } let(:service_call) do - Protocols::BuildProtocolFromClientService + ProtocolImporters::BuildProtocolFromClientService .call(protocol_client_id: 'id', protocol_source: 'protocolsio/v3', user_id: user.id, team_id: team.id) end let(:normalized_response) do @@ -25,7 +25,7 @@ describe Protocols::BuildProtocolFromClientService do context 'when have valid arguments' do before do allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) - .to(receive(:load_protocol).and_return(normalized_response)) + .to(receive(:normalize_protocol).and_return(normalized_response)) # Do not generate and request real images allow(ProtocolImporters::AttachmentsBuilder).to(receive(:generate).and_return([])) end diff --git a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb index a22ad921d..926103b67 100644 --- a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb @@ -3,11 +3,11 @@ require 'rails_helper' describe ProtocolImporters::ProtocolNormalizer do - describe '.load_all_protocols' do - it { expect { subject.load_all_protocols({}) }.to raise_error(NotImplementedError) } + describe '.normalize_all_protocols' do + it { expect { subject.normalize_all_protocols({}) }.to raise_error(NotImplementedError) } end - describe '.load_protocol' do - it { expect { subject.load_protocol({}) }.to raise_error(NotImplementedError) } + describe '.normalize_protocols' do + it { expect { subject.normalize_protocol({}) }.to raise_error(NotImplementedError) } end end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index 323750761..6ba04df62 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do + let(:client_data) { double('api_response') } + let(:response) do JSON.parse(file_fixture('protocol_importers/protocols_io/v3/single_protocol.json').read) .to_h.with_indifferent_access @@ -19,28 +21,27 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do .to_h.with_indifferent_access end - describe '#load_protocol' do + describe '#normalize_protocol' do before do - allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to(receive_message_chain(:single_protocol, :request, :last_uri, :to_s) - .and_return('https://www.protocols.io/api/v3/protocols/9451')) + allow(client_data).to(receive_message_chain(:request, :last_uri, :to_s) + .and_return('https://www.protocols.io/api/v3/protocols/9451')) end context 'when have all data' do it 'should normalize data correctly' do - allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to receive_message_chain(:single_protocol, :parsed_response).and_return(response) + allow(client_data).to receive_message_chain(:parsed_response) + .and_return(response) - expect(subject.load_protocol(response).deep_stringify_keys).to be == normalized_result + expect(subject.normalize_protocol(client_data).deep_stringify_keys).to be == normalized_result end end context 'when do not have name' do it 'sets nil for name' do - allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to receive_message_chain(:single_protocol, :parsed_response).and_return(response_without_title) + allow(client_data).to receive_message_chain(:parsed_response) + .and_return(response_without_title) - expect(subject.load_protocol(response)[:protocol][:name]).to be_nil + expect(subject.normalize_protocol(client_data)[:protocol][:name]).to be_nil end end end From 362b0cd45d019d6841e5cca2e2accf385df35ffc Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 10 Jun 2019 13:39:53 +0200 Subject: [PATCH 13/73] Hound fixes --- .../build_protocol_from_client_service.rb | 6 ++---- app/utilities/protocol_importers/protocol_normalizer.rb | 4 ++-- .../protocols_io/v3/protocol_normalizer_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/services/protocol_importers/build_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb index 3fbc90ab8..d4c449f96 100644 --- a/app/services/protocol_importers/build_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -24,10 +24,8 @@ module ProtocolImporters pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, user: @user, team: @team) - @pio_protocol = pio.build - unless @pio_protocol.valid? - @errors[:protocol] = pio.protocol.errors - end + + @errors[:protocol] = pio.protocol.errors unless @pio_protocol.valid? rescue StandardError => e @errors[:build_protocol] = e.message ensure diff --git a/app/utilities/protocol_importers/protocol_normalizer.rb b/app/utilities/protocol_importers/protocol_normalizer.rb index bd98725b8..a05c0ed4b 100644 --- a/app/utilities/protocol_importers/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocol_normalizer.rb @@ -2,11 +2,11 @@ module ProtocolImporters class ProtocolNormalizer - def normalize_all_protocols(client_data) + def normalize_all_protocols(_client_data) raise NotImplementedError end - def normalize_protocol(client_data) + def normalize_protocol(_client_data) raise NotImplementedError end end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index 6ba04df62..55a42d7e4 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -30,7 +30,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do context 'when have all data' do it 'should normalize data correctly' do allow(client_data).to receive_message_chain(:parsed_response) - .and_return(response) + .and_return(response) expect(subject.normalize_protocol(client_data).deep_stringify_keys).to be == normalized_result end @@ -39,7 +39,7 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do context 'when do not have name' do it 'sets nil for name' do allow(client_data).to receive_message_chain(:parsed_response) - .and_return(response_without_title) + .and_return(response_without_title) expect(subject.normalize_protocol(client_data)[:protocol][:name]).to be_nil end From eb275c4d12ad1e029f656c3bb9ff48032df95f5f Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Mon, 10 Jun 2019 19:37:00 +0200 Subject: [PATCH 14/73] Add tests for BuildProtocolService and fix bugs --- .../build_protocol_from_client_service.rb | 7 ++++--- .../build_protocol_from_client_service_spec.rb | 14 +++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/services/protocol_importers/build_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb index d4c449f96..00aec7fe6 100644 --- a/app/services/protocol_importers/build_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -18,17 +18,18 @@ module ProtocolImporters return self unless valid? # TODO: check for errors - api_response = api_client.single_protocol(id: @id) - normalized_hash = normalizer.load_protocol(api_response.parsed_response) + api_response = api_client.single_protocol(@id) + normalized_hash = normalizer.normalize_protocol(api_response) pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, user: @user, team: @team) + @pio_protocol = pio.build @errors[:protocol] = pio.protocol.errors unless @pio_protocol.valid? + self rescue StandardError => e @errors[:build_protocol] = e.message - ensure self end diff --git a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb index 9f0dfd09d..ddaaf8fdf 100644 --- a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb +++ b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb @@ -24,11 +24,23 @@ describe ProtocolImporters::BuildProtocolFromClientService do context 'when have valid arguments' do before do + client_data = double('api_response') + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:single_protocol) + .and_return(client_data)) + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) - .to(receive(:normalize_protocol).and_return(normalized_response)) + .to(receive(:normalize_protocol).with(client_data) + .and_return(normalized_response)) + # Do not generate and request real images allow(ProtocolImporters::AttachmentsBuilder).to(receive(:generate).and_return([])) end + + it 'returns ProtocolIntermediateObject' do + expect(service_call.pio_protocol).to be_instance_of(Protocol) + end # more tests will be implemented when add error handling to service end end From 0d43ecdfabdf0dde35c52a5fa52e3fd1dd830234 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Tue, 11 Jun 2019 15:32:23 +0200 Subject: [PATCH 15/73] Import protocol service after object is built --- .../import_protocol_service.rb | 49 +++++++++++++++ spec/factories/tables.rb | 2 +- .../import_protocol_service_spec.rb | 59 +++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 app/services/protocol_importers/import_protocol_service.rb create mode 100644 spec/services/protocol_importers/import_protocol_service_spec.rb diff --git a/app/services/protocol_importers/import_protocol_service.rb b/app/services/protocol_importers/import_protocol_service.rb new file mode 100644 index 000000000..bd430e389 --- /dev/null +++ b/app/services/protocol_importers/import_protocol_service.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module ProtocolImporters + class ImportProtocolService + extend Service + + attr_reader :errors, :protocol + + def initialize(protocol_params:, steps_params:, team_id:, user_id:) + @user = User.find user_id + @team = Team.find team_id + @protocol_params = protocol_params + @steps_params = steps_params + @errors = {} + end + + def call + return self unless valid? + + @protocol = Protocol.new(@protocol_params.merge!(added_by: @user, team: @team)) + + @protocol.steps << @steps_params.collect do |step_params| + Step.new(step_params.merge(user: @user, completed: false)) + end + + @errors[:protocol] = @protocol.errors.messages unless @protocol.save + + self + end + + def succeed? + @errors.none? + end + + private + + def valid? + unless [@protocol_params, @user, @team].all? + @errors[:invalid_arguments] = { + 'user': @user, + 'team': @team, + '@protocol_params': @protocol_params + }.map { |key, value| "Can't find #{key.capitalize}" if value.nil? }.compact + return false + end + true + end + end +end diff --git a/spec/factories/tables.rb b/spec/factories/tables.rb index f43110090..423710a27 100644 --- a/spec/factories/tables.rb +++ b/spec/factories/tables.rb @@ -3,6 +3,6 @@ FactoryBot.define do factory :table do name { Faker::Name.unique.name } - contents { { some_data: 'needs to be here' } } + contents { { data: [%w(A B C), %w(D E F), %w(G H I)] } } end end diff --git a/spec/services/protocol_importers/import_protocol_service_spec.rb b/spec/services/protocol_importers/import_protocol_service_spec.rb new file mode 100644 index 000000000..af31a9646 --- /dev/null +++ b/spec/services/protocol_importers/import_protocol_service_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::ImportProtocolService do + let(:user) { create :user } + let(:team) { create :team } + let(:protocol_params) { attributes_for :protocol, :in_public_repository } + let(:steps_params) do + [ + attributes_for(:step).merge!(assets_attributes: [attributes_for(:asset)]), + attributes_for(:step).merge!(tables_attributes: [attributes_for(:table), attributes_for(:table)]) + ] + end + + let(:service_call) do + ProtocolImporters::ImportProtocolService + .call(protocol_params: protocol_params, steps_params: steps_params, user_id: user.id, team_id: team.id) + end + + context 'when have invalid arguments' do + it 'returns an error when can\'t find user' do + allow(User).to receive(:find).and_return(nil) + + expect(service_call.errors).to have_key(:invalid_arguments) + end + + it 'returns invalid protocol when can\'t save it' do + # step with file without name + steps_invalid_params = [ + attributes_for(:step).merge!(assets_attributes: [attributes_for(:asset).except(:file_file_name)]) + ] + + s = ProtocolImporters::ImportProtocolService.call(protocol_params: protocol_params, + steps_params: steps_invalid_params, + user_id: user.id, team_id: team.id) + + expect(s.protocol).to be_invalid + end + end + + context 'when have valid arguments' do + before do + @protocol = Protocol.new + end + + # rubocop:disable MultilineMethodCallIndentation + it do + expect do + service_result = service_call + @protocol = service_result.protocol + end.to change { Protocol.all.count }.by(1) + .and change { @protocol.steps.count }.by(2) + .and change { Table.joins(:step_table).where('step_tables.step_id': @protocol.steps.pluck(:id)).count }.by(2) + .and change { Asset.joins(:step_asset).where('step_assets.step_id': @protocol.steps.pluck(:id)).count }.by(1) + end + # rubocop:enable MultilineMethodCallIndentation + end +end From 0b60da9b4c39c9dfd876c5949ae802f9bbfbd962 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Wed, 12 Jun 2019 07:19:14 +0200 Subject: [PATCH 16/73] Add ExternalProtocols skeleton tab --- app/views/protocols/index.html.erb | 39 ++++++------------- .../index/_external_protocols_tab.html.erb | 1 + .../index/_protocols_datatable.html.erb | 26 +++++++++++++ config/locales/en.yml | 1 + 4 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 app/views/protocols/index/_external_protocols_tab.html.erb create mode 100644 app/views/protocols/index/_protocols_datatable.html.erb diff --git a/app/views/protocols/index.html.erb b/app/views/protocols/index.html.erb index dff082c91..3caec8c30 100644 --- a/app/views/protocols/index.html.erb +++ b/app/views/protocols/index.html.erb @@ -13,6 +13,10 @@ + @@ -89,7 +93,7 @@
- <% else %> + <% elsif @type == :archive %>
<%= t("protocols.index.archive.description") %>
@@ -101,32 +105,13 @@
<% end %> -
-
- - - - - - - - <% if @type == :public %> - - - <% elsif @type == :private %> - - - <% else %> - - - <% end %> - - - - -
<%= t("protocols.index.thead_name") %><%= t("protocols.index.thead_keywords") %><%= t("protocols.index.thead_nr_of_linked_children") %><%= t("protocols.index.thead_published_by") %><%= t("protocols.index.thead_published_on") %><%= t("protocols.index.thead_added_by") %><%= t("protocols.index.thead_created_at") %><%= t("protocols.index.thead_archived_by") %><%= t("protocols.index.thead_archived_on") %><%= t("protocols.index.thead_updated_at") %>
-
-
+ <%# Main tab content %> + <% if @type == :external_protocols %> + <%= render partial: "protocols/index/external_protocols_tab.html.erb" %> + <% else %> + <%= render partial: "protocols/index/protocols_datatable.html.erb" %> + <% end %> + diff --git a/app/views/protocols/index/_external_protocols_tab.html.erb b/app/views/protocols/index/_external_protocols_tab.html.erb new file mode 100644 index 000000000..db381cb1c --- /dev/null +++ b/app/views/protocols/index/_external_protocols_tab.html.erb @@ -0,0 +1 @@ +External protocols diff --git a/app/views/protocols/index/_protocols_datatable.html.erb b/app/views/protocols/index/_protocols_datatable.html.erb new file mode 100644 index 000000000..966844125 --- /dev/null +++ b/app/views/protocols/index/_protocols_datatable.html.erb @@ -0,0 +1,26 @@ +
+
+ + + + + + + + <% if @type == :public %> + + + <% elsif @type == :private %> + + + <% else %> + + + <% end %> + + + + +
<%= t("protocols.index.thead_name") %><%= t("protocols.index.thead_keywords") %><%= t("protocols.index.thead_nr_of_linked_children") %><%= t("protocols.index.thead_published_by") %><%= t("protocols.index.thead_published_on") %><%= t("protocols.index.thead_added_by") %><%= t("protocols.index.thead_created_at") %><%= t("protocols.index.thead_archived_by") %><%= t("protocols.index.thead_archived_on") %><%= t("protocols.index.thead_updated_at") %>
+
+
diff --git a/config/locales/en.yml b/config/locales/en.yml index 0ab703562..934da5aa8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1632,6 +1632,7 @@ en: navigation: public: "Team protocols" private: "My protocols" + external_protocols: "External protocols" archive: "Archive" public_description: "Team protocols are visible and can be used by everyone from the team." private_description: "My protocols are only visible to you." From d103f8ffec2e276c0566ab4761c025619fa1050c Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Wed, 12 Jun 2019 23:38:04 +0200 Subject: [PATCH 17/73] Finish external protocols screen Closes SCI-3534 --- .../external_protocols/protocolsio_logo.png | Bin 0 -> 67747 bytes .../stylesheets/protocol_management.scss | 121 +++++++++++++++++- .../index/_external_protocols_tab.html.erb | 72 ++++++++++- config/locales/en.yml | 14 ++ 4 files changed, 205 insertions(+), 2 deletions(-) create mode 100755 app/assets/images/external_protocols/protocolsio_logo.png diff --git a/app/assets/images/external_protocols/protocolsio_logo.png b/app/assets/images/external_protocols/protocolsio_logo.png new file mode 100755 index 0000000000000000000000000000000000000000..32298495a3e348f335e9d6dcb9e0717332f54112 GIT binary patch literal 67747 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­?N?`J9ZHJtpo&z0v0E^TPd$tXfnByr}F3De1ck= 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 +

+
+ +
+ <%= image_tag 'external_protocols/protocolsio_logo.png', + class: 'protocolsio-logo' %> + <%= t('protocols.index.external_protocols.protocolsio_title') %> +
+ <%= form_tag '#', + method: :get, + class: 'protocols-search-bar' do %> + +
+ + + + +
+ <% end %> +
+
+ + +
+
+
+ <%= t('protocols.index.external_protocols.sort_by.title') %> +
+ + + + +
+
+ +
+ <%= t('protocols.index.external_protocols.list_panel.empty_text') %> +
+
+ +
+
+
+ <%= t('protocols.index.external_protocols.preview_panel.empty_title') %> +
+
+
+

+
+
+
+ <%= t('protocols.index.external_protocols.preview_panel.empty_subtext') %> +
+
+
+
+ + diff --git a/config/locales/en.yml b/config/locales/en.yml index 934da5aa8..ea9f82064 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1733,6 +1733,20 @@ en: row_failed: "Failed" row_in_repository_public: "%{protocol} - into Team protocols" row_in_repository_private: "%{protocol} - into My protocols" + external_protocols: + search_bar_placeholder: 'Search for protocols' + protocolsio_title: 'Protocols.io' + sort_by: + title: 'Show first:' + alphabetically: 'alphabetically' + newest: 'newest' + oldest: 'oldest' + list_panel: + empty_text: 'Search for protocols above to list them here' + preview_panel: + empty_title: 'PROTOCOL PREVIEW' + empty_subtext: 'Click on the protocol in the list to preview it here' + steps: completed: 'Completed' uncompleted: 'Uncompleted' From 218cf84775cf1f8169a068b2ec516d2fa4cd11de Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Wed, 12 Jun 2019 23:44:31 +0200 Subject: [PATCH 18/73] Hound fixes --- app/assets/stylesheets/protocol_management.scss | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/protocol_management.scss b/app/assets/stylesheets/protocol_management.scss index c463bcd9e..43a3ae2a4 100644 --- a/app/assets/stylesheets/protocol_management.scss +++ b/app/assets/stylesheets/protocol_management.scss @@ -66,9 +66,9 @@ align-items: center; display: flex; flex-direction: row; - padding-right: 0px; justify-content: space-between; margin-bottom: 15px; + padding-right: 0; .protocolsio-logo { height: 30px; @@ -82,7 +82,7 @@ } .protocols-search-bar { - padding-right: 0px; + padding-right: 0; width: 70%; .input-group { @@ -106,7 +106,7 @@ border-right: 1px solid $color-silver-chalice; border-top: 1px solid $color-silver-chalice; height: 600px; - padding-right: 0px; + padding-right: 0; .row { margin-left: 0; @@ -116,8 +116,8 @@ border-bottom: 1px solid $color-silver-chalice; span { - vertical-align: middle; color: $color-silver-chalice; + vertical-align: middle; } .btn-group { @@ -147,11 +147,11 @@ } .protocol-preview-panel { - border-top: 1px solid $color-silver-chalice; background-color: $color-concrete; + border-top: 1px solid $color-silver-chalice; height: 600px; + padding-left: 0; padding-top: 79px; - padding-left: 0px; .row { margin-left: 0; From 7adef9cc9e4a3fdb748cfabc93bc92dd83eb89f0 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 12:16:23 +0200 Subject: [PATCH 19/73] Change sort buttons positions Closes SCI-3534 --- .../stylesheets/protocol_management.scss | 50 ++++++++++--------- .../index/_external_protocols_tab.html.erb | 14 +++--- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/app/assets/stylesheets/protocol_management.scss b/app/assets/stylesheets/protocol_management.scss index 43a3ae2a4..bfa9ac1c5 100644 --- a/app/assets/stylesheets/protocol_management.scss +++ b/app/assets/stylesheets/protocol_management.scss @@ -95,6 +95,32 @@ .input-group-addon { background: $color-white; + color: $color-silver-chalice; + } + } + } + } + + .protocol-sort { + padding-left: 7px; + + span { + color: $color-silver-chalice; + font-size: 14px; + } + + .btn-group { + margin-bottom: 10px; + margin-top: 6px; + padding-left: 3px; + + label { + padding-bottom: 0; + padding-top: 0; + + &.active { + background: $color-gainsboro; + border-radius: 5px; color: $color-emperor; } } @@ -112,30 +138,6 @@ margin-left: 0; margin-right: 0; - &.protocol-sort { - border-bottom: 1px solid $color-silver-chalice; - - span { - color: $color-silver-chalice; - vertical-align: middle; - } - - .btn-group { - margin-bottom: 10px; - margin-top: 10px; - - label { - padding-bottom: 0; - padding-top: 0; - - &.active { - background: $color-gainsboro; - border-radius: 5px; - color: $color-emperor; - } - } - } - } } // When no search result is present diff --git a/app/views/protocols/index/_external_protocols_tab.html.erb b/app/views/protocols/index/_external_protocols_tab.html.erb index a271620dd..5b64b40e4 100644 --- a/app/views/protocols/index/_external_protocols_tab.html.erb +++ b/app/views/protocols/index/_external_protocols_tab.html.erb @@ -22,12 +22,8 @@ <% end %> - - - -
-
-
+
+
<%= t('protocols.index.external_protocols.sort_by.title') %>
+
+
+ + +
+
<%= t('protocols.index.external_protocols.list_panel.empty_text') %> From 94e7fe1e6c5d409e00cc0e5751f371fd15585beb Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Tue, 11 Jun 2019 17:04:59 +0200 Subject: [PATCH 20/73] OnlineSourcesController skeleton and #new action tested --- app/controllers/online_sources_controller.rb | 54 ++++++++++++++++++ config/routes.rb | 2 + .../online_sources_controller_spec.rb | 55 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 app/controllers/online_sources_controller.rb create mode 100644 spec/controllers/online_sources_controller_spec.rb diff --git a/app/controllers/online_sources_controller.rb b/app/controllers/online_sources_controller.rb new file mode 100644 index 000000000..0078cbd10 --- /dev/null +++ b/app/controllers/online_sources_controller.rb @@ -0,0 +1,54 @@ +class OnlineSourcesController < ApplicationController + # GET + def index + # list_protocols = SearchService.call(index_params) + succeed = false + list_protocols = [ + { name: 'Protocol1' }, + { name: 'Protocol2' }, + { name: 'Protocol3' } + ] + + if succeed + render json: list_protocols + else + render json: { + errors: { protocol: 'error_placeholder' } + }, status: 400 + end + end + + # GET + def show + + end + + # GET team_build_online_sources_protocol + def new + service_call = ProtocolImporters::BuildProtocolFromClientService.call( + protocol_source: new_params[:protocol_source], + protocol_client_id: new_params[:protocol_client_id], + user_id: current_user.id, + team_id: current_team.id + ) + + if service_call.succeed? + render json: service_call.pio_protocol + else + render json: { errors: service_call.errors }, status: 400 + end + end + + def create + end + + private + + def index_params + params.permit(:protocol_source, :key, :page_id, :page_size, :order_field, :order_dir) + end + + def new_params + params.permit(:protocol_source, :protocol_client_id) + end +end diff --git a/config/routes.rb b/config/routes.rb index 260961435..36784f962 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -209,6 +209,8 @@ Rails.application.routes.draw do match '*path', to: 'teams#routing_error', via: [:get, :post, :put, :patch] + + get 'build_online_sources_protocol', to: 'online_sources#new' end get 'projects/archive', to: 'projects#archive', as: 'projects_archive' diff --git a/spec/controllers/online_sources_controller_spec.rb b/spec/controllers/online_sources_controller_spec.rb new file mode 100644 index 000000000..bfcab6a0f --- /dev/null +++ b/spec/controllers/online_sources_controller_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe OnlineSourcesController, type: :controller do + login_user + + let(:user) { subject.current_user } + let(:team) { create :team, created_by: user } + let!(:user_team) { create :user_team, :admin, user: user, team: team } + + describe 'GET new' do + let(:params) do + { + team_id: team.id, + protocol_source: 'protocolsio/v3', + page_id: 1, + page_size: 10, + order_field: 'activity', + order_dir: 'desc' + } + end + + let(:action) { get :new, params: params } + + it 'returns JSON, 200 response when protocol parsing was valid' do + # Setup double + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(true) + allow(service).to(receive(:pio_protocol)).and_return({}) + + allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService).to(receive(:call)).and_return(service) + + # Call action + action + expect(response).to have_http_status(:success) + expect(response.content_type).to eq 'application/json' + end + + it 'returns JSON, 400 response when protocol parsing was invalid' do + # Setup double + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(false) + allow(service).to(receive(:errors)).and_return({}) + + allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService).to(receive(:call)).and_return(service) + + # Call action + action + expect(response).to have_http_status(:bad_request) + expect(response.content_type).to eq 'application/json' + end + + end +end From eb0d677a36df5a3a47f7e369eb9ab4fee79b8b41 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Tue, 11 Jun 2019 17:09:12 +0200 Subject: [PATCH 21/73] Refactor pio_protocol -> built_protocol --- app/controllers/online_sources_controller.rb | 2 +- .../build_protocol_from_client_service.rb | 6 +++--- spec/controllers/online_sources_controller_spec.rb | 2 +- .../build_protocol_from_client_service_spec.rb | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/online_sources_controller.rb b/app/controllers/online_sources_controller.rb index 0078cbd10..910c0c6c3 100644 --- a/app/controllers/online_sources_controller.rb +++ b/app/controllers/online_sources_controller.rb @@ -33,7 +33,7 @@ class OnlineSourcesController < ApplicationController ) if service_call.succeed? - render json: service_call.pio_protocol + render json: service_call.built_protocol else render json: { errors: service_call.errors }, status: 400 end diff --git a/app/services/protocol_importers/build_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb index 00aec7fe6..434a1cfe6 100644 --- a/app/services/protocol_importers/build_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -4,7 +4,7 @@ module ProtocolImporters class BuildProtocolFromClientService extend Service - attr_reader :errors, :pio_protocol + attr_reader :errors, :built_protocol def initialize(protocol_client_id:, protocol_source:, user_id:, team_id:) @id = protocol_client_id @@ -25,8 +25,8 @@ module ProtocolImporters user: @user, team: @team) - @pio_protocol = pio.build - @errors[:protocol] = pio.protocol.errors unless @pio_protocol.valid? + @built_protocol = pio.build + @errors[:protocol] = pio.protocol.errors unless @built_protocol.valid? self rescue StandardError => e @errors[:build_protocol] = e.message diff --git a/spec/controllers/online_sources_controller_spec.rb b/spec/controllers/online_sources_controller_spec.rb index bfcab6a0f..c41d6e5f1 100644 --- a/spec/controllers/online_sources_controller_spec.rb +++ b/spec/controllers/online_sources_controller_spec.rb @@ -27,7 +27,7 @@ describe OnlineSourcesController, type: :controller do # Setup double service = double('success_service') allow(service).to(receive(:succeed?)).and_return(true) - allow(service).to(receive(:pio_protocol)).and_return({}) + allow(service).to(receive(:built_protocol)).and_return({}) allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService).to(receive(:call)).and_return(service) diff --git a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb index ddaaf8fdf..84356e2dd 100644 --- a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb +++ b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb @@ -39,7 +39,7 @@ describe ProtocolImporters::BuildProtocolFromClientService do end it 'returns ProtocolIntermediateObject' do - expect(service_call.pio_protocol).to be_instance_of(Protocol) + expect(service_call.built_protocol).to be_instance_of(Protocol) end # more tests will be implemented when add error handling to service end From 9baa66c9987090ccb75acfde493ded77a56d16bf Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 00:17:32 +0200 Subject: [PATCH 22/73] Refactor OnlineSources -> ExternalProtocols --- ...e_sources_controller.rb => external_protocols_controller.rb} | 2 +- config/routes.rb | 2 +- ...controller_spec.rb => external_protocols_controller_spec.rb} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/controllers/{online_sources_controller.rb => external_protocols_controller.rb} (95%) rename spec/controllers/{online_sources_controller_spec.rb => external_protocols_controller_spec.rb} (96%) diff --git a/app/controllers/online_sources_controller.rb b/app/controllers/external_protocols_controller.rb similarity index 95% rename from app/controllers/online_sources_controller.rb rename to app/controllers/external_protocols_controller.rb index 910c0c6c3..157cfd9a0 100644 --- a/app/controllers/online_sources_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -1,4 +1,4 @@ -class OnlineSourcesController < ApplicationController +class ExternalProtocolsController < ApplicationController # GET def index # list_protocols = SearchService.call(index_params) diff --git a/config/routes.rb b/config/routes.rb index 36784f962..f4f114cc6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -210,7 +210,7 @@ Rails.application.routes.draw do to: 'teams#routing_error', via: [:get, :post, :put, :patch] - get 'build_online_sources_protocol', to: 'online_sources#new' + get 'build_external_protocol', to: 'external_protocols#new' end get 'projects/archive', to: 'projects#archive', as: 'projects_archive' diff --git a/spec/controllers/online_sources_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb similarity index 96% rename from spec/controllers/online_sources_controller_spec.rb rename to spec/controllers/external_protocols_controller_spec.rb index c41d6e5f1..11a9a2d99 100644 --- a/spec/controllers/online_sources_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe OnlineSourcesController, type: :controller do +describe ExternalProtocolsController, type: :controller do login_user let(:user) { subject.current_user } From 2a61075156dc094fd71a338d8749d72a529a23a1 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 01:02:44 +0200 Subject: [PATCH 23/73] Add ExternalProtocols#create action and tests --- .../external_protocols_controller.rb | 32 +++++++++- config/routes.rb | 1 + .../external_protocols_controller_spec.rb | 60 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index 157cfd9a0..e9f19f154 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -1,4 +1,7 @@ class ExternalProtocolsController < ApplicationController + before_action :load_vars + before_action :check_import_permissions, only: [:create] + # GET def index # list_protocols = SearchService.call(index_params) @@ -29,7 +32,7 @@ class ExternalProtocolsController < ApplicationController protocol_source: new_params[:protocol_source], protocol_client_id: new_params[:protocol_client_id], user_id: current_user.id, - team_id: current_team.id + team_id: @team.id ) if service_call.succeed? @@ -39,11 +42,30 @@ class ExternalProtocolsController < ApplicationController end end + # POST import_external_protocol def create + service_call = ProtocolImporters::ImportProtocolService.call( + protocol_params: create_params[:protocol_params], + steps_params: create_params[:steps_paramas], + user_id: current_user.id, + team_id: @team.id + ) + + if service_call.succeed? + render json: service_call.protocol + else + render json: { errors: service_call.errors }, status: 400 + end end private + def load_vars + @team = Team.find_by_id(params[:team_id]) + + render_404 unless @team + end + def index_params params.permit(:protocol_source, :key, :page_id, :page_size, :order_field, :order_dir) end @@ -51,4 +73,12 @@ class ExternalProtocolsController < ApplicationController def new_params params.permit(:protocol_source, :protocol_client_id) end + + def create_params + params.permit(:protocol_params, :steps_params) + end + + def check_import_permissions + render_403 unless can_create_protocols_in_repository?(@team) + end end diff --git a/config/routes.rb b/config/routes.rb index f4f114cc6..49ab711c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -211,6 +211,7 @@ Rails.application.routes.draw do via: [:get, :post, :put, :patch] get 'build_external_protocol', to: 'external_protocols#new' + post 'import_external_protocol', to: 'external_protocols#create' end get 'projects/archive', to: 'projects#archive', as: 'projects_archive' diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index 11a9a2d99..a4853d770 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -52,4 +52,64 @@ describe ExternalProtocolsController, type: :controller do end end + + describe 'POST create' do + context 'when user has import permissions for the team' do + let(:params) do + { + team_id: team.id, + protocol_params: {}, + steps_params: {} + } + end + + let(:action) { post :create, params: params } + + it 'returns JSON, 200 response when protocol parsing was valid' do + # Setup double + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(true) + allow(service).to(receive(:protocol)).and_return({}) + + allow_any_instance_of(ProtocolImporters::ImportProtocolService).to(receive(:call)).and_return(service) + + # Call action + action + expect(response).to have_http_status(:success) + expect(response.content_type).to eq 'application/json' + end + + it 'returns JSON, 400 response when protocol parsing was invalid' do + # Setup double + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(false) + allow(service).to(receive(:errors)).and_return({}) + + allow_any_instance_of(ProtocolImporters::ImportProtocolService).to(receive(:call)).and_return(service) + + # Call action + action + expect(response).to have_http_status(:bad_request) + expect(response.content_type).to eq 'application/json' + end + end + + context 'when user has no import permissions for the team' do + let(:user_two) { create :user } + let(:team_two) { create :team, created_by: user_two } + + let(:params) do + { + team_id: team_two.id, + protocol_params: {}, + steps_params: {} + } + end + + it 'returns 403 when trying to import to forbidden team' do + post :create, params: params + expect(response).to have_http_status(:forbidden) + end + end + end end From f7c6f78925bc2222e96ef2511a30d46afd1332d7 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 08:49:03 +0200 Subject: [PATCH 24/73] Add external_protocols#index action --- .../external_protocols_controller.rb | 17 +++++++++-- .../_list_of_protocol_cards.html.erb | 5 ++++ .../_protocol_card.html.erb | 3 ++ config/routes.rb | 9 ++++-- .../external_protocols_controller_spec.rb | 28 ++++++++++++++++++- 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 app/views/protocol_importers/_list_of_protocol_cards.html.erb create mode 100644 app/views/protocol_importers/_protocol_card.html.erb diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index e9f19f154..f9603c2cc 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ExternalProtocolsController < ApplicationController before_action :load_vars before_action :check_import_permissions, only: [:create] @@ -5,15 +7,20 @@ class ExternalProtocolsController < ApplicationController # GET def index # list_protocols = SearchService.call(index_params) - succeed = false - list_protocols = [ + succeed = true + protocols = [ { name: 'Protocol1' }, { name: 'Protocol2' }, { name: 'Protocol3' } ] if succeed - render json: list_protocols + render json: { + html: render_to_string( + partial: 'protocol_importers/list_of_protocol_cards.html.erb', + locals: { protocols: protocols } + ) + } else render json: { errors: { protocol: 'error_placeholder' } @@ -70,6 +77,10 @@ class ExternalProtocolsController < ApplicationController params.permit(:protocol_source, :key, :page_id, :page_size, :order_field, :order_dir) end + def show_params + params.permit(:protocol_source, :protocol_id) + end + def new_params params.permit(:protocol_source, :protocol_client_id) end diff --git a/app/views/protocol_importers/_list_of_protocol_cards.html.erb b/app/views/protocol_importers/_list_of_protocol_cards.html.erb new file mode 100644 index 000000000..4164c5c72 --- /dev/null +++ b/app/views/protocol_importers/_list_of_protocol_cards.html.erb @@ -0,0 +1,5 @@ +<% protocols.each do |protocol| %> + <%= render partial: 'protocol_importers/protocol_card', + locals: { protocol: protocol } %> +
+<% end %> diff --git a/app/views/protocol_importers/_protocol_card.html.erb b/app/views/protocol_importers/_protocol_card.html.erb new file mode 100644 index 000000000..f7311f7da --- /dev/null +++ b/app/views/protocol_importers/_protocol_card.html.erb @@ -0,0 +1,3 @@ +
+

<%= protocol[:name] %>

+
diff --git a/config/routes.rb b/config/routes.rb index 49ab711c3..40f0c91c1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -206,12 +206,15 @@ Rails.application.routes.draw do get 'atwho_my_modules', to: 'at_who#my_modules' get 'atwho_menu_items', to: 'at_who#menu_items' end + + # External protocols routes + get 'list_external_protocol', to: 'external_protocols#index' + get 'build_external_protocol', to: 'external_protocols#new' + post 'import_external_protocol', to: 'external_protocols#create' + match '*path', to: 'teams#routing_error', via: [:get, :post, :put, :patch] - - get 'build_external_protocol', to: 'external_protocols#new' - post 'import_external_protocol', to: 'external_protocols#create' end get 'projects/archive', to: 'projects#archive', as: 'projects_archive' diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index a4853d770..35625bc40 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -9,6 +9,33 @@ describe ExternalProtocolsController, type: :controller do let(:team) { create :team, created_by: user } let!(:user_team) { create :user_team, :admin, user: user, team: team } + describe 'GET index' do + let(:params) do + { + team_id: team.id, + key: 'search_string', + protocol_source: 'protocolsio/v3', + page_id: 1, + page_size: 10, + order_field: 'activity', + order_dir: 'desc' + } + end + + let(:action) { get :index, params: params } + + it 'returns JSON, 200 response when protocol parsing was valid' do + action + expect(response).to have_http_status(:success) + expect(response.content_type).to eq 'application/json' + end + + it 'contains html key in the response' do + action + expect(JSON.parse(response.body)).to have_key('html') + end + end + describe 'GET new' do let(:params) do { @@ -50,7 +77,6 @@ describe ExternalProtocolsController, type: :controller do expect(response).to have_http_status(:bad_request) expect(response.content_type).to eq 'application/json' end - end describe 'POST create' do From c8b8bcffdb91ccb7486b1815b6a33585afbaa48d Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 11:17:08 +0200 Subject: [PATCH 25/73] Add external_protocols#show action --- .../external_protocols_controller.rb | 14 ++++++ config/routes.rb | 1 + .../external_protocols_controller_spec.rb | 48 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index f9603c2cc..2a188c67e 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -30,7 +30,21 @@ class ExternalProtocolsController < ApplicationController # GET def show + # TODO: this should be refactored, it's only for placeholding + endpoint_name = Constants::PROTOCOLS_ENDPOINTS.dig(*show_params[:protocol_source] + .split('/').map(&:to_sym)) + api_client = "ProtocolImporters::#{endpoint_name}::ApiClient".constantize.new + html_preview = api_client.protocol_html_preview(show_params[:protocol_id]) + render json: { + protocol_source: show_params[:protocol_source], + protocol_id: show_params[:protocol_id], + html: html_preview + } and return + rescue StandardError => e + render json: { + errors: [show_protocol: e.message] + }, status: 400 end # GET team_build_online_sources_protocol diff --git a/config/routes.rb b/config/routes.rb index 40f0c91c1..9f01c39cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -209,6 +209,7 @@ Rails.application.routes.draw do # External protocols routes get 'list_external_protocol', to: 'external_protocols#index' + get 'show_external_protocol', to: 'external_protocols#show' get 'build_external_protocol', to: 'external_protocols#new' post 'import_external_protocol', to: 'external_protocols#create' diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index 35625bc40..69e704a90 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -36,6 +36,54 @@ describe ExternalProtocolsController, type: :controller do end end + describe 'GET show' do + let(:params) do + { + team_id: team.id, + protocol_source: 'protocolsio/v3', + protocol_id: 'protocolsio_uri' + } + end + + let(:action) { get :show, params: params } + + it 'returns JSON, 200 response when preview was successfully returned' do + html_preview = '' + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_html_preview)) + .and_return(html_preview) + + # Call action + action + expect(response).to have_http_status(:success) + expect(response.content_type).to eq 'application/json' + end + + it 'should return html preview in the JSON' do + html_preview = '' + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_html_preview)) + .and_return(html_preview) + + # Call action + action + expect(JSON.parse(response.body)['html']).to eq(html_preview) + end + + it 'returns error JSON and 400 response when something went wrong' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_html_preview)) + .and_raise(StandardError) + + # Call action + action + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to have_key('errors') + end + end + describe 'GET new' do let(:params) do { From 4f6397b5b9930dc60b648823fcd9d5aafc03a465 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Thu, 13 Jun 2019 13:28:08 +0200 Subject: [PATCH 26/73] Add form to external_sources#new action Closes SCI-3532 --- .../external_protocols_controller.rb | 13 ++++-- .../protocol_importers/_import_form.html.erb | 19 ++++++++ .../external_protocols_controller_spec.rb | 44 +++++++++++-------- 3 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 app/views/protocol_importers/_import_form.html.erb diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index 2a188c67e..a4cae5862 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -4,7 +4,7 @@ class ExternalProtocolsController < ApplicationController before_action :load_vars before_action :check_import_permissions, only: [:create] - # GET + # GET list_external_protocol def index # list_protocols = SearchService.call(index_params) succeed = true @@ -28,7 +28,7 @@ class ExternalProtocolsController < ApplicationController end end - # GET + # GET show_external_protocol def show # TODO: this should be refactored, it's only for placeholding endpoint_name = Constants::PROTOCOLS_ENDPOINTS.dig(*show_params[:protocol_source] @@ -47,7 +47,7 @@ class ExternalProtocolsController < ApplicationController }, status: 400 end - # GET team_build_online_sources_protocol + # GET build_online_sources_protocol def new service_call = ProtocolImporters::BuildProtocolFromClientService.call( protocol_source: new_params[:protocol_source], @@ -57,7 +57,12 @@ class ExternalProtocolsController < ApplicationController ) if service_call.succeed? - render json: service_call.built_protocol + render json: { + html: render_to_string( + partial: 'protocol_importers/import_form.html.erb', + locals: { protocol: service_call.built_protocol } + ) + } else render json: { errors: service_call.errors }, status: 400 end diff --git a/app/views/protocol_importers/_import_form.html.erb b/app/views/protocol_importers/_import_form.html.erb new file mode 100644 index 000000000..026cc60f2 --- /dev/null +++ b/app/views/protocol_importers/_import_form.html.erb @@ -0,0 +1,19 @@ +<%= form_for protocol, + url: team_import_external_protocol_path(team_id: current_team.id), + method: :post, + remote: true do |f|%> + + <%= f.text_field :name %> + <%= f.text_field :authors %> + <%= f.text_field :description %> + <%= f.text_field :published_on, disabled: true %> + +
+ <% protocol.steps.order(:position).each do |step| %> + <%= render partial: "steps/step.html.erb", locals: { step: step, preview: true } %> + <% end %> +
+ + <%= f.submit %> + +<% end %> diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index 69e704a90..cb750c186 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -51,8 +51,7 @@ describe ExternalProtocolsController, type: :controller do html_preview = '' allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to(receive(:protocol_html_preview)) - .and_return(html_preview) + .to(receive(:protocol_html_preview)).and_return(html_preview) # Call action action @@ -64,8 +63,7 @@ describe ExternalProtocolsController, type: :controller do html_preview = '' allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to(receive(:protocol_html_preview)) - .and_return(html_preview) + .to(receive(:protocol_html_preview)).and_return(html_preview) # Call action action @@ -74,8 +72,7 @@ describe ExternalProtocolsController, type: :controller do it 'returns error JSON and 400 response when something went wrong' do allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) - .to(receive(:protocol_html_preview)) - .and_raise(StandardError) + .to(receive(:protocol_html_preview)).and_raise(StandardError) # Call action action @@ -98,27 +95,38 @@ describe ExternalProtocolsController, type: :controller do let(:action) { get :new, params: params } - it 'returns JSON, 200 response when protocol parsing was valid' do - # Setup double - service = double('success_service') - allow(service).to(receive(:succeed?)).and_return(true) - allow(service).to(receive(:built_protocol)).and_return({}) + context 'successful response' do + let(:protocol) { create :protocol } - allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService).to(receive(:call)).and_return(service) + before do + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(true) + allow(service).to(receive(:built_protocol)).and_return(protocol) - # Call action - action - expect(response).to have_http_status(:success) - expect(response.content_type).to eq 'application/json' + allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService) + .to(receive(:call)).and_return(service) + end + + it 'returns JSON, 200 response when protocol parsing was valid' do + action + expect(response).to have_http_status(:success) + expect(response.content_type).to eq 'application/json' + end + + it 'should return html form in the JSON' do + action + expect(JSON.parse(response.body)).to have_key('html') + end end it 'returns JSON, 400 response when protocol parsing was invalid' do # Setup double - service = double('success_service') + service = double('failed_service') allow(service).to(receive(:succeed?)).and_return(false) allow(service).to(receive(:errors)).and_return({}) - allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService).to(receive(:call)).and_return(service) + allow_any_instance_of(ProtocolImporters::BuildProtocolFromClientService) + .to(receive(:call)).and_return(service) # Call action action From 7fad5fc59443857bb49553c374328901a4a86a7f Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Thu, 13 Jun 2019 14:17:15 +0200 Subject: [PATCH 27/73] Add search service and normalizer for list --- .../search_protocols_service.rb | 70 + .../protocol_importers/protocol_normalizer.rb | 2 +- .../protocols_io/v3/protocol_normalizer.rb | 18 + config/initializers/constants.rb | 2 + .../protocol_importers/normalized_list.json | 94 + .../protocols_io/v3/protocols.json | 3072 +++++++++++++++++ .../search_protocols_service_spec.rb | 52 + .../protocol_normalizer_spec.rb | 6 +- .../v3/protocol_normalizer_spec.rb | 29 +- 9 files changed, 3334 insertions(+), 11 deletions(-) create mode 100644 app/services/protocol_importers/search_protocols_service.rb create mode 100644 spec/fixtures/files/protocol_importers/normalized_list.json create mode 100644 spec/fixtures/files/protocol_importers/protocols_io/v3/protocols.json create mode 100644 spec/services/protocol_importers/search_protocols_service_spec.rb diff --git a/app/services/protocol_importers/search_protocols_service.rb b/app/services/protocol_importers/search_protocols_service.rb new file mode 100644 index 000000000..2a6a11ae3 --- /dev/null +++ b/app/services/protocol_importers/search_protocols_service.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module ProtocolImporters + class SearchProtocolsService + extend Service + + attr_reader :errors, :protocols_list + + CONSTANTS = Constants::PROTOCOLS_IO_V3_API + + def initialize(protocol_source:, query_params: {}) + @protocol_source = protocol_source + @query_params = query_params + @errors = Hash.new { |h, k| h[k] = {} } + end + + def call + return self unless valid? + + api_response = api_client.protocol_list(@query_params) + + @protocols_list = normalizer.normalize_list(api_response) + + self + end + + def succeed? + @errors.none? + end + + private + + def valid? + # try if key is not empty + @errors[:invalid_params][:key] = 'Key cannot be empty' if @query_params[:key].blank? + + # try if page id is ok + if @query_params[:page_id] && !@query_params[:page_id].positive? + @errors[:invalid_params][:page_id] = 'Page needs to be positive' + end + + # try if order_field is ok + if CONSTANTS[:available_order_fields]&.exclude? @query_params[:order_field].to_sym + @errors[:invalid_params][:order_field] = 'Order field is not ok' + end + + # try if order dir is ok + if CONSTANTS[:available_order_dirs]&.exclude? @query_params[:order_dir].to_sym + @errors[:invalid_params][:order_dir] = 'Order dir is not ok' + end + + # try if endpints exists + @errors[:invalid_params][:source_endpoint] = 'Wrong source endpoint' unless endpoint_name&.is_a?(String) + + succeed? + end + + def endpoint_name + Constants::PROTOCOLS_ENDPOINTS.dig(*@protocol_source.split('/').map(&:to_sym)) + end + + def api_client + "ProtocolImporters::#{endpoint_name}::ApiClient".constantize.new + end + + def normalizer + "ProtocolImporters::#{endpoint_name}::ProtocolNormalizer".constantize.new + end + end +end diff --git a/app/utilities/protocol_importers/protocol_normalizer.rb b/app/utilities/protocol_importers/protocol_normalizer.rb index a05c0ed4b..ee8ec027d 100644 --- a/app/utilities/protocol_importers/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocol_normalizer.rb @@ -2,7 +2,7 @@ module ProtocolImporters class ProtocolNormalizer - def normalize_all_protocols(_client_data) + def normalize_list(_client_data) raise NotImplementedError end diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index 9a24d71fa..a517e3bcc 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -50,6 +50,24 @@ module ProtocolImporters { protocol: normalized_data } end + + def normalize_list(client_data) + # client_data is HttpParty ApiReponse object + protocols_hash = client_data.parsed_response.with_indifferent_access[:items] + normalized_data = {} + normalized_data[:protocols] = protocols_hash.map do |e| + { + "id": e[:id], + "title": e[:title], + "created_on": e[:created_on], + "authors": e[:authors].map { |a| a[:name] }.join(', '), + "nr_of_steps": e[:stats][:number_of_steps], + "nr_of_views": e[:stats][:number_of_views], + "uri": e[:uri] + } + end + normalized_data + end end end end diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 2f5507712..692f05b95 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -203,6 +203,8 @@ class Constants base_uri: 'https://www.protocols.io/api/v3/', default_timeout: 10, debug_level: :debug, + available_order_fields: %i(title created_on), + available_order_dirs: %i(asc desc), endpoints: { protocols: { default_query_params: { diff --git a/spec/fixtures/files/protocol_importers/normalized_list.json b/spec/fixtures/files/protocol_importers/normalized_list.json new file mode 100644 index 000000000..d2ad475a7 --- /dev/null +++ b/spec/fixtures/files/protocol_importers/normalized_list.json @@ -0,0 +1,94 @@ +{ + "protocols": [ + { + "id": 22532, + "title": "Producing rooted cassava plantlets for use in pot experiments", + "created_on": 1556012701, + "authors": "Matema Imakumbili", + "nr_of_steps": 13, + "nr_of_views": 37, + "uri": "producing-rooted-cassava-plantlets-for-use-in-pot-z9cf92w" + }, + { + "id": 832, + "title": "Preparation of Virus DNA from Seawater for Metagenomics", + "created_on": 1434495014, + "authors": "Matthew Sullivan Lab", + "nr_of_steps": 11, + "nr_of_views": 457, + "uri": "Preparation-of-Virus-DNA-from-Seawater-for-Metagen-c28yhv" + }, + { + "id": 14506, + "title": "MALE CIRCUMCISION FOR PREVENTION OF HETEROSEXUAL TRANSMISSION OF HIV IN ADULT MALES IN SOWETO, what do indicators and incidence rates show?untitled protocol", + "created_on": 1533553359, + "authors": "Hillary Mukudu, Neil Martinson, Benn Sartorius", + "nr_of_steps": 0, + "nr_of_views": 5, + "uri": "male-circumcision-for-prevention-of-heterosexual-t-seiebce" + }, + { + "id": 10927, + "title": "physiological and biochemical parameters", + "created_on": 1521454618, + "authors": "Amor Slama, Elhem Mallek-Maalej, Hatem Ben Mohamed, Thouraya Rhim, Leila Radhouane, Amor SLAMA", + "nr_of_steps": 0, + "nr_of_views": 34, + "uri": "physiological-and-biochemical-parameters-nwpdfdn" + }, + { + "id": 822, + "title": "Iron Chloride Precipitation of Viruses from Seawater", + "created_on": 1434402745, + "authors": "Seth John, Bonnie Poulos, Christine Schirmer", + "nr_of_steps": 17, + "nr_of_views": 1805, + "uri": "Iron-Chloride-Precipitation-of-Viruses-from-Seawat-c2wyfd" + }, + { + "id": 12115, + "title": "Measuring specific leaf area and water content", + "created_on": 1526074093, + "authors": "Etienne Laliberté", + "nr_of_steps": 33, + "nr_of_views": 259, + "uri": "measuring-specific-leaf-area-and-water-content-p3tdqnn" + }, + { + "id": 1842, + "title": "Purification of viral assemblages from seawater in CsCl gradients", + "created_on": 1445287995, + "authors": "Janice E. Lawrence and Grieg F. Steward", + "nr_of_steps": 23, + "nr_of_views": 116, + "uri": "Purification-of-viral-assemblages-from-seawater-in-d2s8ed" + }, + { + "id": 12540, + "title": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "created_on": 1527418313, + "authors": "Benjamin Hume, Maren Ziegler, Christian Voolstra, Julie Poulain, Xavier Pochon, Sarah Romac, Emilie Boissin, Colomban de Vargas, Serge Planes, Patrick Wincker", + "nr_of_steps": 1, + "nr_of_views": 234, + "uri": "an-improved-primer-set-and-pcr-amplification-proto-qg4dtyw" + }, + { + "id": 985, + "title": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "created_on": 1435347035, + "authors": "Chisholm Lab", + "nr_of_steps": 10, + "nr_of_views": 85, + "uri": "NATURAL-SEAWATER-BASED-PRO99-MEDIUM-c7zzp5" + }, + { + "id": 1033, + "title": "SN Maintenance Medium for Synechococcus", + "created_on": 1435778857, + "authors": "JB Waterbury \u0026 JM Willey", + "nr_of_steps": 6, + "nr_of_views": 33, + "uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35" + } + ] +} diff --git a/spec/fixtures/files/protocol_importers/protocols_io/v3/protocols.json b/spec/fixtures/files/protocol_importers/protocols_io/v3/protocols.json new file mode 100644 index 000000000..29ca56de6 --- /dev/null +++ b/spec/fixtures/files/protocol_importers/protocols_io/v3/protocols.json @@ -0,0 +1,3072 @@ +{ + "items": [ + { + "id": 22532, + "title": "Producing rooted cassava plantlets for use in pot experiments", + "title_html": "Producing rooted cassava plantlets for use in pot experiments", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.z9cf92w", + "doi_status": 0, + "uri": "producing-rooted-cassava-plantlets-for-use-in-pot-z9cf92w", + "type_id": 1, + "published_on": 1556312593, + "stats": { + "number_of_views": 37, + "number_of_steps": 13, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1556012701, + "categories": null, + "creator": { + "name": "Matema Imakumbili", + "affiliation": "Sokoine University of Agriculture", + "affiliations": [], + "username": "matema-imakumbili", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bge63yrw.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/bge63yrw.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "", + "total_collections": 0, + "number_of_steps": 13, + "authors": [ + { + "name": "Matema Imakumbili", + "affiliation": "Sokoine University of Agriculture", + "affiliations": [], + "username": "matema-imakumbili", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bge63yrw.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/bge63yrw.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 832, + "title": "Preparation of Virus DNA from Seawater for Metagenomics", + "title_html": "Preparation of Virus DNA from Seawater for Metagenomics", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.c28yhv", + "doi_status": 0, + "uri": "Preparation-of-Virus-DNA-from-Seawater-for-Metagen-c28yhv", + "type_id": 1, + "published_on": 1451931897, + "stats": { + "number_of_views": 457, + "number_of_steps": 11, + "number_of_bookmarks": 11, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1434495014, + "categories": null, + "creator": { + "name": "VERVE Team", + "affiliation": "University of Arizona", + "affiliations": [], + "username": "verve-team", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "", + "total_collections": 0, + "number_of_steps": 11, + "authors": [ + { + "name": "Matthew Sullivan Lab", + "affiliation": "Matthew Sullivan Lab, University of Arizona, The Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Preparation of Virus DNA from Seawater for Metagenomics", + "title_html": "Preparation of Virus DNA from Seawater for Metagenomics", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "Preparation-of-Virus-DNA-from-Seawater-for-Metagen-c28yhv", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931897, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + }, + { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Preparation of Virus DNA from Seawater for Metagenomics", + "title_html": "Preparation of Virus DNA from Seawater for Metagenomics", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "Preparation-of-Virus-DNA-from-Seawater-for-Metagen-c28yhv", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931897, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 1, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 14506, + "title": "MALE CIRCUMCISION FOR PREVENTION OF HETEROSEXUAL TRANSMISSION OF HIV IN ADULT MALES IN SOWETO, what do indicators and incidence rates show?untitled protocol", + "title_html": "MALE CIRCUMCISION FOR PREVENTION OF HETEROSEXUAL TRANSMISSION OF HIV IN ADULT MALES IN SOWETO, what do indicators and incidence rates show?untitled protocol", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.seiebce", + "doi_status": 0, + "uri": "male-circumcision-for-prevention-of-heterosexual-t-seiebce", + "type_id": 1, + "published_on": 1552061162, + "stats": { + "number_of_views": 5, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1533553359, + "categories": null, + "creator": { + "name": "Hillary Mukudu", + "affiliation": "University of Witwatersrand", + "affiliations": [], + "username": "hillary-mukudu", + "link": "https://doi.org/10.1371/journal.pone.0213571", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/u6qjk36.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/u6qjk36.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "PLOS One", + "journal_name": "PLOS One", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "https://doi.org/10.1371/journal.pone.0213571", + "total_collections": 0, + "number_of_steps": 0, + "authors": [ + { + "name": "Hillary Mukudu", + "affiliation": "University of Witwatersrand", + "affiliations": [], + "username": "hillary-mukudu", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/u6qjk36.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/u6qjk36.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Neil Martinson", + "affiliation": "University of Witwatersrand", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Benn Sartorius", + "affiliation": "University of Witwatersrand", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 10927, + "title": "physiological and biochemical parameters", + "title_html": "physiological and biochemical parameters", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.nwpdfdn", + "doi_status": 0, + "uri": "physiological-and-biochemical-parameters-nwpdfdn", + "type_id": 1, + "published_on": 1521463354, + "stats": { + "number_of_views": 34, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1521454618, + "categories": null, + "creator": { + "name": "Amor SLAMA", + "affiliation": "", + "affiliations": [], + "username": "amor-slama", + "link": null, + "image": { + "source": "/img/avatars/014.png", + "placeholder": "/img/avatars/014.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "", + "total_collections": 0, + "number_of_steps": 0, + "authors": [ + { + "name": "Amor Slama", + "affiliation": "Science Faculty of Bizerte, Carthage University, Tunisia.", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Elhem Mallek-Maalej", + "affiliation": "Science Faculty of Bizerte, Carthage University, Tunisia.", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Hatem Ben Mohamed", + "affiliation": "Arid and Oases Cropping Laboratory, Arid Regions Institute of Medenine, Tunisia", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Thouraya Rhim", + "affiliation": "Horticulture Laboratory, National Institute of Agronomic Research, Tunisia", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Leila Radhouane", + "affiliation": "Plant Physiology Laboratory, National Institute of Agronomic Research, Tunisia", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Amor SLAMA", + "affiliation": "Plant Physiology Laboratory, National Institute of Agronomic Research, Tunisia", + "affiliations": [], + "username": "amor-slama", + "link": null, + "image": { + "source": "/img/avatars/014.png", + "placeholder": "/img/avatars/014.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 643, + "uri": "plantae", + "title": "Plantae", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 643, + "uri": "plantae", + "title": "Plantae", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": "Steven Burgess", + "affiliation": null, + "affiliations": [], + "username": "steven-burgess", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/z4keiee.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/z4keiee.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 1, + "is_my": false, + "is_request": true, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": "Steven Burgess", + "affiliation": null, + "affiliation_url": null, + "username": "steven-burgess", + "link": null + }, + "protocol": { + "id": 0, + "title": "physiological and biochemical parameters", + "title_html": "physiological and biochemical parameters", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "physiological-and-biochemical-parameters-nwpdfdn", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1528810343, + "resolve_on": 0, + "resolved_user": { + "name": "Amor SLAMA", + "affiliation": null, + "affiliations": [], + "username": "amor-slama", + "link": null, + "image": { + "source": "/img/avatars/014.png", + "placeholder": "/img/avatars/014.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 822, + "title": "Iron Chloride Precipitation of Viruses from Seawater", + "title_html": "Iron Chloride Precipitation of Viruses from Seawater", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.c2wyfd", + "doi_status": 0, + "uri": "Iron-Chloride-Precipitation-of-Viruses-from-Seawat-c2wyfd", + "type_id": 1, + "published_on": 1437416188, + "stats": { + "number_of_views": 1805, + "number_of_steps": 17, + "number_of_bookmarks": 15, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1434402745, + "categories": null, + "creator": { + "name": "Lauren Chittick", + "affiliation": "", + "affiliations": [], + "username": "lauren-chittick", + "link": null, + "image": { + "source": "/img/avatars/005.png", + "placeholder": "/img/avatars/005.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 1, + "link": "", + "total_collections": 0, + "number_of_steps": 17, + "authors": [ + { + "name": "Seth John", + "affiliation": "Matthew Sullivan Lab, University of Arizona, Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": " Bonnie Poulos", + "affiliation": "Matthew Sullivan Lab, University of Arizona, Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": " Christine Schirmer", + "affiliation": "Matthew Sullivan Lab, University of Arizona, Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Iron Chloride Precipitation of Viruses from Seawater", + "title_html": "Iron Chloride Precipitation of Viruses from Seawater", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "Iron-Chloride-Precipitation-of-Viruses-from-Seawat-c2wyfd", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1437416188, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + }, + { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Iron Chloride Precipitation of Viruses from Seawater", + "title_html": "Iron Chloride Precipitation of Viruses from Seawater", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "Iron-Chloride-Precipitation-of-Viruses-from-Seawat-c2wyfd", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1437416188, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 1, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 12115, + "title": "Measuring specific leaf area and water content", + "title_html": "Measuring specific leaf area and water content", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/xrmja7w.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/xrmja7w.jpg" + }, + "doi": "dx.doi.org/10.17504/protocols.io.p3tdqnn", + "doi_status": 0, + "uri": "measuring-specific-leaf-area-and-water-content-p3tdqnn", + "type_id": 1, + "published_on": 1526329408, + "stats": { + "number_of_views": 259, + "number_of_steps": 33, + "number_of_bookmarks": 3, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1526074093, + "categories": null, + "creator": { + "name": "Etienne Laliberté", + "affiliation": "Université de Montréal", + "affiliations": [], + "username": "etienne-lalibert", + "link": "www.caboscience.org", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uqxja7w.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/uqxja7w.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 1, + "link": "www.caboscience.org", + "total_collections": 0, + "number_of_steps": 33, + "authors": [ + { + "name": "Etienne Laliberté", + "affiliation": "Université de Montréal", + "affiliations": [], + "username": "etienne-lalibert", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uqxja7w.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/uqxja7w.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 620, + "uri": "lefo", + "title": "Plant Functional Ecology Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uqyja7w.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/uqyja7w.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 620, + "uri": "lefo", + "title": "Plant Functional Ecology Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uqyja7w.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/uqyja7w.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Measuring specific leaf area and water content", + "title_html": "Measuring specific leaf area and water content", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "measuring-specific-leaf-area-and-water-content-p3tdqnn", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1526329418, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + }, + { + "id": 642, + "uri": "cabo", + "title": "Canadian Airborne Biodiversity Observatory", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uq2ja7w.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/uq2ja7w.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 642, + "uri": "cabo", + "title": "Canadian Airborne Biodiversity Observatory", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/uq2ja7w.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/uq2ja7w.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Measuring specific leaf area and water content", + "title_html": "Measuring specific leaf area and water content", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "measuring-specific-leaf-area-and-water-content-p3tdqnn", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1526329418, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 1842, + "title": "Purification of viral assemblages from seawater in CsCl gradients", + "title_html": "Purification of viral assemblages from seawater in CsCl gradients", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.d2s8ed", + "doi_status": 0, + "uri": "Purification-of-viral-assemblages-from-seawater-in-d2s8ed", + "type_id": 1, + "published_on": 1447702883, + "stats": { + "number_of_views": 116, + "number_of_steps": 23, + "number_of_bookmarks": 2, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1445287995, + "categories": null, + "creator": { + "name": "Janice Lawrence", + "affiliation": "", + "affiliations": [], + "username": "janice-lawrence", + "link": "http://www.aslo.org/books/mave/MAVE_166.pdf", + "image": { + "source": "/img/avatars/006.png", + "placeholder": "/img/avatars/006.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "http://www.aslo.org/books/mave/MAVE_166.pdf", + "total_collections": 0, + "number_of_steps": 23, + "authors": [ + { + "name": "Janice E. Lawrence and Grieg F. Steward", + "affiliation": "Manual of Aquatic Viral Ecology", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "Purification of viral assemblages from seawater in CsCl gradients", + "title_html": "Purification of viral assemblages from seawater in CsCl gradients", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "Purification-of-viral-assemblages-from-seawater-in-d2s8ed", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1447702883, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 12540, + "title": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "title_html": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.qg4dtyw", + "doi_status": 0, + "uri": "an-improved-primer-set-and-pcr-amplification-proto-qg4dtyw", + "type_id": 1, + "published_on": 1527422155, + "stats": { + "number_of_views": 234, + "number_of_steps": 1, + "number_of_bookmarks": 2, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 1, + "created_on": 1527418313, + "categories": null, + "creator": { + "name": "Benjamin Hume", + "affiliation": "", + "affiliations": [], + "username": "benjamin-hume", + "link": "https://peerj.com/articles/4816/", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 1, + "link": "https://peerj.com/articles/4816/", + "total_collections": 0, + "number_of_steps": 1, + "authors": [ + { + "name": "Benjamin Hume", + "affiliation": "King Abdullah University of Science and Technology", + "affiliations": [], + "username": "benjamin-hume", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Maren Ziegler", + "affiliation": "King Abdullah University of Science and Technology", + "affiliations": [], + "username": "maren-ziegler", + "link": null, + "image": { + "source": "/img/avatars/018.png", + "placeholder": "/img/avatars/018.png" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Christian Voolstra", + "affiliation": "King Abdullah University of Science and Technology", + "affiliations": [], + "username": "christian-voolstra", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/u8qjme6.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/u8qjme6.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Julie Poulain", + "affiliation": "CEA–Institut de Biologie François Jacob, Genoscope, Evry, France; CNRS, UMR 8030, Evry, France; Université d’Evry, UMR 8030, Evry, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Xavier Pochon", + "affiliation": "Coastal and Freshwater Group, Cawthron Institute, Nelson, New Zealand; Institute of Marine Science, University of Auckland, Warkworth, New Zealand", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Sarah Romac", + "affiliation": "CNRS, UMR 7144, EPEP & Sorbonne Universités, UPMC Université Paris 06; Station Biologique de Roscoff, Roscoff, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Emilie Boissin", + "affiliation": "PSL Research University: EPHE-UPVD-CNRS, USR 3278 CRIOBE, Université de Perpignan, Perpignan Cedex, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Colomban de Vargas", + "affiliation": "CNRS, UMR 7144, EPEP & Sorbonne Universités, UPMC Université Paris 06; Station Biologique de Roscoff, Roscoff, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Serge Planes", + "affiliation": "PSL Research University: EPHE-UPVD-CNRS, USR 3278 CRIOBE, Université de Perpignan, Perpignan Cedex, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + { + "name": "Patrick Wincker", + "affiliation": "CEA–Institut de Biologie François Jacob, Genoscope, Evry, France; CNRS, UMR 8030, Evry, France; Université d’Evry, UMR 8030, Evry, France", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [ + { + "id": 11238, + "title": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "title_html": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": null, + "doi_status": 0, + "uri": "an-improved-primer-set-and-pcr-amplification-proto-n8edhte", + "type_id": 0, + "published_on": 1527422375, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1522822041, + "categories": null, + "creator": { + "name": "Benjamin Hume", + "affiliation": null, + "affiliations": [], + "username": "benjamin-hume", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": null, + "journal_name": null, + "journal_link": null, + "article_citation": null + }, + { + "id": 12540, + "title": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "title_html": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": null, + "doi_status": 0, + "uri": "an-improved-primer-set-and-pcr-amplification-proto-qg4dtyw", + "type_id": 0, + "published_on": 1527422155, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 1, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 1, + "created_on": 1527418313, + "categories": null, + "creator": { + "name": "Benjamin Hume", + "affiliation": null, + "affiliations": [], + "username": "benjamin-hume", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/vi8jpve.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": null, + "journal_name": null, + "journal_link": null, + "article_citation": null + } + ], + "groups": [ + { + "id": 653, + "uri": "reefgenomics", + "title": "reefgenomics", + "image": { + "source": "https://www.protocols.io/img/group_placeholder.png", + "placeholder": "https://www.protocols.io/img/group_placeholder.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 653, + "uri": "reefgenomics", + "title": "reefgenomics", + "image": { + "source": "https://www.protocols.io/img/group_placeholder.png", + "placeholder": "https://www.protocols.io/img/group_placeholder.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "title_html": "An improved primer set and PCR amplification protocol with increased specificity and sensitivity targeting the Symbiodinium ITS2 region using the SymVar primer pair", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "an-improved-primer-set-and-pcr-amplification-proto-qg4dtyw", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1527422160, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_view": 0, + "can_remove": 0, + "can_add": 0, + "can_edit": 0, + "can_publish": 0, + "can_get_doi": 0, + "can_share": 0, + "can_move": 0, + "can_move_outside": 0, + "can_transfer": 0, + "can_download": 1, + "is_locked": 0 + } + }, + { + "id": 985, + "title": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "title_html": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.c7zzp5", + "doi_status": 0, + "uri": "NATURAL-SEAWATER-BASED-PRO99-MEDIUM-c7zzp5", + "type_id": 1, + "published_on": 1451931384, + "stats": { + "number_of_views": 85, + "number_of_steps": 10, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1435347035, + "categories": null, + "creator": { + "name": "VERVE Team", + "affiliation": "University of Arizona", + "affiliations": [], + "username": "verve-team", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "", + "total_collections": 0, + "number_of_steps": 10, + "authors": [ + { + "name": "Chisholm Lab", + "affiliation": "Matthew Sullivan Lab, University of Arizona, Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "title_html": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "NATURAL-SEAWATER-BASED-PRO99-MEDIUM-c7zzp5", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931384, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + }, + { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "title_html": "NATURAL SEAWATER-BASED PRO99 MEDIUM", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "NATURAL-SEAWATER-BASED-PRO99-MEDIUM-c7zzp5", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931384, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 1, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + }, + { + "id": 1033, + "title": "SN Maintenance Medium for Synechococcus", + "title_html": "SN Maintenance Medium for Synechococcus", + "image": { + "source": "https://www.protocols.io/img/default_protocol.png", + "placeholder": "https://www.protocols.io/img/default_protocol.png" + }, + "doi": "dx.doi.org/10.17504/protocols.io.c9hz35", + "doi_status": 0, + "uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35", + "type_id": 1, + "published_on": 1451931479, + "stats": { + "number_of_views": 33, + "number_of_steps": 6, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [], + "version_id": 0, + "created_on": 1435778857, + "categories": null, + "creator": { + "name": "VERVE Team", + "affiliation": "University of Arizona", + "affiliations": [], + "username": "verve-team", + "link": null, + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/bvfdk3e.jpg" + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "journal": "", + "journal_name": "", + "journal_link": null, + "article_citation": null, + "public": 1, + "has_versions": 0, + "link": "", + "total_collections": 0, + "number_of_steps": 6, + "authors": [ + { + "name": "JB Waterbury & JM Willey", + "affiliation": "Matthew Sullivan Lab, University of Arizona, Ohio State University", + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + } + ], + "versions": [], + "groups": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "SN Maintenance Medium for Synechococcus", + "title_html": "SN Maintenance Medium for Synechococcus", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931479, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + }, + { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "request": { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": null, + "research_interests": null, + "website": null, + "location": null, + "affiliation": null, + "status": { + "is_visible": true, + "access_level": 0 + }, + "stats": { + "files": [], + "total_members": 0, + "total_followers": 0, + "total_child_groups": 0, + "total_parent_groups": 0, + "has_collaborations": 0 + }, + "user_status": { + "is_member": false, + "is_confirmed": true, + "is_invited": false, + "is_owner": false, + "is_admin": false, + "is_following": false + }, + "join_link": null, + "token": null, + "owner": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "is_protocol_requested": 0, + "is_group_requested": 0, + "is_my": false, + "is_request": false, + "is_confirmed": 1, + "is_declined": 0, + "requester": { + "name": " ", + "affiliation": null, + "affiliation_url": null, + "username": null, + "link": null + }, + "protocol": { + "id": 0, + "title": "SN Maintenance Medium for Synechococcus", + "title_html": "SN Maintenance Medium for Synechococcus", + "image": { + "source": null, + "placeholder": null + }, + "doi": null, + "doi_status": 0, + "uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35", + "type_id": 1, + "published_on": null, + "stats": { + "number_of_views": 0, + "number_of_steps": 0, + "number_of_bookmarks": 0, + "number_of_comments": 0, + "number_of_exports": 0, + "number_of_runs": 0 + }, + "parent_protocols": [], + "parent_collections": [] + }, + "created_on": 1451931479, + "resolve_on": 0, + "resolved_user": { + "name": " ", + "affiliation": null, + "affiliations": [], + "username": null, + "link": null, + "image": { + "source": null, + "placeholder": null + }, + "badges": [], + "research_interests": null, + "blocked_by_you": false, + "blocked_you": false + }, + "shared": false + } + } + ], + "has_subprotocols": 0, + "is_subprotocol": 0, + "is_bookmarked": 0, + "can_be_copied": 0, + "can_remove_fork": 0, + "forks_count": { + "private": 0, + "public": 0 + }, + "access": { + "can_download": 1 + } + } + ], + "total": 56, + "total_results": 56, + "total_pages": 6, + "extras": { + "2": [ + { + "id": 45, + "uri": "verve-net", + "title": "VERVE Net", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/cdqcwwe.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "Welcome to the Viral Ecology Research and Virtual Exchange network (VERVE Net). This is an online forum to increase connectivity and collaboration among virus ecology researchers. Funded by the Gordon and Betty Moore Foundation." + }, + { + "id": 50, + "uri": "sullivan-lab", + "title": "Sullivan Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/fxc3an.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "As the world faces global change and resource limitation, understanding the planet's microbes (e.g. bacteria, archaea) becomes a necessity. This is because microbes drive the biogeochemistry that runs the planet, and are central to human endeavors, from food to health to industry. Viruses that infect microbes profoundly shape microbial populations and processes by acting as both major predators and sources of new genes. We work to understand viral impacts on globally important microbial processes. As empiricists, we test hypotheses through direct systems-level studies of natural populations, complemented by developing and studying model phage-host systems in the lab with the goal of generating the data required for predictive ecosystem modeling. Practically, we develop new ways to \"see\" viruses — in the microscope, in environmental sequence datasets, in experiments — i.e., we are learning how to \"count\" across different data types." + }, + { + "id": 102, + "uri": "center-for-dark-energy-biosphere-investigations", + "title": "Center for Dark Energy Biosphere Investigations", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/buddabw.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/buddabw.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "The Center for Dark Energy Biosphere Investigations (C-DEBI) is a $47.6M National Science Foundation-funded Science and Technology Center started in 2010 with the mission to explore life beneath the seafloor and make transformative discoveries that advance science, benefit society, and inspire people of all ages and origins." + }, + { + "id": 422, + "uri": "huber-lab", + "title": "Huber Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/k3vg3p6.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/k3vg3p6.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

The Huber lab is broadly interested in how basic earth processes- rocks forming, fluids moving, sediments accumulating- interact to create and maintain life in the oceans. Their research addresses some of the most central questions about the nature and extent of life on Earth in one of its least explored corners, the subseafloor habitat beneath the ocean floor. They investigate subseafloor microbial communities to resolve the extent, function, evolutionary dynamics, and biogeochemical implications of this relatively unexplored ecosystem. The questions their work addresses are universal for understanding the impact of microbial life on both human and planetary health and the selective forces that continue to allow life to establish, thrive, and diversify on Earth.

" + }, + { + "id": 108, + "uri": "protg", + "title": "Protist Research to Optimize Tools in Genetics (PROT-G)", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/e3addmn.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/e3addmn.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

PROT-G is an online forum to create connectivity and collaboration among marine (& other) protist researchers.
      Supported by the Gordon and Betty Moore Foundation

Join
     Create a user profile
     Join the PROT-G group

Collaborate 
     Discover, share, and optimize laboratory, field, and bioinformatic protocols

Communicate
     News: up to date news on experimental model systems 
     Events: workshops & conferences
     Articles: Literature recommendations 

Engage
     Discussions: conversations on cutting edge of genetics tool development in marine protists

" + }, + { + "id": 987, + "uri": "mouse-metabolic-phenotyping-centers", + "title": "Mouse Metabolic Phenotyping Centers", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/bajm36cw.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/bajm36cw.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

The MMPC is a National Institutes of Health-sponsored resource that provides experimental testing services to scientists studying diabetes, obesity, diabetic complications, and other metabolic diseases in mice.

" + }, + { + "id": 233, + "uri": "alverson-lab", + "title": "Alverson Lab", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/ix3dtu6.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/ix3dtu6.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

The goal of our research is to identify the ecological and genomic factors driving the evolution and diversification of diatoms. We address these questions using a blend of phylogenetic, experimental, and comparative genomic approaches. The major areas of current research are described on the Research page of our lab website.

\n

 

" + }, + { + "id": 292, + "uri": "denville-scientific-inc", + "title": "Denville Scientific, Inc.", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/g6jffjn.png", + "placeholder": "https://s3.amazonaws.com/pr-journal/g6jffjn.png" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

Since 1979, Denville Scientific has been providing bioresearch laboratories with innovative, cost-effective products and technical solutions designed to improve laboratory efficiency, safety and results. Our emphasis on quality, value, and customer satisfaction has served our customers and our company well. As a result of our recent growth, we have expanded our facilities, our staff, and our product line to serve you better. In addition to our traditional line of liquid handling products, our new catalog offers detailed descriptions and illustrations on hundreds of new items, many of which demonstrate our commitment to preserving the environment. Our knowledgeable sales staff welcomes your questions and comments. We want to thank our loyal customers for their support and patronage and we welcome those who may be seeing our catalog for the first time. We recognize the vital nature of your research and we realize the significance of our supporting role as a \"supplier to science.\" Denville Scientific is dedicated to providing the scientific community with unique tools and exceptional service. Denville strives to provide American manufactured products of the finest quality.

\n

 

\n

Denville Scientific is a division of Harvard Bioscience, Inc.

" + }, + { + "id": 165, + "uri": "caron-lab-protistan-ecology", + "title": "Caron Lab - Protistan Ecology", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/pbmdv2e.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/pbmdv2e.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "The Caron Lab at the University of Southern California focuses on the study of protist ecology.

Our work includes both field and lab components, and involves techniques such as microscopy, culturing, and molecular methods." + }, + { + "id": 643, + "uri": "plantae", + "title": "Plantae", + "image": { + "source": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg", + "placeholder": "https://s3.amazonaws.com/pr-journal/urzeiee.jpg" + }, + "tech_support": { + "email": null, + "phone": null, + "hide_contact": 0, + "use_email": 0 + }, + "is_member": 0, + "description": "

Home of protocols for plant scientists

" + } + ] + }, + "pagination": { + "current_page": 1, + "total_pages": 6, + "total_results": 56, + "next_page": "https://www.protocols.io/api/v3/protocols?key=water&page_size=10&page_id=2", + "prev_page": null, + "page_size": 10, + "first": 0, + "last": 56, + "changed_on": null + }, + "status_code": 0 +} diff --git a/spec/services/protocol_importers/search_protocols_service_spec.rb b/spec/services/protocol_importers/search_protocols_service_spec.rb new file mode 100644 index 000000000..bc9f0e1cf --- /dev/null +++ b/spec/services/protocol_importers/search_protocols_service_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ProtocolImporters::SearchProtocolsService do + let(:service_call) do + ProtocolImporters::SearchProtocolsService.call(protocol_source: 'protocolsio/v3', + query_params: { + key: 'someting', + page_id: 5, + order_field: 'title', + order_dir: 'asc' + }) + end + + let(:service_call_with_wrong_params) do + ProtocolImporters::SearchProtocolsService.call(protocol_source: 'protocolsio3', + query_params: { + key: '', + page_id: -1, + order_field: 'gender', + order_dir: 'descc' + }) + end + let(:normalized_list) do + JSON.parse(file_fixture('protocol_importers/normalized_list.json').read).to_h.with_indifferent_access + end + + context 'when have invalid attributes' do + it 'returns an error when params are invalid' do + expect(service_call_with_wrong_params.errors).to have_key(:invalid_params) + end + end + + context 'when have valid attributes' do + before do + client_data = double('api_response') + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_list) + .and_return(client_data)) + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) + .to(receive(:normalize_list).with(client_data) + .and_return(normalized_list)) + end + + it 'returns an error when params are invalid' do + expect(service_call.protocols_list).to be == normalized_list + end + end +end diff --git a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb index 926103b67..b5ca4e872 100644 --- a/spec/utilities/protocol_importers/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocol_normalizer_spec.rb @@ -3,11 +3,11 @@ require 'rails_helper' describe ProtocolImporters::ProtocolNormalizer do - describe '.normalize_all_protocols' do - it { expect { subject.normalize_all_protocols({}) }.to raise_error(NotImplementedError) } + describe '.normalize_list' do + it { expect { subject.normalize_list({}) }.to raise_error(NotImplementedError) } end - describe '.normalize_protocols' do + describe '.normalize_protocol' do it { expect { subject.normalize_protocol({}) }.to raise_error(NotImplementedError) } end end diff --git a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb index 55a42d7e4..7bb1408b1 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/protocol_normalizer_spec.rb @@ -5,20 +5,27 @@ require 'rails_helper' describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do let(:client_data) { double('api_response') } - let(:response) do + let(:protocols_io_single_protocol) do JSON.parse(file_fixture('protocol_importers/protocols_io/v3/single_protocol.json').read) .to_h.with_indifferent_access end let(:response_without_title) do - res_without_title = response + res_without_title = protocols_io_single_protocol res_without_title[:protocol].reject! { |a| a == 'title' } res_without_title end - let(:normalized_result) do - JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read) - .to_h.with_indifferent_access + let(:protocols_io_list) do + JSON.parse(file_fixture('protocol_importers/protocols_io/v3/protocols.json').read).to_h.with_indifferent_access + end + + let(:normalized_protocol) do + JSON.parse(file_fixture('protocol_importers/normalized_single_protocol.json').read).to_h.with_indifferent_access + end + + let(:normalized_list) do + JSON.parse(file_fixture('protocol_importers/normalized_list.json').read).to_h.with_indifferent_access end describe '#normalize_protocol' do @@ -30,9 +37,9 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do context 'when have all data' do it 'should normalize data correctly' do allow(client_data).to receive_message_chain(:parsed_response) - .and_return(response) + .and_return(protocols_io_single_protocol) - expect(subject.normalize_protocol(client_data).deep_stringify_keys).to be == normalized_result + expect(subject.normalize_protocol(client_data).deep_stringify_keys).to be == normalized_protocol end end @@ -45,4 +52,12 @@ describe ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer do end end end + + describe '#normalize_list' do + it 'should normalize data correctly' do + allow(client_data).to receive_message_chain(:parsed_response).and_return(protocols_io_list) + + expect(subject.normalize_list(client_data).deep_stringify_keys).to be == normalized_list + end + end end From 88d96c0938ebd454267d9fd72c5f32be95d57837 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Fri, 14 Jun 2019 10:41:51 +0200 Subject: [PATCH 28/73] Use service in controller --- .../external_protocols_controller.rb | 17 +++++------------ .../search_protocols_service.rb | 2 +- .../external_protocols_controller_spec.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index a4cae5862..3f1936f87 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -6,25 +6,18 @@ class ExternalProtocolsController < ApplicationController # GET list_external_protocol def index - # list_protocols = SearchService.call(index_params) - succeed = true - protocols = [ - { name: 'Protocol1' }, - { name: 'Protocol2' }, - { name: 'Protocol3' } - ] + service_call = ProtocolImporters::SearchProtocolsService + .call(protocol_source: 'protocolsio/v3', query_params: index_params) - if succeed + if service_call.succeed? render json: { html: render_to_string( partial: 'protocol_importers/list_of_protocol_cards.html.erb', - locals: { protocols: protocols } + locals: { protocols: service_call.protocols_list } ) } else - render json: { - errors: { protocol: 'error_placeholder' } - }, status: 400 + render json: { errors: service_call.errors }, status: 400 end end diff --git a/app/services/protocol_importers/search_protocols_service.rb b/app/services/protocol_importers/search_protocols_service.rb index 2a6a11ae3..ab418326b 100644 --- a/app/services/protocol_importers/search_protocols_service.rb +++ b/app/services/protocol_importers/search_protocols_service.rb @@ -35,7 +35,7 @@ module ProtocolImporters @errors[:invalid_params][:key] = 'Key cannot be empty' if @query_params[:key].blank? # try if page id is ok - if @query_params[:page_id] && !@query_params[:page_id].positive? + if @query_params[:page_id] && !@query_params[:page_id].to_i.positive? @errors[:invalid_params][:page_id] = 'Page needs to be positive' end diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index cb750c186..94e0cc1c6 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -24,6 +24,14 @@ describe ExternalProtocolsController, type: :controller do let(:action) { get :index, params: params } + before do + service = double('success_service') + allow(service).to(receive(:succeed?)).and_return(true) + allow(service).to(receive(:protocols_list)).and_return({}) + + allow_any_instance_of(ProtocolImporters::SearchProtocolsService).to(receive(:call)).and_return(service) + end + it 'returns JSON, 200 response when protocol parsing was valid' do action expect(response).to have_http_status(:success) From c691ad80b5a9d472ad5f78f54675340c820f401c Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Fri, 14 Jun 2019 11:07:34 +0200 Subject: [PATCH 29/73] Add fields to query params for ApiClient --- app/services/protocol_importers/search_protocols_service.rb | 4 ++-- config/initializers/constants.rb | 3 ++- .../protocol_importers/protocols_io/v3/api_client_spec.rb | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/services/protocol_importers/search_protocols_service.rb b/app/services/protocol_importers/search_protocols_service.rb index ab418326b..abd5a620e 100644 --- a/app/services/protocol_importers/search_protocols_service.rb +++ b/app/services/protocol_importers/search_protocols_service.rb @@ -40,12 +40,12 @@ module ProtocolImporters end # try if order_field is ok - if CONSTANTS[:available_order_fields]&.exclude? @query_params[:order_field].to_sym + if @query_params[:order_field] && CONSTANTS[:available_order_fields].exclude?(@query_params[:order_field]&.to_sym) @errors[:invalid_params][:order_field] = 'Order field is not ok' end # try if order dir is ok - if CONSTANTS[:available_order_dirs]&.exclude? @query_params[:order_dir].to_sym + if @query_params[:order_field] && CONSTANTS[:available_order_dirs].exclude?(@query_params[:order_dir]&.to_sym) @errors[:invalid_params][:order_dir] = 'Order dir is not ok' end diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 692f05b95..71f91152a 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -213,7 +213,8 @@ class Constants order_field: :activity, order_dir: :desc, page_size: 10, - page_id: 1 + page_id: 1, + fields: 'id,title,authors,created_on,uri,stats' } } }, diff --git a/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb index 1ece0aa23..2642f15e4 100644 --- a/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb +++ b/spec/utilities/protocol_importers/protocols_io/v3/api_client_spec.rb @@ -44,7 +44,8 @@ describe ProtocolImporters::ProtocolsIO::V3::ApiClient do order_dir: :asc, order_field: :date, page_id: 2, - page_size: 15 + page_size: 15, + fields: 'somefields' } stub_request(:get, URL).with(query: query) From ebcaa4b01458bad588087d92976e18cf782bbf84 Mon Sep 17 00:00:00 2001 From: Mojca Lorber Date: Tue, 18 Jun 2019 14:01:57 +0200 Subject: [PATCH 30/73] Implement protocols.io error handling for api client and normalizer --- .../external_protocols_controller.rb | 10 +++++- .../build_protocol_from_client_service.rb | 29 ++++++++++++++-- .../search_protocols_service.rb | 27 +++++++++++++-- .../protocols_io/v3/api_client.rb | 23 +++++++++++-- .../protocols_io/v3/api_errors/error.rb | 18 ++++++++++ .../v3/api_errors/invalid_token_error.rb | 12 +++++++ .../missing_or_empty_parameters_error.rb | 12 +++++++ .../v3/api_errors/token_expired_error.rb | 12 +++++++ .../protocols_io/v3/normalizer_error.rb | 16 +++++++++ .../protocols_io/v3/protocol_normalizer.rb | 6 ++++ .../external_protocols_controller_spec.rb | 10 ++++++ ...build_protocol_from_client_service_spec.rb | 34 +++++++++++++++++++ .../search_protocols_service_spec.rb | 34 +++++++++++++++++++ 13 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 app/utilities/protocol_importers/protocols_io/v3/api_errors/error.rb create mode 100644 app/utilities/protocol_importers/protocols_io/v3/api_errors/invalid_token_error.rb create mode 100644 app/utilities/protocol_importers/protocols_io/v3/api_errors/missing_or_empty_parameters_error.rb create mode 100644 app/utilities/protocol_importers/protocols_io/v3/api_errors/token_expired_error.rb create mode 100644 app/utilities/protocol_importers/protocols_io/v3/normalizer_error.rb diff --git a/app/controllers/external_protocols_controller.rb b/app/controllers/external_protocols_controller.rb index 3f1936f87..d6fa2816a 100644 --- a/app/controllers/external_protocols_controller.rb +++ b/app/controllers/external_protocols_controller.rb @@ -27,7 +27,15 @@ class ExternalProtocolsController < ApplicationController endpoint_name = Constants::PROTOCOLS_ENDPOINTS.dig(*show_params[:protocol_source] .split('/').map(&:to_sym)) api_client = "ProtocolImporters::#{endpoint_name}::ApiClient".constantize.new - html_preview = api_client.protocol_html_preview(show_params[:protocol_id]) + + begin + html_preview = api_client.protocol_html_preview(show_params[:protocol_id]) + rescue SocketError, HTTParty::Error => e + render json: { + errors: [network: e.message] + } + return + end render json: { protocol_source: show_params[:protocol_source], diff --git a/app/services/protocol_importers/build_protocol_from_client_service.rb b/app/services/protocol_importers/build_protocol_from_client_service.rb index 434a1cfe6..cf135aa3e 100644 --- a/app/services/protocol_importers/build_protocol_from_client_service.rb +++ b/app/services/protocol_importers/build_protocol_from_client_service.rb @@ -17,9 +17,24 @@ module ProtocolImporters def call return self unless valid? - # TODO: check for errors - api_response = api_client.single_protocol(@id) - normalized_hash = normalizer.normalize_protocol(api_response) + # Call api client + begin + api_response = api_client.single_protocol(@id) + rescue api_errors => e + @errors[e.class.to_s.downcase.to_sym] = e.error_message + return self + rescue SocketError, HTTParty::Error => e + @errors[e.class.to_s.downcase.to_sym] = e.message + return self + end + + # Normalize protocol + begin + normalized_hash = normalizer.normalize_protocol(api_response) + rescue normalizer_errors => e + @errors[e.class.to_s.downcase.to_sym] = e.error_message + return self + end pio = ProtocolImporters::ProtocolIntermediateObject.new(normalized_json: normalized_hash, user: @user, @@ -63,5 +78,13 @@ module ProtocolImporters def normalizer "ProtocolImporters::#{endpoint_name}::ProtocolNormalizer".constantize.new end + + def api_errors + "ProtocolImporters::#{endpoint_name}::ApiErrors::Error".constantize + end + + def normalizer_errors + "ProtocolImporters::#{endpoint_name}::NormalizerError".constantize + end end end diff --git a/app/services/protocol_importers/search_protocols_service.rb b/app/services/protocol_importers/search_protocols_service.rb index abd5a620e..202ec9f1c 100644 --- a/app/services/protocol_importers/search_protocols_service.rb +++ b/app/services/protocol_importers/search_protocols_service.rb @@ -17,9 +17,24 @@ module ProtocolImporters def call return self unless valid? - api_response = api_client.protocol_list(@query_params) + # Call api client + begin + api_response = api_client.protocol_list(@query_params) + rescue api_errors => e + @errors[e.class.to_s.downcase.to_sym] = e.error_message + return self + rescue SocketError, HTTParty::Error => e + @errors[e.class.to_s.downcase.to_sym] = e.message + return self + end - @protocols_list = normalizer.normalize_list(api_response) + # Normalize protocols list + begin + @protocols_list = normalizer.normalize_list(api_response) + rescue normalizer_errors => e + @errors[e.class.to_s.downcase.to_sym] = e.error_message + return self + end self end @@ -66,5 +81,13 @@ module ProtocolImporters def normalizer "ProtocolImporters::#{endpoint_name}::ProtocolNormalizer".constantize.new end + + def api_errors + "ProtocolImporters::#{endpoint_name}::ApiErrors::Error".constantize + end + + def normalizer_errors + "ProtocolImporters::#{endpoint_name}::NormalizerError".constantize + end end end diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb index a4b907a93..c52d2a027 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/api_client.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/api_client.rb @@ -12,9 +12,12 @@ module ProtocolImporters default_timeout CONSTANTS[:default_timeout] logger Rails.logger, CONSTANTS[:debug_level] + attr_reader :errors + def initialize(token = nil) # Currently we support public tokens only (no token needed for public data) @auth = { token: token } + @errors = {} # Set default headers self.class.headers('Authorization': "Bearer #{@auth[:token]}") if @auth[:token].present? @@ -45,12 +48,12 @@ module ProtocolImporters query = CONSTANTS.dig(:endpoints, :protocols, :default_query_params) .merge(query_params) - self.class.get('/protocols', query: query) + check_for_api_errors(self.class.get('/protocols', query: query)) end # Returns full representation of given protocol ID def single_protocol(id) - self.class.get("/protocols/#{id}") + check_for_api_errors(self.class.get("/protocols/#{id}")) end # Returns html preview for given protocol @@ -59,6 +62,22 @@ module ProtocolImporters def protocol_html_preview(uri) self.class.get("https://www.protocols.io/view/#{uri}.html", headers: {}) end + + private + + def check_for_api_errors(response) + if response['status_code'] == 0 + return response + elsif response['status_code'] == 1 + raise ApiErrors::MissingOrEmptyParametersError.new(response['status_code'], response['error_message']) + elsif response['status_code'] == 1218 + raise ApiErrors::InvalidTokenError.new(response['status_code'], response['error_message']) + elsif response['status_code'] == 1219 + raise ApiErrors::TokenExpiredError.new(response['status_code'], response['error_message']) + else + raise ApiErrors::Error.new(response['status_code'], response['error_message']) + end + end end end end diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_errors/error.rb b/app/utilities/protocol_importers/protocols_io/v3/api_errors/error.rb new file mode 100644 index 000000000..822b7bd4c --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/api_errors/error.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + module ApiErrors + class Error < StandardError + attr_reader :status_code, :error_message + + def initialize(status_code, error_message) + @status_code = status_code + @error_message = error_message + end + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_errors/invalid_token_error.rb b/app/utilities/protocol_importers/protocols_io/v3/api_errors/invalid_token_error.rb new file mode 100644 index 000000000..b1e8c738c --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/api_errors/invalid_token_error.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + module ApiErrors + class InvalidTokenError < Error + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_errors/missing_or_empty_parameters_error.rb b/app/utilities/protocol_importers/protocols_io/v3/api_errors/missing_or_empty_parameters_error.rb new file mode 100644 index 000000000..a228c6b9b --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/api_errors/missing_or_empty_parameters_error.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + module ApiErrors + class MissingOrEmptyParametersError < Error + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/api_errors/token_expired_error.rb b/app/utilities/protocol_importers/protocols_io/v3/api_errors/token_expired_error.rb new file mode 100644 index 000000000..e0c533b14 --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/api_errors/token_expired_error.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + module ApiErrors + class TokenExpiredError < Error + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/normalizer_error.rb b/app/utilities/protocol_importers/protocols_io/v3/normalizer_error.rb new file mode 100644 index 000000000..1c33b81d4 --- /dev/null +++ b/app/utilities/protocol_importers/protocols_io/v3/normalizer_error.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module ProtocolImporters + module ProtocolsIO + module V3 + class NormalizerError < StandardError + attr_reader :error_type, :error_message + + def initialize(error_type, error_message) + @error_type = error_type + @error_message = error_message + end + end + end + end +end diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index a517e3bcc..af1df5e4e 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -7,6 +7,9 @@ module ProtocolImporters def normalize_protocol(client_data) # client_data is HttpParty ApiReponse object protocol_hash = client_data.parsed_response.with_indifferent_access[:protocol] + unless protocol_hash.present? + raise NormalizerError.new(:nil_protocol, 'Protocol not present in hash.') + end normalized_data = { uri: client_data.request.last_uri.to_s, @@ -54,6 +57,9 @@ module ProtocolImporters def normalize_list(client_data) # client_data is HttpParty ApiReponse object protocols_hash = client_data.parsed_response.with_indifferent_access[:items] + unless protocols_hash.present? + raise NormalizerError.new(:nil_protocol_items, 'Protocol items not present in hash.') + end normalized_data = {} normalized_data[:protocols] = protocols_hash.map do |e| { diff --git a/spec/controllers/external_protocols_controller_spec.rb b/spec/controllers/external_protocols_controller_spec.rb index 94e0cc1c6..9abf14098 100644 --- a/spec/controllers/external_protocols_controller_spec.rb +++ b/spec/controllers/external_protocols_controller_spec.rb @@ -78,6 +78,16 @@ describe ExternalProtocolsController, type: :controller do expect(JSON.parse(response.body)['html']).to eq(html_preview) end + it 'return network errors when calling api client' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_html_preview) + .and_raise(SocketError)) + + # Call action + action + expect(JSON.parse(response.body)).to have_key('errors') + end + it 'returns error JSON and 400 response when something went wrong' do allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) .to(receive(:protocol_html_preview)).and_raise(StandardError) diff --git a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb index 84356e2dd..e1af19c0a 100644 --- a/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb +++ b/spec/services/protocol_importers/build_protocol_from_client_service_spec.rb @@ -22,6 +22,40 @@ describe ProtocolImporters::BuildProtocolFromClientService do end end + context 'when raise api client error' do + it 'return network errors' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:single_protocol) + .and_raise(SocketError)) + + expect(service_call.errors).to have_key(:socketerror) + end + + it 'return api errors' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:single_protocol) + .and_raise(ProtocolImporters::ProtocolsIO::V3::ApiErrors::MissingOrEmptyParametersError.new('1', 'Missing Or Empty Parameters Error'))) + + expect(service_call.errors).to have_key(:"protocolimporters::protocolsio::v3::apierrors::missingoremptyparameterserror") + end + end + + context 'when normalize protocol fails' do + it 'return normalizer errors' do + client_data = double('api_response') + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:single_protocol) + .and_return(client_data)) + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) + .to(receive(:normalize_protocol).with(client_data) + .and_raise(ProtocolImporters::ProtocolsIO::V3::NormalizerError.new('nil_protocol', 'Nil Protocol'))) + + expect(service_call.errors).to have_key(:"protocolimporters::protocolsio::v3::normalizererror") + end + end + context 'when have valid arguments' do before do client_data = double('api_response') diff --git a/spec/services/protocol_importers/search_protocols_service_spec.rb b/spec/services/protocol_importers/search_protocols_service_spec.rb index bc9f0e1cf..9e85cd6dc 100644 --- a/spec/services/protocol_importers/search_protocols_service_spec.rb +++ b/spec/services/protocol_importers/search_protocols_service_spec.rb @@ -32,6 +32,40 @@ describe ProtocolImporters::SearchProtocolsService do end end + context 'when raise api client error' do + it 'return network errors' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_list) + .and_raise(SocketError)) + + expect(service_call.errors).to have_key(:socketerror) + end + + it 'return api errors' do + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_list) + .and_raise(ProtocolImporters::ProtocolsIO::V3::ApiErrors::MissingOrEmptyParametersError.new('1', 'Missing Or Empty Parameters Error'))) + + expect(service_call.errors).to have_key(:"protocolimporters::protocolsio::v3::apierrors::missingoremptyparameterserror") + end + end + + context 'when normalize protocol fails' do + it 'return normalizer errors' do + client_data = double('api_response') + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ApiClient) + .to(receive(:protocol_list) + .and_return(client_data)) + + allow_any_instance_of(ProtocolImporters::ProtocolsIO::V3::ProtocolNormalizer) + .to(receive(:normalize_list).with(client_data) + .and_raise(ProtocolImporters::ProtocolsIO::V3::NormalizerError.new('nil_items', 'Nil Items'))) + + expect(service_call.errors).to have_key(:"protocolimporters::protocolsio::v3::normalizererror") + end + end + context 'when have valid attributes' do before do client_data = double('api_response') From 2a579f2240957d54296e07ddbaebd510c41e5fe6 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Mon, 17 Jun 2019 17:09:31 +0200 Subject: [PATCH 31/73] Add Warnings, Before start and Guidelinese to protocol description --- .../protocols_io/v3/protocol_normalizer.rb | 21 ++++++++++++------- .../templates/protocol_description.html.erb | 8 +++++-- .../normalized_single_protocol.json | 3 ++- .../protocol_description_builder_spec.rb | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb index a517e3bcc..4fd38f616 100644 --- a/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb +++ b/app/utilities/protocol_importers/protocols_io/v3/protocol_normalizer.rb @@ -18,11 +18,16 @@ module ProtocolImporters name: protocol_hash[:title], description: { body: protocol_hash[:description], - image: protocol_hash[:image][:source] + image: protocol_hash[:image][:source], + extra_content: [] }, authors: protocol_hash[:authors].map { |e| e[:name] }.join(', ') } + { before_start: 'Before start', guidelines: 'Guidelines', warning: 'Warnings' }.each do |k, v| + normalized_data[:description][:extra_content] << { title: v, body: protocol_hash[k] } if protocol_hash[k] + end + normalized_data[:steps] = protocol_hash[:steps].map do |e| { source_id: e[:id], @@ -57,13 +62,13 @@ module ProtocolImporters normalized_data = {} normalized_data[:protocols] = protocols_hash.map do |e| { - "id": e[:id], - "title": e[:title], - "created_on": e[:created_on], - "authors": e[:authors].map { |a| a[:name] }.join(', '), - "nr_of_steps": e[:stats][:number_of_steps], - "nr_of_views": e[:stats][:number_of_views], - "uri": e[:uri] + id: e[:id], + title: e[:title], + created_on: e[:created_on], + authors: e[:authors].map { |a| a[:name] }.join(', '), + nr_of_steps: e[:stats][:number_of_steps], + nr_of_views: e[:stats][:number_of_views], + uri: e[:uri] } end normalized_data diff --git a/app/views/protocol_importers/templates/protocol_description.html.erb b/app/views/protocol_importers/templates/protocol_description.html.erb index 7f209c203..ee083920b 100644 --- a/app/views/protocol_importers/templates/protocol_description.html.erb +++ b/app/views/protocol_importers/templates/protocol_description.html.erb @@ -1,12 +1,16 @@ <% if @description[:body] %>

<%= strip_tags @description[:body] %>

+
<% end %> <% if @description[:image] %>
+
<% end %> <% @description[:extra_content]&.each do |i| %> - <%= strip_tags i[:title] %>:
- <%= strip_tags i[:body] %>
+ <% if i[:body].present? %> +
<%= strip_tags i[:title] %>:
+ <%= strip_tags i[:body] %>
+ <% end %> <% end %> diff --git a/spec/fixtures/files/protocol_importers/normalized_single_protocol.json b/spec/fixtures/files/protocol_importers/normalized_single_protocol.json index 8252450f6..9ade81ca8 100644 --- a/spec/fixtures/files/protocol_importers/normalized_single_protocol.json +++ b/spec/fixtures/files/protocol_importers/normalized_single_protocol.json @@ -9,7 +9,8 @@ "name": "CUT\u0026RUN: Targeted in situ genome-wide profiling with high efficiency for low cell numbers", "description": { "body": "\u003cdiv class = \"text-blocks\"\u003e\u003cdiv class = \"text-block\"\u003eCleavage Under Targets and Release Using Nuclease (CUT\u0026RUN) is an epigenomic profiling strategy in which antibody-targeted controlled cleavage by micrococcal nuclease releases specific protein-DNA complexes into the supernatant for paired-end DNA sequencing. As only the targeted fragments enter into solution, and the vast majority of DNA is left behind, CUT\u0026RUN has exceptionally low background levels. CUT\u0026RUN outperforms the most widely used Chromatin Immunoprecipitation (ChIP) protocols in resolution, signal-to-noise, and depth of sequencing required. In contrast to ChIP, CUT\u0026RUN is free of solubility and DNA accessibility artifacts and can be used to profile insoluble chromatin and to detect long-range 3D contacts without cross-linking. Here we present an improved CUT\u0026RUN protocol that does not require isolation of nuclei and provides high-quality data starting with only 100 cells for a histone modification and 1000 cells for a transcription factor. From cells to purified DNA CUT\u0026RUN requires less than a day at the lab bench.\u003c/div\u003e\u003cdiv class = \"text-block\"\u003eIn summary, CUT\u0026RUN has several advantages over ChIP-seq: (1) The method is performed in situ in non-crosslinked cells and does not require chromatin fragmentation or solubilization; (2) The intrinsically low background allows low sequence depth and identification of low signal genomic features invisible to ChIP; (3) The simple procedure can be completed within a day and is suitable for robotic automation; (4) The method can be used with low cell numbers compared to existing methodologies; (5) A simple spike-in strategy can be used for accurate quantitation of protein-DNA interactions. As such, CUT\u0026RUN represents an attractive replacement for ChIPseq, which is one of the most popular methods in biological research.\u003c/div\u003e\u003c/div\u003e", - "image": "https://s3.amazonaws.com/pr-journal/rzxfw26.png" + "image": "https://s3.amazonaws.com/pr-journal/rzxfw26.png", + "extra_content": [] }, "authors": "Peter J. Skene, Steven Henikoff", "steps": [ diff --git a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb index 9f8b0130b..e05b28352 100644 --- a/spec/utilities/protocol_importers/protocol_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/protocol_description_builder_spec.rb @@ -41,7 +41,7 @@ RSpec.describe ProtocolImporters::ProtocolDescriptionBuilder do context 'when have extra content' do it 'add extra fields as paragraphs' do - expect(described_class.generate(description_with_extra_content).scan('
').size).to be == 6 + expect(described_class.generate(description_with_extra_content).scan('
').size).to be == 10 end end end From 7e6937501689a60ba4faec2e785bdf55bcd41d7c Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Mon, 17 Jun 2019 19:05:32 +0200 Subject: [PATCH 32/73] Drop header column and row when extract tables --- .../protocol_importers/protocol_intermediate_object.rb | 2 +- app/utilities/protocol_importers/tables_builder.rb | 4 +++- .../protocol_importers/tables_builder_spec.rb | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/utilities/protocol_importers/protocol_intermediate_object.rb b/app/utilities/protocol_importers/protocol_intermediate_object.rb index a7e023829..c8e8469f6 100644 --- a/app/utilities/protocol_importers/protocol_intermediate_object.rb +++ b/app/utilities/protocol_importers/protocol_intermediate_object.rb @@ -30,7 +30,7 @@ module ProtocolImporters step = Step.new(step_attributes(s)) step.description = StepDescriptionBuilder.generate(s) step.assets << AttachmentsBuilder.generate(s) - step.tables << TablesBuilder.extract_tables_from_html_string(s[:description][:body]) + step.tables << TablesBuilder.extract_tables_from_html_string(s[:description][:body], true) step end end diff --git a/app/utilities/protocol_importers/tables_builder.rb b/app/utilities/protocol_importers/tables_builder.rb index 242b9a881..401dd289b 100644 --- a/app/utilities/protocol_importers/tables_builder.rb +++ b/app/utilities/protocol_importers/tables_builder.rb @@ -2,7 +2,7 @@ module ProtocolImporters class TablesBuilder - def self.extract_tables_from_html_string(description_string) + def self.extract_tables_from_html_string(description_string, remove_first_column_row = false) tables = [] doc = Nokogiri::HTML(description_string) @@ -17,7 +17,9 @@ module ProtocolImporters row.css('td').each_with_index do |cell, j| two_d_array[i][j] = cell.inner_html end + two_d_array[i].shift if remove_first_column_row end + two_d_array.shift if remove_first_column_row tables << Table.new(contents: { data: two_d_array }.to_json) end diff --git a/spec/utilities/protocol_importers/tables_builder_spec.rb b/spec/utilities/protocol_importers/tables_builder_spec.rb index 36aee139d..c205ecf12 100644 --- a/spec/utilities/protocol_importers/tables_builder_spec.rb +++ b/spec/utilities/protocol_importers/tables_builder_spec.rb @@ -5,6 +5,7 @@ require 'rails_helper' RSpec.describe ProtocolImporters::TablesBuilder do # rubocop:disable Metrics/LineLength let(:description_string) { '
12345678910
abcdefghaa
1112345111
1111111111
asdasdasaasasdsadsaasdas124521
123
' } + let(:description_string_with_headers) { '
123
Ad1d2d3
Bc1c2c3
' } # rubocop:enable Metrics/LineLength let(:extract_tables_from_string_result) { described_class.extract_tables_from_html_string(description_string) } @@ -30,5 +31,14 @@ RSpec.describe ProtocolImporters::TablesBuilder do it 'returns table with 10 columns' do expect(JSON.parse(first_table_in_result.contents)['data'].first.count).to be == 10 end + + context 'when droping headers' do + it 'returns table with 2 rows and 3 columns' do + table = described_class.extract_tables_from_html_string(description_string_with_headers, true).first + + expect(JSON.parse(table.contents)['data'].count).to be == 2 + expect(JSON.parse(table.contents)['data'].first.count).to be == 3 + end + end end end From bf6041e13d98581c194440ad6af141cb8242324a Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Tue, 18 Jun 2019 17:47:08 +0200 Subject: [PATCH 33/73] Update sanitization of html descriptions --- .../protocol_importers/protocol_intermediate_object.rb | 2 +- .../protocol_importers/step_description_builder.rb | 1 + app/utilities/protocol_importers/tables_builder.rb | 8 ++++++++ .../templates/protocol_description.html.erb | 4 ++-- .../templates/step_description.html.erb | 6 +++--- config/initializers/constants.rb | 2 ++ .../protocol_importers/description_with_body_html.json | 2 +- .../step_description_builder_spec.rb | 10 ++++++++-- .../protocol_importers/tables_builder_spec.rb | 7 +++++++ 9 files changed, 33 insertions(+), 9 deletions(-) diff --git a/app/utilities/protocol_importers/protocol_intermediate_object.rb b/app/utilities/protocol_importers/protocol_intermediate_object.rb index c8e8469f6..798420cea 100644 --- a/app/utilities/protocol_importers/protocol_intermediate_object.rb +++ b/app/utilities/protocol_importers/protocol_intermediate_object.rb @@ -28,9 +28,9 @@ module ProtocolImporters def build_steps @normalized_protocol_data[:steps].map do |s| step = Step.new(step_attributes(s)) - step.description = StepDescriptionBuilder.generate(s) step.assets << AttachmentsBuilder.generate(s) step.tables << TablesBuilder.extract_tables_from_html_string(s[:description][:body], true) + step.description = StepDescriptionBuilder.generate(s) step end end diff --git a/app/utilities/protocol_importers/step_description_builder.rb b/app/utilities/protocol_importers/step_description_builder.rb index d510c1372..830778d63 100644 --- a/app/utilities/protocol_importers/step_description_builder.rb +++ b/app/utilities/protocol_importers/step_description_builder.rb @@ -5,6 +5,7 @@ module ProtocolImporters def self.generate(step_json) return '' unless step_json[:description] + step_json[:description][:body] = TablesBuilder.remove_tables_from_html(step_json[:description][:body]) html_string = ApplicationController .renderer .render(template: 'protocol_importers/templates/step_description', diff --git a/app/utilities/protocol_importers/tables_builder.rb b/app/utilities/protocol_importers/tables_builder.rb index 401dd289b..0b3371160 100644 --- a/app/utilities/protocol_importers/tables_builder.rb +++ b/app/utilities/protocol_importers/tables_builder.rb @@ -25,5 +25,13 @@ module ProtocolImporters end tables end + + def self.remove_tables_from_html(description_string) + doc = Nokogiri::HTML(description_string) + doc.search('table').each do |t| + t.swap('

There was a table here, it was moved to tables section.

') + end + doc.css('body').first.inner_html + end end end diff --git a/app/views/protocol_importers/templates/protocol_description.html.erb b/app/views/protocol_importers/templates/protocol_description.html.erb index ee083920b..4c7e89cf7 100644 --- a/app/views/protocol_importers/templates/protocol_description.html.erb +++ b/app/views/protocol_importers/templates/protocol_description.html.erb @@ -1,5 +1,5 @@ <% if @description[:body] %> -

<%= strip_tags @description[:body] %>

+

<%= sanitize(@description[:body], tags: Constants::PROTOCOLS_DESC_TAGS) %>


<% end %> <% if @description[:image] %> @@ -11,6 +11,6 @@ <% @description[:extra_content]&.each do |i| %> <% if i[:body].present? %>
<%= strip_tags i[:title] %>:
- <%= strip_tags i[:body] %>
+ <%= sanitize(i[:body], tags: Constants::PROTOCOLS_DESC_TAGS) %>
<% end %> <% end %> diff --git a/app/views/protocol_importers/templates/step_description.html.erb b/app/views/protocol_importers/templates/step_description.html.erb index 133859fb1..679fec4ac 100644 --- a/app/views/protocol_importers/templates/step_description.html.erb +++ b/app/views/protocol_importers/templates/step_description.html.erb @@ -1,16 +1,16 @@ <% if @step_description[:body] %> -

<%= strip_tags @step_description[:body] %>

+

<%= sanitize(@step_description[:body], tags: Constants::PROTOCOLS_DESC_TAGS) %>

<% end %> <% @step_description[:components]&.each do |component| %> <% sanitized_component = component.except('type') %> - <% sanitized_component[:body] = strip_tags(component[:body]) if component[:body] %> + <% sanitized_component[:body] = sanitize(component[:body], tags: Constants::PROTOCOLS_DESC_TAGS) if component[:body] %> <%= render partial: "protocol_importers/templates/#{component[:type]}", locals: { item: sanitized_component } %> <% end %> <% @step_description[:extra_content]&.each do |i| %> <%= strip_tags i[:title] %>:
- <%= strip_tags i[:body] %>
+ <%= sanitize(i[:body], tags: Constants::PROTOCOLS_DESC_TAGS) %>
<% end %> diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index 71f91152a..12816c31a 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -221,6 +221,8 @@ class Constants source_id: 'protocolsio/v3' }.freeze + PROTOCOLS_DESC_TAGS = %w(a img i br).freeze + #============================================================================= # Other #============================================================================= diff --git a/spec/fixtures/files/protocol_importers/description_with_body_html.json b/spec/fixtures/files/protocol_importers/description_with_body_html.json index 4a91e3c44..7ae1f5e26 100644 --- a/spec/fixtures/files/protocol_importers/description_with_body_html.json +++ b/spec/fixtures/files/protocol_importers/description_with_body_html.json @@ -1,5 +1,5 @@ { "description": { - "body": "
Text only
" + "body": "
Text only
Link tukaj WTF
" } } diff --git a/spec/utilities/protocol_importers/step_description_builder_spec.rb b/spec/utilities/protocol_importers/step_description_builder_spec.rb index 57037ebcc..1621dcf2c 100644 --- a/spec/utilities/protocol_importers/step_description_builder_spec.rb +++ b/spec/utilities/protocol_importers/step_description_builder_spec.rb @@ -37,7 +37,7 @@ RSpec.describe ProtocolImporters::StepDescriptionBuilder do context 'when have only description body' do it 'includes paragraph description' do - expect(described_class.generate(description_only)).to include('

original desc

') + expect(described_class.generate(description_only)).to include('

original desc') end end @@ -47,7 +47,7 @@ RSpec.describe ProtocolImporters::StepDescriptionBuilder do end it 'strips HTML tags from body values for component' do - expect(described_class.generate(description_with_components).scan('alert').size).to be == 0 + expect(described_class.generate(description_with_components).scan('