3 SGN::Controller::AJAX::VectorConstruct - a REST controller class for Vector Constructs.
7 Synchronizes vector constructs into the database from the ETHZ CASS database.
11 Nicolas Morales <nm529@cornell.edu>
15 package SGN
::Controller
::AJAX
::VectorConstruct
;
18 use JSON
-support_by_pp
;
19 use List
::MoreUtils qw
/any /;
22 use SGN
::Model
::Cvterm
;
23 use CXGN
::Stock
::Vector
;
24 use CXGN
::Stock
::Vector
::ParseUpload
;
27 use JSON
::XS qw
| decode_json
|;
31 BEGIN { extends
'Catalyst::Controller::REST' }
34 default => 'application/json',
36 map => { 'application/json' => 'JSON' },
39 sub sync_cass_constructs
: Path
('/ajax/cass_vector_construct/sync') Args
(0) ActionClass
('REST') { }
41 sub sync_cass_constructs_POST
{
45 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
46 my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id);
48 my $construct_names = decode_json
($c->req->param("data"));
49 my %construct_hash = %$construct_names;
50 my $constructs = $construct_hash{construct
};
51 my @construct_array = @
$constructs;
53 my $stock_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'vector_construct', 'stock_type')->cvterm_id();
55 my $create_db = $schema->resultset("General::Db")->find_or_create({
57 description
=> 'Internal ETHZ CASS DB',
59 url
=> 'https://cass.pb.ethz.ch'
62 foreach (@construct_array) {
63 #print STDERR $_->{construct};
64 #print STDERR $_->{construct_id};
65 #print STDERR $_->{level};
67 my $create_stock = $schema->resultset("Stock::Stock")->find_or_create({
68 uniquename
=> $_->{construct
},
69 name
=> $_->{construct
},
70 type_id
=> $stock_type_id,
73 my $create_dbxref = $schema->resultset("General::Dbxref")->find_or_create({
74 db_id
=> $create_db->db_id(),
75 accession
=> $_->{construct_id
},
76 version
=> 'vector_construct',
77 description
=> 'ETHZ_CASS vector_construct id'
80 my $create_stock_dbxref = $schema->resultset("Stock::StockDbxref")->find_or_create({
81 stock_id
=> $create_stock->stock_id(),
82 dbxref_id
=> $create_dbxref->dbxref_id()
86 #print STDERR Dumper $constructs;
87 #print STDERR $status;
89 $c->stash->{rest
} = {response
=>$status};
92 sub create_vector_construct
: Path
('/ajax/create_vector_construct') Args
(0) ActionClass
('REST') { }
94 sub create_vector_construct_POST
{
100 my $user_id = $c->user ?
$c->user->get_object()->get_sp_person_id():undef;
103 $status = sprintf('You must be logged in to add a vector!');
104 $c->stash->{rest
} = {error
=>$status};
108 my $schema = $c->dbic_schema("Bio::Chado::Schema",'sgn_chado', $user_id);
109 my $dbh = $schema->storage()->dbh();
110 my $person = CXGN
::People
::Person
->new($dbh, $user_id);
111 my $user_name = $person->get_username;
113 my $data = decode_json
( encode
("utf8", $c->req->param('data')));
116 my $vector = $_->{uniqueName
} || undef;
117 my $organism = $_->{species_name
} || undef;
118 push @
$vector_list, $vector;
119 push @
$organism_list, $organism;
122 #validate accessions/vector
123 my $validator = CXGN
::List
::Validate
->new();
124 my @absent_accessions = @
{$validator->validate($schema, 'accessions', $vector_list)->{'missing'}};
125 my %accessions_missing_hash = map { $_ => 1 } @absent_accessions;
126 my $existing_vectors = '';
128 my $validator2 = CXGN
::List
::Validate
->new();
129 my @absent_vectors = @
{$validator2->validate($schema, 'vector_constructs', $vector_list)->{'missing'}};
130 my %vectors_missing_hash = map { $_ => 1 } @absent_vectors;
132 foreach (@
$vector_list){
133 if (!exists($accessions_missing_hash{$_})){
134 $existing_vectors = $existing_vectors . $_ ."," ;
136 if (!exists($vectors_missing_hash{$_})){
137 $existing_vectors = $existing_vectors . $_ ."," ;
141 if (length($existing_vectors) >0){
142 $status = sprintf('Existing vectors or accessions in the database: %s', $existing_vectors);
143 $c->stash->{rest
} = {error
=>$status};
148 my $organism_search = CXGN
::BreedersToolbox
::OrganismFuzzySearch
->new({schema
=> $schema});
149 my $organism_result = $organism_search->get_matches($organism_list, '1');
151 my @allowed_organisms;
152 my $missing_organisms = '';
153 my $found = $organism_result->{found
};
156 push @allowed_organisms, $_->{unique_name
};
158 my %allowed_organisms = map {$_=>1} @allowed_organisms;
160 foreach (@
$organism_list){
161 if (!exists($allowed_organisms{$_})){
162 $missing_organisms = $missing_organisms . $_ . ",";
165 if (length($missing_organisms) >0){
166 $status = sprintf('Organisms were not found on the database: %s', $missing_organisms);
167 $c->stash->{rest
} = {error
=>$status};
171 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'vector_construct', 'stock_type')->cvterm_id();
174 my @added_fullinfo_stocks;
175 my $coderef_bcs = sub {
176 foreach my $params (@
$data){
177 my $species = $params->{species_name
} || undef;
178 my $uniquename = $params->{uniqueName
} || undef;
179 my $strain = $params->{Strain
} || undef;
180 my $backbone = $params->{Backbone
} || undef;
181 my $cloning_organism = $params->{CloningOrganism
} || undef;
182 my $inherent_marker = $params->{InherentMarker
} || undef;
183 my $selection_marker = $params->{SelectionMarker
} || undef;
184 my $cassette_name = $params->{CassetteName
} || undef;
185 my $vector_type = $params->{VectorType
} || undef;
186 my $gene = $params->{Gene
} || undef;
187 my $promotors = $params->{Promotors
} || undef;
188 my $terminators = $params->{Terminators
} || undef;
189 my $plant_antibiotic_resistant_marker = $params->{PlantAntibioticResistantMarker
} || undef;
190 my $bacterial_resistant_marker = $params->{BacterialResistantMarker
} || undef;
192 if (exists($allowed_organisms{$species})){
193 my $stock = CXGN
::Stock
::Vector
->new({
195 check_name_exists
=>0,
196 type
=>'vector_construct',
198 sp_person_id
=> $user_id,
199 user_name
=> $user_name,
202 uniquename
=>$uniquename,
205 CloningOrganism
=>$cloning_organism,
206 InherentMarker
=>$inherent_marker,
207 SelectionMarker
=>$selection_marker,
208 CassetteName
=>$cassette_name,
209 VectorType
=>$vector_type,
211 Promotors
=>$promotors,
212 Terminators
=>$terminators,
213 PlantAntibioticResistantMarker
=>$plant_antibiotic_resistant_marker,
214 BacterialResistantMarker
=>$bacterial_resistant_marker
216 my $added_stock_id = $stock->store();
217 push @added_stocks, $added_stock_id;
218 push @added_fullinfo_stocks, [$added_stock_id, $uniquename];
225 my $transaction_error;
228 $schema->txn_do($coderef_bcs);
231 $transaction_error = $_;
234 if ($transaction_error){
235 $status = sprintf('There was an error storing vector %s', $transaction_error);
238 if (scalar(@added_stocks) > 0){
239 my $dbh = $c->dbc->dbh();
240 my $bs = CXGN
::BreederSearch
->new( { dbh
=>$dbh, dbname
=>$c->config->{dbname
}, } );
241 my $refresh = $bs->refresh_matviews($c->config->{dbhost
}, $c->config->{dbname
}, $c->config->{dbuser
}, $c->config->{dbpass
}, 'stockprop', 'concurrent', $c->config->{basepath
});
244 $c->stash->{rest
} = {
247 added
=> \
@added_fullinfo_stocks
251 sub verify_vectors_file
: Path
('/ajax/vectors/verify_vectors_file') : ActionClass
('REST') { }
252 sub verify_vectors_file_POST
: Args
(0) {
258 my $session_id = $c->req->param("sgn_session_id");
261 my $dbh = $c->dbc->dbh;
262 my @user_info = CXGN
::Login
->new($dbh)->query_from_cookie($session_id);
264 $c->stash->{rest
} = {error
=>'You must be logged in to upload this vector info!'};
267 $user_id = $user_info[0];
268 $user_role = $user_info[1];
269 my $p = CXGN
::People
::Person
->new($dbh, $user_id);
270 $user_name = $p->get_username;
273 $c->stash->{rest
} = {error
=>'You must be logged in to upload this vector info!'};
276 $user_id = $c->user()->get_object()->get_sp_person_id();
277 $user_name = $c->user()->get_object()->get_username();
278 $user_role = $c->user->get_object->get_user_type();
281 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado', $user_id);
282 my $upload = $c->req->upload('new_vectors_upload_file');
283 my $do_fuzzy_search = $user_role eq 'curator' && !$c->req->param('fuzzy_check_upload_vectors') ?
0 : 1;
284 my $autogenerate_uniquename = !$c->req->param('autogenerate_uniquename') ?
0 : 1;
286 if ($user_role ne 'curator' && !$do_fuzzy_search) {
287 $c->stash->{rest
} = {error
=>'Only a curator can add vectors without using the fuzzy search!'};
291 # These roles are required by CXGN::UploadFile
292 if ($user_role ne 'curator' && $user_role ne 'submitter' && $user_role ne 'sequencer' ) {
293 $c->stash->{rest
} = {error
=>'Only a curator, submitter or sequencer can upload a file'};
297 my $subdirectory = "vectors_spreadsheet_upload";
298 my $upload_original_name = $upload->filename();
299 my $upload_tempfile = $upload->tempname;
300 my $time = DateTime
->now();
301 my $timestamp = $time->ymd()."_".$time->hms();
303 ## Store uploaded temporary file in archive
304 my $uploader = CXGN
::UploadFile
->new({
305 tempfile
=> $upload_tempfile,
306 subdirectory
=> $subdirectory,
307 archive_path
=> $c->config->{archive_path
},
308 archive_filename
=> $upload_original_name,
309 timestamp
=> $timestamp,
311 user_role
=> $user_role
313 my $archived_filename_with_path = $uploader->archive();
314 my $md5 = $uploader->get_md5($archived_filename_with_path);
315 if (!$archived_filename_with_path) {
316 $c->stash->{rest
} = {error
=> "Could not save file $upload_original_name in archive",};
319 unlink $upload_tempfile;
321 my @editable_vector_props = split ',', $c->config->{editable_vector_props
};
322 my $parser = CXGN
::Stock
::Vector
::ParseUpload
->new(chado_schema
=> $schema, filename
=> $archived_filename_with_path, editable_stock_props
=>\
@editable_vector_props, do_fuzzy_search
=>$do_fuzzy_search, autogenerate_uniquename
=>$autogenerate_uniquename);
323 $parser->load_plugin('VectorsXLS');
324 my $parsed_data = $parser->parse();
327 my $return_error = '';
329 if (!$parser->has_parse_errors() ){
330 $c->stash->{rest
} = {error_string
=> "Could not get parsing errors"};
333 $parse_errors = $parser->get_parse_errors();
335 foreach my $error_string (@
{$parse_errors->{'error_messages'}}){
336 $return_error .= $error_string."<br>";
339 $c->stash->{rest
} = {error_string
=> $return_error, missing_species
=> $parse_errors->{'missing_species'}};
343 my $full_data = $parsed_data->{parsed_data
};
346 while (my ($k,$val) = each %$full_data){
347 push @vector_names, $val->{germplasmName
};
348 $full_vectors{$val->{germplasmName
}} = $val;
351 my $new_list_id = CXGN
::List
::create_list
($c->dbc->dbh, "VectorsIn".$upload_original_name.$timestamp, 'Autocreated when upload vectors from file '.$upload_original_name.$timestamp, $user_id);
352 my $list = CXGN
::List
->new( { dbh
=> $c->dbc->dbh, list_id
=> $new_list_id } );
354 $list->add_bulk(\
@vector_names);
355 $list->type('vector_construct');
359 list_id
=> $new_list_id,
360 full_data
=> \
%full_vectors,
361 absent
=> $parsed_data->{absent_vectors
},
362 fuzzy
=> $parsed_data->{fuzzy_vectors
},
363 found
=> $parsed_data->{found_vectors
},
364 absent_organisms
=> $parsed_data->{absent_organisms
},
365 fuzzy_organisms
=> $parsed_data->{fuzzy_organisms
},
366 found_organisms
=> $parsed_data->{found_organisms
}
369 if ($parsed_data->{error_string
}){
370 $return{error_string
} = $parsed_data->{error_string
};
374 $c->stash->{rest
} = \
%return;
378 sub verify_vectors_fuzzy_options
: Path
('/ajax/vector_list/fuzzy_options') : ActionClass
('REST') { }
380 sub verify_vectors_fuzzy_options_POST
: Args
(0) {
382 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
383 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado', $sp_person_id);
384 my $vector_list_id = $c->req->param('vector_list_id');
385 my $fuzzy_option_hash = decode_json
( encode
("utf8", $c->req->param('fuzzy_option_data')));
386 my $names_to_add = _parse_list_from_json
($c, $c->req->param('names_to_add'));
388 my $list = CXGN
::List
->new( { dbh
=> $c->dbc()->dbh(), list_id
=> $vector_list_id } );
390 my %names_to_add = map {$_ => 1} @
$names_to_add;
391 foreach my $form_name (keys %$fuzzy_option_hash){
392 my $item_name = $fuzzy_option_hash->{$form_name}->{'fuzzy_name'};
393 my $select_name = $fuzzy_option_hash->{$form_name}->{'fuzzy_select'};
394 my $fuzzy_option = $fuzzy_option_hash->{$form_name}->{'fuzzy_option'};
395 if ($fuzzy_option eq 'replace'){
396 $list->replace_by_name($item_name, $select_name);
397 delete $names_to_add{$item_name};
398 } elsif ($fuzzy_option eq 'keep'){
399 $names_to_add{$item_name} = 1;
400 } elsif ($fuzzy_option eq 'remove'){
401 $list->remove_by_name($item_name);
402 delete $names_to_add{$item_name};
403 } elsif ($fuzzy_option eq 'synonymize'){
404 my $stock_id = $schema->resultset('Stock::Stock')->find({uniquename
=>$select_name})->stock_id();
405 my $stock = CXGN
::Chado
::Stock
->new($schema, $stock_id);
406 $stock->add_synonym($item_name);
408 delete $names_to_add{$item_name};
412 my @names_to_add = sort keys %names_to_add;
415 names_to_add
=> \
@names_to_add
417 $c->stash->{rest
} = $rest;
421 sub get_new_vector_uniquename
: Path
('/ajax/get_new_vector_uniquename') : ActionClass
('REST') { }
423 sub get_new_vector_uniquename_GET
: Args
(0) {
425 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
427 my $stock_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'vector_construct', 'stock_type')->cvterm_id();
429 my $stocks = $schema->resultset("Stock::Stock")->search({
430 type_id
=> $stock_type_id,
435 while (my $r = $stocks->next()) {
436 $id = $r->uniquename;
437 if ($id =~ m/T[0-9]+/){
445 #Vector construct has letter T before autogenerated number.
446 $c->stash->{rest
} = [ "T". $max];
450 sub _parse_list_from_json
{
452 my $list_json = shift;
453 my $json = JSON
::XS
->new();
456 debug
($c, "LIST_JSON is utf8? ".utf8
::is_utf8
($list_json)." valid utf8? ".utf8
::valid
($list_json)."\n");
457 print STDERR
"JSON NOW: $list_json\n";
458 my $decoded_list = $json->decode($list_json);
460 my @array_of_list_items = ();
461 if (ref($decoded_list) eq "ARRAY" ) {
462 @array_of_list_items = @
{$decoded_list};
465 debug
($c, "Dont know what to do " );
468 return \
@array_of_list_items;