clean
[sgn.git] / lib / SGN / Controller / AJAX / List.pm
blobf25aea8449729a8679831c384fa73e6ddb0706fc
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;
13 BEGIN { extends 'Catalyst::Controller::REST'; }
15 __PACKAGE__->config(
16 default => 'application/json',
17 stash_key => 'rest',
18 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
21 sub get_list_action :Path('/list/get') Args(0) {
22 my $self = shift;
23 my $c = shift;
25 my $list_id = $c->req->param("list_id");
27 my $user_id = $self->get_user($c);
28 if (!$user_id) {
29 $c->stash->{rest} = { error => 'You must be logged in to use lists.', };
30 return;
33 my $list = $self->retrieve_list($c, $list_id);
35 $c->stash->{rest} = $list;
38 sub get_list_data_action :Path('/list/data') Args(0) {
39 my $self = shift;
40 my $c = shift;
42 my $list_id = $c->req->param("list_id");
44 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
45 my $public = $list->check_if_public();
46 if ($public == 0) {
47 my $error = $self->check_user($c, $list_id);
48 if ($error) {
49 $c->stash->{rest} = { error => $error };
50 return;
54 $list = $self->retrieve_list($c, $list_id);
56 my $metadata = $self->get_list_metadata($c, $list_id);
58 $c->stash->{rest} = {
59 list_id => $list_id,
60 type_id => $metadata->{type_id},
61 type_name => $metadata->{list_type},
62 elements => $list,
63 };
67 sub retrieve_contents :Path('/list/contents') Args(1) {
68 my $self = shift;
69 my $c = shift;
70 my $list_id = shift;
72 my $error = $self->check_user($c, $list_id);
73 if ($error) {
74 $c->stash->{rest} = { error => $error };
75 return;
78 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id=>$list_id });
80 my $elements = $list->elements();
81 $c->stash->{rest} = $elements;
84 sub get_list_metadata {
85 my $self = shift;
86 my $c = shift;
87 my $list_id = shift;
89 my $list = CXGN::List->new( { dbh=> $c->dbc->dbh(), list_id=>$list_id });
91 return { name => $list->name(),
92 description => $list->description(),
93 type_id => $list->type_id(),
94 list_type => $list->type(),
98 sub get_type_action :Path('/list/type') Args(1) {
99 my $self = shift;
100 my $c = shift;
101 my $list_id = shift;
103 my $data = $self->get_list_metadata($c, $list_id);
105 $c->stash->{rest} = { type_id => $data->{type_id},
106 list_type => $data->{list_type},
110 sub update_list_name_action :Path('/list/name/update') :Args(0) {
111 my $self = shift;
112 my $c = shift;
113 my $list_id = $c->req->param('list_id');
114 my $name = $c->req->param('name');
116 my $user_id = $self->get_user($c);
117 my $error = $self->check_user($c, $list_id);
119 if ($error) {
120 $c->stash->{rest} = { error => $error };
121 return;
124 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id=>$list_id });
126 $error = $list->name($name);
128 if ($error) {
129 $c->stash->{rest} = { error => $error };
130 return;
133 $c->stash->{rest} = { success => 1 };
136 sub set_type :Path('/list/type') Args(2) {
137 my $self = shift;
138 my $c = shift;
139 my $list_id = shift;
140 my $type = shift;
142 my $user_id = $self->get_user($c);
144 my $error = $self->check_user($c, $list_id);
145 if ($error) {
146 $c->stash->{rest} = { error => $error };
147 return;
150 my $list = CXGN::List->new( { dbh=> $c->dbc->dbh(), list_id => $list_id });
152 if ($list->owner() != $user_id) {
153 $c->stash->{rest} = { error => "Only the list owner can change the type of a list" };
154 return;
157 $error = $list->type($type);
159 if (!$error) {
160 $c->stash->{rest} = { error => "List type not found: ".$type };
161 return;
164 $c->stash->{rest} = { success => 1 };
167 sub new_list_action :Path('/list/new') Args(0) {
168 my $self = shift;
169 my $c = shift;
171 my $name = $c->req->param("name");
172 my $desc = $c->req->param("desc");
175 my $user_id = $self->get_user($c);
176 if (!$user_id) {
177 $c->stash->{rest} = { error => "You must be logged in to use lists", };
178 return;
181 my $new_list_id = 0;
182 eval {
183 $new_list_id = $self->new_list($c, $name, $desc, $user_id);
186 if ($@) {
187 $c->stash->{rest} = { error => "An error occurred, $@", };
188 return;
190 else {
191 $c->stash->{rest} = { list_id => $new_list_id };
195 sub all_types : Path('/list/alltypes') :Args(0) {
196 my $self = shift;
197 my $c = shift;
199 my $all_types = CXGN::List::all_types($c->dbc->dbh());
201 $c->stash->{rest} = $all_types;
204 sub download_list :Path('/list/download') Args(0) {
205 my $self = shift;
206 my $c = shift;
207 my $list_id = $c->req->param("list_id");
209 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
210 my $public = $list->check_if_public();
211 if ($public == 0) {
212 my $error = $self->check_user($c, $list_id);
213 if ($error) {
214 $c->res->content_type("text/plain");
215 $c->res->body($error);
216 return;
220 $list = $self->retrieve_list($c, $list_id);
222 $c->res->content_type("text/plain");
223 $c->res->body(join "\n", map { $_->[1] } @$list);
226 =head2 available_lists()
228 Usage:
229 Desc: returns the available lists. Optionally, a
230 parameter "list_type" can be provided that will limit the
231 lists to the provided type.
233 Ret:
234 Args:
235 Side Effects:
236 Example:
238 =cut
240 sub available_lists : Path('/list/available') Args(0) {
241 my $self = shift;
242 my $c = shift;
244 my $requested_type = $c->req->param("type");
246 my $user_id = $self->get_user($c);
247 if (!$user_id) {
248 $c->stash->{rest} = { error => "You must be logged in to use lists.", };
249 return;
252 my $lists = CXGN::List::available_lists($c->dbc->dbh(), $user_id, $requested_type);
254 $c->stash->{rest} = $lists;
257 sub available_public_lists : Path('/list/available_public') Args(0) {
258 my $self = shift;
259 my $c = shift;
261 my $requested_type = $c->req->param("type");
263 my $user_id = $self->get_user($c);
264 if (!$user_id) {
265 $c->stash->{rest} = { error => "You must be logged in to use lists.", };
266 return;
269 my $lists = CXGN::List::available_public_lists($c->dbc->dbh(), $requested_type);
271 $c->stash->{rest} = $lists;
274 sub add_item :Path('/list/item/add') Args(0) {
275 my $self = shift;
276 my $c = shift;
278 my $list_id = $c->req->param("list_id");
279 my $element = $c->req->param("element");
281 my $user_id = $self->get_user($c);
283 my $error = $self->check_user($c, $list_id);
284 if ($error) {
285 $c->stash->{rest} = { error => $error };
286 return;
289 $element =~ s/^\s*(.+?)\s*$/$1/;
291 if (!$element) {
292 $c->stash->{rest} = { error => "You must provide an element to add to the list" };
293 return;
296 if (!$list_id) {
297 $c->stash->{rest} = { error => "Please specify a list_id." };
298 return;
301 eval {
302 $self->insert_element($c, $list_id, $element);
304 if ($@) {
305 $c->stash->{rest} = { error => "An error occurred: $@" };
306 return;
308 else {
309 $c->stash->{rest} = [ "SUCCESS" ];
313 sub toggle_public_list : Path('/list/public/toggle') Args(0) {
314 my $self = shift;
315 my $c = shift;
316 my $list_id = $c->req->param("list_id");
318 my $error = $self->check_user($c, $list_id);
319 if ($error) {
320 $c->stash->{rest} = { error => $error };
321 return;
324 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
325 my ($public, $rows_affected) = $list->toggle_public();
326 if ($rows_affected == 1) {
327 $c->stash->{rest} = { r => $public };
328 } else {
329 die;
333 sub make_public_list : Path('/list/public/true') Args(0) {
334 my $self = shift;
335 my $c = shift;
336 my $list_id = $c->req->param("list_id");
338 my $error = $self->check_user($c, $list_id);
339 if ($error) {
340 $c->stash->{rest} = { error => $error };
341 return;
344 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
345 my ($rows_affected) = $list->make_public();
346 if ($rows_affected == 1) {
347 $c->stash->{rest} = { success=>1 };
348 } else {
349 die;
353 sub make_private_list : Path('/list/public/false') Args(0) {
354 my $self = shift;
355 my $c = shift;
356 my $list_id = $c->req->param("list_id");
358 my $error = $self->check_user($c, $list_id);
359 if ($error) {
360 $c->stash->{rest} = { error => $error };
361 return;
364 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
365 my ($rows_affected) = $list->make_private();
366 if ($rows_affected == 1) {
367 $c->stash->{rest} = { success=>1 };
368 } else {
369 die;
373 sub copy_public_list : Path('/list/public/copy') Args(0) {
374 my $self = shift;
375 my $c = shift;
376 my $list_id = $c->req->param("list_id");
378 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
379 my $public = $list->check_if_public();
380 my $user_id = $self->get_user($c);
381 if (!$user_id || $public == 0) {
382 $c->stash->{rest} = { error => 'You must be logged in to use lists and list must be public!' };
383 return;
386 my $copied = $list->copy_public($user_id);
387 if ($copied) {
388 $c->stash->{rest} = { success => 'true' };
389 } else {
390 die;
394 sub add_bulk : Path('/list/add/bulk') Args(0) {
395 my $self = shift;
396 my $c = shift;
397 my $list_id = $c->req->param("list_id");
398 my $elements = $c->req->param("elements");
400 my $user_id = $self->get_user($c);
401 my $error = $self->check_user($c, $list_id);
402 if ($error) {
403 $c->stash->{rest} = { error => $error };
404 return;
407 if (!$elements) {
408 $c->stash->{rest} = { error => "You must provide one or more elements to add to the list" };
409 return;
412 my @elements = split "\t", $elements;
414 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id => $list_id });
416 my @duplicates = ();
417 my $count = 0;
419 my $response = $list->add_bulk(\@elements);
420 #print STDERR Dumper $response;
422 if ($response->{error}) {
423 $c->stash->{rest} = { error => $response->{error}};
424 return;
426 if (scalar(@{$response->{duplicates}}) > 0){
427 $c->stash->{rest} = { duplicates => $response->{duplicates} };
430 $c->stash->{rest}->{success} = $response->{count};
433 sub insert_element : Private {
434 my $self = shift;
435 my $c = shift;
436 my $list_id = shift;
437 my $element = shift;
439 my $list = CXGN::List->new( { dbh=>$c->dbc->dbh(), list_id => $list_id });
441 $list->add_element($element);
444 sub delete_list_action :Path('/list/delete') Args(0) {
445 my $self = shift;
446 my $c = shift;
448 my $list_id = $c->req->param("list_id");
450 my $error = $self->check_user($c, $list_id);
451 if ($error) {
452 $c->stash->{rest} = { error => $error };
453 return;
456 $error = CXGN::List::delete_list($c->dbc->dbh(), $list_id);
458 if ($error) {
459 $c->stash->{rest} = { error => $error };
461 else {
462 $c->stash->{rest} = [ 1 ];
467 sub exists_list_action : Path('/list/exists') Args(0) {
468 my $self =shift;
469 my $c = shift;
470 my $name = $c->req->param("name");
472 my $user_id = $self->get_user($c);
473 if (!$user_id) {
474 $c->stash->{rest} = { error => 'You need to be logged in to use lists.' };
477 my $list_id = CXGN::List::exists_list($c->dbc->dbh(), $name, $user_id);
479 if ($list_id) {
480 $c->stash->{rest} = { list_id => $list_id };
482 else {
483 $c->stash->{rest} = { list_id => undef };
487 sub exists_item_action : Path('/list/exists_item') :Args(0) {
488 my $self =shift;
489 my $c = shift;
490 my $list_id = $c->req->param("list_id");
491 my $name = $c->req->param("name");
493 my $error = $self->check_user($c, $list_id);
494 if ($error) {
495 $c->stash->{rest} = { error => $error };
496 return;
499 my $user_id = $self->get_user($c);
501 if ($self->get_list_owner($c, $list_id) != $user_id) {
502 $c->stash->{rest} = { error => "You have insufficient privileges to manipulate this list.", };
503 return;
506 my $list_item_id = $self->exists_item($c, $list_id, $name);
508 if ($list_item_id) {
509 $c->stash->{rest} = { list_item_id => $list_item_id };
511 else {
512 $c->stash->{rest} = { list_item_id => 0 };
516 sub list_size : Path('/list/size') Args(0) {
517 my $self = shift;
518 my $c = shift;
519 my $list_id = $c->req->param("list_id");
521 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
522 my $count = $list->list_size();
524 $c->stash->{rest} = { count => $count };
527 sub validate : Path('/list/validate') Args(2) {
528 my $self = shift;
529 my $c = shift;
530 my $list_id = shift;
531 my $type = shift;
533 my $list = $self->retrieve_list($c, $list_id);
535 my @flat_list = map { $_->[1] } @$list;
537 my $lv = CXGN::List::Validate->new();
538 my $data = $lv->validate($c->dbic_schema("Bio::Chado::Schema"), $type, \@flat_list);
540 $c->stash->{rest} = $data;
543 sub transform :Path('/list/transform/') Args(2) {
544 my $self = shift;
545 my $c = shift;
546 my $list_id = shift;
547 my $transform_name = shift;
549 my $t = CXGN::List::Transform->new();
551 my $data = $self->get_list_metadata($c, $list_id);
553 my $list_data = $self->retrieve_list($c, $list_id);
555 my @list_items = map { $_->[1] } @$list_data;
557 my $result = $t->transform($c->dbic_schema("Bio::Chado::Schema"), $transform_name, \@list_items);
559 if (exists($result->{missing}) && (scalar(@{$result->{missing}}) > 0)) {
560 $c->stash->{rest} = { error => "This lists contains elements that cannot be converted. Not converting list.", };
561 return;
564 $c->stash->{rest} = $result;
568 sub replace_elements :Path('/list/item/replace') Args(2) {
569 my $self = shift;
570 my $c = shift;
572 my $list_id = shift;
573 my $new_list = shift; # tab delimited new list elements
577 sub combine_lists : Path('/list/combine') Args(2) {
578 my $self = shift;
579 my $c = shift;
580 my $list1_id = shift;
581 my $list2_id = shift;
583 my $list1 = $self->get_list($c, $list1_id);
584 my $list2 = $self->get_list($c, $list2_id);
586 my $combined_list_id = $self->new_list(
587 $c,
588 $list1->{name}."_".$list2->{name},
589 $list1->{description}.", ".$list2->{description});
591 my @combined_elements = (@{$list1->{elements}}, @{$list2->{elements}});
593 my @unique_elements = uniq(@combined_elements);
595 foreach my $item (@unique_elements) {
596 $self->add_item($c, $combined_list_id, $item);
600 sub intersect_lists : Path('/list/intersect') Args(2) {
601 my $self = shift;
602 my $c = shift;
603 my $list1_id = shift;
604 my $list2_id = shift;
606 my $list1 = $self->get_list($c, $list1_id);
607 my $list2 = $self->get_list($c, $list2_id);
609 my $combined_list_id = $self->new_list(
610 $c,
611 $list1->{name}."_".$list2->{name}."_intersect",
612 $list1->{description}.", ".$list2->{description});
614 my @intersect_elements = ();
616 my $list1_hashref; my $list2_hashref;
617 map { $list1_hashref->{$_}=1 } @{$list1->{elements}};
618 map { $list2_hashref->{$_}=1 } @{$list2->{elements}};
620 foreach my $item (keys(%{$list1_hashref})) {
621 if (exists($list1_hashref->{$item}) && exists($list2_hashref->{$item})) {
622 push @intersect_elements, $item;
626 my @unique_elements = uniq(@intersect_elements);
628 foreach my $item (@unique_elements) {
629 $self->add_item($c, $combined_list_id, $item);
634 sub remove_element_action :Path('/list/item/remove') Args(0) {
635 my $self = shift;
636 my $c = shift;
638 my $list_id = $c->req->param("list_id");
639 my $item_id = $c->req->param("item_id");
641 my $error = $self->check_user($c, $list_id);
643 if ($error) {
644 $c->stash->{rest} = { error => $error };
645 return;
648 my $response = $self->remove_element($c, $list_id, $item_id);
650 $c->stash->{rest} = $response;
654 sub update_element_action :Path('/list/item/update') Args(0) {
655 my $self = shift;
656 my $c = shift;
658 my $list_id = $c->req->param("list_id");
659 my $item_id = $c->req->param("item_id");
660 my $content = $c->req->param("content");
661 print STDERR "update ".$list_id." ".$item_id." ".$content."\n";
663 my $error = $self->check_user($c, $list_id);
665 if ($error) {
666 $c->stash->{rest} = { error => $error };
667 return;
670 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
671 $error = $list->update_element_by_id($item_id, $content);
673 if ($error) {
674 $c->stash->{rest} = { error => "An error occurred while attempting to update item $item_id" };
676 else {
677 $c->stash->{rest} = { success => 1 };
681 sub new_list : Private {
682 my $self = shift;
683 my $c = shift;
684 my ($name, $desc, $owner) = @_;
686 my $user_id = $self->get_user($c);
688 my $new_list_id = CXGN::List::create_list($c->dbc->dbh(), $name, $desc, $owner);
690 return $new_list_id;
694 sub get_list : Private {
695 my $self = shift;
696 my $c = shift;
697 my $list_id = shift;
699 my $list = $self->retrieve_list($c, $list_id);
701 my ($name, $desc, $type_id, $list_type) = $self->get_list_metadata($c, $list_id);
703 $c->stash->{rest} = {
704 name => $name,
705 description => $desc,
706 type_id => $type_id,
707 type_name => $list_type,
708 elements => $list,
712 sub retrieve_list : Private {
713 my $self = shift;
714 my $c = shift;
715 my $list_id = shift;
717 my $list = CXGN::List->new( { dbh => $c->dbc->dbh, list_id=>$list_id });
718 my $public = $list->check_if_public();
719 if ($public == 0) {
720 my $error = $self->check_user($c, $list_id);
721 if ($error) {
722 $c->stash->{rest} = { error => $error };
723 return;
726 my $list_elements_with_ids = $list->retrieve_elements_with_ids($list_id);
728 #print STDERR "LIST ELEMENTS WITH IDS: ".Dumper($list_elements_with_ids);
729 return $list_elements_with_ids;
733 sub remove_element : Private {
734 my $self = shift;
735 my $c = shift;
736 my $list_id = shift;
737 my $item_id = shift;
740 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
741 my $error = $list->remove_element_by_id($item_id);
743 if ($error) {
744 return { error => "An error occurred while attempting to delete item $item_id" };
746 else {
747 return { success => 1 };
752 sub exists_item : Private {
753 my $self = shift;
754 my $c = shift;
755 my $list_id = shift;
756 my $item = shift;
758 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
759 my $list_item_id = $list->exists_element($item);
760 return $list_item_id;
763 sub get_list_owner : Private {
764 my $self = shift;
765 my $c = shift;
766 my $list_id = shift;
768 my $list = CXGN::List->new( { dbh => $c->dbc()->dbh(), list_id => $list_id });
769 my $owner = $list->owner();
771 return $owner;
774 sub get_user : Private {
775 my $self = shift;
776 my $c = shift;
778 my $user = $c->user();
780 if ($user) {
781 my $user_object = $c->user->get_object();
782 return $user_object->get_sp_person_id();
784 return undef;
787 sub check_user : Private {
788 my $self = shift;
789 my $c = shift;
790 my $list_id = shift;
792 my $user_id = $self->get_user($c);
794 my $error = "";
796 if (!$user_id) {
797 $error = "You must be logged in to manipulate this list.";
800 elsif ($self->get_list_owner($c, $list_id) != $user_id) {
801 $error = "You have insufficient privileges to manipulate this list.";
803 return $error;