Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 1878 copy cell lines #1923

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 29 additions & 50 deletions app/api/chemotion/cell_line_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@ class CellLineAPI < Grape::API
include Grape::Kaminari
helpers ParamsHelpers
helpers ContainerHelpers
helpers CellLineApiParamsHelpers

rescue_from ActiveRecord::RecordNotFound do
error!('Ressource not found', 401)
end
resource :cell_lines do
desc 'return cell lines of a collection'
params do
optional :collection_id, type: Integer, desc: 'Collection id'
optional :sync_collection_id, type: Integer, desc: 'SyncCollectionsUser id'
optional :filter_created_at, type: Boolean, desc: 'filter by created at or updated at'
optional :from_date, type: Integer, desc: 'created_date from in ms'
optional :to_date, type: Integer, desc: 'created_date to in ms'
use :cell_line_get_params
end
paginate per_page: 5, offset: 0
before do
params[:per_page].to_i > 50 && (params[:per_page] = 50)
end

get do
scope = if params[:collection_id]
begin
Expand Down Expand Up @@ -82,29 +80,7 @@ class CellLineAPI < Grape::API

desc 'Create a new Cell line sample'
params do
optional :organism, type: String, desc: 'name of the donor organism of the cell'
optional :tissue, type: String, desc: 'tissue from which the cell originates'
requires :amount, type: Integer, desc: 'amount of cells'
requires :unit, type: String, desc: 'unit of cell amount'
requires :passage, type: Integer, desc: 'passage of cells'
optional :disease, type: String, desc: 'deasease of cells'
requires :material_names, type: String, desc: 'names of cell line e.g. name1;name2'
requires :collection_id, type: Integer, desc: 'Collection of the cell line sample'
optional :cell_type, type: String, desc: 'type of cells'
optional :biosafety_level, type: String, desc: 'biosafety_level of cells'
optional :growth_medium, type: String, desc: 'growth medium of cells'
optional :variant, type: String, desc: 'variant of cells'
optional :optimal_growth_temp, type: Float, desc: 'optimal_growth_temp of cells'
optional :cryo_pres_medium, type: String, desc: 'cryo preservation medium of cells'
optional :gender, type: String, desc: 'gender of donor organism'
optional :material_description, type: String, desc: 'description of cell line concept'
optional :contamination, type: String, desc: 'contamination of a cell line sample'
requires :source, type: String, desc: 'source of a cell line sample'
optional :name, type: String, desc: 'name of a cell line sample'
optional :mutation, type: String, desc: 'mutation of a cell line'
optional :description, type: String, desc: 'description of a cell line sample'
optional :short_label, type: String, desc: 'short label of a cell line sample'
requires :container, type: Hash, desc: 'root Container of element'
use :cell_line_creation_params
end
post do
error!('401 Unauthorized', 401) unless current_user.collections.find(params[:collection_id])
Expand All @@ -116,28 +92,7 @@ class CellLineAPI < Grape::API
end
desc 'Update a Cell line sample'
params do
requires :cell_line_sample_id, type: String, desc: 'id of the cell line to update'
optional :organism, type: String, desc: 'name of the donor organism of the cell'
optional :mutation, type: String, desc: 'mutation of a cell line'
optional :tissue, type: String, desc: 'tissue from which the cell originates'
requires :amount, type: Integer, desc: 'amount of cells'
requires :unit, type: String, desc: 'unit of amount of cells'
optional :passage, type: Integer, desc: 'passage of cells'
optional :disease, type: String, desc: 'deasease of cells'
optional :material_names, type: String, desc: 'names of cell line e.g. name1;name2'
optional :collection_id, type: Integer, desc: 'Collection of the cell line sample'
optional :cell_type, type: String, desc: 'type of cells'
optional :biosafety_level, type: String, desc: 'biosafety_level of cells'
optional :variant, type: String, desc: 'variant of cells'
optional :optimal_growth_temp, type: Float, desc: 'optimal_growth_temp of cells'
optional :cryo_pres_medium, type: String, desc: 'cryo preservation medium of cells'
optional :gender, type: String, desc: 'gender of donor organism'
optional :material_description, type: String, desc: 'description of cell line concept'
optional :contamination, type: String, desc: 'contamination of a cell line sample'
optional :source, type: String, desc: 'source of a cell line sample'
optional :name, type: String, desc: 'name of a cell line sample'
optional :description, type: String, desc: 'description of a cell line sample'
requires :container, type: Hash, desc: 'root Container of element'
use :cell_line_update_params
end
put do
use_case = Usecases::CellLines::Update.new(params, current_user)
Expand All @@ -146,12 +101,36 @@ class CellLineAPI < Grape::API
return present cell_line_sample, with: Entities::CellLineSampleEntity
end

