chado_feature__residues.inc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. <?php
  2. class chado_feature__residues extends TripalField {
  3. /**
  4. * @see TripalField::fieldInfo()
  5. */
  6. static function fieldInfo() {
  7. return array(
  8. 'label' => t('Sequences'),
  9. 'description' => t('A field for managing nucleotide and protein residues.'),
  10. 'default_widget' => 'chado_feature__residues_widget',
  11. 'default_formatter' => 'chado_feature__residues_formatter',
  12. 'settings' => array(),
  13. 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
  14. 'storage' => array(
  15. 'type' => 'field_chado_storage',
  16. 'module' => 'tripal_chado',
  17. 'active' => TRUE
  18. ),
  19. );
  20. }
  21. /**
  22. * @see TripalField::can_attach()
  23. */
  24. protected function setCanAttach() {
  25. $table_name = $this->details['chado_table'];
  26. $type_table = $this->details['chado_type_table'];
  27. $type_field = $this->details['chado_type_column'];
  28. $cv_id = $this->details['chado_cv_id'];
  29. $cvterm_id = $this->details['chado_cvterm_id'];
  30. // If this is not the feature table then we don't want to attach.
  31. if ($table_name == 'feature') {
  32. $this->can_attach = TRUE;
  33. return;
  34. }
  35. $this->can_attach = FALSE;
  36. }
  37. /**
  38. * @see TripalField::setFieldName()
  39. */
  40. protected function setFieldName() {
  41. $table_name = $this->details['chado_table'];
  42. $type_table = $this->details['chado_type_table'];
  43. $type_field = $this->details['chado_type_column'];
  44. $cv_id = $this->details['chado_cv_id'];
  45. $cvterm_id = $this->details['chado_cvterm_id'];
  46. $this->field_name = 'feature__residues';
  47. }
  48. /**
  49. * @see TripalField::create_info()
  50. */
  51. function createInfo() {
  52. if (!$this->can_attach) {
  53. return;
  54. }
  55. $table_name = $this->details['chado_table'];
  56. $type_table = $this->details['chado_type_table'];
  57. $type_field = $this->details['chado_type_column'];
  58. $cv_id = $this->details['chado_cv_id'];
  59. $cvterm_id = $this->details['chado_cvterm_id'];
  60. return array(
  61. 'field_name' => $this->field_name,
  62. 'type' => 'chado_feature__residues',
  63. 'cardinality' => 1,
  64. 'locked' => FALSE,
  65. 'storage' => array(
  66. 'type' => 'field_chado_storage',
  67. ),
  68. 'settings' => array(
  69. 'chado_table' => $table_name,
  70. 'chado_column' => 'residues',
  71. 'semantic_web' => tripal_get_chado_semweb_term($table_name, 'residues'),
  72. ),
  73. );
  74. }
  75. /**
  76. * @see TripalField::createInstanceInfo()
  77. */
  78. function createInstanceInfo() {
  79. if (!$this->can_attach) {
  80. return;
  81. }
  82. $table_name = $this->details['chado_table'];
  83. $type_table = $this->details['chado_type_table'];
  84. $type_field = $this->details['chado_type_column'];
  85. $cv_id = $this->details['chado_cv_id'];
  86. $cvterm_id = $this->details['chado_cvterm_id'];
  87. return array(
  88. 'field_name' => $this->field_name,
  89. 'entity_type' => $this->entity_type,
  90. 'bundle' => $this->bundle->name,
  91. 'label' => 'Sequences',
  92. 'description' => 'All available sequences for this record.',
  93. 'required' => FALSE,
  94. 'settings' => array(
  95. 'auto_attach' => FALSE,
  96. ),
  97. 'widget' => array(
  98. 'type' => 'chado_feature__residues_widget',
  99. 'settings' => array(
  100. 'display_label' => 1,
  101. ),
  102. ),
  103. 'display' => array(
  104. 'default' => array(
  105. 'label' => 'above',
  106. 'type' => 'chado_feature__residues_formatter',
  107. 'settings' => array(),
  108. ),
  109. ),
  110. );
  111. }
  112. /**
  113. * @see TripalField::widgetInfo()
  114. */
  115. public static function widgetInfo() {
  116. return array(
  117. 'chado_feature__residues_widget' => array(
  118. 'label' => t('Sequence'),
  119. 'field types' => array('chado_feature__residues'),
  120. ),
  121. );
  122. }
  123. /**
  124. * @see TripalField::formatterInfo()
  125. */
  126. static function formatterInfo() {
  127. return array(
  128. 'chado_feature__residues_formatter' => array(
  129. 'label' => t('Sequences'),
  130. 'field types' => array('chado_feature__residues'),
  131. ),
  132. );
  133. }
  134. /**
  135. * @see TripalField::formatterView()
  136. */
  137. static function formatterView(&$element, $entity_type, $entity, $field,
  138. $instance, $langcode, $items, $display) {
  139. $element[0] = array(
  140. // We create a render array to produce the desired markup,
  141. '#type' => 'markup',
  142. '#markup' => '',
  143. );
  144. $num_bases = 50;
  145. $feature = $entity->chado_record;
  146. foreach ($items as $delta => $item) {
  147. // If there are no residues then skip this one.
  148. if (!is_array($item['value']) or !array_key_exists('residues', $item['value'])) {
  149. continue;
  150. }
  151. $residues = $item['value']['residues'];
  152. $label = $item['value']['label'];
  153. $defline = $item['value']['defline'];
  154. $content = '<p>' . $label . '<p>';
  155. $content .= '<pre class="residues-formatter">';
  156. $content .= '>' . $defline . "<br>";
  157. $content .= wordwrap($residues, $num_bases, "<br>", TRUE);
  158. $content .= '</pre>';
  159. $element[$delta] = array(
  160. // We create a render array to produce the desired markup,
  161. '#type' => 'markup',
  162. '#markup' => $content,
  163. );
  164. }
  165. }
  166. /**
  167. * @see TripalField::widgetForm()
  168. */
  169. public static function widgetForm(&$widget, &$form, &$form_state, $field, $instance,
  170. $langcode, $items, $delta, $element) {
  171. $settings = $field['settings'];
  172. $field_name = $field['field_name'];
  173. $field_type = $field['type'];
  174. $field_table = $field['settings']['chado_table'];
  175. $field_column = $field['settings']['chado_column'];
  176. // Get the field defaults.
  177. $residues = '';
  178. if (count($items) > 0 and array_key_exists('feature__residues', $items[0])) {
  179. $residues = $items[0]['feature__residues'];
  180. }
  181. if (array_key_exists('values', $form_state)) {
  182. $residues = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'feature__residues');
  183. }
  184. $widget['value'] = array(
  185. '#type' => 'value',
  186. '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
  187. );
  188. $widget['feature__residues'] = array(
  189. '#type' => 'textarea',
  190. '#title' => $element['#title'],
  191. '#description' => $element['#description'],
  192. '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
  193. '#default_value' => $residues,
  194. '#delta' => $delta,
  195. '#element_validate' => array('chado_feature__residues_widget_validate'),
  196. '#cols' => 30,
  197. );
  198. }
  199. /**
  200. * @see TripalField::load()
  201. */
  202. static function load($field, $entity, $details = array()) {
  203. $field_name = $field['field_name'];
  204. $feature = $details['record'];
  205. $num_seqs = 0;
  206. // We don't want to get the sequence for traditionally large types. They are
  207. // too big, bog down the web browser, take longer to load and it's not
  208. // reasonable to print them on a page.
  209. if(strcmp($feature->type_id->name,'scaffold') == 0 or
  210. strcmp($feature->type_id->name,'chromosome') == 0 or
  211. strcmp($feature->type_id->name,'supercontig') == 0 or
  212. strcmp($feature->type_id->name,'pseudomolecule') == 0) {
  213. $entity->{$field_name}['und'][$num_seqs]['value'] = array(
  214. '@type' => 'SO:0000110',
  215. 'type' => 'sequence_feature',
  216. 'label' => 'Residues',
  217. 'defline' => ">This sequence is too large for this display.",
  218. 'residues' => '',
  219. );
  220. $entity->{$field_name}['und'][$num_seqs]['feature__residues'] = '';
  221. }
  222. else {
  223. $feature = chado_expand_var($feature,'field','feature.residues');
  224. if ($feature->residues) {
  225. $entity->{$field_name}['und'][$num_seqs]['value'] = array(
  226. '@type' => 'SO:0000110',
  227. 'type' => 'sequence_feature',
  228. 'label' => 'Raw Sequence',
  229. 'defline' => tripal_get_fasta_defline($feature, '', NULL, '', strlen($feature->residues)),
  230. 'residues' => $feature->residues,
  231. );
  232. $entity->{$field_name}['und'][$num_seqs]['feature__residues'] = $feature->residues;
  233. }
  234. else {
  235. $entity->{$field_name}['und'][$num_seqs]['value'] = array();
  236. $entity->{$field_name}['und'][$num_seqs]['feature__residues'] = '';
  237. }
  238. }
  239. $num_seqs++;
  240. // Add in the protein sequences
  241. $values = array(
  242. 'object_id' => $feature->feature_id,
  243. 'subject_id' => array(
  244. 'type_id' => array(
  245. 'name' => 'polypeptide'
  246. ),
  247. ),
  248. 'type_id' => array(
  249. 'name' => 'derives_from',
  250. ),
  251. );
  252. $options = array(
  253. 'return_array' => 1,
  254. 'include_fk' => array(
  255. 'subject_id' => 1
  256. ),
  257. );
  258. $protein_rels = chado_generate_var('feature_relationship', $values, $options);
  259. foreach ($protein_rels as $protein_rel) {
  260. $protein_rel = chado_expand_var($protein_rel, 'field', 'feature.residues');
  261. if ($protein_rel->subject_id->residues) {
  262. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  263. '@type' => 'SO:0000104',
  264. 'type' => 'polypeptide',
  265. 'label' => 'Protein Sequence',
  266. 'defline' => tripal_get_fasta_defline($protein_rel->subject_id, '', NULL, '', strlen($protein_rel->subject_id->residues)),
  267. 'residues' => $protein_rel->subject_id->residues,
  268. );
  269. }
  270. }
  271. // Add in sequences from alignments.
  272. $options = array(
  273. 'return_array' => 1,
  274. 'include_fk' => array(
  275. 'srcfeature_id' => array(
  276. 'type_id' => 1
  277. ),
  278. 'feature_id' => array(
  279. 'type_id' => 1
  280. ),
  281. ),
  282. );
  283. $feature = chado_expand_var($feature, 'table', 'featureloc', $options);
  284. $featureloc_sequences = self::get_featureloc_sequences($feature->feature_id, $feature->featureloc->feature_id);
  285. // Add in the coding sequences.
  286. $values = array(
  287. 'object_id' => $feature->feature_id,
  288. 'subject_id' => array(
  289. 'type_id' => array(
  290. 'name' => 'CDS'
  291. ),
  292. ),
  293. 'type_id' => array(
  294. 'name' => 'part_of',
  295. ),
  296. );
  297. $options = array(
  298. 'order_by' => array('rank' => 'ASC'),
  299. 'return_array' => 1,
  300. 'include_fk' => array(
  301. 'subject_id' => 1
  302. ),
  303. );
  304. $cds_rels = chado_generate_var('feature_relationship', $values, $options);
  305. $coding_seq = '';
  306. foreach ($cds_rels as $cds_rel) {
  307. $cds_rel = chado_expand_var($cds_rel, 'field', 'feature.residues');
  308. if ($cds_rel->subject_id->residues) {
  309. $coding_seq .= $cds_rel->subject_id->residues;
  310. }
  311. }
  312. if ($coding_seq) {
  313. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  314. '@type' => 'SO:0000316',
  315. 'type' => 'coding_sequence',
  316. 'label' => 'Coding sequence (CDS)',
  317. 'defline' => tripal_get_fasta_defline($feature, '', $feature->featureloc->feature_id[0], '', strlen($coding_seq)),
  318. 'residues' => $coding_seq,
  319. );
  320. }
  321. foreach($featureloc_sequences as $src => $attrs){
  322. // the $attrs array has the following keys
  323. // * id: a unique identifier combining the feature id with the cvterm id
  324. // * type: the type of sequence (e.g. mRNA, etc)
  325. // * location: the alignment location
  326. // * defline: the definition line
  327. // * formatted_seq: the formatted sequences
  328. // * featureloc: the feature object aligned to
  329. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  330. 'residues' => $attrs['residues'],
  331. '@type' => 'SO:0000110',
  332. 'type' => 'sequence_feature',
  333. 'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', strlen($attrs['residues'])),
  334. 'label' => 'Sequence from alignment at ' . $attrs['location'],
  335. );
  336. // check to see if this alignment has any CDS. If so, generate a CDS sequence
  337. $cds_sequence = tripal_get_feature_sequences(
  338. array(
  339. 'feature_id' => $feature->feature_id,
  340. 'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
  341. 'name' => $feature->name,
  342. 'featureloc_id' => $attrs['featureloc']->featureloc_id,
  343. ),
  344. array(
  345. 'derive_from_parent' => 1, // CDS are in parent-child relationships so we want to use the sequence from the parent
  346. 'aggregate' => 1, // we want to combine all CDS for this feature into a single sequence
  347. 'sub_feature_types' => array('CDS'), // we're looking for CDS features
  348. 'is_html' => 0
  349. )
  350. );
  351. if (count($cds_sequence) > 0) {
  352. // the tripal_get_feature_sequences() function can return multiple sequences
  353. // if a feature is aligned to multiple places. In the case of CDSs we expect
  354. // that one mRNA is only aligned to a single location on the assembly so we
  355. // can access the CDS sequence with index 0.
  356. if ($cds_sequence[0]['residues']) {
  357. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  358. 'residues' => $cds_sequence[0]['residues'],
  359. '@type' => 'SO:0000316',
  360. 'type' => 'coding_sequence',
  361. 'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', $cds_sequence[0]['length']),
  362. 'label' => 'Coding sequence (CDS) from alignment at ' . $attrs['location'],
  363. );
  364. }
  365. }
  366. }
  367. }
  368. /**
  369. *
  370. * @param unknown $feature_id
  371. * @param unknown $featurelocs
  372. * @return multitype:|Ambigous <multitype:, an>
  373. */
  374. private function get_featureloc_sequences($feature_id, $featurelocs) {
  375. // if we don't have any featurelocs then no point in continuing
  376. if (!$featurelocs) {
  377. return array();
  378. }
  379. // get the list of relationships (including any aggregators) and iterate
  380. // through each one to find information needed to color-code the reference sequence
  381. $relationships = self::get_aggregate_relationships($feature_id);
  382. if (!$relationships) {
  383. return array();
  384. }
  385. // iterate through each of the realtionships features and get their
  386. // locations
  387. foreach ($relationships as $rindex => $rel) {
  388. // get the featurelocs for each of the relationship features
  389. $rel_featurelocs = self::get_featurelocs($rel->subject_id, 'as_child', 0);
  390. foreach ($rel_featurelocs as $rfindex => $rel_featureloc) {
  391. // keep track of this unique source feature
  392. $src = $rel_featureloc->src_feature_id . "-" . $rel_featureloc->src_cvterm_id;
  393. // copy over the results to the relationship object. Since there can
  394. // be more than one feature location for each relationship feature we
  395. // use the '$src' variable to keep track of these.
  396. $rel->featurelocs = new stdClass();
  397. $rel->featurelocs->$src = new stdClass();
  398. $rel->featurelocs->$src->src_uniquename = $rel_featureloc->src_uniquename;
  399. $rel->featurelocs->$src->src_cvterm_id = $rel_featureloc->src_cvterm_id;
  400. $rel->featurelocs->$src->src_cvname = $rel_featureloc->src_cvname;
  401. $rel->featurelocs->$src->fmin = $rel_featureloc->fmin;
  402. $rel->featurelocs->$src->fmax = $rel_featureloc->fmax;
  403. $rel->featurelocs->$src->src_name = $rel_featureloc->src_name;
  404. // keep track of the individual parts for each relationship
  405. $start = $rel->featurelocs->$src->fmin;
  406. $end = $rel->featurelocs->$src->fmax;
  407. $type = $rel->subject_type;
  408. $rel_locs[$src]['parts'][$start][$type]['start'] = $start;
  409. $rel_locs[$src]['parts'][$start][$type]['end'] = $end;
  410. $rel_locs[$src]['parts'][$start][$type]['type'] = $type;
  411. }
  412. }
  413. // the featurelocs array provided to the function contains the locations
  414. // where this feature is found. We want to get the sequence for each
  415. // location and then annotate it with the parts found from the relationships
  416. // locations determiend above.
  417. $floc_sequences = array();
  418. foreach ($featurelocs as $featureloc) {
  419. // build the src name so we can keep track of the different parts for each feature
  420. $src = $featureloc->srcfeature_id->feature_id . "-" . $featureloc->srcfeature_id->type_id->cvterm_id;
  421. // orient the parts to the beginning of the feature sequence
  422. if (!empty($rel_locs[$src]['parts'])) {
  423. $parts = $rel_locs[$src]['parts'];
  424. $rparts = array(); // we will fill this up if we're on the reverse strand
  425. foreach ($parts as $start => $types) {
  426. foreach ($types as $type_name => $type) {
  427. if ($featureloc->strand >= 0) {
  428. // this is on the forward strand. We need to convert the start on the src feature to the
  429. // start on this feature's sequence
  430. $parts[$start][$type_name]['start'] = $parts[$start][$type_name]['start'] - $featureloc->fmin;
  431. $parts[$start][$type_name]['end'] = $parts[$start][$type_name]['end'] - $featureloc->fmin;
  432. $parts[$start][$type_name]['type'] = $type_name;
  433. }
  434. else {
  435. // this is on the reverse strand. We need to swap the start and stop and calculate from the
  436. // begining of the reverse sequence
  437. $size = ($featureloc->fmax - $featureloc->fmin);
  438. $start_orig = $parts[$start][$type_name]['start'];
  439. $end_orig = $parts[$start][$type_name]['end'];
  440. $new_start = $size - ($end_orig - $featureloc->fmin);
  441. $new_end = $size - ($start_orig - $featureloc->fmin);
  442. $rparts[$new_start][$type_name]['start'] = $new_start;
  443. $rparts[$new_start][$type_name]['end'] = $new_end;
  444. $rparts[$new_start][$type_name]['type'] = $type_name;
  445. }
  446. }
  447. }
  448. // now sort the parts
  449. // if we're on the reverse strand we need to resort
  450. if ($featureloc->strand >= 0) {
  451. usort($parts, 'chado_feature__residues_sort_rel_parts_by_start');
  452. }
  453. else {
  454. usort($rparts, 'chado_feature__residues_sort_rel_parts_by_start');
  455. $parts = $rparts;
  456. }
  457. $floc_sequences[$src]['id'] = $src;
  458. $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
  459. $args = array(':feature_id' => $featureloc->srcfeature_id->feature_id);
  460. $start = $featureloc->fmin + 1;
  461. $size = $featureloc->fmax - $featureloc->fmin;
  462. // TODO: fix the hard coded $start and $size
  463. // the $start and $size variables are hard-coded in the SQL statement
  464. // because the db_query function places quotes around all placeholders
  465. // (e.g. :start & :size) and screws up the substring function
  466. $sql = "
  467. SELECT substring(residues from $start for $size) as residues
  468. FROM {feature}
  469. WHERE feature_id = :feature_id
  470. ";
  471. $sequence = chado_query($sql, $args)->fetchObject();
  472. $residues = $sequence->residues;
  473. if ($featureloc->strand < 0) {
  474. $residues = tripal_reverse_compliment_sequence($residues);
  475. }
  476. $strand = '.';
  477. if ($featureloc->strand == 1) {
  478. $strand = '+';
  479. }
  480. elseif ($featureloc->strand == -1) {
  481. $strand = '-';
  482. }
  483. $floc_sequences[$src]['location'] = tripal_get_location_string($featureloc);
  484. $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, '', $featureloc, '', strlen($residues));
  485. $floc_sequences[$src]['featureloc'] = $featureloc;
  486. $floc_sequences[$src]['residues'] = $residues;
  487. //$floc_sequences[$src]['formatted_seq'] = tripal_feature_color_sequence($residues, $parts, $floc_sequences[$src]['defline']);
  488. }
  489. }
  490. return $floc_sequences;
  491. }
  492. /**
  493. * Get features related to the current feature to a given depth. Recursive function.
  494. *
  495. * @param $feature_id
  496. * @param $substitute
  497. * @param $levels
  498. * @param $base_type_id
  499. * @param $depth
  500. *
  501. * @ingroup tripal_feature
  502. */
  503. private function get_aggregate_relationships($feature_id, $substitute=1,
  504. $levels=0, $base_type_id=NULL, $depth=0) {
  505. // we only want to recurse to as many levels deep as indicated by the
  506. // $levels variable, but only if this variable is > 0. If 0 then we
  507. // recurse until we reach the end of the relationships tree.
  508. if ($levels > 0 and $levels == $depth) {
  509. return NULL;
  510. }
  511. // first get the relationships for this feature
  512. return self::get_relationships($feature_id, 'as_object');
  513. }
  514. /**
  515. * Get the relationships for a feature.
  516. *
  517. * @param $feature_id
  518. * The feature to get relationships for
  519. * @param $side
  520. * The side of the relationship this feature is (ie: 'as_subject' or 'as_object')
  521. *
  522. * @ingroup tripal_feature
  523. */
  524. private function get_relationships($feature_id, $side = 'as_subject') {
  525. // get the relationships for this feature. The query below is used for both
  526. // querying the object and subject relationships
  527. $sql = "
  528. SELECT
  529. FS.name as subject_name, FS.uniquename as subject_uniquename,
  530. CVTS.name as subject_type, CVTS.cvterm_id as subject_type_id,
  531. FR.subject_id, FR.type_id as relationship_type_id, FR.object_id, FR.rank,
  532. CVT.name as rel_type,
  533. FO.name as object_name, FO.uniquename as object_uniquename,
  534. CVTO.name as object_type, CVTO.cvterm_id as object_type_id
  535. FROM {feature_relationship} FR
  536. INNER JOIN {cvterm} CVT ON FR.type_id = CVT.cvterm_id
  537. INNER JOIN {feature} FS ON FS.feature_id = FR.subject_id
  538. INNER JOIN {feature} FO ON FO.feature_id = FR.object_id
  539. INNER JOIN {cvterm} CVTO ON FO.type_id = CVTO.cvterm_id
  540. INNER JOIN {cvterm} CVTS ON FS.type_id = CVTS.cvterm_id
  541. ";
  542. if (strcmp($side, 'as_object')==0) {
  543. $sql .= " WHERE FR.object_id = :feature_id";
  544. }
  545. if (strcmp($side, 'as_subject')==0) {
  546. $sql .= " WHERE FR.subject_id = :feature_id";
  547. }
  548. $sql .= " ORDER BY FR.rank";
  549. // get the relationships
  550. $results = chado_query($sql, array(':feature_id' => $feature_id));
  551. // iterate through the relationships, put these in an array and add
  552. // in the Drupal node id if one exists
  553. $i=0;
  554. $esql = "
  555. SELECT entity_id
  556. FROM {chado_entity}
  557. WHERE data_table = 'feature' AND record_id = :feature_id";
  558. $relationships = array();
  559. while ($rel = $results->fetchObject()) {
  560. $entity = db_query($esql, array(':feature_id' => $rel->subject_id))->fetchObject();
  561. if ($entity) {
  562. $rel->subject_entity_id = $entity->entity_id;
  563. }
  564. $entity = db_query($esql, array(':feature_id' => $rel->object_id))->fetchObject();
  565. if ($entity) {
  566. $rel->object_entity_id = $entity->entity_id;
  567. }
  568. $relationships[$i++] = $rel;
  569. }
  570. return $relationships;
  571. }
  572. /**
  573. * Load the locations for a given feature
  574. *
  575. * @param $feature_id
  576. * The feature to look up locations for
  577. * @param $side
  578. * Whether the feature is the scrfeature, 'as_parent', or feature, 'as_child'
  579. * @param $aggregate
  580. * Whether or not to get the locations for related features
  581. *
  582. * @ingroup tripal_feature
  583. */
  584. private function get_featurelocs($feature_id, $side = 'as_parent', $aggregate = 1) {
  585. $sql = "
  586. SELECT
  587. F.name, F.feature_id, F.uniquename,
  588. FS.name as src_name, FS.feature_id as src_feature_id, FS.uniquename as src_uniquename,
  589. CVT.name as cvname, CVT.cvterm_id,
  590. CVTS.name as src_cvname, CVTS.cvterm_id as src_cvterm_id,
  591. FL.fmin, FL.fmax, FL.is_fmin_partial, FL.is_fmax_partial,FL.strand, FL.phase
  592. FROM {featureloc} FL
  593. INNER JOIN {feature} F ON FL.feature_id = F.feature_id
  594. INNER JOIN {feature} FS ON FS.feature_id = FL.srcfeature_id
  595. INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
  596. INNER JOIN {cvterm} CVTS ON FS.type_id = CVTS.cvterm_id
  597. ";
  598. if (strcmp($side, 'as_parent')==0) {
  599. $sql .= "WHERE FL.srcfeature_id = :feature_id ";
  600. }
  601. if (strcmp($side, 'as_child')==0) {
  602. $sql .= "WHERE FL.feature_id = :feature_id ";
  603. }
  604. $flresults = chado_query($sql, array(':feature_id' => $feature_id));
  605. // copy the results into an array
  606. $i=0;
  607. $featurelocs = array();
  608. while ($loc = $flresults->fetchObject()) {
  609. // if a drupal node exists for this feature then add the nid to the
  610. // results object
  611. $loc->feid = tripal_get_chado_entity_id('feature', $loc->feature_id);
  612. $loc->seid = tripal_get_chado_entity_id('feature', $loc->src_feature_id);
  613. // add the result to the array
  614. $featurelocs[$i++] = $loc;
  615. }
  616. // Add the relationship feature locs if aggregate is turned on
  617. if ($aggregate and strcmp($side, 'as_parent')==0) {
  618. // get the relationships for this feature without substituting any children
  619. // for the parent. We want all relationships
  620. $relationships = tripal_feature_get_aggregate_relationships($feature_id, 0);
  621. foreach ($relationships as $rindex => $rel) {
  622. // get the featurelocs for each of the relationship features
  623. $rel_featurelocs = tripal_feature_load_featurelocs($rel->subject_id, 'as_child', 0);
  624. foreach ($rel_featurelocs as $findex => $rfloc) {
  625. $featurelocs[$i++] = $rfloc;
  626. }
  627. }
  628. }
  629. usort($featurelocs, 'chado_feature__residues_sort_locations');
  630. return $featurelocs;
  631. }
  632. }
  633. /**
  634. * Callback function for validating the chado_feature__residues_widget.
  635. */
  636. function chado_feature__residues_widget_validate($element, &$form_state) {
  637. $field_name = $element['#parents'][0];
  638. // Remove any white spaces.
  639. $residues = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'feature__residues');
  640. if ($residues) {
  641. $residues = preg_replace('/\s/', '', $residues);
  642. tripal_chado_set_field_form_values($field_name, $form_state, $residues, 0, 'feature__residues');
  643. }
  644. }
  645. /**
  646. * Used to sort the list of relationship parts by start position
  647. *
  648. * @ingroup tripal_feature
  649. */
  650. function chado_feature__residues_sort_rel_parts_by_start($a, $b) {
  651. foreach ($a as $type_name => $details) {
  652. $astart = $a[$type_name]['start'];
  653. break;
  654. }
  655. foreach ($b as $type_name => $details) {
  656. $bstart = $b[$type_name]['start'];
  657. break;
  658. }
  659. return strnatcmp($astart, $bstart);
  660. }
  661. /**
  662. * Used to sort the feature locs by start position
  663. *
  664. * @param $a
  665. * One featureloc record (as an object)
  666. * @param $b
  667. * The other featureloc record (as an object)
  668. *
  669. * @return
  670. * Which feature location comes first
  671. *
  672. * @ingroup tripal_feature
  673. */
  674. function chado_feature__residues_sort_locations($a, $b) {
  675. return strnatcmp($a->fmin, $b->fmin);
  676. }