chado_feature__residues.inc 26 KB

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