desc 'Copy a cell line'
params do
requires :id, type: Integer, desc: 'id of cell line sample to copy'
requires :collection_id, type: Integer, desc: 'id of collection of copied cell line sample'
requires :container, type: Hash, desc: 'root container of element'
end
namespace :copy do
post do
cell_line_to_copy = @current_user.cellline_samples.where(id: [params[:id]]).reorder('id')

error!('401 Unauthorized', 401) unless ElementsPolicy.new(@current_user, cell_line_to_copy).update?

begin
use_case = Usecases::CellLines::Copy.new(cell_line_to_copy.first, @current_user, params[:collection_id])
copied_cell_line_sample = use_case.execute!
copied_cell_line_sample.container = update_datamodel(params[:container])
rescue StandardError => e
error!(e, 400)
end
return present copied_cell_line_sample, with: Entities::CellLineSampleEntity
end
end

resource :names do
desc 'Returns all accessable cell line material names and their id'
get 'all' do
return present CelllineMaterial.all, with: Entities::CellLineMaterialNameEntity
end
end

resource :material do
params do
requires :id, type: Integer, desc: 'id of cell line material to load'
Expand Down
64 changes: 64 additions & 0 deletions app/api/helpers/cell_line_api_params_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# frozen_string_literal: true

module CellLineApiParamsHelpers
extend Grape::API::Helpers

params :cell_line_get_params do
optional :collection_id, type: Integer, desc: 'Collection id'
optional :sync_collection_id, type: Integer, desc: 'SyncCollectionsUser id'
optional :filter_created_at, type: Boolean, desc: 'filter by created at or updated at'
optional :from_date, type: Integer, desc: 'created_date from in ms'
optional :to_date, type: Integer, desc: 'created_date to in ms'
end

params :cell_line_creation_params do
optional :organism, type: String, desc: 'name of the donor organism of the cell'
optional :tissue, type: String, desc: 'tissue from which the cell originates'
requires :amount, type: Integer, desc: 'amount of cells'
requires :unit, type: String, desc: 'unit of cell amount'
requires :passage, type: Integer, desc: 'passage of cells'
optional :disease, type: String, desc: 'deasease of cells'
requires :material_names, type: String, desc: 'names of cell line e.g. name1;name2'
requires :collection_id, type: Integer, desc: 'Collection of the cell line sample'
optional :cell_type, type: String, desc: 'type of cells'
optional :biosafety_level, type: String, desc: 'biosafety_level of cells'
optional :growth_medium, type: String, desc: 'growth medium of cells'
optional :variant, type: String, desc: 'variant of cells'
optional :optimal_growth_temp, type: Float, desc: 'optimal_growth_temp of cells'
optional :cryo_pres_medium, type: String, desc: 'cryo preservation medium of cells'
optional :gender, type: String, desc: 'gender of donor organism'
optional :material_description, type: String, desc: 'description of cell line concept'
optional :contamination, type: String, desc: 'contamination of a cell line sample'
requires :source, type: String, desc: 'source of a cell line sample'
optional :name, type: String, desc: 'name of a cell line sample'
optional :mutation, type: String, desc: 'mutation of a cell line'
optional :description, type: String, desc: 'description of a cell line sample'
optional :short_label, type: String, desc: 'short label of a cell line sample'
requires :container, type: Hash, desc: 'root Container of element'
end

