tripal_chado.phylotree.inc 19 KB

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