tripal_chado.phylotree.inc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. <?php
  2. /**
  3. * Prepares a phylogenetic tree for viewing.
  4. *
  5. * @param $phylotree
  6. */
  7. function tripal_phylogeny_prepare_tree_viewer($phylotree)
  8. {
  9. // If the phylotree is not provided then just return;
  10. if (!$phylotree) {
  11. tripal_report_error('tripal_phylotree', TRIPAL_ERROR, 'tripal_phylogeny_prepare_tree_viewer: must provide a $phylotree argument.');
  12. }
  13. // Don't prepare for viewing more than once.
  14. if (property_exists($phylotree, 'prepared_to_view') and
  15. $phylotree->prepared_to_view == TRUE) {
  16. return;
  17. }
  18. $module_path = drupal_get_path('module', 'tripal_chado');
  19. drupal_add_js('https://d3js.org/d3.v3.min.js', 'external');
  20. drupal_add_js("$module_path/theme/js/d3.phylogram.js");
  21. drupal_add_js("$module_path/theme/js/tripal_phylogeny.js");
  22. drupal_add_css("$module_path/theme/css/tripal_phylogeny.css");
  23. drupal_add_library('system', 'ui.dialog');
  24. // Don't show tick marks for the taxonomy tree.
  25. $skip_ticks = 0;
  26. if (!is_null($phylotree->type_id) and ($phylotree->type_id->name == 'taxonomy' or $phylotree->type_id->name == 'Species tree')) {
  27. $skip_ticks = 1;
  28. }
  29. // Get the node colors as set by the administrator.
  30. $colors = [];
  31. $color_defaults = variable_get("tripal_phylogeny_org_colors", [
  32. '1' => [
  33. 'organism' => '',
  34. 'color' => '',
  35. ],
  36. ]);
  37. foreach ($color_defaults as $i => $details) {
  38. if ($details['organism']) {
  39. // Strip the [id:xxx] from the name
  40. $organism_id = preg_replace('/^.+\[id: (\d+)\].*$/', '\1', $details['organism']);
  41. $colors[$organism_id] = $details['color'];
  42. }
  43. }
  44. drupal_add_js([
  45. 'tripal_chado' => [
  46. 'phylotree_url' => url('phylotree/' . $phylotree->phylotree_id),
  47. 'phylotree_theme_url' => url($module_path . '/theme'),
  48. 'tree_options' => [
  49. 'phylogram_width' => variable_get('tripal_phylogeny_default_phylogram_width', 350),
  50. 'root_node_size' => variable_get('tripal_phylogeny_default_root_node_size', 3),
  51. 'interior_node_size' => variable_get('tripal_phylogeny_default_interior_node_size', 1),
  52. 'leaf_node_size' => variable_get('tripal_phylogeny_default_leaf_node_size', 6),
  53. 'skipTicks' => $skip_ticks,
  54. 'phylogram_scale' => variable_get('tripal_phylogeny_default_phylogram_scale', 1),
  55. ],
  56. 'org_colors' => $colors,
  57. ],
  58. ], 'setting');
  59. if (!property_exists($phylotree, 'has_nodes')) {
  60. // If the nodes haven't loaded then set a value so the template can
  61. // choose not to show the phylogram.
  62. $values = ['phylotree_id' => $phylotree->phylotree_id];
  63. $options = ['limit' => 1, 'offset' => 0, 'has_record' => 1];
  64. $phylotree->has_nodes = chado_select_record('phylonode', ['phylonode_id'], $values, $options);
  65. }
  66. if (!property_exists($phylotree, 'has_features')) {
  67. // If the nodes haven't loaded then set a value so the template can
  68. // choose not to show the circular dendrogram. The chado_select_record()
  69. // API call can't do this query so we have to do it manually.
  70. $sql = "
  71. SELECT count(*) as num_features
  72. FROM {phylonode}
  73. WHERE NOT feature_id IS NULL and phylotree_id = :phylotree_id
  74. LIMIT 1 OFFSET 0
  75. ";
  76. $phylotree->has_features = chado_query($sql, [':phylotree_id' => $phylotree->phylotree_id])->fetchField();
  77. }
  78. $phylotree->prepared_to_view = TRUE;
  79. }
  80. /**
  81. * Get json representation of a phylotree id.
  82. *
  83. * This function is meant to be called via AJAX.
  84. *
  85. * @param int $phylotree_id
  86. * the ID of the phylotree node.
  87. *
  88. * @return string json
  89. *
  90. * @ingroup tripal_phylogeny
  91. */
  92. function tripal_phylogeny_ajax_get_tree_json($phylotree_id)
  93. {
  94. $phylotree = chado_generate_var('phylotree', ['phylotree_id' => $phylotree_id]);
  95. // For backwards compatibility with Tripal v2 and the legacy modules of
  96. // Tripal v3 we have two different SQL statements.
  97. if (module_exists('tripal_phylogeny')) {
  98. // This SQL gets all of the phylonodes for a given tree as well as the
  99. // features and organisms with which it is associated. Each phylonode
  100. // can be associated with an organism in one of two ways: 1) via a
  101. // feature linked by the phylonode.feature_id field or 2) via a
  102. // a record in the phylonde_organism table. Therefore both types of
  103. // organism records are returned in the query below, but those
  104. // retrieved via a FK link on features are prefixed with 'fo_'.
  105. $sql = "
  106. SELECT
  107. n.phylonode_id, n.parent_phylonode_id, n.label AS name, n.distance AS length,
  108. f.feature_id, f.name AS feature_name,
  109. cvt.name AS cvterm_name,
  110. o.organism_id, o.common_name, o.abbreviation, o.genus, o.species,
  111. fo.organism_id AS fo_organism_id, fo.common_name AS fo_common_name,
  112. fo.abbreviation AS fo_abbreviation, fo.genus as fo_genus, fo.species AS fo_species,
  113. cf.nid AS feature_nid,
  114. fco.nid AS fo_organism_nid,
  115. co.nid AS organism_nid
  116. FROM {phylonode} n
  117. LEFT OUTER JOIN {cvterm} cvt ON n.type_id = cvt.cvterm_id
  118. LEFT OUTER JOIN {feature} f ON n.feature_id = f.feature_id
  119. LEFT OUTER JOIN [chado_feature] cf ON cf.feature_id = f.feature_id
  120. LEFT OUTER JOIN {organism} fo ON f.organism_id = fo.organism_id
  121. LEFT OUTER JOIN [chado_organism] fco ON fco.organism_id = fo.organism_id
  122. LEFT OUTER JOIN {phylonode_organism} po ON po.phylonode_id = n.phylonode_id
  123. LEFT OUTER JOIN {organism} o ON PO.organism_id = o.organism_id
  124. LEFT OUTER JOIN [chado_organism] co ON co.organism_id = o.organism_id
  125. WHERE n.phylotree_id = :phylotree_id
  126. ";
  127. } else {
  128. $sql = "
  129. SELECT
  130. n.phylonode_id, n.parent_phylonode_id, n.label AS name, n.distance AS length,
  131. f.feature_id, f.name AS feature_name,
  132. cvt.name AS cvterm_name,
  133. o.organism_id, o.common_name, o.abbreviation, o.genus, o.species,
  134. fo.organism_id AS fo_organism_id, fo.common_name AS fo_common_name,
  135. fo.abbreviation AS fo_abbreviation, fo.genus as fo_genus, fo.species AS fo_species
  136. FROM {phylonode} n
  137. LEFT OUTER JOIN {cvterm} cvt ON n.type_id = cvt.cvterm_id
  138. LEFT OUTER JOIN {feature} f ON n.feature_id = f.feature_id
  139. LEFT OUTER JOIN {organism} fo ON f.organism_id = fo.organism_id
  140. LEFT OUTER JOIN {phylonode_organism} po ON po.phylonode_id = n.phylonode_id
  141. LEFT OUTER JOIN {organism} o ON PO.organism_id = o.organism_id
  142. WHERE n.phylotree_id = :phylotree_id
  143. ";
  144. }
  145. $args = [':phylotree_id' => $phylotree_id];
  146. $results = chado_query($sql, $args);
  147. // Fetch all the phylonodes into an assoc array indexed by phylonode_id.
  148. // Convert from resultset record to array, fixing datatypes. chado_query
  149. // returns numeric as string and fun stuff like that.
  150. $phylonodes = [];
  151. $root_phylonode_ref = NULL;
  152. if ($results) {
  153. while ($r = $results->fetchObject()) {
  154. $phylonode_id = (int)$r->phylonode_id;
  155. // expect all nodes to have these properties
  156. $node = [
  157. 'phylonode_id' => $phylonode_id,
  158. 'parent_phylonode_id' => (int)$r->parent_phylonode_id,
  159. 'length' => (double)$r->length,
  160. 'cvterm_name' => $r->cvterm_name,
  161. ];
  162. // If the nodes are taxonomic then set an equal distance
  163. if ($phylotree->type_id->name == 'taxonomy' or $phylotree->type_id->name == 'Speces tree') {
  164. $node['length'] = 0.001;
  165. }
  166. // Other props may exist only for leaf nodes
  167. if ($r->name) {
  168. $node['name'] = $r->name;
  169. }
  170. // If this node is associated with a feature then add in the details
  171. if ($r->feature_id) {
  172. $node['feature_id'] = (int)$r->feature_id;
  173. $node['feature_name'] = $r->feature_name;
  174. if (module_exists('tripal_phylogeny')) {
  175. $node['feature_nid'] = (int)$r->feature_nid;
  176. } else {
  177. $entity_id = chado_get_record_entity_by_table('feature', $r->feature_id);
  178. $node['feature_eid'] = (int)$entity_id;
  179. }
  180. }
  181. // Add in the organism fields when they are available via the
  182. // phylonode_organism table.
  183. if ($r->organism_id) {
  184. $node['organism_id'] = (int)$r->organism_id;
  185. $node['common_name'] = $r->common_name;
  186. $node['abbreviation'] = $r->abbreviation;
  187. $node['genus'] = $r->genus;
  188. $node['species'] = $r->species;
  189. if (module_exists('tripal_phylogeny')) {
  190. $node['organism_nid'] = (int)$r->organism_nid;
  191. } else {
  192. $entity_id = chado_get_record_entity_by_table('organism', $r->organism_id);
  193. $node['organism_eid'] = (int)$entity_id;
  194. }
  195. // If the node does not have a name but is linked to an organism
  196. // then set the name to be that of the genus and species.
  197. if (!$r->name) {
  198. $node['name'] = $r->genus . ' ' . $r->species;
  199. }
  200. }
  201. // Add in the organism fields when they are available via the
  202. // the phylonode.feature_id FK relationship.
  203. if ($r->fo_organism_id) {
  204. $node['fo_organism_id'] = (int)$r->fo_organism_id;
  205. $node['fo_common_name'] = $r->fo_common_name;
  206. $node['fo_abbreviation'] = $r->fo_abbreviation;
  207. $node['fo_genus'] = $r->fo_genus;
  208. $node['fo_species'] = $r->fo_species;
  209. if (module_exists('tripal_phylogeny')) {
  210. $node['fo_organism_nid'] = (int)$r->fo_organism_nid;
  211. } else {
  212. $entity_id = chado_get_record_entity_by_table('organism', $r->fo_organism_id);
  213. $node['fo_organism_eid'] = (int)$entity_id;
  214. }
  215. }
  216. // Add this node to the list, organized by ID.
  217. $phylonodes[$phylonode_id] = $node;
  218. }
  219. // Populate the children[] arrays for each node.
  220. foreach ($phylonodes as $key => &$node) {
  221. if ($node['parent_phylonode_id'] !== 0) {
  222. $parent_ref = &$phylonodes[$node['parent_phylonode_id']];
  223. // Append node reference to children.
  224. $parent_ref['children'][] = &$node;
  225. } else {
  226. $root_phylonode_ref = &$node;
  227. }
  228. }
  229. }
  230. // dump datastructure as json to browser. drupal sets the mime-type correctly.
  231. drupal_json_output($root_phylonode_ref);
  232. }
  233. /**
  234. * @file
  235. * This file contains the functions used for administration of the module
  236. *
  237. */
  238. function tripal_phylogeny_admin_phylotrees_listing()
  239. {
  240. $output = '';
  241. // set the breadcrumb
  242. $breadcrumb = [];
  243. $breadcrumb[] = l('Home', '<front>');
  244. $breadcrumb[] = l('Administration', 'admin');
  245. $breadcrumb[] = l('Tripal', 'admin/tripal');
  246. $breadcrumb[] = l('Data Storage', 'admin/tripal/storage');
  247. $breadcrumb[] = l('Chado', 'admin/tripal/storage/chado');
  248. drupal_set_breadcrumb($breadcrumb);
  249. // Add the view
  250. $view = views_embed_view('tripal_phylogeny_admin_phylotree', 'default');
  251. if (isset($view)) {
  252. $output .= $view;
  253. } else {
  254. $output .= '<p>The Phylotree module uses primarily views to provide an '
  255. . 'administrative interface. Currently one or more views needed for this '
  256. . 'administrative interface are disabled. <strong>Click each of the following links to '
  257. . 'enable the pertinent views</strong>:</p>';
  258. $output .= '<ul>';
  259. $output .= '<li>' . l('Phylotree View', 'admin/tripal/extension/tripal_phylogeny/views/phylotree/enable') . '</li>';
  260. $output .= '</ul>';
  261. }
  262. return $output;
  263. }
  264. /**
  265. *
  266. * @param unknown $form
  267. * @param unknown $form_state
  268. */
  269. function tripal_phylogeny_default_plots_form($form, &$form_state)
  270. {
  271. $form = [];
  272. $form['plot_settings'] = [
  273. '#type' => 'fieldset',
  274. '#title' => t('Plot Settings'),
  275. '#description' => t('You can customize settings for each plot'),
  276. '#collapsible' => TRUE,
  277. '#collapsed' => FALSE,
  278. ];
  279. $form['plot_settings']['phylogram_width'] = [
  280. '#type' => 'textfield',
  281. '#title' => 'Tree Width',
  282. '#description' => 'Please specify the width in pixels for the phylogram',
  283. '#default_value' => variable_get('tripal_phylogeny_default_phylogram_width', 350),
  284. '#element_validate' => [
  285. 'element_validate_integer_positive',
  286. ],
  287. '#size' => 5,
  288. ];
  289. $form['plot_settings']['phylogram_scale'] = [
  290. '#type' => 'select',
  291. '#title' => t('Phylogram Scale'),
  292. '#description' => 'Please specify the scale to use.',
  293. '#default_value' => variable_get('tripal_phylogeny_default_phylogram_scale', 1),
  294. '#options' => array(
  295. 1 => t('Linear'),
  296. 2 => t('Logarithmic'),
  297. ),
  298. '#size' => 2,
  299. ];
  300. $form['node_settings'] = [
  301. '#type' => 'fieldset',
  302. '#title' => t('Node Settings'),
  303. '#description' => t('You can customize settings for the nodes on the trees.'),
  304. '#collapsible' => TRUE,
  305. '#collapsed' => FALSE,
  306. ];
  307. $form['node_settings']['root_node_size'] = [
  308. '#type' => 'textfield',
  309. '#title' => 'Root Node Size',
  310. '#description' => 'Please specify a size for the root node size. If set to zero, the node will not appear.',
  311. '#default_value' => variable_get('tripal_phylogeny_default_root_node_size', 3),
  312. '#element_validate' => [
  313. 'element_validate_integer',
  314. ],
  315. '#size' => 3,
  316. ];
  317. $form['node_settings']['interior_node_size'] = [
  318. '#type' => 'textfield',
  319. '#title' => 'Interor Node Size',
  320. '#description' => 'Please specify a size for the interior node size. If set to zero, the node will not appear.',
  321. '#default_value' => variable_get('tripal_phylogeny_default_interior_node_size', 0),
  322. '#element_validate' => [
  323. 'element_validate_integer',
  324. ],
  325. '#size' => 3,
  326. ];
  327. $form['node_settings']['leaf_node_size'] = [
  328. '#type' => 'textfield',
  329. '#title' => 'Leaf Node Size',
  330. '#description' => 'Please specify a size for the leaf node size. If set to zero, the node will not appear.',
  331. '#default_value' => variable_get('tripal_phylogeny_default_leaf_node_size', 6),
  332. '#element_validate' => [
  333. 'element_validate_integer',
  334. ],
  335. '#size' => 3,
  336. ];
  337. // Get the number of organism colors that already exist. If the site admin
  338. // has set colors then those settings will be in a Drupal variable which we
  339. // will retrieve. Otherwise the num_orgs defaults to 1 and a single
  340. // set of fields is provided.
  341. $num_orgs = variable_get("tripal_phylogeny_num_orgs", 1);
  342. if (array_key_exists('values', $form_state) and array_key_exists('num_orgs', $form_state['values'])) {
  343. $num_orgs = $form_state['values']['num_orgs'];
  344. }
  345. // The default values for each organism color are provided in a d
  346. // Drupal variable that gets set when the form is set.
  347. $color_defaults = variable_get("tripal_phylogeny_org_colors", [
  348. '1' => [
  349. 'organism' => '',
  350. 'color' => '',
  351. ],
  352. ]);
  353. $form['node_settings']['desc'] = [
  354. '#type' => 'item',
  355. '#title' => t('Node Colors by Organism'),
  356. '#markup' => t('If the trees are associated with features (e.g. proteins)
  357. then the nodes can be color-coded by their organism. This helps the user
  358. visualize which nodes belong to each organism. Please enter the
  359. name of the organism and it\'s corresponding color in HEX code (e.g. #FF0000 == red).
  360. Organisms that are not given a color will be gray.'),
  361. ];
  362. $form['node_settings']['org_table']['num_orgs'] = [
  363. '#type' => 'value',
  364. '#value' => $num_orgs,
  365. ];
  366. // Iterate through the number of organism colors and add a field for each one.
  367. for ($i = 0; $i < $num_orgs; $i++) {
  368. $form['node_settings']['org_table']['organism_' . $i] = [
  369. '#type' => 'textfield',
  370. '#default_value' => array_key_exists($i, $color_defaults) ? $color_defaults[$i]['organism'] : '',
  371. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/organism",
  372. '#description' => t('Please enter the name of the organism.'),
  373. '#size' => 30,
  374. ];
  375. $form['node_settings']['org_table']['color_' . $i] = [
  376. '#type' => 'textfield',
  377. '#description' => t('Please provide a color in Hex format (e.g. #FF0000).'),
  378. '#default_value' => array_key_exists($i, $color_defaults) ? $color_defaults[$i]['color'] : '',
  379. '#suffix' => "<div id=\"color-box-$i\" style=\"width: 30px;\"></div>",
  380. '#size' => 10,
  381. ];
  382. }
  383. $form['node_settings']['org_table']['add'] = [
  384. '#type' => 'submit',
  385. '#name' => 'add',
  386. '#value' => 'Add',
  387. '#ajax' => [
  388. 'callback' => "tripal_phylogeny_default_plots_form_ajax_callback",
  389. 'wrapper' => 'tripal_phylogeny_default_plots_form',
  390. 'effect' => 'fade',
  391. 'method' => 'replace',
  392. ],
  393. ];
  394. $form['node_settings']['org_table']['remove'] = [
  395. '#type' => 'submit',
  396. '#name' => 'remove',
  397. '#value' => 'Remove',
  398. '#ajax' => [
  399. 'callback' => "tripal_phylogeny_default_plots_form_ajax_callback",
  400. 'wrapper' => 'tripal_phylogeny_default_plots_form',
  401. 'effect' => 'fade',
  402. 'method' => 'replace',
  403. ],
  404. ];
  405. $form['node_settings']['org_table']['#theme'] = 'tripal_phylogeny_admin_org_color_tables';
  406. $form['node_settings']['org_table']['#prefix'] = '<div id="tripal_phylogeny_default_plots_form">';
  407. $form['node_settings']['org_table']['#suffix'] = '</div>';
  408. $form['submit'] = [
  409. '#type' => 'submit',
  410. '#name' => 'submit',
  411. '#value' => 'Save Configuration',
  412. ];
  413. $form['#submit'][] = 'tripal_phylogeny_default_plots_form_submit';
  414. return $form;
  415. }
  416. /**
  417. * Validate the phylotree settings forms
  418. *
  419. * @ingroup tripal_phylogeny
  420. */
  421. function tripal_phylogeny_default_plots_form_validate($form, &$form_state)
  422. {
  423. }
  424. /**
  425. *
  426. * @param unknown $form
  427. * @param unknown $form_state
  428. */
  429. function tripal_phylogeny_default_plots_form_submit($form, &$form_state)
  430. {
  431. // Rebuild this form after submission so that any changes are reflected in
  432. // the flat tables.
  433. $form_state['rebuild'] = TRUE;
  434. if ($form_state['clicked_button']['#name'] == 'submit') {
  435. variable_set('tripal_phylogeny_default_phylogram_width', $form_state['values']['phylogram_width']);
  436. variable_set('tripal_phylogeny_default_root_node_size', $form_state['values']['root_node_size']);
  437. variable_set('tripal_phylogeny_default_interior_node_size', $form_state['values']['interior_node_size']);
  438. variable_set('tripal_phylogeny_default_leaf_node_size', $form_state['values']['leaf_node_size']);
  439. variable_set('tripal_phylogeny_default_phylogram_scale', $form_state['values']['phylogram_scale']);
  440. $num_orgs = $form_state['values']['num_orgs'];
  441. variable_set("tripal_phylogeny_num_orgs", $num_orgs);
  442. $colors = [];
  443. for ($i = 0; $i < $num_orgs; $i++) {
  444. $colors[$i] = [
  445. 'organism' => $form_state['values']['organism_' . $i],
  446. 'color' => $form_state['values']['color_' . $i],
  447. ];
  448. }
  449. variable_set("tripal_phylogeny_org_colors", $colors);
  450. }
  451. if ($form_state['clicked_button']['#name'] == 'add') {
  452. $form_state['values']['num_orgs']++;
  453. }
  454. if ($form_state['clicked_button']['#name'] == 'remove') {
  455. $form_state['values']['num_orgs']--;
  456. }
  457. }
  458. /**
  459. *
  460. * @param unknown $variables
  461. */
  462. function theme_tripal_phylogeny_admin_org_color_tables($variables)
  463. {
  464. $fields = $variables['element'];
  465. $num_orgs = $fields['num_orgs']['#value'];
  466. $headers = ['Organism', 'Color', ''];
  467. $rows = [];
  468. for ($i = 0; $i < $num_orgs; $i++) {
  469. $add_button = ($i == $num_orgs - 1) ? drupal_render($fields['add']) : '';
  470. $del_button = ($i == $num_orgs - 1 and $i != 0) ? drupal_render($fields['remove']) : '';
  471. $rows[] = [
  472. drupal_render($fields['organism_' . $i]),
  473. drupal_render($fields['color_' . $i]),
  474. $add_button . $del_button,
  475. ];
  476. }
  477. $table_vars = [
  478. 'header' => $headers,
  479. 'rows' => $rows,
  480. 'attributes' => [],
  481. 'sticky' => FALSE,
  482. 'colgroups' => [],
  483. 'empty' => '',
  484. ];
  485. $form['orgs']['num_orgs'] = $fields['num_orgs'];
  486. return theme('table', $table_vars);
  487. }
  488. /**
  489. * Ajax callback function for the gensas_job_view_panel_form.
  490. *
  491. * @param $form
  492. * @param $form_state
  493. */
  494. function tripal_phylogeny_default_plots_form_ajax_callback($form, $form_state)
  495. {
  496. return $form['node_settings']['org_table'];
  497. }