params :cell_line_update_params do
requires :cell_line_sample_id, type: String, desc: 'id of the cell line to update'
optional :organism, type: String, desc: 'name of the donor organism of the cell'
optional :mutation, type: String, desc: 'mutation of a cell line'
optional :tissue, type: String, desc: 'tissue from which the cell originates'
requires :amount, type: Integer, desc: 'amount of cells'
requires :unit, type: String, desc: 'unit of amount of cells'
optional :passage, type: Integer, desc: 'passage of cells'
optional :disease, type: String, desc: 'deasease of cells'
optional :material_names, type: String, desc: 'names of cell line e.g. name1;name2'
optional :collection_id, type: Integer, desc: 'Collection of the cell line sample'
optional :cell_type, type: String, desc: 'type of cells'
optional :biosafety_level, type: String, desc: 'biosafety_level of cells'
optional :variant, type: String, desc: 'variant of cells'
optional :optimal_growth_temp, type: Float, desc: 'optimal_growth_temp of cells'
optional :cryo_pres_medium, type: String, desc: 'cryo preservation medium of cells'
optional :gender, type: String, desc: 'gender of donor organism'
optional :material_description, type: String, desc: 'description of cell line concept'
optional :contamination, type: String, desc: 'contamination of a cell line sample'
optional :source, type: String, desc: 'source of a cell line sample'
optional :name, type: String, desc: 'name of a cell line sample'
optional :description, type: String, desc: 'description of a cell line sample'
requires :container, type: Hash, desc: 'root Container of element'
end
end
2 changes: 2 additions & 0 deletions app/models/cellline_sample.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class CelllineSample < ApplicationRecord
belongs_to :cellline_material
belongs_to :creator, class_name: 'User', foreign_key: 'user_id'

has_many :sync_collections_users, through: :collections

scope :by_sample_name, lambda { |query, collection_id|
joins(:collections).where(collections: { id: collection_id })
.where('name ILIKE ?', "%#{sanitize_sql_like(query)}%")
Expand Down
18 changes: 17 additions & 1 deletion app/packs/src/components/contextActions/CreateButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export default class CreateButton extends React.Component {
return uiState.reaction.checkedIds.first();
}

getCellLineId(){
let uiState = UIStore.getState();
return uiState.cell_line.checkedIds.first();
}

isCopySampleDisabled() {
let sampleFilter = this.getSampleFilter();
return !sampleFilter.all && sampleFilter.included_ids.size == 0;
Expand Down Expand Up @@ -105,6 +110,17 @@ export default class CreateButton extends React.Component {
ElementActions.copyReactionFromId(reactionId);
}

isCopyCellLineDisabled() {
let cellLineId = this.getCellLineId();
return !cellLineId;
}

copyCellLine() {
let uiState = UIStore.getState();
let cellLineId = this.getCellLineId();
ElementActions.copyCellLineFromId(parseInt(cellLineId),uiState.currentCollection.id);
}

createWellplateFromSamples() {
let uiState = UIStore.getState();
let sampleFilter = this.filterParamsFromUIStateByElementType(uiState, "sample");
Expand Down Expand Up @@ -272,8 +288,8 @@ export default class CreateButton extends React.Component {
<MenuItem divider />
<MenuItem onSelect={() => this.copySample()} disabled={this.isCopySampleDisabled()}>Copy Sample</MenuItem>
<MenuItem onSelect={() => this.copyReaction()} disabled={this.isCopyReactionDisabled()}>Copy Reaction</MenuItem>
<MenuItem onSelect={() => this.copyCellLine()} disabled={this.isCopyCellLineDisabled()}>Copy Cell line</MenuItem>
</SplitButton>

)
}
}
Expand Down
21 changes: 21 additions & 0 deletions app/packs/src/fetchers/CellLinesFetcher.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CellLine from 'src/models/cellLine/CellLine';
import Container from 'src/models/Container';
import BaseFetcher from 'src/fetchers/BaseFetcher';
import AttachmentFetcher from 'src/fetchers/AttachmentFetcher';
import GenericElsFetcher from 'src/fetchers/GenericElsFetcher';
Expand Down Expand Up @@ -125,6 +126,26 @@ export default class CellLinesFetcher {
}).then((response) => response.json());
}

