tripal_phylogeny.chado_node.inc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. <?php
  2. /**
  3. * @file
  4. * Implements the phylotree node content type
  5. */
  6. /**
  7. * Implements hook_node_info().
  8. *
  9. * Provide information to drupal about the node types that we're creating
  10. * in this module.
  11. *
  12. * @ingroup tripal_phylogeny
  13. */
  14. function tripal_phylogeny_node_info() {
  15. $nodes = array();
  16. $nodes['chado_phylotree'] = array(
  17. 'name' => t('Phylotree'),
  18. 'base' => 'chado_phylotree',
  19. 'description' => t('A phylotree from the chado database'),
  20. 'has_title' => TRUE,
  21. 'locked' => TRUE,
  22. 'chado_node_api' => array(
  23. 'base_table' => 'phylotree',
  24. 'hook_prefix' => 'chado_phylotree',
  25. 'record_type_title' => array(
  26. 'singular' => t('Phylotree'),
  27. 'plural' => t('Phylotrees')
  28. ),
  29. /* sync_filters: tripal is hardcoded to look for this
  30. sync_filter settings: type_id and organism_id. (phylotree does
  31. not have organism_id but need to set it false anyways. */
  32. 'sync_filters' => array(
  33. 'type_id' => FALSE,
  34. 'organism_id' => FALSE
  35. ),
  36. )
  37. );
  38. return $nodes;
  39. }
  40. /**
  41. * Implements hook_node_view(). Acts on all content types
  42. *
  43. * @ingroup tripal_phylogeny
  44. */
  45. function tripal_phylogeny_node_view($node, $view_mode, $langcode) {
  46. if($node->type != 'chado_phylotree') { return; }
  47. switch($view_mode) {
  48. case 'full':
  49. $node->content['tripal_phylogeny_base'] = array(
  50. '#theme' => 'tripal_phylogeny_base',
  51. '#node' => $node,
  52. '#tripal_toc_id' => 'base',
  53. '#tripal_toc_title' => 'Overview',
  54. '#weight' => -100,
  55. );
  56. $node->content['tripal_phylogeny_phylogram'] = array(
  57. '#theme' => 'tripal_phylogeny_phylogram',
  58. '#node' => $node,
  59. '#tripal_toc_id' => 'phylotree_phylogram',
  60. '#tripal_toc_title' => 'Phylogram',
  61. '#weight' => -90,
  62. );
  63. $node->content['tripal_phylogeny_taxonomic_tree'] = array(
  64. '#theme' => 'tripal_phylogeny_taxonomic_tree',
  65. '#node' => $node,
  66. '#tripal_toc_id' => 'tripal_phylogeny_taxonomic_tree',
  67. '#tripal_toc_title' => 'Taxonomic Tree',
  68. '#weight' => -80,
  69. );
  70. $node->content['tripal_phylogeny_organisms'] = array(
  71. '#theme' => 'tripal_phylogeny_organisms',
  72. '#node' => $node,
  73. '#tripal_toc_id' => 'phylotree_organisms',
  74. '#tripal_toc_title' => 'Organisms',
  75. '#weight' => -70,
  76. );
  77. $node->content['tripal_phylogeny_references'] = array(
  78. '#theme' => 'tripal_phylogeny_references',
  79. '#node' => $node,
  80. '#tripal_toc_id' => 'phylotree_references',
  81. '#tripal_toc_title' => 'Cross References',
  82. );
  83. $node->content['tripal_phylogeny_analysis'] = array(
  84. '#theme' => 'tripal_phylogeny_analysis',
  85. '#node' => $node,
  86. '#tripal_toc_id' => 'phylotree_analysis',
  87. '#tripal_toc_title' => 'Analysis',
  88. );
  89. break;
  90. case 'teaser':
  91. $node->content['tripal_phylogeny_teaser'] = array(
  92. '#theme' => 'tripal_phylogeny_teaser',
  93. '#node' => $node,
  94. );
  95. break;
  96. }
  97. }
  98. /**
  99. * Implementation of hook_form().
  100. *
  101. * @ingroup tripal_phylogeny
  102. */
  103. function chado_phylotree_form($node, &$form_state) {
  104. $form = array();
  105. // Default values can come in the following ways:
  106. //
  107. // 1) as elements of the $node object. This occurs when editing an existing phylotree
  108. // 2) in the $form_state['values'] array which occurs on a failed validation or
  109. // ajax callbacks from non submit form elements
  110. // 3) in the $form_state['input'[ array which occurs on ajax callbacks from submit
  111. // form elements and the form is being rebuilt
  112. //
  113. // set form field defaults
  114. $phylotree = null;
  115. $phylotree_id = null;
  116. $tree_name = '';
  117. $leaf_type = '';
  118. $analysis_id = '';
  119. $dbxref = '';
  120. $comment = '';
  121. $tree_required = TRUE;
  122. $tree_file = '';
  123. $name_re = '';
  124. $match = '';
  125. // If we are editing an existing node then the phylotree is already part of the node.
  126. if (property_exists($node, 'phylotree')) {
  127. $phylotree = $node->phylotree;
  128. $phylotree = chado_expand_var($phylotree, 'field', 'phylotree.comment');
  129. $phylotree_id = $phylotree->phylotree_id;
  130. $tree_name = $phylotree->name;
  131. $leaf_type = $phylotree->type_id ? $phylotree->type_id->name : '';
  132. $comment = $phylotree->comment;
  133. $analysis_id = $phylotree->analysis_id ? $phylotree->analysis_id->analysis_id : '';
  134. $dbxref = $phylotree->dbxref_id->db_id->name . ":" . $phylotree->dbxref_id->accession;
  135. $name_re = $phylotree->tripal_variables->phylotree_name_re;
  136. $match = $phylotree->tripal_variables->phylotree_use_uniquename;
  137. // If the dbxref is the null db then hide it.
  138. if ($phylotree->dbxref_id->db_id->name == 'null') {
  139. $dbxref = '';
  140. }
  141. // Get the tree file name. If the file was added via the Drupal interface
  142. // then a numeric file_id will be present in the phylotree_tree_file
  143. // variable. If not then the tree was loaded on the command-line and
  144. // the actual filename is in this variable.
  145. $file_id = $phylotree->tripal_variables->phylotree_tree_file;
  146. if (is_numeric($file_id)) {
  147. $file = file_load($file_id);
  148. if ($file) {
  149. $tree_file = $file->filename;
  150. }
  151. }
  152. else {
  153. $tree_file = $file_id;
  154. }
  155. // The tree file is not a required input field when editing the node.
  156. $tree_required = FALSE;
  157. // Keep track of the phylotree id.
  158. $form['phylotree_id'] = array(
  159. '#type' => 'value',
  160. '#value' => $phylotree_id,
  161. );
  162. }
  163. // If we are re constructing the form from a failed validation or ajax callback
  164. // then use the $form_state['values'] values.
  165. if (array_key_exists('values', $form_state) and isset($form_state['values']['tree_name'])) {
  166. $tree_name = $form_state['values']['tree_name'];
  167. $leaf_type = $form_state['values']['leaf_type'];
  168. $analysis_id = $form_state['values']['analysis_id'];
  169. $dbxref = $form_state['values']['dbxref'];
  170. $comment = $form_state['values']['description'];
  171. }
  172. // If we are re building the form from after submission (from ajax call) then
  173. // the values are in the $form_state['input'] array.
  174. if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
  175. $tree_name = $form_state['input']['tree_name'];
  176. $leaf_type = $form_state['input']['leaf_type'];
  177. $analysis_id = $form_state['input']['analysis_id'];
  178. $comment = $form_state['input']['description'];
  179. $dbxref = $form_state['input']['dbxref'];
  180. }
  181. $form['tree_name']= array(
  182. '#type' => 'textfield',
  183. '#title' => t('Tree Name'),
  184. '#required' => TRUE,
  185. '#default_value' => $tree_name,
  186. '#description' => t('Enter the name used to refer to this phylogenetic tree.'),
  187. '#maxlength' => 255
  188. );
  189. $type_cv = tripal_get_default_cv('phylotree', 'type_id');
  190. $so_cv = tripal_get_cv(array('name' => 'sequence'));
  191. $cv_id = $so_cv->cv_id;
  192. if (!$so_cv) {
  193. drupal_set_message('The Sequence Ontolgoy does not appear to be imported.
  194. Please import the Sequence Ontology before adding a tree.', 'error');
  195. }
  196. $form['leaf_type'] = array(
  197. '#title' => t('Tree Type'),
  198. '#type' => 'textfield',
  199. '#description' => t("Choose the tree type. The type is
  200. a valid Sequence Ontology (SO) term. For example, trees derived
  201. from protein sequences should use the SO term 'polypeptide'.
  202. Alternatively, a phylotree can be used for representing a taxonomic
  203. tree. In this case, the word 'taxonomy' should be used."),
  204. '#required' => TRUE,
  205. '#default_value' => $leaf_type,
  206. '#autocomplete_path' => "admin/tripal/chado/tripal_cv/cvterm/auto_name/$cv_id",
  207. );
  208. // Get the list of analyses.
  209. $sql = "SELECT * FROM {analysis} ORDER BY name";
  210. $arset = chado_query($sql);
  211. $analyses = array();
  212. $analyses[''] = '';
  213. while ($analysis = $arset->fetchObject()) {
  214. $analyses[$analysis->analysis_id] = $analysis->name;
  215. }
  216. $form['analysis_id'] = array(
  217. '#title' => t('Analysis'),
  218. '#type' => 'select',
  219. '#description' => t("Choose the analysis from which this phylogenetic tree was derived"),
  220. '#required' => TRUE,
  221. '#default_value' => $analysis_id,
  222. '#options' => $analyses,
  223. );
  224. $form['dbxref'] = array(
  225. '#title' => t('Database Cross-Reference'),
  226. '#type' => 'textfield',
  227. '#description' => t("Enter a database cross-reference of the form
  228. [DB name]:[accession]. The database name must already exist in the
  229. database. If the accession does not exist it is automatically added."),
  230. '#required' => FALSE,
  231. '#default_value' => $dbxref,
  232. );
  233. $form['description']= array(
  234. '#type' => 'textarea',
  235. '#title' => t('Description'),
  236. '#required' => TRUE,
  237. '#default_value' => $comment,
  238. '#description' => t('Enter a description for this tree.'),
  239. );
  240. $upload_location = tripal_get_files_stream('tripal_phylogeny');
  241. $form['tree_file'] = array(
  242. '#type' => 'fieldset',
  243. '#title' => t('Tree File Import'),
  244. '#collapsible' => FALSE,
  245. );
  246. $description = t('Please provide a file in the Newick format that contains
  247. the nodes of this tree.');
  248. if ($tree_file) {
  249. $form['tree_file']['curr_file'] = array(
  250. '#type' => 'item',
  251. '#title' => 'Current Tree File',
  252. '#markup' => $tree_file,
  253. );
  254. $description = t('Please provide a file in the Newick format that
  255. contains the nodes of this tree. Please note that uploading a new
  256. file will overwrite the current tree.');
  257. }
  258. $form['tree_file']['tree_file'] = array(
  259. '#type' => 'managed_file',
  260. '#title' => t('New Tree File'),
  261. '#description' => $description,
  262. '#upload_location' => $upload_location,
  263. '#upload_validators' => array(
  264. // We don't want to require a specific file extension so leave the array empty.
  265. 'file_validate_extensions' => array(),
  266. // The following is for checking the Newick file format.
  267. 'chado_phylotree_validate_newick_format' => array(),
  268. ),
  269. '#required' => $tree_required,
  270. );
  271. $form['tree_file']['name_re'] = array(
  272. '#title' => t('Feature Name Regular Expression'),
  273. '#type' => 'textfield',
  274. '#description' => t('If this is a phylogenetic (non taxonomic) tree, then
  275. the tree nodes will be automatically associated with features. However,
  276. if the nodes in the tree file are not exactly as the names of features
  277. but have enough information to uniquely identify the feature then you
  278. may provide a regular expression that the importer will use to extract
  279. the feature names from the node names.'),
  280. '#default_value' => $name_re,
  281. );
  282. $form['tree_file']['match'] = array(
  283. '#title' => t('Use Unique Feature Name'),
  284. '#type' => 'checkbox',
  285. '#description' => t('If this is a phylogenetic (non taonomic tree) and the nodes ' .
  286. 'should match the unique name of the feature rather than the name of the feautre ' .
  287. 'then select this box. If unselected the loader will try to match the feature ' .
  288. 'using the feature name.'),
  289. '#default_value' => $match,
  290. );
  291. return $form;
  292. }
  293. /**
  294. * A validation function for checking the newick file format.
  295. *
  296. * @param stdClass $file
  297. * A Drupal file object.
  298. */
  299. function chado_phylotree_validate_newick_format(stdClass $file) {
  300. // An array of strings where each string represents a unique error
  301. // when examining the file.
  302. $errors = array();
  303. // TODO: check the newick file format for errors.
  304. return $errors;
  305. }
  306. /**
  307. * Implementation of hook_validate().
  308. *
  309. * This validation is being used for three activities:
  310. * CASE A: Update a node that exists in both drupal and chado
  311. * CASE B: Synchronizing a node from chado to drupal
  312. * CASE C: Inserting a new node that exists in niether drupal nor chado
  313. *
  314. * @ingroup tripal_phylogeny
  315. */
  316. function chado_phylotree_validate($node, $form, &$form_state) {
  317. // We are syncing if we do not have a node ID but we do have a phylotree_id. We don't
  318. // need to validate during syncing so just skip it.
  319. if (is_null($node->nid) and property_exists($node, 'phylotree_id') and $node->phylotree_id != 0) {
  320. return;
  321. }
  322. // Remove surrounding white-space on submitted values.
  323. $node->tree_name = trim($node->tree_name);
  324. $node->description = trim($node->description);
  325. $node->dbxref = trim($node->dbxref);
  326. // if this is a delete then don't validate
  327. if ($node->op == 'Delete') {
  328. return;
  329. }
  330. $errors = array();
  331. $warnings = array();
  332. $options = array(
  333. 'name' => $node->tree_name,
  334. 'description' => $node->description,
  335. 'analysis_id' => $node->analysis_id,
  336. 'leaf_type' => $node->leaf_type,
  337. 'tree_file' => $node->tree_file,
  338. 'format' => 'newick',
  339. 'dbxref' => $node->dbxref,
  340. 'match' => $node->match,
  341. 'name_re' => $node->name_re,
  342. );
  343. // If we have a node id already then this is an update:
  344. if ($node->nid) {
  345. $options['phylotree_id'] = $node->phylotree_id;
  346. tripal_validate_phylotree('update', $options, $errors, $warnings);
  347. }
  348. else {
  349. tripal_validate_phylotree('insert', $options, $errors, $warnings);
  350. }
  351. // Now set form errors if any errors were detected.
  352. if (count($errors) > 0) {
  353. foreach($errors as $field => $message) {
  354. if ($field == 'name') {
  355. $field = 'tree_name';
  356. }
  357. form_set_error($field, $message);
  358. }
  359. }
  360. // Add any warnings if any were detected
  361. if (count($warnings) > 0) {
  362. foreach($warnings as $field => $message) {
  363. drupal_set_message($message, 'warning');
  364. }
  365. }
  366. }
  367. /**
  368. * Implements hook_node_presave(). Acts on all node content types.
  369. *
  370. * @ingroup tripal_phylogeny
  371. */
  372. function tripal_phylogeny_node_presave($node) {
  373. switch ($node->type) {
  374. // This step is for setting the title for the Drupal node. This title
  375. // is permanent and thus is created to be unique. Title changes provided
  376. // by tokens are generated on the fly dynamically, but the node title
  377. // seen in the content listing needs to be set here. Do not call
  378. // the chado_get_node_title() function here to set the title as the node
  379. // object isn't properly filled out and the function will fail.
  380. case 'chado_phylotree':
  381. // for a form submission the 'phylotreename' field will be set,
  382. // for a sync, we must pull from the phylotree object
  383. if (property_exists($node, 'phylotreename')) {
  384. // set the title
  385. $node->title = $node->tree_name;
  386. }
  387. else if (property_exists($node, 'phylotree')) {
  388. $node->title = $node->phylotree->name;
  389. }
  390. break;
  391. }
  392. }
  393. /**
  394. * Implements hook_node_insert().
  395. * Acts on all content types.
  396. *
  397. * @ingroup tripal_phylogeny
  398. */
  399. function tripal_phylogeny_node_insert($node) {
  400. switch ($node->type) {
  401. case 'chado_phylotree':
  402. $phylotree_id = chado_get_id_from_nid('phylotree', $node->nid);
  403. $values = array('phylotree_id' => $phylotree_id);
  404. $phylotree = chado_generate_var('phylotree', $values);
  405. $phylotree = chado_expand_var($phylotree, 'field', 'phylotree.comment');
  406. $node->phylotree = $phylotree;
  407. // Now use the API to set the path.
  408. chado_set_node_url($node);
  409. // Now get the title.
  410. $node->title = chado_get_node_title($node);
  411. break;
  412. }
  413. }
  414. /**
  415. * Implements hook_node_update().
  416. * Acts on all content types.
  417. *
  418. * @ingroup tripal_phylogeny
  419. */
  420. function tripal_phylogeny_node_update($node) {
  421. switch ($node->type) {
  422. case 'chado_phylotree':
  423. $phylotree_id = chado_get_id_from_nid('phylotree', $node->nid);
  424. $values = array('phylotree_id' => $phylotree_id);
  425. $phylotree = chado_generate_var('phylotree', $values);
  426. $phylotree = chado_expand_var($phylotree, 'field', 'phylotree.comment');
  427. $node->phylotree = $phylotree;
  428. // Now get the title
  429. $node->title = chado_get_node_title($node);
  430. break;
  431. }
  432. }
  433. /**
  434. * Implements [content_type]_chado_node_default_title_format().
  435. *
  436. * Defines a default title format for the Chado Node API to set the titles on
  437. * Chado phylotree nodes based on chado fields.
  438. *
  439. * @ingroup tripal_phylogeny
  440. */
  441. function chado_phylotree_chado_node_default_title_format() {
  442. return '[phylotree.name]';
  443. }
  444. /**
  445. * Implements hook_chado_node_default_url_format().
  446. *
  447. * Designates a default URL format for phylotree nodes.
  448. */
  449. function chado_phylotree_chado_node_default_url_format() {
  450. return '/phylotree/[phylotree.name]';
  451. }
  452. /**
  453. * Implements hook_insert().
  454. *
  455. * When a new chado_phylotree node is created we also need to add
  456. * information to our chado_phylotree table. This function is called
  457. * on insert of a new node of type 'chado_phylotree' and inserts the
  458. * necessary information.
  459. *
  460. * @ingroup tripal_phylogeny
  461. */
  462. function chado_phylotree_insert($node) {
  463. global $user;
  464. $node->tree_name = trim($node->tree_name);
  465. $node->description = trim($node->description);
  466. $node->dbxref = trim($node->dbxref);
  467. // if there is a phylotree_id in the $node object then this must
  468. // be a sync (not an insert) so we can skip adding the phylotree as it is
  469. // already there, although we do need to proceed with the rest of the
  470. // insert.
  471. $phylotree_id = NULL;
  472. if (!property_exists($node, 'phylotree_id')) {
  473. $options = array(
  474. 'name' => $node->tree_name,
  475. 'description' => $node->description,
  476. 'analysis_id' => $node->analysis_id,
  477. 'leaf_type' => $node->leaf_type,
  478. 'tree_file' => $node->tree_file,
  479. 'format' => 'newick',
  480. 'dbxref' => $node->dbxref,
  481. 'match' => $node->match,
  482. 'name_re' => $node->name_re,
  483. );
  484. $errors = array();
  485. $warnings = array();
  486. if (tripal_insert_phylotree($options, $errors, $warnings)) {
  487. $phylotree_id = $options['phylotree_id'];
  488. // Add the Tripal variables to this node.
  489. tripal_add_node_variable($node->nid, 'phylotree_name_re', $node->name_re);
  490. tripal_add_node_variable($node->nid, 'phylotree_use_uniquename', $node->match);
  491. tripal_add_node_variable($node->nid, 'phylotree_tree_file', $node->tree_file);
  492. }
  493. else {
  494. drupal_set_message(t('Unable to insert phylotree.'), 'error');
  495. tripal_report_error('tripal_phylogeny', TRIPAL_WARNING,
  496. 'Insert phylotree: Unable to insert phylotree where values: %values',
  497. array('%values' => print_r($options, TRUE))
  498. );
  499. }
  500. }
  501. else {
  502. $phylotree_id = $node->phylotree_id;
  503. }
  504. // Make sure the entry for this phylotree doesn't already exist in the
  505. // chado_phylotree table if it doesn't exist then we want to add it.
  506. $check_org_id = chado_get_id_from_nid('phylotree', $node->nid);
  507. if (!$check_org_id) {
  508. $record = new stdClass();
  509. $record->nid = $node->nid;
  510. $record->vid = $node->vid;
  511. $record->phylotree_id = $phylotree_id;
  512. drupal_write_record('chado_phylotree', $record);
  513. }
  514. }
  515. /**
  516. * Implements hook_update().
  517. *
  518. * @ingroup tripal_phylogeny
  519. */
  520. function chado_phylotree_update($node) {
  521. global $user;
  522. $node->tree_name = trim($node->tree_name);
  523. $node->description = trim($node->description);
  524. $node->dbxref = trim($node->dbxref);
  525. // Get the phylotree_id for this node.
  526. $phylotree_id = chado_get_id_from_nid('phylotree', $node->nid) ;
  527. $options = array(
  528. 'phylotree_id' => $node->phylotree_id,
  529. 'name' => $node->tree_name,
  530. 'description' => $node->description,
  531. 'analysis_id' => $node->analysis_id,
  532. 'leaf_type' => $node->leaf_type,
  533. 'tree_file' => $node->tree_file,
  534. 'format' => 'newick',
  535. 'dbxref' => $node->dbxref,
  536. 'match' => $node->match,
  537. 'name_re' => $node->name_re,
  538. );
  539. $success = tripal_update_phylotree($phylotree_id, $options);
  540. if (!$success) {
  541. drupal_set_message("Unable to update phylotree.", "error");
  542. tripal_report_error('tripal_phylogeny', TRIPAL_WARNING,
  543. 'Update phylotree: Unable to update phylotree where values: %values',
  544. array('%values' => print_r($options, TRUE))
  545. );
  546. return;
  547. }
  548. // Remove any variables and then add back the variables from the form.
  549. tripal_delete_node_variables($node->nid);
  550. tripal_add_node_variable($node->nid, 'phylotree_name_re', $node->name_re);
  551. tripal_add_node_variable($node->nid, 'phylotree_use_uniquename', $node->match);
  552. tripal_add_node_variable($node->nid, 'phylotree_tree_file', $node->tree_file);
  553. }
  554. /**
  555. * Implements hook_load().
  556. *
  557. * When a node is requested by the user this function is called to allow us
  558. * to add auxiliary data to the node object.
  559. *
  560. * @ingroup tripal_phylogeny
  561. */
  562. function chado_phylotree_load($nodes) {
  563. foreach ($nodes as $nid => $node) {
  564. $phylotree_id = chado_get_id_from_nid('phylotree', $nid);
  565. // If the nid does not have a matching record then skip this node.
  566. // this can happen with orphaned nodes.
  567. if (!$phylotree_id) {
  568. continue;
  569. }
  570. // Build the Chado variable for the phylotree.
  571. $values = array('phylotree_id' => $phylotree_id);
  572. $phylotree = chado_generate_var('phylotree', $values);
  573. $nodes[$nid]->phylotree = $phylotree;
  574. // Expand the comment field, chado_generate_var() omits it by default
  575. // because it is a large text field.
  576. $phylotree = chado_expand_var($phylotree, 'field', 'phylotree.comment');
  577. // Add non Chado information to the object. These variables are needed
  578. // for the edit/update forms.
  579. $phylotree->tripal_variables = new stdClass;
  580. $variables = tripal_get_node_variables($nid, 'phylotree_name_re');
  581. $phylotree->tripal_variables->phylotree_name_re = count($variables) > 0 ? $variables[0]->value : '';
  582. $variables = tripal_get_node_variables($nid, 'phylotree_use_uniquename');
  583. $phylotree->tripal_variables->phylotree_use_uniquename = count($variables) > 0 ? $variables[0]->value : '';
  584. $variables = tripal_get_node_variables($nid, 'phylotree_tree_file');
  585. $phylotree->tripal_variables->phylotree_tree_file = count($variables) > 0 ? $variables[0]->value : '';
  586. // Set the title for this node.
  587. $node->title = chado_get_node_title($node);
  588. }
  589. }
  590. /**
  591. * Implements hook_delete().
  592. *
  593. * Delete data from drupal and chado databases when a node is deleted
  594. *
  595. * @ingroup tripal_phylogeny
  596. */
  597. function chado_phylotree_delete(&$node) {
  598. $phylotree_id = chado_get_id_from_nid('phylotree', $node->nid);
  599. // if we don't have a phylotree id for this node then this isn't a node of
  600. // type chado_phylotree or the entry in the chado_phylotree table was lost.
  601. if (!$phylotree_id) {
  602. return;
  603. }
  604. // Remove data from {chado_phylotree}, {node} and {node_revisions} tables of
  605. // drupal database
  606. $sql_del = "DELETE FROM {chado_phylotree} WHERE nid = :nid AND vid = :vid";
  607. db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
  608. $sql_del = "DELETE FROM {node_revision} WHERE nid = :nid AND vid = :vid";
  609. db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
  610. $sql_del = "DELETE FROM {node} WHERE nid = :nid AND vid = :vid";
  611. db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
  612. // Remove data from phylotree and phylotreeprop tables of chado
  613. // database as well
  614. chado_query("DELETE FROM {phylotree} WHERE phylotree_id = :phylotree_id", array(':phylotree_id' => $phylotree_id));
  615. }
  616. /**
  617. * Implement hook_node_access().
  618. *
  619. * This hook allows node modules to limit access to the node types they define.
  620. *
  621. * @param $node
  622. * The node on which the operation is to be performed, or, if it does not yet exist, the
  623. * type of node to be created
  624. *
  625. * @param $op
  626. * The operation to be performed
  627. *
  628. * @param $account
  629. * A user object representing the user for whom the operation is to be performed
  630. *
  631. * @return
  632. * If the permission for the specified operation is not set then return FALSE. If the
  633. * permission is set then return NULL as this allows other modules to disable
  634. * access. The only exception is when the $op == 'create'. We will always
  635. * return TRUE if the permission is set.
  636. *
  637. * @ingroup tripal_phylogeny
  638. */
  639. function chado_phylotree_node_access($node, $op, $account) {
  640. $node_type = $node;
  641. if (is_object($node)) {
  642. $node_type = $node->type;
  643. }
  644. if($node_type == 'chado_phylotree') {
  645. if ($op == 'create') {
  646. if (!user_access('create chado_phylotree content', $account)) {
  647. return NODE_ACCESS_DENY;
  648. }
  649. return NODE_ACCESS_ALLOW;
  650. }
  651. if ($op == 'update') {
  652. if (!user_access('edit chado_phylotree content', $account)) {
  653. return NODE_ACCESS_DENY;
  654. }
  655. }
  656. if ($op == 'delete') {
  657. if (!user_access('delete chado_phylotree content', $account)) {
  658. return NODE_ACCESS_DENY;
  659. }
  660. }
  661. if ($op == 'view') {
  662. if (!user_access('access chado_phylotree content', $account)) {
  663. return NODE_ACCESS_DENY;
  664. }
  665. }
  666. return NODE_ACCESS_IGNORE;
  667. }
  668. }
  669. /**
  670. * phylotree_by_name
  671. *
  672. * Lookup tree by name, and redirect to that drupal node. This could
  673. * also be done for example by URL aliases, for example using pathauto
  674. * module.
  675. *
  676. * @param int $phylotree_id
  677. * @return string json
  678. * @ingroup tripal_phylogeny
  679. */
  680. function phylotree_by_name($name) {
  681. $sql = <<<SQL
  682. SELECT cp.nid as node_id
  683. FROM chado.phylotree t
  684. LEFT JOIN chado_phylotree cp on cp.phylotree_id = t.phylotree_id
  685. WHERE t.name = :name
  686. SQL;
  687. $args = array(':name' => $name);
  688. $result = chado_query( $sql, $args );
  689. $nid = $result->fetchField();
  690. drupal_goto("node/$nid");
  691. }
  692. /**
  693. * phylotree_json
  694. * Get json representation of a phylotree id. See menu item for JSON service
  695. * endpoint.
  696. *
  697. * @param int $phylotree_id
  698. * @return string json
  699. * @ingroup tripal_phylogeny
  700. */
  701. function phylotree_json($phylotree_id) {
  702. $phylotree = chado_generate_var('phylotree', array('phylotree_id' => $phylotree_id));
  703. // This SQL gets all of the phylonodes for a given tree as well as the
  704. // features and organisms with which it is assocaited. Each phylonode
  705. // can be associated with an orgnaism in one of two ways: 1) via a
  706. // feature linked by the phylonode.feature_id field or 2) via a
  707. // a record in the phylonde_organsim table. Therefore both types of
  708. // organism records are returned in the query below, but those
  709. // retrieved via a FK link on features are prefixed with 'fo_'.
  710. $sql = "
  711. SELECT
  712. n.phylonode_id, n.parent_phylonode_id, n.label AS name, n.distance AS length,
  713. f.feature_id, f.name AS feature_name,
  714. cvt.name AS cvterm_name,
  715. o.organism_id, o.common_name, o.abbreviation, o.genus, o.species,
  716. fo.organism_id AS fo_organism_id, fo.common_name AS fo_common_name,
  717. fo.abbreviation AS fo_abbreviation, fo.genus as fo_genus, fo.species AS fo_species,
  718. cf.nid AS feature_node_id,
  719. fco.nid AS fo_organism_node_id,
  720. co.nid AS organism_node_id
  721. FROM {phylonode} n
  722. LEFT OUTER JOIN {cvterm} cvt ON n.type_id = cvt.cvterm_id
  723. LEFT OUTER JOIN {feature} f ON n.feature_id = f.feature_id
  724. LEFT OUTER JOIN public.chado_feature cf ON cf.feature_id = f.feature_id
  725. LEFT OUTER JOIN {organism} fo ON f.organism_id = fo.organism_id
  726. LEFT OUTER JOIN public.chado_organism fco ON fco.organism_id = fo.organism_id
  727. LEFT OUTER JOIN {phylonode_organism} po ON po.phylonode_id = n.phylonode_id
  728. LEFT OUTER JOIN {organism} o ON PO.organism_id = o.organism_id
  729. LEFT OUTER JOIN public.chado_organism co ON co.organism_id = o.organism_id
  730. WHERE n.phylotree_id = :phylotree_id
  731. ";
  732. $args = array(':phylotree_id' => $phylotree_id);
  733. $result = chado_query($sql, $args);
  734. // Fetch all the phylonodes into an assoc array indexed by phylonode_id.
  735. // Convert from resultset record to array, fixing datatypes. chado_query
  736. // returns numeric as string and fun stuff like that.
  737. $phylonodes = array();
  738. $root_phylonode_ref = null;
  739. // Get the tree properties
  740. $root_size = variable_get('tripal_phylogeny_default_root_node_size', 5);
  741. $internal_size = variable_get('tripal_phylogeny_default_internal_node_size', 5);
  742. $leaf_size = variable_get('tripal_phylogeny_default_leaf_node_size', 5);
  743. foreach ($result as $r) {
  744. $phylonode_id = (int) $r->phylonode_id;
  745. // expect all nodes to have these properties
  746. $node = array(
  747. 'phylonode_id' => $phylonode_id,
  748. 'parent_phylonode_id' => (int) $r->parent_phylonode_id,
  749. 'length' => (double) $r->length,
  750. 'cvterm_name' => $r->cvterm_name
  751. );
  752. // If the nodes are taxonomic then set an equal distnace
  753. if ($phylotree->type_id->name == 'taxonomy') {
  754. $node['length'] = 0.001;
  755. }
  756. // Other props may exist only for leaf nodes
  757. if ($r->name) {
  758. $node['name'] = $r->name;
  759. }
  760. // If this node is associated with a feature then add in the details
  761. if ($r->feature_id) {
  762. $node['feature_id'] = (int) $r->feature_id;
  763. $node['feature_name'] = $r->feature_name;
  764. $node['feature_node_id'] = (int) $r->feature_node_id;
  765. }
  766. // Add in the organism fields when they are available via the
  767. // phylonode_organism table.
  768. if ($r->organism_id) {
  769. $node['organism_id'] = (int) $r->organism_id;
  770. $node['common_name'] = $r->common_name;
  771. $node['abbreviation'] = $r->abbreviation;
  772. $node['genus'] = $r->genus;
  773. $node['species'] = $r->species;
  774. $node['organism_node_id'] = (int) $r->organism_node_id;
  775. // If the node does not have a name but is linked to an organism
  776. // then set the name to be that of the genus and species.
  777. if (!$r->name) {
  778. $node['name'] = $r->genus . ' ' . $r->species;
  779. }
  780. }
  781. // Add in the organism fields when they are available via the
  782. // the phylonode.feature_id FK relationship.
  783. if ($r->fo_organism_id) {
  784. $node['fo_organism_id'] = (int) $r->fo_organism_id;
  785. $node['fo_common_name'] = $r->fo_common_name;
  786. $node['fo_abbreviation'] = $r->fo_abbreviation;
  787. $node['fo_genus'] = $r->fo_genus;
  788. $node['fo_species'] = $r->fo_species;
  789. $node['fo_organism_node_id'] = (int) $r->fo_organism_node_id;
  790. }
  791. // Add this node to the list, organized by ID.
  792. $phylonodes[$phylonode_id] = $node;
  793. }
  794. // Populate the children[] arrays for each node.
  795. foreach ($phylonodes as $key => &$node) {
  796. if ($node['parent_phylonode_id'] !== 0) {
  797. $parent_ref = &$phylonodes[ $node['parent_phylonode_id']];
  798. // Append node refernce to children.
  799. $parent_ref['children'][] = &$node;
  800. }
  801. else {
  802. $root_phylonode_ref = &$node;
  803. }
  804. }
  805. // dump datastructure as json to browser. drupal sets the mime-type correctly.
  806. drupal_json_output($root_phylonode_ref);
  807. }
  808. /**
  809. * Phylotree feature summary.
  810. *
  811. * Get an array of feature counts by organism. key = organism
  812. * abbreviation. value = number of features for this phylotree having
  813. * this organism.
  814. *
  815. * @param int phylotree_id
  816. * @return array
  817. * @ingroup tripal_phylogeny
  818. */
  819. function phylotree_feature_summary($phylotree_id) {
  820. $sql = "
  821. SELECT o.abbreviation, COUNT(o.organism_id) AS count
  822. FROM {phylonode} n
  823. LEFT OUTER JOIN {feature} f ON n.feature_id = f.feature_id
  824. LEFT OUTER JOIN {organism} o ON f.organism_id = o.organism_id
  825. WHERE n.phylotree_id = :phylotree_id
  826. AND n.feature_id IS NOT NULL
  827. GROUP BY o.organism_id
  828. ";
  829. $args = array(':phylotree_id' => $phylotree_id);
  830. $result = chado_query($sql, $args);
  831. $summary = array();
  832. foreach($result as $r) {
  833. $summary[$r->abbreviation] = $r->count;
  834. }
  835. return $summary;
  836. }