tripal_phylogeny.chado_node.inc 31 KB

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