tripal_chado.phylotree.inc 19 KB

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