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 $organism = $c->dbic_schema('Bio::Chado::Schema','sgn_chado')
181 ->resultset('Organism::Organism')
182 ->search({ species
=> { ilike
=> $c->req->body_parameters->{species
} }})
185 ## validate our conditions
186 my @validate = ( [ RC_METHOD_NOT_ALLOWED
,
187 'Only POST requests are allowed for this page.',
188 sub { $c->req->method eq 'POST' }
191 'Organism not found',
196 my ( $status, $message, $test ) = @
$_;
197 unless( $test->() ) {
198 $c->throw( http_status
=> $status, public_message
=> $message );
203 # if this fails, it will throw an acception and will (probably
204 # rightly) be counted as a server error
205 $organism->create_organismprops(
210 $self->rendered_organism_tree_cache->remove( 'sol100' ); #< invalidate the sol100 cached image tree
211 $c->res->redirect( $c->uri_for( $self->action_for('view_sol100'))->relative());
215 sub invalidate_organism_tree_cache
:Args
(0) {
217 $self->rendered_organism_tree_cache->remove( 'sol100' ); #< invalidate the sol100 cached image tree
222 #Chaining base to fetch a particular organism, chaining onto this like
223 #/organism/<org_id>/<more_stuff>
224 sub find_organism
:Chained
('/') :PathPart
('organism') :CaptureArgs
(1) {
225 my ( $self, $c, $organism_id ) = @_;
228 $c->dbic_schema('CXGN::Biosource::Schema','sgn_chado')
229 ->resultset('Organism::Organism');
231 if( $organism_id =~ /\D/ ) {
232 (my $species = $organism_id) =~ s/_/ /g;
233 $rs = $rs->search_rs({ 'lower(me.species)' => lc $species });
235 $rs = $rs->search_rs({ organism_id
=> $organism_id });
238 my ( $organism ) = my @organisms = $rs->all;
239 $c->throw_client_error('Multiple matching organisms') if @organisms > 1;
240 $c->throw_404('Organism not found') unless $organism;
244 organism_id
=> $organism->organism_id,
245 organism
=> $organism,
251 Public Path: /organism/<organism_id>/view
253 Action for viewing an organism detail page. Currently just redirects
254 to the legacy /chado/organism.pl.
258 sub view_organism
:Chained
('find_organism') :PathPart
('view') :Args
(0) {
259 my ( $self, $c ) = @_;
261 return unless $c->stash->{organism_id
};
263 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
264 my $organism = CXGN
::Chado
::Organism
->new($schema, $c->stash->{organism_id
});
265 $c->stash->{organism
} = $organism;
266 $c->stash->{na
}= qq| <span
class="ghosted">N
/A</span
> |;
267 $c->stash->{genus
} = $c->stash->{organism_rs
}->first()->genus();
268 $c->stash->{taxon
} = $organism->get_taxon();
269 $c->stash->{organism_name
} = $c->stash->{organism_rs
}->first()->species();
271 my $common_name = $c->stash->{organism_rs
}->first()->common_name();
272 $c->stash->{common_name
} = lc($common_name);
273 $common_name = ucfirst($common_name);
274 $c->stash->{comment
} = $c->stash->{organism_rs
}->first()->comment();
276 my $organismprop_rs = $schema->resultset('Organism::Organismprop')->search( { organism_id
=>$c->stash->{organism_id
} });
278 $c->stash->{description
} = CXGN
::Tools
::Text
::format_field_text
($organism->get_comment());
280 @
{$c->stash->{synonyms
}} = $organism->get_synonyms();
282 $c->stash->{loci
} = "<a href=\"/search/locus\">".$organism->get_loci_count().'</a>';
284 $c->stash->{taxonomy
} = join ", ", reverse(get_parentage
($organism));
287 my @dbxrefs = $organism->get_dbxrefs();
290 foreach my $dbxref (@dbxrefs) {
291 my $accession = $dbxref->accession();
292 my ($db) = $dbxref->search_related("db");
293 my $db_name = $db->name();
294 my $full_url = $db->urlprefix . $db->url();
296 if ( $db_name =~ m/(DB:)(.*)/ ) {
301 qq|<a href
= "$full_url$accession">$db_name ID
: $accession</a ><br />|;
303 if ( $db_name eq 'SolCyc_by_species' ) {
304 my $solcyc = $accession;
306 $solcyc =~ s/$solcyc/\u\L$solcyc/g;
307 $solcyc = $solcyc . "Cyc";
308 $solcyc_link = "See <a href=\"$full_url$accession\">$solcyc</a>";
312 my $logged_user = $c->user;
313 my $person_id = $logged_user->get_object->get_sp_person_id if $logged_user;
314 my $privileged_user = ($logged_user && ( $logged_user->check_roles('curator') || $logged_user->check_roles('sequencer') || $logged_user->check_roles('submitter') ) ) ;
316 $c->stash->{privileged_user
} = $privileged_user;
318 $c->stash->{solcyc_link
} = $solcyc_link;
319 $c->stash->{accessions
} = $accessions;
320 my $na = qq| <span
class="ghosted">N
/A</span
> |;
321 $c->stash->{ploidy
} = $organism->get_ploidy() || $na;
322 $c->stash->{genome_size
} = $organism->get_genome_size() || $na;
323 $c->stash->{chromosome_number
} = $organism->get_chromosome_number() || $na;
324 my @image_ids = $organism->get_image_ids();
325 my @image_objects = map { SGN
::Image
->new($c->dbc->dbh, $_, $c ) } @image_ids;
326 $c->stash->{image_objects
} = \
@image_objects;
328 $self->transcript_data($c);
329 $self->phenotype_data($c);
338 my @map_data = $c->stash->{organism
}->get_map_data();
339 foreach my $info (@map_data) {
340 my $map_id = $info->[1];
341 my $short_name = $info->[0];
342 $maps .= "<a href=\"/cview/map.pl?map_id=$map_id\">$short_name</a><br />";
344 $c->stash->{maps
} = $maps;
347 sub transcript_data
{
351 my @libraries = $c->stash->{organism
}->get_library_list();
353 my $attribution = $c->stash->{organism
}->get_est_attribution();
355 $c->stash->{libraries
} = \
@libraries;
356 $c->stash->{est_attribution
} = $attribution;
365 ####################### QTL DISPLAY #############
366 my $common_name = $c->stash->{common_name
};
367 my @qtl_data = qtl_populations
($common_name);
368 unless (@qtl_data) { @qtl_data = ['N/A', 'N/A'];}
371 $c->stash->{qtl_data
} = \
@qtl_data;
377 my $schema = $c->dbic_schema('Bio::Chado::Schema','sgn_chado');
378 my $organism = $c->stash->{organism
};
379 my $organism_id = $organism->get_organism_id;
380 my $pheno_count = $organism->get_phenotype_count();
381 my $onto_count = $schema->resultset("Stock::StockCvterm")->search_related('stock', {
382 organism_id
=> $organism_id } )->count;
383 my $trait_count = $schema->resultset("NaturalDiversity::NdExperimentPhenotype")->search_related('nd_experiment')->search_related('nd_experiment_stocks')->search_related('stock', { organism_id
=> $organism_id } )->count;
386 qq|<a href
= "/search/stocks?organism=$organism_id">$pheno_count</a
>|;
387 $c->stash->{phenotypes
} = $pheno_list;
389 qq|<a href
= "/search/stocks?organism=$organism_id">$onto_count</a
>|;
390 $c->stash->{onto_count
} = $onto_list;
392 qq|<a href
= "/search/stocks?organism=$organism_id">$trait_count</a
>|;
393 $c->stash->{trait_count
} = $trait_list;
402 a hashref of organism sets (DBIC resultsets) as:
405 description => 'user-visible description string for the set',
406 resultset => DBIC resultset of organisms in that set,
410 currently defined sets are:
414 the SOL100 organisms, which are organisms in solanaceae that have a
415 'web visible' organismprop set
419 all organisms in the Solanaceae family
423 all organisms in the Rubiaceae family
425 =head3 Plantaginaceae
427 all organisms in the Plantaginaceae family
429 =head3 web_visible_Solanaceae
431 organisms in Solanaceae that have their 'web visible' organismprop set
433 =head3 web_visible_Rubiaceae
435 organisms in Rubiaceae that have their 'web visible' organismprop set
437 =head3 web_visible_Plantaginaceae
439 organisms in Plantaginaceae that have their 'web visible' organismprop set
443 has
'organism_sets' => (
447 ); sub _build_organism_sets
{
449 my $schema = $self->_app->dbic_schema('Bio::Chado::Schema','sgn_chado');
452 # define a set of SOL100 organisms
453 $org_sets{'sol100'} = {
454 description
=> 'SOL100 Organisms',
455 root_species
=> 'Solanaceae',
456 resultset
=> $schema->resultset( "Cv::Cvterm" )
457 ->search({ name
=> 'sol100' })
458 ->search_related( 'organismprops' )
459 ->search_related_rs( 'organism' )
462 # define sets of web-visible organisms, by family
463 for my $family (qw( Solanaceae Rubiaceae Plantaginaceae )) {
464 my $pns = $schema->resultset('Organism::Organism')
465 ->search({ 'me.species' => $family })
466 ->search_related('phylonode_organisms')
467 ->search_related('phylonode',
468 { 'cv.name' => 'taxonomy' },
469 { join => { type
=> 'cv' }},
472 $pns = $self->_child_phylonodes( $pns )
473 ->search_related_rs('phylonode_organism');
475 # set of all organisms in that family
476 $org_sets{$family} = {
477 description
=> $family,
478 root_species
=> $family,
479 resultset
=> $pns->search_related_rs('organism'),
482 # set of only web-visible organisms in that family
483 $org_sets{"web_visible_$family"} = {
484 description
=> $family,
485 root_species
=> $family,
486 resultset
=> $pns->search_related_rs(
488 { 'cv.name' => 'local',
489 'type.name' => 'web visible',
491 { join => { organismprops
=> { type
=> 'cv' }}},
498 # take a resultset of phylonodes, construct a resultset of the child
499 # phylonodes. temporary workaround until the extended_rels branch is
500 # merged into DBIx::Class and DBIx::Class::Tree::NestedSet is ported
502 sub _child_phylonodes
{
503 my ( $self, $phylonodes ) = @_;
505 my %child_phylonode_conditions;
506 while( my $pn = $phylonodes->next ) {
507 push @
{ $child_phylonode_conditions{ '-or' }} => {
508 'left_idx' => { '>' => $pn->left_idx },
509 'right_idx' => { '<' => $pn->right_idx },
510 'phylotree_id' => $pn->phylotree_id,
514 return $phylonodes->result_source->resultset
515 ->search( \
%child_phylonode_conditions );
519 =head2 species_data_summary_cache
521 L<Cache> object containing species data summaries, as:
525 'Common Name' => common_name,
531 Access with C<$controller-E<gt>species_data_summary_cache->thaw($organism_id )>,
532 do not use Cache's C<get> method.
536 has
'species_data_summary_cache' => (
539 ); sub _build_species_data_summary_cache
{
540 my ($cache_class, $config) = shift->_species_summary_cache_configuration;
541 return $cache_class->new( %$config );
544 sub _species_summary_cache_configuration
{
547 my $schema = $self->_app->dbic_schema( 'Bio::Chado::Schema', 'sgn_chado' );
549 return 'Cache::File', {
550 cache_root
=> $self->_app->path_to( $self->_app->tempfiles_subdir('species_summary_cache') ),
551 default_expires
=> '6 hours',
553 load_callback
=> sub {
554 my $cache_entry = shift;
555 my $org_id = $cache_entry->key;
556 my $org = CXGN
::Chado
::Organism
->new( $schema, $org_id )
558 no warnings
'uninitialized';
559 return Storable
::nfreeze
({
560 'Common Name' => $org->get_group_common_name,
561 'Loci' => $org->get_loci_count,
562 'Phenotypes' => $org->get_phenotype_count,
563 'Maps Available' => $org->has_avail_map,
564 'Genome Information' => $org->has_avail_genome ?
'yes': 'no',
565 'Libraries' => scalar( $org->get_library_list ),
571 =head2 rendered_organism_tree_cache
573 A cache of rendered organism trees, as
577 newick => 'newick string',
579 image_map => 'html image map',
580 image_map_name => 'name of the image map for <img usemap="" ... />',
585 has
'rendered_organism_tree_cache' => (
588 ); sub _build_rendered_organism_tree_cache
{
592 cache_root
=> $self->_app->path_to( $self->_app->tempfiles_subdir('cache','rendered_organism_tree_cache') ),
593 default_expires
=> 'never',
595 load_callback
=> sub {
596 my $set_name = shift->key;
597 my $set = $self->organism_sets->{ $set_name };
598 my $root_species = $set->{root_species
} or die "no root species defined for org set $set_name";
599 my $species_names = [ $set->{resultset
}->get_column('species')->all ];
601 if( @
$species_names ) {
602 my $orgtree = $self->_render_organism_tree(
603 $self->_app->dbic_schema('Bio::Chado::Schema','sgn_chado'),
607 return Storable
::nfreeze
( $orgtree );
610 return Storable
::nfreeze
( {} );
616 # takes dbic schema, root species name, arrayref of species names to
617 # render returns hashref of newick string, png data, and an HTML image
621 # newick => 'newick string',
623 # image_map => 'html image map',
624 # image_map_name => 'name of the image map for <img usemap="" ... />',
626 sub _render_organism_tree
{
627 my ( $self, $schema, $root_species, $species_names ) = @_;
629 $self->_app->log->debug( "rendering org for root species '$root_species'" ) if $self->_app->debug;
631 my $tree = CXGN
::Phylo
::OrganismTree
->new( $schema );
634 my $newick_string = $tree->build_tree(
637 $self->species_data_summary_cache,
640 my $cache = $self->species_data_summary_cache();
641 foreach my $n (@
$species_names) {
642 my $ors = CXGN
::Chado
::Organism
::get_organism_by_species
($n, $schema);
645 my $genome_info = $cache->thaw($ors->organism_id())->{'Genome Information'};
646 if ($genome_info =~ /y/i) {
647 $tree->hilite_species([170,220,180], [$n]);
651 my $image_map_name = $root_species.'_map';
652 my $image_map = $tree->get_renderer
653 ->get_html_image_map( $image_map_name );
654 my $image_png = $tree->render_png( undef, 1 );
657 newick
=> $newick_string,
659 image_map
=> $image_map,
660 image_map_name
=> $image_map_name,
669 =head2 qtl_populations
671 Usage: my @qtl_data = qtl_populations($common_name);
672 Desc: returns a list of qtl populations (hyperlinked to the pop page)
673 and counts of traits assayed for QTL for the corresponding population
674 Ret: an array of array of populations and trait counts or undef
675 Args: organism group common name
683 sub qtl_populations
{
684 my $gr_common_name = shift;
685 my $qtl_tool = CXGN
::Phenome
::Qtl
::Tools
->new();
687 my @org_pops = $qtl_tool->qtl_pops_by_common_name($gr_common_name);
691 foreach my $org_pop (@org_pops) {
692 my $pop_id = $org_pop->get_population_id();
693 my $pop_name = $org_pop->get_name();
694 my $pop_link = qq |<a href
="/qtl/view/$pop_id">$pop_name</a
>|;
695 my @traits = $org_pop->get_cvterms();
696 my $count = scalar(@traits);
698 push @pop_data, [ map { $_ } ( $pop_link, $count ) ];
707 my $organism = shift;
708 my $parent = $organism->get_parent();
712 my $species = $parent->get_species();
713 my $taxon = $parent->get_taxon();
715 push @taxonomy, tooltipped_text
( $species, $taxon );
716 @taxonomy = (@taxonomy, get_parentage
($parent));
721 __PACKAGE__
->meta->make_immutable;