static copyCellLine(cellLineId, collectionId){
const params = {
id: cellLineId,
collection_id: collectionId,
container: Container.init()
}

return fetch('/api/v1/cell_lines/copy/', {
credentials: 'same-origin',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(params)
}).then((response) => response.json())
.then((json) => CellLine.createFromRestResponse(collectionId, json))

}

static update(cellLineItem) {
const params = extractApiParameter(cellLineItem);
const promise = CellLinesFetcher.uploadAttachments(cellLineItem)
Expand Down
11 changes: 11 additions & 0 deletions app/packs/src/stores/alt/actions/ElementActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,17 @@ class ElementActions {
return cellLineSample;
}

copyCellLineFromId(id,collectionId ) {
return (dispatch) => {
CellLinesFetcher.copyCellLine(id,collectionId)
.then((result) => {
dispatch(result);
}).catch((errorMessage) => {
console.log(errorMessage);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a response to the user that something went wrong

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an error that can occur normally or should it not actually fail? In the latter case, I think that logging is enough. Especially since all other operations in the ElementAction only log to the console in the case of an error. What do you think?

});
};
}

splitAsSubsamples(ui_state) {
return (dispatch) => {
SamplesFetcher.splitAsSubsamples(ui_state)
Expand Down
5 changes: 5 additions & 0 deletions app/packs/src/stores/alt/stores/ElementStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class ElementStore {
handleCopyReaction: ElementActions.copyReaction,
handleCopyResearchPlan: ElementActions.copyResearchPlan,
handleCopyElement: ElementActions.copyElement,
handleCopyCellLine: ElementActions.copyCellLineFromId,
handleOpenReactionDetails: ElementActions.openReactionDetails,

handleBulkCreateWellplatesFromSamples:
Expand Down Expand Up @@ -1008,6 +1009,10 @@ class ElementStore {
Aviator.navigate(`/collection/${result.colId}/${result.element.type}/copy`);
}

handleCopyCellLine(result){
Aviator.navigate(`/collection/${result.collectionId}/cell_line/${result.id}`);
}

handleOpenReactionDetails(reaction) {
this.changeCurrentElement(reaction);
this.handleRefreshElements('sample')
Expand Down
55 changes: 55 additions & 0 deletions app/usecases/cell_lines/copy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module Usecases
module CellLines
class Copy
def initialize(cell_line_sample_to_copy, current_user, collection_id)
@current_user = current_user
@cell_line_sample_to_copy = cell_line_sample_to_copy
@collection_id = collection_id
end

def execute!
@current_user.increment_counter('celllines') # rubocop: disable Rails/SkipsModelValidations
copied_cell_line_sample = copy_cellline_sample
create_collection_links(copied_cell_line_sample.id)
copied_cell_line_sample
end

private

def copy_cellline_sample
CelllineSample.create(
cellline_material: @cell_line_sample_to_copy.cellline_material,
creator: @current_user,
amount: @cell_line_sample_to_copy[:amount],
unit: @cell_line_sample_to_copy[:unit],
passage: @cell_line_sample_to_copy[:passage],
contamination: @cell_line_sample_to_copy[:contamination],
name: @cell_line_sample_to_copy[:name],
description: @cell_line_sample_to_copy[:description],
short_label: create_short_label,
)
end

def create_short_label
"#{@current_user.name_abbreviation}-C#{@current_user.counters['celllines']}"
end

def create_collection_links(id)
CollectionsCellline.create(
collection: Collection.find(@collection_id),
cellline_sample_id: id,
)
CollectionsCellline.create(
collection: all_collection_of_current_user,
cellline_sample_id: id,
)
end

def all_collection_of_current_user
Collection.get_all_collection_for_user(@current_user.id)
end
end
end
end
Loading
Loading