PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/WormBase/API/Object.pm

https://bitbucket.org/tharris/wormbase/
Perl | 1044 lines | 688 code | 143 blank | 213 comment | 91 complexity | 436f85ced7d91dd8052847568d41390e MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. package WormBase::API::Object;
  2. use Moose;
  3. use overload '~~' => \&_overload_ace, fallback => 1;
  4. =head1 NAME
  5. WormBase::Model - Model superclass
  6. =head1 DESCRIPTION
  7. The WormBase model superclass. Methods that need to be accessed in
  8. more than a single model belong here.
  9. =head1 METHODS
  10. =head1 AUTHOR
  11. Todd Harris
  12. =head1 LICENSE
  13. This library is free software, you can redistribute it and/or modify
  14. it under the same terms as Perl itself.
  15. =cut
  16. sub _overload_ace {
  17. my ($self,$param)=@_;
  18. if($param =~ s/^@//) {my @results=eval {$self->object->$param}; return \@results;}
  19. else { return eval {$self->object->$param};}
  20. }
  21. #use Bio::Graphics::Browser;
  22. # extends 'WormBase::API';
  23. # Provided with a list of objects, turn them into a data structure like
  24. # data => { obj1 => { id => 'OBJECT ID',
  25. # label => 'text label' // generically object name
  26. # class => 'OBJECT CLASS',
  27. # },
  28. # }
  29. sub _wrap {
  30. my $self = shift;
  31. my @objects = grep defined, @_ or return;
  32. my @wrapped = map {
  33. my $class = WormBase::API::ModelMap->ACE2WB_MAP->{class}->{$_->class};
  34. # or croak "Cannot find WB class for Ace class ", $_->class;
  35. WormBase::API::Factory->create($class, {
  36. object => $_->fetch, # should step into object, if haven't done so
  37. dsn => $self->dsn,
  38. pre_compile => $self->pre_compile,
  39. tmp_base => $self->tmp_base,
  40. log => $self->log,
  41. });
  42. } @objects; # end of map
  43. # User might have passed and expected just a single object
  44. return wantarray ? @wrapped : $wrapped[0];
  45. }
  46. # Get a direct handle to the AceDB.
  47. # DEPRECATED? REDUNDANT?
  48. sub dbh_ace { shift->{ace_model}->{dbh}; }
  49. #################################################
  50. #
  51. # AGGREGATED INFORMATION
  52. #
  53. # The following methods serve to aggregate
  54. # information that occur in multiple Acedb
  55. # classes. They do not correspond to simple
  56. # tags.
  57. #
  58. ################################################
  59. sub reactome_knowledgebase {
  60. my ($self,$proteins) = @_;
  61. my @data;
  62. foreach my $protein (@$proteins) {
  63. my @db_entry = $protein->at('DB_info.Database');
  64. my ($reactome_name,$reactome_id);
  65. foreach (@db_entry) {
  66. next unless $_ eq 'Reactome';
  67. my @fields = $_->row;
  68. my $reactome_id = $fields[2];
  69. # VIEW
  70. # TODO: This needs to be in the template
  71. # push @rows,a({-href=>sprintf($fields[0]->URL_constructor,$reactome_id)},$reactome_id);
  72. push @data,$reactome_id;
  73. }
  74. }
  75. return \@data;
  76. }
  77. # Fetch all of the strains for a given object and extract some pertinent
  78. # info (like is it available from the CGC).
  79. sub strains {
  80. my ($self) = @_;
  81. my $object = $self->object;
  82. my @strains;
  83. foreach ($object->Strain(-filled=>1)) {
  84. my $cgc = ($_->Location eq 'CGC') ? 1 : 0;
  85. my @genes = $_->Gene;
  86. my $is_solo = (@genes == 1) ? 1 : 0;
  87. # Boolean flags for CGC availability, is a strain only carrying the gene
  88. push @strains,["$_",$cgc,$is_solo];
  89. }
  90. return \@strains;
  91. }
  92. # This could be generic. See also Variation.
  93. sub alleles {
  94. my ($self) = @_;
  95. my $object = $self->object;
  96. # Typically fetching alleles from gene, but might also be from variation
  97. $object = ($object->class eq 'Gene') ? $object : $object->Gene;
  98. my @clean;
  99. foreach ($object->Allele) {
  100. unless ($_->SNP) {
  101. push @clean,"$_";
  102. }
  103. }
  104. return \@clean;
  105. }
  106. sub polymorphisms {
  107. my ($self) = @_;
  108. my $object = $self->object;
  109. # Typically fetching alleles from gene, but might also be from variation
  110. $object = ($object->class eq 'Gene') ? $object : $object->Gene;
  111. my @poly;
  112. foreach ($object->Allele) {
  113. if ($_->SNP) {
  114. push @poly,"$_";
  115. }
  116. }
  117. return \@poly;
  118. }
  119. # This is used in Gene::inparanoid_groups.
  120. # This grosses me out.
  121. sub wb_protein {
  122. my ($self,$species) = @_;
  123. return 1 if ($species =~ /elegans|briggsae|pacificus|brenneri|jacchus|hapla|japonica|remanei|malayi|brenneri|incognita|contortus/i);
  124. return 0;
  125. }
  126. # Map a given ID to a species (This might also be a method instead of an ID)
  127. # Because of recpirocal BLASTing with elegans and briggsae and database XREFs
  128. # always try to use the ID of the hit first when doing identifications
  129. # This is used in Gene::inparanoid_groups();
  130. sub id2species {
  131. my ($self,$id) = @_;
  132. # Ordered according to (guesstimated) probability
  133. # It *seems* like this belongs in configuration but
  134. # it requires regexps...
  135. return 'Caenorhabditis briggsae' if ($id =~ /WP\:CBP/i || $id =~ /briggsae/ || $id =~ /^BP/);
  136. return 'Caenorhabditis elegans' if ($id =~ /worm/i || $id =~ /^WP/);
  137. return 'Caenorhabditis remanei' if ($id =~ /^RP\:/i || $id =~ /remanei/) ; # Temporary IDs for C. remanei
  138. return 'Drosophila melanogaster' if ($id =~ /fly.*\:CG/i);
  139. return 'Drosophila pseudoobscura' if ($id =~ /fly.*\:GA/i);
  140. return 'Saccharomyces cerevisiae' if ($id =~ /^SGD/i || $id =~ /yeast/);
  141. return 'Schizosaccharomyces pombe' if ($id =~ /pombe/);
  142. return 'Homo sapiens' if ($id =~ /ensembl/ || $id =~ /ensp\d+/);
  143. return 'Rattus norvegicus' if ($id =~ /rgd/i);
  144. return 'Anopheles gambiae' if ($id =~ /ensang/i);
  145. return 'Apis mellifera' if ($id =~ /ensapmp/i);
  146. return 'Canis familiaris' if ($id =~ /enscafp/i);
  147. return 'Danio rerio' if ($id =~ /ensdarp/i);
  148. return 'Dictyostelium discoideum' if ($id =~ /ddb\:ddb/i);
  149. return 'Fugu rubripes' if ($id =~ /sinfrup/i);
  150. return 'Gallus gallus' if ($id =~ /ensgalp/i);
  151. return 'Mus musculus' if ($id =~ /mgi/i);
  152. return 'Oryza sativa' if ($id =~ /^GR/i);
  153. return 'Pan troglodytes' if ($id =~ /ensptrp/i);
  154. return 'Plasmodium falciparum' if ($id =~ /pfalciparum/);
  155. return 'Tetraodon nigroviridis' if ($id =~ /gstenp/i);
  156. }
  157. # Generically construct a GBrowse img
  158. # Args: Bio::DB::GFF segment,species, [ tracks ], { options }, width
  159. # This is typically called by genomic_environs() ina subclass...
  160. sub build_gbrowse_img {
  161. my ($self,$segment,$tracks,$options,$width) = @_;
  162. my $species = $self->_parsed_species();
  163. # open the browser for drawing pictures
  164. my $BROWSER = Bio::Graphics::Browser->new or die;
  165. # NOTE! The path to the configuration directory MUST be supplied by W::M::* adaptor!
  166. $BROWSER->read_configuration($self->{gbrowse_conf_dir}) or die "Couldn't read or find the gbrowse configuration directory";
  167. $BROWSER->source($species);
  168. $BROWSER->width($width || '500');
  169. $BROWSER->config->set('general','empty_tracks' => 'suppress');
  170. $BROWSER->config->set('general','keystyle' => 'none');
  171. my $absref = $segment->abs_ref;
  172. my $absstart = $segment->abs_start;
  173. my $absend = $segment->abs_end;
  174. ($absstart,$absend) = ($absend,$absstart) if $absstart>$absend;
  175. my $length = $segment->length;
  176. # add another 10% to left and right
  177. my $start = int($absstart - 0.1*$length);
  178. my $stop = int($absend + 0.1*$length);
  179. my $db = $segment->factory;
  180. my ($new_segment) = $db->segment(-name=>$absref,
  181. -start=>$start,
  182. -stop=>$stop);
  183. my ($img,$junk) = $BROWSER->render_panels({segment => $new_segment,
  184. options => \%$options,
  185. labels => $tracks,
  186. title => "Genomic segment: $absref:$absstart..$absend",
  187. keystyle => 'between',
  188. do_map => 0,
  189. drag_n_drop => 0,
  190. });
  191. $img =~ s|/Users/todd/Documents/projects/wormbase/website/trunk/root||g;
  192. $img =~ s/border="0"/border="1"/;
  193. $img =~ s/detailed view/browse region/g;
  194. $img =~ s/usemap=\S+//;
  195. my %data = (
  196. img => $img,
  197. start => $start,
  198. stop => $stop,
  199. species => $species,
  200. chromosome => $absref);
  201. return \%data;
  202. }
  203. # DEPRECATED?
  204. #################################################
  205. # Phenotypes
  206. # (Was: DisplayPhenotypes, is_NOT_phene, FormatPhenotypeHash, etc)
  207. # (ie used in RNAi, Seq, Variation)
  208. #################################################
  209. # Return a list of phenotypes observed
  210. sub phenotypes_observed {
  211. my ($self) = @_;
  212. my $object = $self->object;
  213. my $phenes = $self->_get_phenotypes($object);
  214. return $phenes;
  215. }
  216. # Return a list of phenotypes not observed
  217. sub phenotypes_not_observed {
  218. my ($self) = @_;
  219. my $object = $self->object;
  220. my $phenes = $self->_get_phenotypes($object,'NOT');
  221. return $phenes;
  222. }
  223. sub _get_phenotypes {
  224. my ($self,$object,$not) = @_;
  225. my $positives = [];
  226. my $negatives = [];
  227. my (@phenotypes) = $object->Phenotype;
  228. my $data = $self->_parse_hash(\@phenotypes);
  229. ($positives,$negatives) = $self->_is_NOT_phene($data);
  230. my $parsed;
  231. if ($not) {
  232. $parsed = $self->_parse_phenotype_hash($negatives);
  233. } else {
  234. $parsed = $self->_parse_phenotype_hash($positives);
  235. }
  236. return $parsed;
  237. }
  238. # Determine which of a list of Phenotypes are NOTs
  239. # Return a sorted list of positive/not positive phenotypes
  240. sub _is_NOT_phene {
  241. my ($self,$data) = @_;
  242. my $positives = [];
  243. my $negatives = [];
  244. foreach my $entry (@$data) {
  245. if ($entry->{is_not}) {
  246. push @$negatives,$entry;
  247. } else {
  248. push @$positives,$entry;
  249. }
  250. }
  251. return ($positives,$negatives);
  252. }
  253. # Return the best name for a phenotype object. This is really common_name...
  254. # Pick the best display new for new Phenotype-ontology objects
  255. # and append a short name if one exists
  256. sub best_phenotype_name {
  257. my ($self,$phenotype) = @_;
  258. my $name = ($phenotype =~ /WBPheno.*/) ? $phenotype->Primary_name : $phenotype;
  259. $name =~ s/_/ /g;
  260. $name = $phenotype->Short_name . " ($name)" if $phenotype->Short_name;
  261. return $name;
  262. }
  263. # Was: ElegansSubs::AlleleDescription and MutantPhenotype
  264. # TODO: Need to attach the evidence to each Remark
  265. # TODO: This probably should be a function of the VIEW
  266. sub phenotype_remark {
  267. my ($self) = @_;
  268. my $object = $self->object;
  269. # Some inconsistency in Ace models here
  270. my @remarks = $object->Remark,
  271. eval { $object->Phenotype_remark },
  272. eval { $object->Phenotype };
  273. my $formatted_remarks = $self->_cross_reference_remarks(\@remarks);
  274. # push @desc,GetEvidenceNew(-object => $allele->Phenotype_remark,
  275. # -format => 'inline',
  276. # -display_label => 1);
  277. # push @desc,GetEvidenceNew(-object => $allele->Remark,
  278. # -format => 'inline',
  279. # -display_label => 1);
  280. # return unless @desc;
  281. # return join(br,@desc); # . '.'; Don't add punctuation, Mary Ann does
  282. return $formatted_remarks;
  283. }
  284. # This was MutantPhenotype
  285. sub _cross_reference_remarks {
  286. my ($self,$remarks) = @_;
  287. # cross-reference laboratories
  288. foreach my $d (@$remarks) {
  289. $d =~ s/;\s+([A-Z]{2})(?=[;\]])
  290. /"; ".ObjectLink($1,undef,'Laboratory')
  291. /exg;
  292. # cross-reference genes
  293. #### $d =~ s/\b([a-z]+-\d+)\b
  294. #### /ObjectLink($1,undef,'Locus')
  295. #### /exg;
  296. # cross-reference other stuff
  297. #### my %xref = map {$_=>$_} @xref;
  298. #### $d =~ s/\b(.+?)\b/$xref{$1} ? ObjectLink($xref{$1}) : $1/gie;
  299. }
  300. return $remarks;
  301. }
  302. # TODO: This should be in SUPER
  303. # TODO: Part of View? Part of Model::Super?
  304. sub fasta {
  305. my ($self,$name,$protein) = @_;
  306. $protein ||= '';
  307. my @markup;
  308. for (my $i=0; $i < length $protein; $i += 10) {
  309. push (@markup,[$i,$i % 80 ? ' ':"\n"]);
  310. }
  311. $self->markup(\$protein,\@markup);
  312. return $protein;
  313. }
  314. # insert HTML tags into a string without disturbing order
  315. sub markup {
  316. my ($self,$string,$markups) = @_;
  317. for my $m (sort {$b->[0]<=>$a->[0]} @$markups) { #insert later tags first so position remains correct
  318. my ($position,$markup) = @$m;
  319. next unless $position <= length $$string;
  320. substr($$string,$position,0) = $markup;
  321. }
  322. }
  323. #################################################
  324. # Hash parsing and formatting
  325. # Was: ParseHash FormatEvidenceHash, etc
  326. #
  327. # I'm not sure where this belongs.
  328. # On one hand, we should be able to deliver this
  329. # information via webservices. That is, it shouldn't
  330. # be locked up in the view.
  331. #
  332. # On the other, it seems much more view specific.
  333. #
  334. # 2. I should just have top level categories like
  335. # parse_evidence_hash, parse_molecular_change_hash, etc.
  336. # These would return data structure suitable for display
  337. # in the view.
  338. #
  339. # In fact, these could also be actions and ajax targets (ie /parse_hash/node, maybe)
  340. #
  341. # I'd also like to make the parse_hash method private so that it is
  342. # only called by the parse* methods. Unfortunately, some formatting
  343. # needs just the raw hash -- there is code redundancy with duplication
  344. # in parsing, formatting, view decisions, etc. See for example,the
  345. # RNAi phenoypes section of Gene.pm
  346. #
  347. # To further confuse matters, I have a parse_hash, evidence
  348. # formatting, etc as part of the view. Clearly, this requires
  349. # additional thought.
  350. #
  351. #################################################
  352. =pod
  353. =item _parse_hash(@params)
  354. Generically parse an evidence, paper, or molecular info
  355. hash from a node of an object tree.
  356. Options
  357. -node A node of an object tree (or an array reference of nodes)
  358. If an array reference of nodes is passed, the resulting data structure
  359. will be an array of structures.
  360. Returns
  361. A data structure suitable for further parsing/display or
  362. null if no Evidence hash exists to the right of the provided node.
  363. =cut
  364. #'
  365. sub _parse_year {
  366. my ($self,$date) = @_;
  367. $date =~ /(\d\d\d\d)/;
  368. my $year = $1 || $date;
  369. return $year;
  370. }
  371. sub check_empty {
  372. # if flag == 0 meaning empty to the right
  373. my ($self,$nodes)=@_;
  374. $nodes = [$nodes] unless ref $nodes eq 'ARRAY';
  375. my $flag = 0;
  376. foreach my $node (@$nodes) {
  377. foreach my $type ($node->col) {
  378. $flag = 1;
  379. last;
  380. }
  381. last if($flag);
  382. }
  383. return $flag;
  384. }
  385. sub evidence {
  386. my ($self,$tag)=@_;
  387. my @nodes=$self->object->$tag;
  388. return $self->_get_evidence(@nodes);
  389. }
  390. sub _get_evidence {
  391. my ($self,$nodes,$evidence_type)=@_;
  392. $nodes = [$nodes] unless ref $nodes eq 'ARRAY';
  393. my %data;
  394. foreach my $node (@$nodes) {
  395. next unless $node;
  396. foreach my $type ($node->col) {
  397. next if ($type eq 'CGC_data_submission') ;
  398. #if only extracting one/more specific evidence types
  399. if(defined $evidence_type) {
  400. next unless(grep /^$type$/ , @$evidence_type);
  401. }
  402. #the goal is to deal label and link seperately?
  403. foreach my $evidence ($type->col) {
  404. my $label = $evidence;
  405. my $class = eval { $evidence->class } ;
  406. if ($type eq 'Paper_evidence') {
  407. my @authors = eval { $evidence->Author };
  408. my $authors = @authors <= 2 ? (join ' and ',@authors) : "$authors[0] et al.";
  409. my $year = $self->_parse_year($evidence->Publication_date);
  410. $label = "$authors, $year";
  411. } elsif ($type eq 'Person_evidence' || $type eq 'Curator_confirmed') {
  412. $label = $evidence->Standard_name;
  413. } elsif ($type eq 'Accession_evidence') {
  414. my ($database,$accession) = $evidence->row;
  415. if(defined $accession && $accession) {
  416. ($evidence,$class) = ($accession,$database);
  417. $label = "$database:$accession";
  418. }
  419. } elsif($type eq 'GO_term_evidence') {
  420. my $desc = $evidence->Term || $evidence->Definition;
  421. $label .= (($desc) ? "($desc)" : '');
  422. }elsif ($type eq 'Protein_id_evidence') {
  423. $class = "Entrezp";
  424. } elsif ($type eq 'RNAi_evidence') {
  425. $label = $evidence->History_name? $evidence . ' (' . $evidence->History_name . ')' : $evidence;
  426. } elsif ($type eq 'Date_last_updated') {
  427. $label =~ s/\s00:00:00//;
  428. undef $class;
  429. }
  430. # $type =~ s/_/ /g;
  431. $data{$type}{$evidence}{id} = "$evidence";
  432. $data{$type}{$evidence}{label} = "$label";
  433. $data{$type}{$evidence}{class} = lc($class) if(defined $class);
  434. }
  435. }
  436. }
  437. return \%data;
  438. }
  439. sub _parse_hash {
  440. my ($self,$nodes) = @_;
  441. # Mimic the passing of an array reference. Blech.
  442. $nodes = [$nodes] unless ref $nodes eq 'ARRAY';
  443. # The data structure - a hash of hashes, each pointing to an array
  444. my $data = [];
  445. # Collect all the hashes available for each node
  446. foreach my $node (@$nodes) {
  447. # Save all the top level tags as keys in a perl
  448. # hash for easier parsing and formatting
  449. my %hash = map { $_ => $_ } eval { $node->col };
  450. my $is_not = 1 if (defined $hash{Not}); # Keep track if this is a Not Phene annotation
  451. push @{$data},{ node => $node,
  452. hash => \%hash,
  453. is_not => $is_not || 0,
  454. };
  455. }
  456. return $data;
  457. }
  458. # NOT DONE YET!
  459. sub _parse_evidence_hash {
  460. my @p = @_;
  461. my ($data,$format,$display_tag,$link_tag,$display_label,$detail) =
  462. rearrange([qw/DATA FORMAT DISPLAY_TAG LINK_TAG DISPLAY_LABEL DETAIL/],@p);
  463. my @rows; # Each row in the table corresponds to an object (each row is stringified)
  464. my $join = ($format eq 'table') ? '<br>' : ', ';
  465. my $all_evidence = {};
  466. foreach my $entry (@$data) {
  467. my $hash = $entry->{hash};
  468. my $node = $entry->{node};
  469. # Conditionally format the data for each type of evidence
  470. foreach my $key (keys %$hash) {
  471. my $type = $hash->{$key};
  472. # Suppress the display of Curator_confirmed
  473. next if $type eq 'Curator_confirmed';
  474. # Just grab the first level entries for each.
  475. # For the evidence hash, Accession_evidence and Author_evidence
  476. # have additional information
  477. my @sources = eval { $type->col };
  478. # Add appropriate markup for each type of Evidence seen
  479. # Lots of redundancy here - first we parse the data, then add primary formatting
  480. # then secondary formatting (ie table, etc)
  481. # This could all be much cleaner (albeit less flexible) with templates
  482. if ($type eq 'Paper_evidence') {
  483. #!! my @papers = _format_paper_evidence(\@sources);
  484. #!! $data = join($join,@papers);
  485. } elsif ($type eq 'Published_as') {
  486. #!! $data = join($join,map { ObjectLink($_,undef,'_blank') } @sources);
  487. } elsif ($type eq 'Person_evidence' || $type eq 'Curator_confirmed') {
  488. #!! $data = join($join,map {ObjectLink($_->Standard_name,undef,'_blank')} @sources);
  489. } elsif ($type eq 'Author_evidence') {
  490. #!! $data = join($join,map { a({-href=>'/db/misc/author?name='.$_,-target=>'_blank'},$_) } @sources);
  491. } elsif ($type eq 'Accession_evidence') {
  492. foreach my $entry (@sources) {
  493. my ($database,$accession) = $entry->row;
  494. #!! my $accession_links ||= Configuration->Protein_links; # misnomer
  495. #!! my $link_rule = $accession_links->{$database};
  496. #!! $data = $link_rule ? a({-href=>sprintf($link_rule,$accession),
  497. #!! -target=>'_blank'},"$database:$accession")
  498. #!! : ObjectLink($accession,"$database:$accession");
  499. }
  500. } elsif ($type eq 'Protein_id_evidence') {
  501. #!! $data = join($join,map { a({-href=>Configuration->Entrezp},$_) } @sources);
  502. # Lots of generic entries that just need to be linked
  503. } elsif ($type eq 'GO_term_evidence' || $type eq 'Laboratory_evidence') {
  504. #!! $data = join($join,map {ObjectLink($_) } @sources);
  505. } elsif ($type eq 'Expr_pattern_evidence') {
  506. #!! $data = join($join,map {ObjectLink($_) } @sources);
  507. } elsif ($type eq 'Microarray_results_evidence') {
  508. #!! $data = join($join,map {ObjectLink($_) } @sources);
  509. } elsif ($type eq 'RNAi_evidence') {
  510. #!! $data = join($join,map {ObjectLink($_,$_->History_name ? $_ . ' (' . $_->History_name . ')' : $_) } @sources);
  511. } elsif ($type eq 'Gene_regulation_evidence') {
  512. #!! $data = join($join,map {ObjectLink($_) } @sources);
  513. } elsif ($type eq 'CGC_data_submission') {
  514. } elsif ($type =~ /Inferred_automatically/i) {
  515. #!! $data = join($join,map { $_ } @sources);
  516. } elsif ($type eq 'Date_last_updated') {
  517. #!! ($data) = @sources;
  518. #!! $data =~ s/\s00:00:00//;
  519. }
  520. $type =~ s/_/ /g;
  521. # Retain $node again since this is an object
  522. push @{$all_evidence->{$type}},
  523. { type => $type,
  524. data => $data,
  525. node => $node,
  526. };
  527. }
  528. }
  529. # Format the evidence as requested
  530. my $return;
  531. if ($format eq 'table') {
  532. foreach my $tag (keys %$all_evidence) {
  533. my @evidence = @{$all_evidence->{$tag}};
  534. my $table =
  535. start_table()
  536. . TR(th('Evidence type')
  537. . th('Source'));
  538. my $count = 0;
  539. foreach (@evidence) {
  540. my $node = $_->{node};
  541. # Only need to do this for the first iteration
  542. if ($count == 0) {
  543. if ($display_tag) {
  544. $link_tag = 1 if $node eq 'Evidence'; # hack for cases in which evidence is attached
  545. #!! my $description = $link_tag ? $node :
  546. #!! ref $node && $node->class eq 'Gene_name' ?
  547. #!! a({-href=>Object2URL(GeneName2Gene($node))},$node)
  548. #!! : ObjectLink($node);
  549. #!! $return .= $description;
  550. }
  551. $count++;
  552. $return .= h3('Supported by:');
  553. }
  554. my $type = $_->{type};
  555. my $data = $_->{data};
  556. #!! $table .= TR(td({-valign=>'top'},$type),
  557. #!! td($data));
  558. }
  559. #!! $table .= end_table();
  560. #!! $return .= $table;
  561. }
  562. } else {
  563. # Returning stringified form of evidence
  564. my @entries;
  565. foreach my $tag (keys %$all_evidence) {
  566. my @evidence = @{$all_evidence->{$tag}};
  567. my $count = 0;
  568. foreach (@evidence) {
  569. my $node = $_->{node};
  570. if ($count == 0) { # necessary on first iteration only. stoopid, I know
  571. if ($display_tag) {
  572. $link_tag = 1 if $node eq 'Evidence'; # hack for cases in which evidence is attached
  573. #!! my $description = $link_tag ? $node :
  574. #!! ref $node && $node->class eq 'Gene_name' ?
  575. #!! a({-href=>Object2URL(GeneName2Gene($node))},$node)
  576. #!! : ObjectLink($node);
  577. #!! $return .= $description;
  578. }
  579. $count++;
  580. }
  581. my $type = $_->{type};
  582. my $data = $_->{data};
  583. push @entries,($display_label) ? "via " . lc($type) . ': ' . $data : $data;
  584. }
  585. }
  586. $return .= join('; ',@entries);
  587. }
  588. return undef unless $return;
  589. return $return;
  590. }
  591. ## Data is a collection of one or more phenotype
  592. ## hashes with top-level tags already extracted
  593. ## THIS COULD MOVE TO THE VIEW...
  594. sub _parse_phenotype_hash {
  595. my ($self,$data) = @_;
  596. # These tags have a single entry following them
  597. # They should *not* have any evidence hashes, either
  598. # The contents of these entries can be fetched as
  599. # $tag->col
  600. my %evidence_only = map { $_ => 1 }
  601. qw/
  602. Not
  603. Recessive
  604. Semi_dominant
  605. Dominant
  606. Haplo_insufficient
  607. Paternal
  608. /;
  609. my %simple = map { $_ => 1 }
  610. qw/
  611. Quantity_description
  612. /;
  613. my %nested = map { $_ => 1 }
  614. qw/
  615. Penetrance
  616. Quantity
  617. Loss_of_function
  618. Gain_of_function
  619. Other_allele_type
  620. Temperature_sensitive
  621. Maternal
  622. Phenotype_assay
  623. /;
  624. my %is_row = map { $_ => 1 } qw/Quantity Range/;
  625. # Prioritize display of tags
  626. my @tags = qw/
  627. Not
  628. Penetrance Recessive Semi_dominant Dominant
  629. Haplo_insufficient
  630. Loss_of_function
  631. Gain_of_function
  632. Other_allele_type
  633. Temperature_sensitive
  634. Maternal
  635. Paternal
  636. Phenotype_assay
  637. Quantity_description
  638. Quantity
  639. Paper_evidence
  640. Person_evidence
  641. Remark
  642. /;
  643. my $stash = [];
  644. foreach my $entry (@$data) {
  645. my @this_data = ();
  646. my $hash = $entry->{hash};
  647. my $node = $entry->{node}; # Node is the originating object
  648. foreach my $tag_priority (@tags) {
  649. next unless (my $tag = $hash->{$tag_priority});
  650. my $formatted_tag = $tag;
  651. $formatted_tag =~ s/_/ /g;
  652. # Fetch the first entries to the right of each tag
  653. my @sources = eval { $tag->col };
  654. # Add appropriate markup for each tag seen
  655. # Lots of redundancy here - first we parse the data, then add primary formatting
  656. # then secondary formatting (ie table, etc)
  657. if ($tag eq 'Paper_evidence') {
  658. # We will format the papers elswhere
  659. # @sources = _format_paper_evidence(\@sources);
  660. } elsif ($tag eq 'Person_evidence' || $tag eq 'Curator_confirmed') {
  661. } elsif (defined $evidence_only{$tag}) {
  662. @sources = ( $tag );
  663. } elsif ($tag eq 'Remark' || $tag eq 'Quantity_description') {
  664. } elsif ($tag eq 'Phenotype_assay') {
  665. my @parsed;
  666. # Step into the Phenotype_assay object, displaying select tags.
  667. if (@sources) {
  668. my ($cell,$evidence);
  669. foreach my $condition (@sources) {
  670. if ($data) {
  671. # TODO: FETCH THE EVIDENCE FROM $data
  672. }
  673. push @parsed,"$condition: $data";
  674. }
  675. @sources = @parsed;
  676. }
  677. # Handle tags that contain substructure
  678. } elsif (defined $nested{$tag}) {
  679. my @subtags = $tag->col;
  680. @subtags = $tag if $tag eq 'Quantity';
  681. my @cells;
  682. foreach my $subtag (@subtags) {
  683. # Ignore the value if we have an Evidence hash
  684. # to the right. All set to fetch evidence
  685. my ($value,$evi);
  686. if ($subtag->right =~ /evidence/) {
  687. } else {
  688. # HACK - Range and Quantity are rows
  689. if (defined ($is_row{$subtag})) {
  690. my (@values) = $subtag->row;
  691. $value = join("-",$values[1],$values[2]);
  692. $value = '100%' if $value eq '100-100';
  693. } else {
  694. $value = $subtag->right;
  695. }
  696. }
  697. $subtag =~ s/_/ /g;
  698. $formatted_tag = "$formatted_tag: $subtag";
  699. @sources = ($value);
  700. }
  701. }
  702. push @this_data,[$formatted_tag,\@sources];
  703. }
  704. push @{$stash},{ node => $node,
  705. rows => \@this_data };
  706. }
  707. return $stash;
  708. }
  709. # THIS PROBABLY BELONGS AS A COMPONENT OF THE VIEW INSTEAD OF THE MODEL
  710. sub _parse_molecular_change_hash {
  711. my ($self,$entry,$tag) = @_;
  712. # Generically parse the hash
  713. my $data = $self->_parse_hash($entry);
  714. return unless keys %{$data} >= 1; # Nothing to build a table from
  715. my @types = qw/Missense Nonsense Frameshift Silent Splice_site/;
  716. my @locations = qw/Intron Coding_exon Noncoding_exon Promoter UTR_3 UTR_5 Genomic_neighbourhood/;
  717. # Select items that we will try and translate
  718. # Currently, this needs to be
  719. # 1. Affects Predicted_CDS
  720. # 2. A missense or nonsense allele
  721. # 3. Contained in a coding_exon
  722. my %parameters_seen;
  723. my %do_translation = map { $_ => 1 } (qw/Missense Nonsense/);
  724. # Under no circumstances try and translate the following
  725. my %no_translation = map { $_ => 1 } (qw/Frameshift Deletion Insertion/);
  726. # The following entries should be examined for the presence
  727. # of associated Evidence hashes
  728. my @with_evidence =
  729. qw/
  730. Missense
  731. Silent
  732. Nonsense
  733. Splice_site
  734. Frameshift
  735. Intron
  736. Coding_exon
  737. Noncoding_exon
  738. Promoter
  739. UTR_3
  740. UTR_5
  741. Genomic_neighbourhood
  742. /;
  743. my (@protein_effects,@contained_in);
  744. foreach my $entry (@$data) {
  745. my $hash = $entry->{hash};
  746. my $node = $entry->{node};
  747. # Conditionally format the data for each type of evidence
  748. # Curation often has the type of change and its location
  749. # What type of change is this?
  750. foreach my $type (@types) {
  751. my $obj = $hash->{$type};
  752. my @data = eval { $obj->row };
  753. next unless @data;
  754. my $clean_tag = ucfirst($type);
  755. $clean_tag =~ s/_/ /g;
  756. $parameters_seen{$type}++;
  757. my ($pos,$text,$evi,$evi_method,$kind);
  758. if ($type eq 'Missense') {
  759. ($type,$pos,$text,$evi) = @data;
  760. } elsif ($type eq 'Nonsense' || $type eq 'Splice_site') {
  761. ($type,$kind,$text,$evi) = @data;
  762. } elsif ($type eq 'Frameshift') {
  763. ($type,$text,$evi) = @data;
  764. } else {
  765. ($type,$text,$evi) = @data;
  766. }
  767. if ($evi) {
  768. ### ($evi_method) = GetEvidenceNew(-object => $text,
  769. ### -format => 'inline',
  770. ### -display_label => 1,
  771. ### );
  772. }
  773. push @protein_effects,[$clean_tag,$pos || undef,$text,
  774. $evi_method ? " ($evi_method)" : undef];
  775. }
  776. # Where is this change located?
  777. foreach my $location (@locations) {
  778. my $obj = $hash->{$location};
  779. my @data = eval { $obj->col };
  780. next unless @data;
  781. $parameters_seen{$location}++;
  782. #####
  783. ##### my ($evidence) = GetEvidenceNew(-object => $obj,
  784. ##### -format => 'inline',
  785. ##### -display_label => 1
  786. ##### );
  787. my $evidence;
  788. my $clean_tag = ucfirst($location);
  789. $clean_tag =~ s/_/ /g;
  790. push @contained_in,[$clean_tag,undef,undef,
  791. $evidence ? " ($evidence)" : undef];
  792. }
  793. }
  794. my $do_translation;
  795. foreach (keys %parameters_seen) {
  796. $do_translation++ if (defined $do_translation{$_} && !defined $no_translation{$_});
  797. }
  798. return (\@protein_effects,\@contained_in,$do_translation);
  799. }
  800. # Part of the old Best_BLAST_Hits table
  801. sub _covered {
  802. my ($self,@starts) = @_;
  803. # linearize
  804. my @segs;
  805. for my $s (@starts) {
  806. my @ends = $s->col;
  807. # Major kludge for architecture-dependent Perl bug(?) in interpreting integers as strings
  808. $s = "$s.0";
  809. push @segs,map {[$s,"$_.0"]} @ends;
  810. }
  811. my @sorted = sort {$a->[0]<=>$b->[0]} @segs;
  812. my @merged;
  813. foreach (@sorted) {
  814. my ($start,$end) = @$_;
  815. if ($merged[-1] && $merged[-1][1]>$start) {
  816. $merged[-1][1] = $end if $end > $merged[-1][1];
  817. } else {
  818. push @merged,$_;
  819. }
  820. }
  821. my $total = 0;
  822. foreach my $merged (@merged) {
  823. $total += $merged->[1]-$merged->[0];
  824. }
  825. $total;
  826. }
  827. # The rearrange helper method
  828. # CAN BE PURGED ONCE _parse_evidence_hash is dealt with
  829. sub rearrange {
  830. my ($self,$order,@param) = @_;
  831. return unless @param;
  832. my %param;
  833. if (ref $param[0] eq 'HASH') {
  834. %param = %{$param[0]};
  835. } else {
  836. # Named parameter must begin with hyphen
  837. return @param unless (defined($param[0]) && substr($param[0],0,1) eq '-');
  838. my $i;
  839. for ($i=0;$i<@param;$i+=2) {
  840. $param[$i]=~s/^\-//; # get rid of initial - if present
  841. $param[$i]=~tr/a-z/A-Z/; # parameters are upper case
  842. }
  843. %param = @param; # convert into associative array
  844. }
  845. my(@return_array);
  846. local($^W) = 0;
  847. my($key)='';
  848. foreach $key (@$order) {
  849. my($value);
  850. if (ref($key) eq 'ARRAY') {
  851. foreach (@$key) {
  852. last if defined($value);
  853. $value = $param{$_};
  854. delete $param{$_};
  855. }
  856. } else {
  857. $value = $param{$key};
  858. delete $param{$key};
  859. }
  860. push(@return_array,$value);
  861. }
  862. push (@return_array,{%param}) if %param;
  863. return @return_array;
  864. }
  865. __PACKAGE__->meta->make_immutable;
  866. 1;