Merge remote-tracking branch 'origin/topic/stock_search'
[sgn.git] / lib / SGN / Controller / AJAX / Stock.pm
blobd8558d615ddd06d20b64fe068e0883bf7d6e8825
2 =head1 NAME
4 SGN::Controller::AJAX::Stock - a REST controller class to provide the
5 backend for objects linked with stocks
7 =head1 DESCRIPTION
9 Add new stock properties, stock dbxrefs and so on.
11 =head1 AUTHOR
13 Lukas Mueller <lam87@cornell.edu>
14 Naama Menda <nm249@cornell.edu>
17 =cut
19 package SGN::Controller::AJAX::Stock;
21 use Moose;
23 use List::MoreUtils qw /any /;
24 use Try::Tiny;
25 use CXGN::Phenome::Schema;
26 use CXGN::Phenome::Allele;
27 use CXGN::Page::FormattingHelpers qw/ columnar_table_html info_table_html /;
28 use Scalar::Util qw(looks_like_number);
30 BEGIN { extends 'Catalyst::Controller::REST' }
32 __PACKAGE__->config(
33 default => 'application/json',
34 stash_key => 'rest',
35 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
39 =head2 add_stockprop
42 L<Catalyst::Action::REST> action.
44 Stores a new stockprop in the database
46 =cut
48 sub stockprop : Local : ActionClass('REST') { }
50 sub stockprop_POST {
51 my ( $self, $c ) = @_;
52 my $response;
53 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
54 if ( any { $_ eq 'curator' || $_ eq 'submitter' || $_ eq 'sequencer' } $c->user->roles() ) {
55 my $req = $c->req;
57 my $stock_id = $c->req->param('stock_id');
58 my $propvalue = $c->req->param('propvalue');
59 my $type_id = $c->req->param('type_id');
60 my ($existing_prop) = $schema->resultset("Stock::Stockprop")->search( {
61 stock_id => $stock_id,
62 type_id => $type_id,
63 value => $propvalue, } );
64 if ($existing_prop) { $response = { error=> 'type_id/propvalue '.$type_id." ".$propvalue." already associated" } ;
65 }else {
67 my $prop_rs = $schema->resultset("Stock::Stockprop")->search( {
68 stock_id => $stock_id,
69 type_id => $type_id, } );
70 my $rank = $prop_rs ? $prop_rs->get_column('rank')->max : -1 ;
71 $rank++;
73 try {
74 $schema->resultset("Stock::Stockprop")->find_or_create( {
75 stock_id => $stock_id,
76 type_id => $type_id,
77 value => $propvalue,
78 rank => $rank, } );
79 $response = { message => "stock_id $stock_id and type_id $type_id associated with value $propvalue", }
80 } catch {
81 $response = { error => "Failed: $_" }
84 } else { $c->stash->{rest} = { error => 'user does not have a curator/sequencer/submitter account' };
89 sub stockprop_GET {
90 my ($self, $c) = @_;
91 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
92 my $stock_id = $c->req->param('stock_id');
93 my $stock = $c->stash->{stock};
94 my $type_id; ###
95 my $prop_rs = $stock->stockprops(
96 { type_id => $type_id, } );
97 # print the prop name and value#
98 $c->stash->{rest} = ['sucess'];
101 sub associate_locus:Path('/ajax/stock/associate_locus') :ActionClass('REST') {}
103 sub associate_locus_POST :Args(0) {
104 my ($self, $c) = @_;
105 $c->stash->{rest} = { error => "Nothing here, it's a POST.." } ;
108 sub associate_locus_GET :Args(0) {
109 my ( $self, $c ) = @_;
110 my $stock_id = $c->req->param('object_id');
111 ##my $allele_id = $c->req->param('allele_id');
112 #Phytoene synthase 1 (psy1) Allele: 1
113 #phytoene synthase 1 (psy1)
114 my $locus_input = $c->req->param('loci');
116 my ($locus_data, $allele_symbol) = split (/ Allele: / ,$locus_input);
117 my $is_default = $allele_symbol ? 'f' : 't' ;
118 $locus_data =~ m/(.*)\s\((.*)\)/ ;
119 my $locus_name = $1;
120 my $locus_symbol = $2;
122 my ($allele) = $c->dbic_schema('CXGN::Phenome::Schema')
123 ->resultset('Locus')
124 ->search({
125 locus_symbol => $locus_symbol,
127 ->search_related('alleles' , {
128 allele_symbol => $allele_symbol,
129 is_default => $is_default} );
130 if (!$allele) {
131 $c->stash->{rest} = { error => "no allele found for locus '$locus_data' (allele: '$allele_symbol')" };
132 return;
134 my $stock = $c->dbic_schema('Bio::Chado::Schema' , 'sgn_chado')
135 ->resultset("Stock::Stock")->find({stock_id => $stock_id } ) ;
136 my $allele_id = $allele->allele_id;
137 if (!$c->user) {
138 $c->stash->{rest} = { error => 'Must be logged in for associating loci! ' };
139 return;
141 if ( any { $_ eq 'curator' || $_ eq 'submitter' || $_ eq 'sequencer' } $c->user->roles() ) {
142 # if this fails, it will throw an acception and will (probably
143 # rightly) be counted as a server error
144 if ($stock && $allele_id) {
145 try {
146 $stock->create_stockprops(
147 { 'sgn allele_id' => $allele->allele_id },
148 { cv_name => 'local', allow_duplicate_values => 1, autocreate => 1 },
150 $c->stash->{rest} = ['success'];
151 # need to update the loci div!!
152 return;
153 } catch {
154 $c->stash->{rest} = { error => "Failed: $_" };
155 return;
157 } else {
158 $c->stash->{rest} = { error => 'need both valid stock_id and allele_id for adding the stockprop! ' };
160 } else {
161 $c->stash->{rest} = { error => 'No privileges for adding new loci. You must have an sgn submitter account. Please contact sgn-feedback@solgenomics.net for upgrading your user account. ' };
165 sub display_alleles : Chained('/stock/get_stock') :PathPart('alleles') : ActionClass('REST') { }
167 sub display_alleles_GET {
168 my ($self, $c) = @_;
170 my $stock = $c->stash->{stock};
171 my $allele_ids = $c->stash->{stockprops}->{'sgn allele_id'};
172 my $dbh = $c->dbc->dbh;
173 my @allele_data;
174 my $hashref;
175 foreach my $allele_id (@$allele_ids) {
176 my $allele = CXGN::Phenome::Allele->new($dbh, $allele_id);
177 my $phenotype = $allele->get_allele_phenotype();
178 my $allele_link = qq|<a href="/phenome/allele.pl?allele_id=$allele_id">$phenotype </a>|;
179 my $locus_id = $allele->get_locus_id;
180 my $locus_name = $allele->get_locus_name;
181 my $locus_link = qq|<a href="/phenome/locus_display.pl?locus_id=$locus_id">$locus_name </a>|;
182 push @allele_data,
185 $locus_link,
186 $allele->get_allele_name,
187 $allele_link
191 $hashref->{html} = @allele_data ?
192 columnar_table_html(
193 headings => [ "Locus name", "Allele symbol", "Phenotype" ],
194 data => \@allele_data,
195 ) : undef ;
196 $c->stash->{rest} = $hashref;
199 ##############
202 sub display_ontologies : Chained('/stock/get_stock') :PathPart('ontologies') : ActionClass('REST') { }
204 sub display_ontologies_GET {
205 my ($self, $c) = @_;
206 my $schema = $c->dbic_schema("Bio::Chado::Schema", 'sgn_chado');
207 my $stock = $c->stash->{stock};
208 my $sp_cvterms = $c->stash->{stock_cvterms}->{SP};
209 my $po_cvterms = $c->stash->{stock_cvterms}->{PO} ;
210 # should GO be here too?
211 my $go_cvterms = $c->stash->{stock_cvterms}->{GO};
212 my @stock_cvterms;
213 push @stock_cvterms, @$sp_cvterms if $sp_cvterms;
214 push @stock_cvterms, @$po_cvterms if $po_cvterms;
215 ################################
216 ###the following code should be re-formatted in JSON object,
217 #and the html generated in the javascript code
218 ### making this more reusable !
219 ###############################
220 my $hashref;
221 # need to check if the user is logged in, and has editing privileges
222 my $privileged;
223 if ($c->user) {
224 if ( $c->user->check_roles('curator') || $c->user->check_roles('submitter') || $c->user->check_roles('sequencer') ) { $privileged = 1; }
226 # the ontology term is a stock_cvterm
227 # the evidence details are in stock_cvtermprop (relationship, evidence_code,
228 # evidence_description, evidence_with, reference, obsolete
229 # and the metadata for sp_person_id, create_date, etc.)
230 my @obs_annot;
231 #keys= cvterms, values= hash of arrays
232 #(keys= ontology details, values= list of evidences)
233 my %ont_hash = () ;
234 #some cvterms to be used for the evidence codes
235 my $cvterm_rs = $schema->resultset("Cv::Cvterm");
236 my ($rel_cvterm) = $cvterm_rs->search( { name => 'relationship'} );
237 my ($evidence_cvterm) = $cvterm_rs->search( { name => 'evidence_code' } );
238 # go over the lists of Bio::Chado::Schema::Cv::Cvterm objects
239 # and build the annotation details
240 foreach (@stock_cvterms) {
241 my $cv_name = $_->cvterm->cv->name;
242 my $cvterm_id = $_->cvterm->cvterm_id;
243 my $cvterm_name = $_->cvterm->name;
244 my $db_name = $_->cvterm->dbxref->db->name;
245 my $accession = $_->cvterm->dbxref->accession;
246 my $db_accession = $accession;
247 $db_accession = $cvterm_id if $db_name eq 'SP';
248 my $url = $_->cvterm->dbxref->db->urlprefix . $_->cvterm->dbxref->db->url;
249 my $cvterm_link =
250 qq |<a href="/chado/cvterm.pl?cvterm_id=$cvterm_id" target="blank">$cvterm_name</a>|;
251 # the stock_cvtermprop objects have all the evidence and metadata for the annotation
252 my $props = $_->stock_cvtermprops;
253 my ($relationship_id) = $props->search( { type_id =>$rel_cvterm->cvterm_id} )->single ? $props->search( { type_id =>$rel_cvterm->cvterm_id} )->single->value : undef; # should be 1 relationship per annotation
254 my ($evidence_code_id) = $props->search( { type_id => $evidence_cvterm->cvterm_id })->single ? $props->search( { type_id => $evidence_cvterm->cvterm_id })->single->value : undef;
255 # should be 1 evidence_code
256 ############
257 my $evidence_desc_name;
258 my $rel_name = $relationship_id ? $cvterm_rs->find({ cvterm_id=>$relationship_id})->name : undef;
259 my $ev_name = $evidence_code_id ? $cvterm_rs->find({ cvterm_id=>$evidence_code_id})->name : undef;
260 #if the cvterm has an obsolete property (must have a true value
261 # since annotations can be obsolete and un-obsolete, it is possible
262 # to have an obsolete property with value = 0, meaning the annotation
263 # is not obsolete.
264 # build the unobsolete link
265 my ($obsolete_prop) = $props->search(
267 value => '1',
268 'type.name' => 'obsolete',
270 { join => 'type' } , );
271 if ($obsolete_prop) {
272 my $unobsolete = $privileged ? qq| <a href="/ajax/stock/unobsolete_annotation(| . $obsolete_prop->stock_cvtermprop_id . qq|)">[unobsolete]</a> | : undef;
273 ### NEED TO MAKE AN AJAX REQUEST #############
274 # onclick: $obsolete_prop->update( {value => '0' } );
276 # generate the list of obsolete annotations
277 push @obs_annot,
278 $rel_name . " "
279 . $cvterm_link . " ("
280 . $ev_name . ")"
281 . $unobsolete;
282 }else {
283 my $ontology_details = $rel_name
284 . qq| $cvterm_link ($db_name:<a href="$url$db_accession" target="blank"> $accession</a>)<br />|;
285 # build the obsolete link if the user has editing privileges
286 my $obsolete_link = $privileged ? qq| <a href="/ajax/stock/obsolete_annotation/| .$_->stock_cvterm_id . qq|/">[delete]</a> | : undef ;
287 my ($ev_with) = $props->search( {'type.name' => 'evidence_with'} , { join => 'type' } )->single;
288 my $ev_with_dbxref = $ev_with ? $schema->resultset("General::Dbxref")->find( { dbxref_id=> $ev_with->value } ) : undef;
289 my $ev_with_url = $ev_with_dbxref ? $ev_with_dbxref->urlprefix . $ev_with_dbxref->url . $ev_with_dbxref->accession : undef;
290 my $ev_with_acc = $ev_with_dbxref ? $ev_with_dbxref->accession : undef ;
291 # the reference is a stock_cvterm.pub_id
292 my ($reference) = $_->pub;
293 my $reference_dbxref = $reference ? $reference->pub_dbxrefs->first->dbxref : undef;
294 my $reference_url = $reference_dbxref ? $reference_dbxref->db->urlprefix . $reference_dbxref->db->url . $reference_dbxref->accession : undef;
295 my $reference_acc = $reference_dbxref ? $reference_dbxref->accession : undef;
296 my $display_ref = $reference_acc =~ /^\d/ ? 1 : 0;
297 # the submitter is a sp_person_id prop
298 my ($submitter) = $props->search( {'type.name' => 'sp_person_id'} , { join => 'type' } );
299 my $sp_person_id = $submitter ? $submitter->value : undef;
300 my $person= CXGN::People::Person->new($c->dbc->dbh, $sp_person_id);
301 my $submitter_info = qq| <a href="solpeople/personal_info.pl?sp_person_id=$sp_person_id">| . $person->get_first_name . " " . $person->get_last_name . "</a>" ;
302 my ($date) = $props->search( {'type.name' => 'create_date'} , { join => 'type' } )->first || undef ; # $props->search( {'type.name' => 'modified_date'} , { join => 'type' } ) ;
303 my $evidence_date = $date ? substr $date->value , 0, 10 : undef;
305 # add an empty row if there is more than 1 evidence code
306 my $ev_string;
307 $ev_string .= "<hr />" if $ont_hash{$cv_name}{$ontology_details};
308 no warnings 'uninitialized';
309 $ev_string .= $ev_name . "<br />";
310 $ev_string .= $evidence_desc_name . "<br />" if $evidence_desc_name;
311 $ev_string .= "<a href=\"$ev_with_url\">$ev_with_acc</a><br />" if $ev_with_acc;
312 $ev_string .="<a href=\"$reference_url\">$reference_acc</a><br />" if $display_ref;
313 $ev_string .= "$submitter_info $evidence_date $obsolete_link";
314 $ont_hash{$cv_name}{$ontology_details} .= $ev_string;
317 my $ontology_evidence;
319 #now we should have an %ont_hash with all the details we need for printing ...
320 #hash keys are the cv names ..
321 for my $cv_name ( sort keys %ont_hash ) {
322 my @evidence;
323 #and for each ontology annotation create an array ref of evidences
324 for my $ont_detail ( sort keys %{ $ont_hash{$cv_name} } ) {
325 push @evidence,
326 [ $ont_detail, $ont_hash{$cv_name}{$ont_detail} ];
328 my $ev = join "\n", map {
329 qq|<div class="term">$_->[0]</div>\n|
330 .qq|<div class="evidence">$_->[1]</div>\n|;
331 } @evidence;
332 $ontology_evidence .= info_table_html(
333 $cv_name => $ev,
334 __border => 0,
335 __tableattrs => 'width="100%"',
338 #display ontology annotation form
339 if ( @obs_annot && $privileged ) {
340 ##NEED TO RE-WRITE print _obsoleted $ontology_evidence .= print_obsoleted(@obs_annot);
342 $hashref->{html} = $ontology_evidence;
343 $c->stash->{rest} = $hashref;
346 ############
347 sub associate_ontology:Path('/ajax/stock/associate_ontology') :ActionClass('REST') {}
349 sub associate_ontology_POST :Args(0) {
350 my ($self, $c) = @_;
351 $c->stash->{rest} = { error => "Nothing here, it's a POST.." } ;
355 sub associate_ontology_GET :Args(0) {
356 my ( $self, $c ) = @_;
358 my $params = map { $_ => $c->req->param($_) } qw/
359 object_id ontology_input relationship evidence_code evidence_description
360 evidence_with reference
363 my $stock_id = $c->req->param('object_id');
364 my $ontology_input = $c->req->param('term_name');
365 my $relationship = $c->req->param('relationship'); # a cvterm_id
366 my $evidence_code = $c->req->param('evidence_code'); # a cvterm_id
367 my $evidence_description = $c->req->param('evidence_description'); # a cvterm_id
368 my $evidence_with = $c->req->param('evidence_with'); # a dbxref_id (type='evidence_with' value = 'dbxref_id'
369 my $logged_user = $c->user;
370 my $logged_person_id = $logged_user->get_object->get_sp_person_id if $logged_user;
372 my $reference = $c->req->param('reference'); # a pub_id
374 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
375 my $cvterm_rs = $schema->resultset('Cv::Cvterm');
376 my ($pub) = $reference ? $reference :
377 $schema->resultset('Pub::Pub')->search( { title=> 'curator' } )->first; # a pub for 'cuurator' should already be in the sgn database. can add here $curator_cvterm->create_with ... and then create the curator pub with type_id of $curator_cvterm
379 #solanaceae_phenotype--SP:000001--fruit size
380 my ($cv_name, $db_accession, $cvterm_name) = split /--/ , $ontology_input;
381 my ($db_name, $accession) = split ':' , $db_accession;
383 my ($cvterm) = $schema
384 ->resultset('General::Db')
385 ->search({ 'me.name' => $db_name, } )->search_related('dbxrefs' , { accession => $accession } )
386 ->search_related('cvterm')->first; # should be only 1 cvterm per dbxref
387 if (!$cvterm) {
388 $c->stash->{rest} = { error => "no ontology term found for term $db_name : $accession" };
389 return;
391 my ($stock) = $c->stash->{stock} || $schema->resultset("Stock::Stock")->find( { stock_id => $stock_id } );
393 my $cvterm_id = $cvterm->cvterm_id;
394 if (!$c->user) {
395 $c->stash->{rest} = { error => 'Must be logged in for associating ontology terms! ' };
396 return;
398 if ( any { $_ eq 'curator' || $_ eq 'submitter' || $_ eq 'sequencer' } $c->user->roles() ) {
399 # if this fails, it will throw an acception and will (probably
400 # rightly) be counted as a server error
401 #########################################################
402 if ($stock && $cvterm_id) {
403 try {
404 #check if the stock_cvterm exists
405 my $s_cvterm_rs = $stock->search_related(
406 'stock_cvterms', { cvterm_id => $cvterm_id, pub_id => $pub->pub_id } );
407 # if it exists , we need to increment the rank
408 my $rank = 0;
409 if ($s_cvterm_rs->first) {
410 $rank = $s_cvterm_rs->get_column('rank')->max + 1;
411 # now check if the evidence codes already exists
412 my ($rel_prop, $ev_prop, $desc_prop, $with_prop);
413 my $eprops = $s_cvterm_rs->search_related('stock_cvtermprops');
414 $rel_prop = $eprops->search( {
415 type_id => $cvterm_rs->search( { name => 'relationship'})->single->cvterm_id,
416 value => $relationship })->first;
418 $ev_prop = $eprops->search( {
419 type_id => $cvterm_rs->search( { name => 'evidence_code'})->single->cvterm_id,
420 value => $evidence_code })->first;
422 $desc_prop = $eprops->search( {
423 type_id => $cvterm_rs->search( { name => 'evidence description'})->single->cvterm_id,
424 value => $evidence_description })->first if $evidence_description;
426 $with_prop = $eprops->search( {
427 type_id => $cvterm_rs->search( { name => 'evidence_with'})->single->cvterm_id,
428 value => $evidence_with })->first if $evidence_with;
430 # return error if annotation + evidence exist
431 if ($rel_prop && $ev_prop) {
432 $c->stash->{rest} = { error => "Annotation exists with these evidence codes! " };
433 return;
436 # now store a new stock_cvterm
437 my $s_cvterm = $stock->create_related('stock_cvterms', {
438 cvterm_id => $cvterm_id,
439 pub_id => $pub->pub_id,
440 rank => $rank, } );
441 #########
442 $s_cvterm->create_stock_cvtermprops(
443 { 'relationship' => $relationship } , { db_name => 'OBO_REL', cv_name =>'relationship' } ) if looks_like_number($relationship);
444 $s_cvterm->create_stock_cvtermprops(
445 { 'evidence_code' => $evidence_code } , { db_name => 'ECO', cv_name =>'evidence_code' } ) if looks_like_number($evidence_code);
446 $s_cvterm->create_stock_cvtermprops(
447 { 'evidence_description' => $evidence_description } , { cv_name =>'null', autocreate => 1 } ) if looks_like_number($evidence_description);
448 $s_cvterm->create_stock_cvtermprops(
449 { 'evidence_with' => $evidence_with } , { cv_name =>'local' , autocreate=>1} ) if looks_like_number($evidence_with);
450 # store the person loading the annotation
451 $s_cvterm->create_stock_cvtermprops(
452 { 'sp_person_id' => $logged_person_id } , { cv_name =>'local' , autocreate=>1} );
453 #store today's date
454 my $val = "now()";
455 $s_cvterm->create_stock_cvtermprops(
456 { 'create_date' => \$val } , { cv_name =>'local' , autocreate=>1, allow_duplicate_values => 1} );
458 $c->stash->{rest} = ['success'];
459 return;
460 } catch {
461 $c->stash->{rest} = { error => "Failed: $_" };
462 # send an email to sgn bugs
463 $c->stash->{email} = {
464 to => 'sgn-bugs@sgn.cornell.edu',
465 from => 'sgn-bugs@sgn.cornell.edu',
466 subject => 'Associate ontology failed! Stock_id = $stock_id',
467 body => '$_',
469 $c->forward( $c->view('Email') );
470 return;
472 # if you reached here this means associate_ontology worked. Now send an email to sgn-db-curation
473 $c->stash->{email} = {
474 to => 'sgn-db-curation@sgn.cornell.edu',
475 from => 'sgn-bugs@sgn.cornell.edu',
476 subject => 'New ontology term loaded. Stock $stock_id',
477 body => "User " . $logged_user->get_object->get_first_name . " " . $logged_user->get_object->get_last_name . "has stored a new ontology term for stock $stock_id http://solgenomics.net/stock/$stock_id/view",
479 $c->forward( $c->view('Email') );
481 } else {
482 $c->stash->{rest} = { error => 'need both valid stock_id and cvterm_id for adding an ontology term to this stock! ' };
484 } else {
485 $c->stash->{rest} = { error => 'No privileges for adding new ontology terms. You must have an sgn submitter account. Please contact sgn-feedback@solgenomics.net for upgrading your user account. ' };
489 sub references : Chained('/stock/get_stock') :PathPart('references') : ActionClass('REST') { }
492 sub references_GET :Args(0) {
493 my ($self, $c) = @_;
494 my $stock = $c->stash->{stock};
495 # get a list of references
496 my $q = "SELECT dbxref.dbxref_id, pub.pub_id, accession,title
497 FROM public.stock_pub
498 JOIN public.pub USING (pub_id)
499 JOIN public.pub_dbxref USING (pub_id)
500 JOIN public.dbxref USING (dbxref_id)
501 WHERE stock_id= ?";
502 my $sth = $c->dbc->dbh->prepare($q);
503 $sth->execute($stock->get_stock_id);
504 my $response_hash={};
505 while (my ($dbxref_id, $pub_id, $accession, $title) = $sth->fetchrow_array) {
506 $response_hash->{$pub_id} = $accession . ": " . $title;
508 $c->stash->{rest} = $response_hash;
513 sub obsolete_annotation :Path('ajax/stock/obsolete_annotation') : ActionClass('REST') { }
516 sub obsolete_annotation_POST :Args(1) {
517 my ($self, $c, $stock_cvtermprop_id) = @_;
518 my $stock = $c->stash->{stock};
519 my ($prop) = $stock->stock_cvterms->find_related->('stock_cvtermprops', { stock_cvtermprop_id => $stock_cvtermprop_id } );
521 my $response ={} ;
522 if ($prop && $c->user ) {
523 $prop->update( { value => '1' } ) ;
524 $c->stash->{rest} = ['success'];
525 return;
526 } else { $response->{error} = 'stock_cvtermprop $stock_cvtermprop_id does not exists! ';
528 $c->stash->{rest} = $response;
531 sub unobsolete_annotation :Path('ajax/stock/unobsolete_annotation') : ActionClass('REST') { }
533 sub unobsolete_annotation_POST :Args(1) {
534 my ($self, $c, $stock_cvtermprop_id) = @_;
535 my $stock = $c->stash->{stock};
536 my ($prop) = $stock->stock_cvterms->find_related->('stock_cvtermprops', { stock_cvtermprop_id => $stock_cvtermprop_id } );
538 my $response ={} ;
539 if ($prop) {
540 $prop->update( { value => '0' } ) ;
541 $c->stash->{rest} = ['success'];
542 return;
543 } else { $response->{error} = 'stock_cvtermprop $stock_cvtermprop_id does not exists! '; }
544 $c->stash->{rest} = $response;
547 =head2 autocomplete
549 Public Path: /ajax/stock/trait_autocomplete
551 Autocomplete a trait name. Takes a single GET param,
552 C<term>, responds with a JSON array of completions for that term.
553 Finds only traits that exist in nd_experiment_phenotype
555 =cut
557 sub trait_autocomplete : Local : ActionClass('REST') { }
559 sub trait_autocomplete_GET :Args(0) {
560 my ( $self, $c ) = @_;
562 my $term = $c->req->param('term');
563 # trim and regularize whitespace
564 $term =~ s/(^\s+|\s+)$//g;
565 $term =~ s/\s+/ /g;
566 my @response_list;
567 my $q = "select distinct cvterm.name from stock join nd_experiment_stock using (stock_id) join nd_experiment_phenotype using (nd_experiment_id) join phenotype using (phenotype_id) join cvterm on cvterm_id = phenotype.observable_id WHERE cvterm.name ilike ?";
568 my $sth = $c->dbc->dbh->prepare($q);
569 $sth->execute( '%'.$term.'%');
570 while (my ($term_name) = $sth->fetchrow_array ) {
571 push @response_list, $term_name;
573 $c->{stash}->{rest} = \@response_list;