3 SGN::Controller::Organism - Catalyst controller for dealing with
8 package SGN
::Controller
::Organism
;
11 use namespace
::autoclean
;
13 BEGIN { extends
'Catalyst::Controller' }
19 use JSON
::Any
; my $json = JSON
::Any
->new;
20 use List
::MoreUtils qw
/ any /;
23 use CXGN
::Chado
::Organism
;
25 use CXGN
::Phylo
::OrganismTree
;
26 use CXGN
::Page
::FormattingHelpers qw
| tooltipped_text
|;
27 use CXGN
::Tools
::Text
;
31 with
'Catalyst::Component::ApplicationAttribute';
37 Public Path: /organism/all/view
39 Display the sgn data overview page.
43 sub view_all
:Path
('/organism/all/view') :Args
(0) {
44 my ( $self, $c ) = @_;
46 while( my ( $set_name, $set_callback ) = each %{ $self->organism_sets } ) {
47 next unless $set_name =~ /^web_visible_(.+)/;
50 my $tree = $self->rendered_organism_tree_cache->thaw( $set_name );
51 $tree->{set_name
} = $set_name;
52 $c->stash->{organism_trees
}->{$family_name} = $tree;
56 # add image_uris to each of the organism tree records
57 foreach my $v (values %{ $c->stash->{organism_trees
} }) {
58 $v->{image_uri
} = $c->uri_for( $self->action_for('organism_tree_image'), [ $v->{set_name
} ])->relative();
63 template
=> '/content/sgn_data.mas',
70 # /organism/set/<set_name>
71 sub get_organism_set
:Chained
('/') :PathPart
('organism/set') :CaptureArgs
(1) {
72 my ( $self, $c, $set_name ) = @_;
74 $c->stash->{organism_set_name
} = $set_name;
75 $c->stash->{organism_set
} = $self->organism_sets->{ $set_name }
76 or $c->debug && $c->log->debug("no set found called '$set_name'");
79 # /organism/tree/<set_name>
80 sub get_organism_tree
:Chained
('/') :PathPart
('organism/tree') :CaptureArgs
(1) {
81 my ( $self, $c, $set_name ) = @_;
83 $c->stash->{organism_set_name
} = $set_name;
84 # the Cache::Entry for the slot in the cache for this organism tree
85 $c->stash->{organism_tree_cache_entry
} = $self->rendered_organism_tree_cache->entry( $set_name );
88 =head2 organism_tree_image
90 Public Path: /organism/tree/<set_name>/image
92 Get a PNG organism tree image
96 sub organism_tree_image
:Chained
('get_organism_tree') :PathPart
('image') {
97 my ( $self, $c ) = @_;
99 my $image = $c->stash->{organism_tree_cache_entry
}->thaw
102 $image->{png
} or die "no png data for organism set '".$c->stash->{organism_set_name
}."'! cannot serve image. Dump of cache entry: \n".Data
::Dumper
::Dumper
( $image );
104 $c->res->body( $image->{png
} );
105 $c->res->content_type( 'image/png' );
108 =head2 clear_organism_tree
110 Public Path: /organism/tree/<set_name>/flush
112 Flush a cached organism tree image, so that the next call to serve the
113 organism tree image or html will regenerate it.
117 # /organism/tree/<set_name>/flush
118 sub clear_organism_tree
:Chained
('get_organism_tree') :PathPart
('flush') {
119 my ( $self, $c ) = @_;
121 $c->stash->{organism_tree_cache_entry
}->remove;
122 $c->res->content_type('application/json');
124 { status
: "success" }
131 Public Path: /organism/sol100/view
133 Display the sol100 organisms page.
137 sub view_sol100
:Path
('sol100/view') :Args
(0) {
138 my ( $self, $c ) = @_;
140 my ($person_id, $user_type) = CXGN
::Login
->new( $c->dbc->dbh )->has_session();
141 print STDERR
"ACTION: ".$self->action_for('organism_tree_image')."\n";
143 print STDERR
"IMAGE URI: ".$c->uri_for( $self->action_for('organism_tree_image'), ['sol100'] )->relative()."\n";
146 template
=> "/sequencing/sol100.mas",
151 %{ $self->rendered_organism_tree_cache->thaw( 'sol100' ) },
155 image_uri
=> $c->uri_for( $self->action_for('organism_tree_image'), ['sol100'] )->relative(),
158 show_org_add_form
=> ( $user_type && any
{$user_type eq $_} qw( curator submitter sequencer ) ),
159 organism_add_uri
=> '/organism/sol100/add_organism', #$self->action_for('add_sol100_organism')),
160 organism_autocomplete_uri
=> $c->uri_for( 'autocomplete'),#$self->action_for('autocomplete')), #, ['Solanaceae'])->relative(),
165 =head2 add_sol100_organism
167 Public Path: /organism/sol100/add_organism
169 POST target to add an organism to the set of sol100 organisms. Takes
170 one param, C<species>, which is the exact string species name in the
173 After adding, redirects to C<view_sol100>.
177 sub add_sol100_organism
:Path
('sol100/add_organism') :Args
(0) {
178 my ( $self, $c ) = @_;
180 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
181 my $organism = $c->dbic_schema('Bio::Chado::Schema','sgn_chado', $sp_person_id)
182 ->resultset('Organism::Organism')
183 ->search({ species
=> { ilike
=> $c->req->body_parameters->{species
} }})
186 ## validate our conditions
187 my @validate = ( [ RC_METHOD_NOT_ALLOWED
,
188 'Only POST requests are allowed for this page.',
189 sub { $c->req->method eq 'POST' }
192 'Organism not found',
197 my ( $status, $message, $test ) = @
$_;
198 unless( $test->() ) {
199 $c->throw( http_status
=> $status, public_message
=> $message );
204 # if this fails, it will throw an acception and will (probably
205 # rightly) be counted as a server error
206 $organism->create_organismprops(
211 $self->rendered_organism_tree_cache->remove( 'sol100' ); #< invalidate the sol100 cached image tree
212 $c->res->redirect( $c->uri_for( $self->action_for('view_sol100'))->relative());
216 sub invalidate_organism_tree_cache
:Args
(0) {
218 $self->rendered_organism_tree_cache->remove( 'sol100' ); #< invalidate the sol100 cached image tree
223 #Chaining base to fetch a particular organism, chaining onto this like
224 #/organism/<org_id>/<more_stuff>
225 sub find_organism
:Chained
('/') :PathPart
('organism') :CaptureArgs
(1) {
226 my ( $self, $c, $organism_id ) = @_;
228 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
230 $c->dbic_schema('CXGN::Biosource::Schema','sgn_chado', $sp_person_id)
231 ->resultset('Organism::Organism');
233 if( $organism_id =~ /\D/ ) {
234 (my $species = $organism_id) =~ s/_/ /g;
235 $rs = $rs->search_rs({ 'lower(me.species)' => lc $species });
237 $rs = $rs->search_rs({ organism_id
=> $organism_id });
240 my ( $organism ) = my @organisms = $rs->all;
241 $c->throw_client_error('Multiple matching organisms') if @organisms > 1;
242 $c->throw_404('Organism not found') unless $organism;
246 organism_id
=> $organism->organism_id,
247 organism
=> $organism,
253 Public Path: /organism/<organism_id>/view
255 Action for viewing an organism detail page. Currently just redirects
256 to the legacy /chado/organism.pl.
260 sub view_organism
:Chained
('find_organism') :PathPart
('view') :Args
(0) {
261 my ( $self, $c ) = @_;
263 return unless $c->stash->{organism_id
};
265 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
266 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado', $sp_person_id);
267 my $organism = CXGN
::Chado
::Organism
->new($schema, $c->stash->{organism_id
});
268 $c->stash->{organism
} = $organism;
269 $c->stash->{na
}= qq| <span
class="ghosted">N
/A</span
> |;
270 $c->stash->{genus
} = $c->stash->{organism_rs
}->first()->genus();
271 $c->stash->{taxon
} = $organism->get_taxon();
272 $c->stash->{organism_name
} = $c->stash->{organism_rs
}->first()->species();
274 my $common_name = $c->stash->{organism_rs
}->first()->common_name();
275 $c->stash->{common_name
} = lc($common_name);
276 $common_name = ucfirst($common_name);
277 $c->stash->{comment
} = $c->stash->{organism_rs
}->first()->comment();
279 my $organismprop_rs = $schema->resultset('Organism::Organismprop')->search( { organism_id
=>$c->stash->{organism_id
} });
281 $c->stash->{description
} = CXGN
::Tools
::Text
::format_field_text
($organism->get_comment());
283 @
{$c->stash->{synonyms
}} = $organism->get_synonyms();
285 $c->stash->{loci
} = "<a href=\"/search/locus\">".$organism->get_loci_count().'</a>';
287 $c->stash->{taxonomy
} = join ", ", reverse(get_parentage
($organism));
290 my @dbxrefs = $organism->get_dbxrefs();
293 foreach my $dbxref (@dbxrefs) {
294 my $accession = $dbxref->accession();
295 my ($db) = $dbxref->search_related("db");
296 my $db_name = $db->name();
297 my $full_url = $db->urlprefix . $db->url();
299 if ( $db_name =~ m/(DB:)(.*)/ ) {
304 qq|<a href
= "$full_url$accession">$db_name ID
: $accession</a ><br />|;
306 if ( $db_name eq 'SolCyc_by_species' ) {
307 my $solcyc = $accession;
309 $solcyc =~ s/$solcyc/\u\L$solcyc/g;
310 $solcyc = $solcyc . "Cyc";
311 $solcyc_link = "See <a href=\"$full_url$accession\">$solcyc</a>";
315 my $logged_user = $c->user;
316 my $person_id = $logged_user->get_object->get_sp_person_id if $logged_user;
317 my $privileged_user = ($logged_user && ( $logged_user->check_roles('curator') || $logged_user->check_roles('sequencer') || $logged_user->check_roles('submitter') ) ) ;
319 $c->stash->{privileged_user
} = $privileged_user;
321 $c->stash->{solcyc_link
} = $solcyc_link;
322 $c->stash->{accessions
} = $accessions;
323 my $na = qq| <span
class="ghosted">N
/A</span
> |;
324 $c->stash->{ploidy
} = $organism->get_ploidy() || $na;
325 $c->stash->{genome_size
} = $organism->get_genome_size() || $na;
326 $c->stash->{chromosome_number
} = $organism->get_chromosome_number() || $na;
327 my @image_ids = $organism->get_image_ids();
328 $c->stash->{images
} = \
@image_ids;
330 $self->transcript_data($c);
331 $self->phenotype_data($c);
340 my @map_data = $c->stash->{organism
}->get_map_data();
341 foreach my $info (@map_data) {
342 my $map_id = $info->[1];
343 my $short_name = $info->[0];
344 $maps .= "<a href=\"/cview/map.pl?map_id=$map_id\">$short_name</a><br />";
346 $c->stash->{maps
} = $maps;
349 sub transcript_data
{
353 my @libraries = $c->stash->{organism
}->get_library_list();
355 my $attribution = $c->stash->{organism
}->get_est_attribution();
357 $c->stash->{libraries
} = \
@libraries;
358 $c->stash->{est_attribution
} = $attribution;
367 ####################### QTL DISPLAY #############
368 my $common_name = $c->stash->{common_name
};
369 my @qtl_data = qtl_populations
($common_name);
370 unless (@qtl_data) { @qtl_data = ['N/A', 'N/A'];}
373 $c->stash->{qtl_data
} = \
@qtl_data;
379 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
380 my $schema = $c->dbic_schema('Bio::Chado::Schema','sgn_chado', $sp_person_id);
381 my $organism = $c->stash->{organism
};
382 my $organism_id = $organism->get_organism_id;
383 my $pheno_count = $organism->get_phenotype_count();
384 my $onto_count = $schema->resultset("Stock::StockCvterm")->search_related('stock', {
385 organism_id
=> $organism_id } )->count;
386 my $trait_count = $schema->resultset("NaturalDiversity::NdExperimentPhenotype")->search_related('nd_experiment')->search_related('nd_experiment_stocks')->search_related('stock', { organism_id
=> $organism_id } )->count;
389 qq|<a href
= "/search/stocks?organism=$organism_id">$pheno_count</a
>|;
390 $c->stash->{phenotypes
} = $pheno_list;
392 qq|<a href
= "/search/stocks?organism=$organism_id">$onto_count</a
>|;
393 $c->stash->{onto_count
} = $onto_list;
395 qq|<a href
= "/search/stocks?organism=$organism_id">$trait_count</a
>|;
396 $c->stash->{trait_count
} = $trait_list;
405 a hashref of organism sets (DBIC resultsets) as:
408 description => 'user-visible description string for the set',
409 resultset => DBIC resultset of organisms in that set,
413 currently defined sets are:
417 the SOL100 organisms, which are organisms in solanaceae that have a
418 'web visible' organismprop set
422 all organisms in the Solanaceae family
426 all organisms in the Rubiaceae family
428 =head3 Plantaginaceae
430 all organisms in the Plantaginaceae family
432 =head3 web_visible_Solanaceae
434 organisms in Solanaceae that have their 'web visible' organismprop set
436 =head3 web_visible_Rubiaceae
438 organisms in Rubiaceae that have their 'web visible' organismprop set
440 =head3 web_visible_Plantaginaceae
442 organisms in Plantaginaceae that have their 'web visible' organismprop set
446 has
'organism_sets' => (
450 ); sub _build_organism_sets
{
453 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
454 my $schema = $self->_app->dbic_schema('Bio::Chado::Schema','sgn_chado', $sp_person_id);
457 # define a set of SOL100 organisms
458 $org_sets{'sol100'} = {
459 description
=> 'SOL100 Organisms',
460 root_species
=> 'Solanaceae',
461 resultset
=> $schema->resultset( "Cv::Cvterm" )
462 ->search({ name
=> 'sol100' })
463 ->search_related( 'organismprops' )
464 ->search_related_rs( 'organism' )
467 # define sets of web-visible organisms, by family
468 for my $family (qw( Solanaceae Rubiaceae Plantaginaceae )) {
469 my $pns = $schema->resultset('Organism::Organism')
470 ->search({ 'me.species' => $family })
471 ->search_related('phylonode_organisms')
472 ->search_related('phylonode',
473 { 'cv.name' => 'taxonomy' },
474 { join => { type
=> 'cv' }},
477 $pns = $self->_child_phylonodes( $pns )
478 ->search_related_rs('phylonode_organism');
480 # set of all organisms in that family
481 $org_sets{$family} = {
482 description
=> $family,
483 root_species
=> $family,
484 resultset
=> $pns->search_related_rs('organism'),
487 # set of only web-visible organisms in that family
488 $org_sets{"web_visible_$family"} = {
489 description
=> $family,
490 root_species
=> $family,
491 resultset
=> $pns->search_related_rs(
493 { 'cv.name' => 'local',
494 'type.name' => 'web visible',
496 { join => { organismprops
=> { type
=> 'cv' }}},
503 # take a resultset of phylonodes, construct a resultset of the child
504 # phylonodes. temporary workaround until the extended_rels branch is
505 # merged into DBIx::Class and DBIx::Class::Tree::NestedSet is ported
507 sub _child_phylonodes
{
508 my ( $self, $phylonodes ) = @_;
510 my %child_phylonode_conditions;
511 while( my $pn = $phylonodes->next ) {
512 push @
{ $child_phylonode_conditions{ '-or' }} => {
513 'left_idx' => { '>' => $pn->left_idx },
514 'right_idx' => { '<' => $pn->right_idx },
515 'phylotree_id' => $pn->phylotree_id,
519 return $phylonodes->result_source->resultset
520 ->search( \
%child_phylonode_conditions );
524 =head2 species_data_summary_cache
526 L<Cache> object containing species data summaries, as:
530 'Common Name' => common_name,
536 Access with C<$controller-E<gt>species_data_summary_cache->thaw($organism_id )>,
537 do not use Cache's C<get> method.
541 has
'species_data_summary_cache' => (
544 ); sub _build_species_data_summary_cache
{
545 my ($cache_class, $config) = shift->_species_summary_cache_configuration;
546 return $cache_class->new( %$config );
549 sub _species_summary_cache_configuration
{
550 my ( $self, $c ) = @_;
552 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
553 my $schema = $self->_app->dbic_schema( 'Bio::Chado::Schema', 'sgn_chado', $sp_person_id);
555 return 'Cache::File', {
556 cache_root
=> $self->_app->path_to( $self->_app->tempfiles_subdir('species_summary_cache') ),
557 default_expires
=> '6 hours',
559 load_callback
=> sub {
560 my $cache_entry = shift;
561 my $org_id = $cache_entry->key;
562 my $org = CXGN
::Chado
::Organism
->new( $schema, $org_id )
564 no warnings
'uninitialized';
565 return Storable
::nfreeze
({
566 'Common Name' => $org->get_group_common_name,
567 'Loci' => $org->get_loci_count,
568 'Phenotypes' => $org->get_phenotype_count,
569 'Maps Available' => $org->has_avail_map,
570 'Genome Information' => $org->has_avail_genome ?
'yes': 'no',
571 'Libraries' => scalar( $org->get_library_list ),
577 =head2 rendered_organism_tree_cache
579 A cache of rendered organism trees, as
583 newick => 'newick string',
585 image_map => 'html image map',
586 image_map_name => 'name of the image map for <img usemap="" ... />',
591 has
'rendered_organism_tree_cache' => (
594 ); sub _build_rendered_organism_tree_cache
{
595 my ( $self, $c ) = @_;
596 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
599 cache_root
=> $self->_app->path_to( $self->_app->tempfiles_subdir('cache','rendered_organism_tree_cache') ),
600 default_expires
=> 'never',
602 load_callback
=> sub {
603 my $set_name = shift->key;
604 my $set = $self->organism_sets->{ $set_name };
605 my $root_species = $set->{root_species
} or die "no root species defined for org set $set_name";
606 my $species_names = [ $set->{resultset
}->get_column('species')->all ];
608 if( @
$species_names ) {
609 my $orgtree = $self->_render_organism_tree(
610 $self->_app->dbic_schema('Bio::Chado::Schema','sgn_chado', $sp_person_id),
614 return Storable
::nfreeze
( $orgtree );
617 return Storable
::nfreeze
( {} );
623 # takes dbic schema, root species name, arrayref of species names to
624 # render returns hashref of newick string, png data, and an HTML image
628 # newick => 'newick string',
630 # image_map => 'html image map',
631 # image_map_name => 'name of the image map for <img usemap="" ... />',
633 sub _render_organism_tree
{
634 my ( $self, $schema, $root_species, $species_names ) = @_;
636 $self->_app->log->debug( "rendering org for root species '$root_species'" ) if $self->_app->debug;
638 my $tree = CXGN
::Phylo
::OrganismTree
->new( $schema );
641 my $newick_string = $tree->build_tree(
644 $self->species_data_summary_cache,
647 my $cache = $self->species_data_summary_cache();
648 foreach my $n (@
$species_names) {
649 my $ors = CXGN
::Chado
::Organism
::get_organism_by_species
($n, $schema);
652 my $genome_info = $cache->thaw($ors->organism_id())->{'Genome Information'};
653 if ($genome_info =~ /y/i) {
654 $tree->hilite_species([170,220,180], [$n]);
658 my $image_map_name = $root_species.'_map';
659 my $image_map = $tree->get_renderer
660 ->get_html_image_map( $image_map_name );
661 my $image_png = $tree->render_png( undef, 1 );
664 newick
=> $newick_string,
666 image_map
=> $image_map,
667 image_map_name
=> $image_map_name,
676 =head2 qtl_populations
678 Usage: my @qtl_data = qtl_populations($common_name);
679 Desc: returns a list of qtl populations (hyperlinked to the pop page)
680 and counts of traits assayed for QTL for the corresponding population
681 Ret: an array of array of populations and trait counts or undef
682 Args: organism group common name
690 sub qtl_populations
{
691 my $gr_common_name = shift;
692 my $qtl_tool = CXGN
::Phenome
::Qtl
::Tools
->new();
694 my @org_pops = $qtl_tool->qtl_pops_by_common_name($gr_common_name);
698 foreach my $org_pop (@org_pops) {
699 my $pop_id = $org_pop->get_population_id();
700 my $pop_name = $org_pop->get_name();
701 my $pop_link = qq |<a href
="/qtl/view/$pop_id">$pop_name</a
>|;
702 my @traits = $org_pop->get_cvterms();
703 my $count = scalar(@traits);
705 push @pop_data, [ map { $_ } ( $pop_link, $count ) ];
714 my $organism = shift;
715 my $parent = $organism->get_parent();
719 my $species = $parent->get_species();
720 my $taxon = $parent->get_taxon();
722 push @taxonomy, tooltipped_text
( $species, $taxon );
723 @taxonomy = (@taxonomy, get_parentage
($parent));
728 __PACKAGE__
->meta->make_immutable;