seedlot upload with accession synonyms. seedlot upload works to update existing seedlots
[sgn.git] / lib / SGN / Controller / AJAX / List.pm
blobf1056ad0c2a2bb92d452f228850ea7b5175ad095
2 package SGN::Controller::AJAX::List;
4 use Moose;
6 use List::MoreUtils qw | uniq |;
7 use Data::Dumper;
9 use CXGN::List;
10 use CXGN::List::Validate;
11 use CXGN::List::Transform;
12 use CXGN::List::FuzzySearch;
13 use CXGN::List::Desynonymize;
14 use CXGN::Cross;
15 use JSON;
17 BEGIN { extends 'Catalyst::Controller::REST'; }
19 __PACKAGE__->config(
20 default => 'application/json',
21 stash_key => 'rest',
22 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
25 sub get_list_action :Path('/list/get') Args(0) {
26 my $self = shift;
27 my $c = shift;
29 my $list_id = $c->req->param("list_id");
31 my $user_id = $self->get_user($c);
32 if (!$user_id) {
33 $c->stash->{rest} = { error => 'You must be logged in to use lists.', };
34 return;
37 my $list = $self->retrieve_list($c, $list_id);
39 $c->stash->{rest} = $list;
42 sub get_list_data_action :Path('/list/data') Args(0) {
43 my $self = shift;
44 my $c = shift;
46 my $list_id = $c->req->param("list_id");
48 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
49 my $public = $list->check_if_public();
50 if ($public == 0) {
51 my $error = $self->check_user($c, $list_id);
52 if ($error) {
53 $c->stash->{rest} = { error => $error };
54 return;
58 $list = $self->retrieve_list($c, $list_id);
60 my $metadata = $self->get_list_metadata($c, $list_id);
62 $c->stash->{rest} = {
63 list_id => $list_id,
64 type_id => $metadata->{type_id},
65 type_name => $metadata->{list_type},
66 elements => $list,
71 sub retrieve_contents :Path('/list/contents') Args(1) {
72 my $self = shift;
73 my $c = shift;
74 my $list_id = shift;
76 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id=>$list_id });
77 my $public = $list->check_if_public();
78 if ($public == 0) {
79 my $error = $self->check_user($c, $list_id);
80 if ($error) {
81 $c->stash->{rest} = { error => $error };
82 return;
86 my $elements = $list->elements();
87 $c->stash->{rest} = $elements;
90 sub get_list_metadata {
91 my $self = shift;
92 my $c = shift;
93 my $list_id = shift;
95 my $list = CXGN::List->new( { dbh=> $c->dbc->dbh(), list_id=>$list_id });
97 return { name => $list->name(),
98 description => $list->description(),
99 type_id => $list->type_id(),
100 list_type => $list->type(),
104 sub get_type_action :Path('/list/type') Args(1) {
105 my $self = shift;
106 my $c = shift;
107 my $list_id = shift;
109 my $data = $self->get_list_metadata($c, $list_id);
111 $c->stash->{rest} = { type_id => $data->{type_id},
112 list_type => $data->{list_type},
116 sub update_list_name_action :Path('/list/name/update') :Args(0) {
117 my $self = shift;
118 my $c = shift;
119 my $list_id = $c->req->param('list_id');
120 my $name = $c->req->param('name');
122 my $user_id = $self->get_user($c);
123 my $error = $self->check_user($c, $list_id);
125 if ($error) {
126 $c->stash->{rest} = { error => $error };
127 return;
130 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id=>$list_id });
132 $error = $list->name($name);
134 if ($error) {
135 $c->stash->{rest} = { error => $error };
136 return;
139 $c->stash->{rest} = { success => 1 };
142 sub set_type :Path('/list/type') Args(2) {
143 my $self = shift;
144 my $c = shift;
145 my $list_id = shift;
146 my $type = shift;
148 my $user_id = $self->get_user($c);
150 my $error = $self->check_user($c, $list_id);
151 if ($error) {
152 $c->stash->{rest} = { error => $error };
153 return;
156 my $list = CXGN::List->new( { dbh=> $c->dbc->dbh(), list_id => $list_id });
158 if ($list->owner() != $user_id) {
159 $c->stash->{rest} = { error => "Only the list owner can change the type of a list" };
160 return;
163 $error = $list->type($type);
165 if (!$error) {
166 $c->stash->{rest} = { error => "List type not found: ".$type };
167 return;
170 $c->stash->{rest} = { success => 1 };
173 sub new_list_action :Path('/list/new') Args(0) {
174 my $self = shift;
175 my $c = shift;
177 my $name = $c->req->param("name");
178 my $desc = $c->req->param("desc");
181 my $user_id = $self->get_user($c);
182 if (!$user_id) {
183 $c->stash->{rest} = { error => "You must be logged in to use lists", };
184 return;
187 my $new_list_id = 0;
188 eval {
189 $new_list_id = $self->new_list($c, $name, $desc, $user_id);
192 if ($@) {
193 $c->stash->{rest} = { error => "An error occurred, $@", };
194 return;
196 else {
197 $c->stash->{rest} = { list_id => $new_list_id };
201 sub all_types : Path('/list/alltypes') :Args(0) {
202 my $self = shift;
203 my $c = shift;
205 my $all_types = CXGN::List::all_types($c->dbc->dbh());
207 $c->stash->{rest} = $all_types;
210 sub download_list :Path('/list/download') Args(0) {
211 my $self = shift;
212 my $c = shift;
213 my $list_id = $c->req->param("list_id");
215 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
216 my $public = $list->check_if_public();
217 if ($public == 0) {
218 my $error = $self->check_user($c, $list_id);
219 if ($error) {
220 $c->res->content_type("text/plain");
221 $c->res->body($error);
222 return;
226 $list = $self->retrieve_list($c, $list_id);
228 $c->res->content_type("text/plain");
229 $c->res->body(join "\n", map { $_->[1] } @$list);
232 =head2 available_lists()
234 Usage:
235 Desc: returns the available lists. Optionally, a
236 parameter "list_type" can be provided that will limit the
237 lists to the provided type.
239 Ret:
240 Args:
241 Side Effects:
242 Example:
244 =cut
246 sub available_lists : Path('/list/available') Args(0) {
247 my $self = shift;
248 my $c = shift;
250 my $requested_type = $c->req->param("type");
252 my $user_id = $self->get_user($c);
253 if (!$user_id) {
254 $c->stash->{rest} = { error => "You must be logged in to use lists.", };
255 return;
258 my $lists = CXGN::List::available_lists($c->dbc->dbh(), $user_id, $requested_type);
260 $c->stash->{rest} = $lists;
263 sub available_public_lists : Path('/list/available_public') Args(0) {
264 my $self = shift;
265 my $c = shift;
267 my $requested_type = $c->req->param("type");
269 my $user_id = $self->get_user($c);
270 if (!$user_id) {
271 $c->stash->{rest} = { error => "You must be logged in to use lists.", };
272 return;
275 my $lists = CXGN::List::available_public_lists($c->dbc->dbh(), $requested_type);
277 $c->stash->{rest} = $lists;
280 sub add_item :Path('/list/item/add') Args(0) {
281 my $self = shift;
282 my $c = shift;
284 my $list_id = $c->req->param("list_id");
285 my $element = $c->req->param("element");
287 my $user_id = $self->get_user($c);
289 my $error = $self->check_user($c, $list_id);
290 if ($error) {
291 $c->stash->{rest} = { error => $error };
292 return;
295 $element =~ s/^\s*(.+?)\s*$/$1/;
297 if (!$element) {
298 $c->stash->{rest} = { error => "You must provide an element to add to the list" };
299 return;
302 if (!$list_id) {
303 $c->stash->{rest} = { error => "Please specify a list_id." };
304 return;
307 eval {
308 $self->insert_element($c, $list_id, $element);
310 if ($@) {
311 $c->stash->{rest} = { error => "An error occurred: $@" };
312 return;
314 else {
315 $c->stash->{rest} = [ "SUCCESS" ];
319 sub toggle_public_list : Path('/list/public/toggle') Args(0) {
320 my $self = shift;
321 my $c = shift;
322 my $list_id = $c->req->param("list_id");
324 my $error = $self->check_user($c, $list_id);
325 if ($error) {
326 $c->stash->{rest} = { error => $error };
327 return;
330 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
331 my ($public, $rows_affected) = $list->toggle_public();
332 if ($rows_affected == 1) {
333 $c->stash->{rest} = { r => $public };
334 } else {
335 die;
339 sub make_public_list : Path('/list/public/true') Args(0) {
340 my $self = shift;
341 my $c = shift;
342 my $list_id = $c->req->param("list_id");
344 my $error = $self->check_user($c, $list_id);
345 if ($error) {
346 $c->stash->{rest} = { error => $error };
347 return;
350 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
351 my ($rows_affected) = $list->make_public();
352 if ($rows_affected == 1) {
353 $c->stash->{rest} = { success=>1 };
354 } else {
355 die;
359 sub make_private_list : Path('/list/public/false') Args(0) {
360 my $self = shift;
361 my $c = shift;
362 my $list_id = $c->req->param("list_id");
364 my $error = $self->check_user($c, $list_id);
365 if ($error) {
366 $c->stash->{rest} = { error => $error };
367 return;
370 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
371 my ($rows_affected) = $list->make_private();
372 if ($rows_affected == 1) {
373 $c->stash->{rest} = { success=>1 };
374 } else {
375 die;
379 sub copy_public_list : Path('/list/public/copy') Args(0) {
380 my $self = shift;
381 my $c = shift;
382 my $list_id = $c->req->param("list_id");
384 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
385 my $public = $list->check_if_public();
386 my $user_id = $self->get_user($c);
387 if (!$user_id || $public == 0) {
388 $c->stash->{rest} = { error => 'You must be logged in to use lists and list must be public!' };
389 return;
392 my $copied = $list->copy_public($user_id);
393 if ($copied) {
394 $c->stash->{rest} = { success => 'true' };
395 } else {
396 die;
400 sub add_cross_progeny : Path('/list/add_cross_progeny') Args(0) {
401 my $self = shift;
402 my $c = shift;
403 my $cross_id_list = decode_json($c->req->param("cross_id_list"));
404 #print STDERR Dumper $cross_id_list;
405 my $list_id = $c->req->param("list_id");
407 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id => $list_id });
409 my %response;
410 $response{'count'} = 0;
411 foreach (@$cross_id_list) {
412 my $cross = CXGN::Cross->new({bcs_schema=>$c->dbic_schema("Bio::Chado::Schema"), cross_stock_id=>$_});
413 my ($maternal_parent, $paternal_parent, $progeny) = $cross->get_cross_relationships();
415 my @accession_names;
416 foreach (@$progeny) {
417 push @accession_names, $_->[0];
420 my $r = $list->add_bulk(\@accession_names);
421 if ($r->{error}) {
422 $c->stash->{rest} = { error => $r->{error}};
423 return;
425 if (scalar(@{$r->{duplicates}}) > 0){
426 $response{'duplicates'} = $r->{duplicates};
428 $response{'count'} += $r->{count};
430 #print STDERR Dumper \%response;
431 $c->stash->{rest} = { duplicates => $response{'duplicates'} };
432 $c->stash->{rest}->{success} = { count => $response{'count'} };
435 sub add_bulk : Path('/list/add/bulk') Args(0) {
436 my $self = shift;
437 my $c = shift;
438 my $list_id = $c->req->param("list_id");
439 my $elements = $c->req->param("elements");
441 my $user_id = $self->get_user($c);
442 my $error = $self->check_user($c, $list_id);
443 if ($error) {
444 $c->stash->{rest} = { error => $error };
445 return;
448 if (!$elements) {
449 $c->stash->{rest} = { error => "You must provide one or more elements to add to the list" };
450 return;
453 my @elements = split "\t", $elements;
455 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id => $list_id });
457 my @duplicates = ();
458 my $count = 0;
460 my $response = $list->add_bulk(\@elements);
461 #print STDERR Dumper $response;
463 if ($response->{error}) {
464 $c->stash->{rest} = { error => $response->{error}};
465 return;
467 if (scalar(@{$response->{duplicates}}) > 0){
468 $c->stash->{rest} = { duplicates => $response->{duplicates} };
471 $c->stash->{rest}->{success} = $response->{count};
474 sub insert_element : Private {
475 my $self = shift;
476 my $c = shift;
477 my $list_id = shift;
478 my $element = shift;
480 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id => $list_id });
482 $list->add_element($element);
485 sub delete_list_action :Path('/list/delete') Args(0) {
486 my $self = shift;
487 my $c = shift;
489 my $list_id = $c->req->param("list_id");
491 my $error = $self->check_user($c, $list_id);
492 if ($error) {
493 $c->stash->{rest} = { error => $error };
494 return;
497 $error = CXGN::List::delete_list($c->dbc->dbh(), $list_id);
499 if ($error) {
500 $c->stash->{rest} = { error => $error };
502 else {
503 $c->stash->{rest} = [ 1 ];
508 sub exists_list_action : Path('/list/exists') Args(0) {
509 my $self =shift;
510 my $c = shift;
511 my $name = $c->req->param("name");
513 my $user_id = $self->get_user($c);
514 if (!$user_id) {
515 $c->stash->{rest} = { error => 'You need to be logged in to use lists.' };
518 my $list_id = CXGN::List::exists_list($c->dbc->dbh(), $name, $user_id);
520 if ($list_id) {
521 $c->stash->{rest} = { list_id => $list_id };
523 else {
524 $c->stash->{rest} = { list_id => undef };
528 sub exists_item_action : Path('/list/exists_item') :Args(0) {
529 my $self =shift;
530 my $c = shift;
531 my $list_id = $c->req->param("list_id");
532 my $name = $c->req->param("name");
534 my $error = $self->check_user($c, $list_id);
535 if ($error) {
536 $c->stash->{rest} = { error => $error };
537 return;
540 my $user_id = $self->get_user($c);
542 if ($self->get_list_owner($c, $list_id) != $user_id) {
543 $c->stash->{rest} = { error => "You have insufficient privileges to manipulate this list.", };
544 return;
547 my $list_item_id = $self->exists_item($c, $list_id, $name);
549 if ($list_item_id) {
550 $c->stash->{rest} = { list_item_id => $list_item_id };
552 else {
553 $c->stash->{rest} = { list_item_id => 0 };
557 sub list_size : Path('/list/size') Args(0) {
558 my $self = shift;
559 my $c = shift;
560 my $list_id = $c->req->param("list_id");
562 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
563 my $count = $list->list_size();
565 $c->stash->{rest} = { count => $count };
568 sub validate : Path('/list/validate') Args(2) {
569 my $self = shift;
570 my $c = shift;
571 my $list_id = shift;
572 my $type = shift;
574 my $list = $self->retrieve_list($c, $list_id);
576 my @flat_list = map { $_->[1] } @$list;
578 my $lv = CXGN::List::Validate->new();
579 my $data = $lv->validate($c->dbic_schema("Bio::Chado::Schema"), $type, \@flat_list);
581 $c->stash->{rest} = $data;
584 sub fuzzysearch : Path('/list/fuzzysearch') Args(2) {
585 my $self = shift;
586 my $c = shift;
587 my $list_id = shift;
588 my $list_type = shift;
590 my $list = $self->retrieve_list($c, $list_id);
592 my @flat_list = map { $_->[1] } @$list;
594 my $f = CXGN::List::FuzzySearch->new();
595 my $data = $f->fuzzysearch($c->dbic_schema("Bio::Chado::Schema"), $list_type, \@flat_list);
597 $c->stash->{rest} = $data;
600 sub transform :Path('/list/transform/') Args(2) {
601 my $self = shift;
602 my $c = shift;
603 my $list_id = shift;
604 my $transform_name = shift;
606 my $t = CXGN::List::Transform->new();
608 my $data = $self->get_list_metadata($c, $list_id);
610 my $list_data = $self->retrieve_list($c, $list_id);
612 my @list_items = map { $_->[1] } @$list_data;
614 my $result = $t->transform($c->dbic_schema("Bio::Chado::Schema"), $transform_name, \@list_items);
616 if (exists($result->{missing}) && (scalar(@{$result->{missing}}) > 0)) {
617 $c->stash->{rest} = { error => "This lists contains elements that cannot be converted. Not converting list.", };
618 return;
621 $c->stash->{rest} = $result;
625 sub replace_elements :Path('/list/item/replace') Args(2) {
626 my $self = shift;
627 my $c = shift;
629 my $list_id = shift;
630 my $new_list = shift; # tab delimited new list elements
634 sub combine_lists : Path('/list/combine') Args(2) {
635 my $self = shift;
636 my $c = shift;
637 my $list1_id = shift;
638 my $list2_id = shift;
640 my $list1 = $self->get_list($c, $list1_id);
641 my $list2 = $self->get_list($c, $list2_id);
643 my $combined_list_id = $self->new_list(
645 $list1->{name}."_".$list2->{name},
646 $list1->{description}.", ".$list2->{description});
648 my @combined_elements = (@{$list1->{elements}}, @{$list2->{elements}});
650 my @unique_elements = uniq(@combined_elements);
652 foreach my $item (@unique_elements) {
653 $self->add_item($c, $combined_list_id, $item);
657 sub intersect_lists : Path('/list/intersect') Args(2) {
658 my $self = shift;
659 my $c = shift;
660 my $list1_id = shift;
661 my $list2_id = shift;
663 my $list1 = $self->get_list($c, $list1_id);
664 my $list2 = $self->get_list($c, $list2_id);
666 my $combined_list_id = $self->new_list(
668 $list1->{name}."_".$list2->{name}."_intersect",
669 $list1->{description}.", ".$list2->{description});
671 my @intersect_elements = ();
673 my $list1_hashref; my $list2_hashref;
674 map { $list1_hashref->{$_}=1 } @{$list1->{elements}};
675 map { $list2_hashref->{$_}=1 } @{$list2->{elements}};
677 foreach my $item (keys(%{$list1_hashref})) {
678 if (exists($list1_hashref->{$item}) && exists($list2_hashref->{$item})) {
679 push @intersect_elements, $item;
683 my @unique_elements = uniq(@intersect_elements);
685 foreach my $item (@unique_elements) {
686 $self->add_item($c, $combined_list_id, $item);
691 sub remove_element_action :Path('/list/item/remove') Args(0) {
692 my $self = shift;
693 my $c = shift;
695 my $list_id = $c->req->param("list_id");
696 my $item_id = $c->req->param("item_id");
698 my $error = $self->check_user($c, $list_id);
700 if ($error) {
701 $c->stash->{rest} = { error => $error };
702 return;
705 my $response = $self->remove_element($c, $list_id, $item_id);
707 $c->stash->{rest} = $response;
711 sub update_element_action :Path('/list/item/update') Args(0) {
712 my $self = shift;
713 my $c = shift;
715 my $list_id = $c->req->param("list_id");
716 my $item_id = $c->req->param("item_id");
717 my $content = $c->req->param("content");
718 my $error = $self->check_user($c, $list_id);
720 if ($content) {
721 print STDERR "update ".$list_id." ".$item_id." ".$content."\n";
723 if ($error) {
724 $c->stash->{rest} = { error => $error };
725 return;
728 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
729 $error = $list->update_element_by_id($item_id, $content);
732 if ($error) {
733 $c->stash->{rest} = { error => "An error occurred while attempting to update item $item_id" };
735 else {
736 $c->stash->{rest} = { success => 1 };
740 sub new_list : Private {
741 my $self = shift;
742 my $c = shift;
743 my ($name, $desc, $owner) = @_;
745 my $user_id = $self->get_user($c);
747 my $new_list_id = CXGN::List::create_list($c->dbc->dbh(), $name, $desc, $owner);
749 return $new_list_id;
753 sub get_list : Private {
754 my $self = shift;
755 my $c = shift;
756 my $list_id = shift;
758 my $list = $self->retrieve_list($c, $list_id);
760 my ($name, $desc, $type_id, $list_type) = $self->get_list_metadata($c, $list_id);
762 $c->stash->{rest} = {
763 name => $name,
764 description => $desc,
765 type_id => $type_id,
766 type_name => $list_type,
767 elements => $list,
771 sub retrieve_list : Private {
772 my $self = shift;
773 my $c = shift;
774 my $list_id = shift;
776 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
777 my $public = $list->check_if_public();
778 if ($public == 0) {
779 my $error = $self->check_user($c, $list_id);
780 if ($error) {
781 $c->stash->{rest} = { error => $error };
782 return;
785 my $list_elements_with_ids = $list->retrieve_elements_with_ids($list_id);
787 #print STDERR "LIST ELEMENTS WITH IDS: ".Dumper($list_elements_with_ids);
788 return $list_elements_with_ids;
792 sub remove_element : Private {
793 my $self = shift;
794 my $c = shift;
795 my $list_id = shift;
796 my $item_id = shift;
799 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
800 my $error = $list->remove_element_by_id($item_id);
802 if ($error) {
803 return { error => "An error occurred while attempting to delete item $item_id" };
805 else {
806 return { success => 1 };
811 sub exists_item : Private {
812 my $self = shift;
813 my $c = shift;
814 my $list_id = shift;
815 my $item = shift;
817 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
818 my $list_item_id = $list->exists_element($item);
819 return $list_item_id;
822 sub get_list_owner : Private {
823 my $self = shift;
824 my $c = shift;
825 my $list_id = shift;
827 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
828 my $owner = $list->owner();
830 return $owner;
833 sub get_user : Private {
834 my $self = shift;
835 my $c = shift;
837 my $user = $c->user();
839 if ($user) {
840 my $user_object = $c->user->get_object();
841 return $user_object->get_sp_person_id();
843 return undef;
846 sub check_user : Private {
847 my $self = shift;
848 my $c = shift;
849 my $list_id = shift;
851 my $user_id = $self->get_user($c);
853 my $error = "";
855 if (!$user_id) {
856 $error = "You must be logged in to manipulate this list.";
859 elsif ($self->get_list_owner($c, $list_id) != $user_id) {
860 $error = "You have insufficient privileges to manipulate this list.";
862 return $error;
865 sub desynonymize_list: Path('/list/desynonymize') Args(0) {
866 my $self = shift;
867 my $c = shift;
869 my $list_id = $c->req->param("list_id");
871 my $user_id = $self->get_user($c);
872 if (!$user_id) {
873 $c->stash->{rest} = { error => 'You must be logged in to use lists.', };
874 return;
876 my $schema = $c->dbic_schema("Bio::Chado::Schema");
877 my $dbh = $schema->storage->dbh;
879 my $list = CXGN::List->new( { dbh => $dbh, list_id => $list_id } );
880 my $flat_list = $list->retrieve_elements_with_ids($list_id);
881 my @name_list = map {@{$_}[1]} @{$flat_list};
882 my $dsyner = CXGN::List::Desynonymize->new();
883 my $results = $dsyner
884 ->desynonymize($schema,$list->type(),\@name_list);
885 $results->{'previous_list'} = \@name_list;
886 $results->{'list_type'} = $list->type();
888 $c->stash->{rest} = $results;