tripal_feature.module 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007
  1. <?php
  2. //
  3. // Copyright 2009 Clemson University
  4. //
  5. /*************************************************************************
  6. *
  7. */
  8. function tripal_feature_init(){
  9. // add the jGCharts JS and CSS
  10. drupal_add_js (drupal_get_path('theme', 'tripal').'/js/tripal_feature.js');
  11. drupal_add_js (drupal_get_path('theme', 'tripal').'/js/jgcharts/jgcharts.js');
  12. }
  13. /************************************************************************
  14. *
  15. */
  16. function tripal_feature_admin () {
  17. // before proceeding check to see if we have any
  18. // currently processing jobs. If so, we don't want
  19. // to give the opportunity to sync libraries
  20. $active_jobs = FALSE;
  21. if(tripal_get_module_active_jobs('tripal_feature')){
  22. $active_jobs = TRUE;
  23. }
  24. if(!$active_jobs){
  25. $form['chado_feature_accession_prefix'] = array (
  26. '#title' => t('Accession Prefix'),
  27. '#type' => t('textfield'),
  28. '#description' => t("Accession numbers for features consist of the ".
  29. "chado feature_id and a site specific prefix. Set the prefix that ".
  30. "will be incorporated in front of each feature_id to form a unique ".
  31. "accession number for this site."),
  32. '#required' => TRUE,
  33. '#default_value' => variable_get('chado_feature_accession_prefix','ID'),
  34. );
  35. $form['chado_feature_types'] = array(
  36. '#title' => t('Feature Types'),
  37. '#type' => 'textarea',
  38. '#description' => t('Enter the names of the sequence types that the ".
  39. "site will support with independent pages. Pages for these data ".
  40. "types will be built automatically for features that exist in the ".
  41. "chado database. The names listed here should be spearated by ".
  42. "spaces or entered separately on new lines. The names must match ".
  43. "exactly (spelling and case) with terms in the sequence ontology'),
  44. '#required' => TRUE,
  45. '#default_value' => variable_get('chado_feature_types','EST contig'),
  46. );
  47. $form['browser'] = array(
  48. '#type' => 'fieldset',
  49. '#title' => t('Feature Browser')
  50. );
  51. $allowedoptions1 ['show_feature_browser'] = "Show the feature browser on the organism page. The browser loads when page loads. This may be slow for large sites.";
  52. $allowedoptions1 ['hide_feature_browser'] = "Hide the feature browser on the organism page. Disables the feature browser completely.";
  53. // $allowedoptions ['allow_feature_browser'] = "Allow loading of the feature browsing through AJAX. For large sites the initial page load will be quick with the feature browser loading afterwards.";
  54. $form['browser']['browse_features'] = array(
  55. '#title' => 'Feature Browser on Organism Page',
  56. '#description' => 'A feature browser can be added to an organism page to allow users to quickly '.
  57. 'access a feature. This will most likely not be the ideal mechanism for accessing feature '.
  58. 'information, especially for large sites, but it will alow users exploring the site (such '.
  59. 'as students) to better understand the data types available on the site.',
  60. '#type' => 'radios',
  61. '#options' => $allowedoptions1,
  62. '#default_value'=>variable_get('tripal_feature_browse_setting',
  63. array('show_feature_browser')),
  64. );
  65. $form['browser']['set_browse_button'] = array(
  66. '#type' => 'submit',
  67. '#value' => t('Set Browser'),
  68. '#weight' => 2,
  69. );
  70. $form['summary'] = array(
  71. '#type' => 'fieldset',
  72. '#title' => t('Feature Summary')
  73. );
  74. $allowedoptions2 ['show_feature_summary'] = "Show the feature summary on the organism page. The summary loads when page loads.";
  75. $allowedoptions2 ['hide_feature_summary'] = "Hide the feature summary on the organism page. Disables the feature summary.";
  76. $form['summary']['feature_summary'] = array(
  77. '#title' => 'Feature Summary on Organism Page',
  78. '#description' => 'A feature summary can be added to an organism page to allow users to see the '.
  79. 'type and quantity of features available for the organism.',
  80. '#type' => 'radios',
  81. '#options' => $allowedoptions2,
  82. '#default_value'=>variable_get('tripal_feature_summary_setting',
  83. array('show_feature_summary')),
  84. );
  85. $form['summary']['set_summary_button'] = array(
  86. '#type' => 'submit',
  87. '#value' => t('Set Summary'),
  88. '#weight' => 2,
  89. );
  90. get_tripal_feature_admin_form_sync_set($form);
  91. get_tripal_feature_admin_form_taxonomy_set($form);
  92. get_tripal_feature_admin_form_reindex_set($form);
  93. get_tripal_feature_admin_form_cleanup_set($form);
  94. } else {
  95. $form['notice'] = array(
  96. '#type' => 'fieldset',
  97. '#title' => t('Feature Management Temporarily Unavailable')
  98. );
  99. $form['notice']['message'] = array(
  100. '#value' => t('Currently, feature management jobs are waiting or ".
  101. "are running. Managemment features have been hidden until these ".
  102. "jobs complete. Please check back later once these jobs have ".
  103. "finished. You can view the status of pending jobs in the Tripal ".
  104. "jobs page.'),
  105. );
  106. }
  107. return system_settings_form($form);
  108. }
  109. /************************************************************************
  110. *
  111. */
  112. function tripal_feature_admin_validate($form, &$form_state) {
  113. global $user; // we need access to the user info
  114. $job_args = array();
  115. // if the user wants to sync up the chado features then
  116. // add the job to the management queue
  117. if ($form_state['values']['op'] == t('Sync all Features')) {
  118. tripal_add_job('Sync all features','tripal_feature',
  119. 'tripal_feature_sync_features',$job_args,$user->uid);
  120. }
  121. if ($form_state['values']['op'] == t('Set/Reset Taxonomy for all feature nodes')) {
  122. tripal_add_job('Set all feature taxonomy','tripal_feature',
  123. 'tripal_features_set_taxonomy',$job_args,$user->uid);
  124. }
  125. if ($form_state['values']['op'] == t('Reindex all feature nodes')) {
  126. tripal_add_job('Reindex all features','tripal_feature',
  127. 'tripal_features_reindex',$job_args,$user->uid);
  128. }
  129. if ($form_state['values']['op'] == t('Clean up orphaned features')) {
  130. tripal_add_job('Cleanup orphaned features','tripal_feature',
  131. 'tripal_features_cleanup',$job_args,$user->uid);
  132. }
  133. if ($form_state['values']['op'] == t('Set Browser')) {
  134. variable_set('tripal_feature_browse_setting',$form_state['values']['browse_features']);
  135. }
  136. if ($form_state['values']['op'] == t('Set Summary')) {
  137. variable_set('tripal_feature_summary_setting',$form_state['values']['feature_summary']);
  138. }
  139. }
  140. /************************************************************************
  141. *
  142. */
  143. function get_tripal_feature_admin_form_cleanup_set(&$form) {
  144. $form['cleanup'] = array(
  145. '#type' => 'fieldset',
  146. '#title' => t('Clean Up')
  147. );
  148. $form['cleanup']['description'] = array(
  149. '#type' => 'item',
  150. '#value' => t("With Drupal and chado residing in different databases ".
  151. "it is possible that nodes in Drupal and features in Chado become ".
  152. "\"orphaned\". This can occur if a feature node in Drupal is ".
  153. "deleted but the corresponding chado feature is not and/or vice ".
  154. "versa. The Cleanup function will also remove nodes for features ".
  155. "that are not in the list of allowed feature types as specified ".
  156. "above. This is helpful when a feature type needs to be ".
  157. "removed but was previously present as Drupal nodes. ".
  158. "Click the button below to resolve these discrepancies."),
  159. '#weight' => 1,
  160. );
  161. $form['cleanup']['button'] = array(
  162. '#type' => 'submit',
  163. '#value' => t('Clean up orphaned features'),
  164. '#weight' => 2,
  165. );
  166. }
  167. /************************************************************************
  168. *
  169. */
  170. function get_tripal_feature_admin_form_reindex_set(&$form) {
  171. $form['reindex'] = array(
  172. '#type' => 'fieldset',
  173. '#title' => t('Reindex')
  174. );
  175. $form['reindex']['description'] = array(
  176. '#type' => 'item',
  177. '#value' => t("Reindexing of nodes is important when content for nodes ".
  178. "is updated external to drupal, such as external uploads to chado. ".
  179. "Features need to be reindexed to ensure that updates to features ".
  180. "are searchable. Depending on the number of features this may take ".
  181. "quite a while. Click the button below to begin reindexing of ".
  182. "features."),
  183. '#weight' => 1,
  184. );
  185. $form['reindex']['button'] = array(
  186. '#type' => 'submit',
  187. '#value' => t('Reindex all feature nodes'),
  188. '#weight' => 2,
  189. );
  190. }
  191. /************************************************************************
  192. *
  193. */
  194. function get_tripal_feature_admin_form_taxonomy_set (&$form) {
  195. $form['taxonomy'] = array(
  196. '#type' => 'fieldset',
  197. '#title' => t('Set Taxonomy')
  198. );
  199. $form['taxonomy']['description'] = array(
  200. '#type' => 'item',
  201. '#value' => t("Drupal allows for assignment of \"taxonomy\" or ".
  202. "catagorical terms to nodes. These terms allow for advanced ".
  203. "filtering during searching."),
  204. '#weight' => 1,
  205. );
  206. $tax_options = array (
  207. 'organism' => t('Organism name'),
  208. 'feature_type' => t('Feature Type (e.g. EST, mRNA, etc.)'),
  209. 'analysis' => t('Analysis Name'),
  210. 'library' => t('Library Name'),
  211. );
  212. $form['taxonomy']['tax_classes'] = array (
  213. '#title' => t('Available Taxonomic Classes'),
  214. '#type' => t('checkboxes'),
  215. '#description' => t("Please select the class of terms to assign to ".
  216. "chado features"),
  217. '#required' => FALSE,
  218. '#prefix' => '<div id="taxclass_boxes">',
  219. '#suffix' => '</div>',
  220. '#options' => $tax_options,
  221. '#weight' => 2,
  222. '#default_value' => variable_get('tax_classes',''),
  223. );
  224. $form['taxonomy']['button'] = array(
  225. '#type' => 'submit',
  226. '#value' => t('Set/Reset Taxonomy for all feature nodes'),
  227. '#weight' => 3,
  228. );
  229. }
  230. /************************************************************************
  231. *
  232. */
  233. function get_tripal_feature_admin_form_sync_set (&$form) {
  234. // get the list of organisms which will be synced.
  235. $feature_sql = "SELECT * FROM {Feature} WHERE uniquename = '%s' and organism_id = %d";
  236. $previous_db = db_set_active('chado');
  237. $feature = db_fetch_object(db_query($feature_sql,$node->title,$node->organism_id));
  238. db_set_active($previous_db);
  239. // define the fieldsets
  240. $form['sync'] = array(
  241. '#type' => 'fieldset',
  242. '#title' => t('Sync Features')
  243. );
  244. $form['sync']['description'] = array(
  245. '#type' => 'item',
  246. '#value' => t("Click the 'Sync all Features' button to create Drupal ".
  247. "content for features in chado. Only features of the types listed ".
  248. "above in the Feature Types box will be synced. Depending on the ".
  249. "number of features in the chado database this may take a long ".
  250. "time to complete. "),
  251. '#weight' => 1,
  252. );
  253. $orgs = tripal_organism_get_synced();
  254. $org_list = '';
  255. foreach($orgs as $org){
  256. $org_list .= "$org->genus $org->species, ";
  257. }
  258. $form['sync']['description2'] = array(
  259. '#type' => 'item',
  260. '#value' => "Only features for the following organisms will be synced: ".
  261. " $org_list",
  262. '#weight' => 1,
  263. );
  264. $form['sync']['button'] = array(
  265. '#type' => 'submit',
  266. '#value' => t('Sync all Features'),
  267. '#weight' => 3,
  268. );
  269. }
  270. /************************************************************************
  271. * Display help and module information
  272. * @param path which path of the site we're displaying help
  273. * @param arg array that holds the current path as would be returned from arg() function
  274. * @return help text for the path
  275. */
  276. function tripal_feature_help($path, $arg) {
  277. $output = '';
  278. switch ($path) {
  279. case "admin/help#tripal_feature":
  280. $output='<p>'.t("Displays links to nodes created on this date").'</p>';
  281. break;
  282. }
  283. return $output;
  284. }
  285. /************************************************************************
  286. * Provide information to drupal about the node types that we're creating
  287. * in this module
  288. */
  289. function tripal_feature_node_info() {
  290. $nodes = array();
  291. $nodes['chado_feature'] = array(
  292. 'name' => t('Feature'),
  293. 'module' => 'chado_feature',
  294. 'description' => t('A feature from the chado database'),
  295. 'has_title' => FALSE,
  296. 'title_label' => t('Feature'),
  297. 'has_body' => FALSE,
  298. 'body_label' => t('Feature Description'),
  299. 'locked' => TRUE
  300. );
  301. return $nodes;
  302. }
  303. /************************************************************************
  304. * Set the permission types that the chado module uses. Essentially we
  305. * want permissionis that protect creation, editing and deleting of chado
  306. * data objects
  307. */
  308. function tripal_feature_perm(){
  309. return array(
  310. 'access chado_feature content',
  311. 'create chado_feature content',
  312. 'delete chado_feature content',
  313. 'edit chado_feature content',
  314. );
  315. }
  316. /************************************************************************
  317. * Set the permission types that the module uses.
  318. */
  319. function chado_feature_access($op, $node, $account) {
  320. if ($op == 'create') {
  321. return user_access('create chado_feature content', $account);
  322. }
  323. if ($op == 'update') {
  324. if (user_access('edit chado_feature content', $account)) {
  325. return TRUE;
  326. }
  327. }
  328. if ($op == 'delete') {
  329. if (user_access('delete chado_feature content', $account)) {
  330. return TRUE;
  331. }
  332. }
  333. if ($op == 'view') {
  334. if (user_access('access chado_feature content', $account)) {
  335. return TRUE;
  336. }
  337. }
  338. return FALSE;
  339. }
  340. /************************************************************************
  341. * Menu items are automatically added for the new node types created
  342. * by this module to the 'Create Content' Navigation menu item. This function
  343. * adds more menu items needed for this module.
  344. */
  345. function tripal_feature_menu() {
  346. $items = array();
  347. // the administative settings menu
  348. $items['admin/tripal/tripal_feature'] = array(
  349. 'title' => 'Features',
  350. 'description' => 'Settings for Chado Features',
  351. 'page callback' => 'drupal_get_form',
  352. 'page arguments' => array('tripal_feature_admin'),
  353. 'access arguments' => array('administer site configuration'),
  354. 'type' => MENU_NORMAL_ITEM,
  355. );
  356. $items['admin/settings/tripal/tripal_feature/load'] = array(
  357. 'title' => 'Bulk Load',
  358. 'description' => 'Upload Data into Chado & Drupal',
  359. 'page callback' => 'tripal_feature_bulkload',
  360. 'access arguments' => array('administer site configuration'),
  361. 'type' => MENU_NORMAL_ITEM,
  362. );
  363. return $items;
  364. }
  365. /************************************************************************
  366. * When a new chado_feature node is created we also need to add information
  367. * to our chado_feature table. This function is called on insert of a new node
  368. * of type 'chado_feature' and inserts the necessary information.
  369. */
  370. function chado_feature_insert($node){
  371. // remove spaces, newlines from residues
  372. $residues = preg_replace("/[\n\r\s]/","",$node->residues);
  373. // If this feature already exists then don't recreate it in chado
  374. // TODO: the unique index in chado for this also includes the type_id. If the site
  375. // ever needs to have the same feature name for different types then this will break.
  376. $feature_sql = "SELECT * FROM {Feature} WHERE uniquename = '%s' and organism_id = %d";
  377. $previous_db = db_set_active('chado');
  378. $feature = db_fetch_object(db_query($feature_sql,$node->title,$node->organism_id));
  379. db_set_active($previous_db);
  380. // if the feature doesn't exist then let's create it in chado.
  381. if(!$feature){
  382. $sql = "INSERT INTO {feature} (organism_id, name, uniquename, residues, seqlen,".
  383. " is_obsolete, type_id)".
  384. " VALUES(%d,'%s','%s','%s',%d, %s, ".
  385. " (SELECT cvterm_id ".
  386. " FROM {CVTerm} CVT ".
  387. " INNER JOIN CV ON CVT.cv_id = CV.cv_id ".
  388. " WHERE CV.name = 'sequence' and CVT.name = '%s'))";
  389. $obsolete = 'FALSE';
  390. if($node->is_obsolete){
  391. $obsolete = 'TRUE';
  392. }
  393. // use chado database
  394. $previous_db = db_set_active('chado');
  395. db_query($sql,$node->organism_id,$node->title,$node->title,
  396. $residues,strlen($residues),$obsolete,$node->feature_type);
  397. // now that we've added the feature, get the feature id for this feature
  398. $feature = db_fetch_object(db_query($feature_sql,$node->title,$node->organism_id));
  399. // now use drupal database
  400. db_set_active($previous_db);
  401. }
  402. // add the genbank accession and synonyms
  403. chado_feature_add_synonyms($node->synonyms,$feature->feature_id);
  404. // make sure the entry for this feature doesn't already exist in the chado_feature table
  405. // if it doesn't exist then we want to add it.
  406. $node_check_sql = "SELECT * FROM {chado_feature} ".
  407. "WHERE feature_id = '%s'";
  408. $node_check = db_fetch_object(db_query($node_check_sql,$feature->feature_id));
  409. if(!$node_check){
  410. // next add the item to the drupal table
  411. $sql = "INSERT INTO {chado_feature} (nid, vid, feature_id, sync_date) ".
  412. "VALUES (%d, %d, %d, " . time() . ")";
  413. db_query($sql,$node->nid,$node->vid,$feature->feature_id);
  414. }
  415. }
  416. /************************************************************************
  417. */
  418. function chado_feature_delete($node){
  419. // get feature_id so we can remove it from chado database
  420. $sql_drupal = "SELECT feature_id ".
  421. "FROM {chado_feature} ".
  422. "WHERE nid = %d AND vid = %d";
  423. $feature_id = db_result(db_query($sql_drupal, $node->nid, $node->vid));
  424. // remove the drupal content
  425. $sql_del = "DELETE FROM {chado_feature} ".
  426. "WHERE nid = %d ".
  427. "AND vid = %d";
  428. db_query($sql_del, $node->nid, $node->vid);
  429. $sql_del = "DELETE FROM {node} ".
  430. "WHERE nid = %d ".
  431. "AND vid = %d";
  432. db_query($sql_del, $node->nid, $node->vid);
  433. $sql_del = "DELETE FROM {node_revisions} ".
  434. "WHERE nid = %d ".
  435. "AND vid = %d";
  436. db_query($sql_del, $node->nid, $node->vid);
  437. // Remove data from feature tables of chado database. This will
  438. // cause a cascade delete and remove all data in referencing tables
  439. // for this feature
  440. $previous_db = db_set_active('chado');
  441. db_query("DELETE FROM {feature} WHERE feature_id = %d", $feature_id);
  442. db_set_active($previous_db);
  443. drupal_set_message("The feature and all associated data were removed from ".
  444. "chado");
  445. }
  446. /************************************************************************
  447. */
  448. function chado_feature_update($node){
  449. if($node->revision){
  450. // TODO -- decide what to do about revisions
  451. } else {
  452. // get the feature for this node:
  453. $sql = 'SELECT feature_id FROM {chado_feature} WHERE vid = %d';
  454. $feature = db_fetch_object(db_query($sql, $node->vid));
  455. // remove spaces, newlines from residues
  456. $residues = preg_replace("/[\n\r\s]/","",$node->residues);
  457. $sql = "UPDATE {feature} ".
  458. " SET residues = '%s', ".
  459. " name = '%s', ".
  460. " uniquename = '%s', ".
  461. " seqlen = %d, ".
  462. " organism_id = %d, ".
  463. " is_obsolete = %s, ".
  464. " type_id = (SELECT cvterm_id ".
  465. " FROM {CVTerm} CVT ".
  466. " INNER JOIN CV ON CVT.cv_id = CV.cv_id ".
  467. " WHERE CV.name = 'sequence' and CVT.name = '%s') ".
  468. "WHERE feature_id = %d ";
  469. $obsolete = 'FALSE';
  470. if($node->is_obsolete){
  471. $obsolete = 'TRUE';
  472. }
  473. $previous_db = db_set_active('chado'); // use chado database
  474. db_query($sql,$residues,$node->title,$node->title,
  475. strlen($residues),$node->organism_id,$obsolete,$node->feature_type,
  476. $feature->feature_id);
  477. db_set_active($previous_db); // now use drupal database
  478. // add the genbank accession & synonyms
  479. // chado_feature_add_gbaccession($node->gbaccession,$feature->feature_id);
  480. chado_feature_add_synonyms($node->synonyms,$feature->feature_id);
  481. }
  482. }
  483. /************************************************************************
  484. *
  485. */
  486. function chado_feature_add_synonyms($synonyms,$feature_id){
  487. // make sure we only have a single space between each synonym
  488. $synonyms = preg_replace("/[\s\n\r]+/"," ",$synonyms);
  489. // split the synonyms into an array based on a space as the delimieter
  490. $syn_array = array();
  491. $syn_array = explode(" ",$synonyms);
  492. // use the chado database
  493. $previous_db = db_set_active('chado');
  494. // remove any old synonyms
  495. $feature_syn_dsql = "DELETE FROM {feature_synonym} WHERE feature_id = %d";
  496. if(!db_query($feature_syn_dsql,$feature_id)){
  497. $error .= "Could not remove synonyms from feature. ";
  498. }
  499. // return if we don't have any synonmys to add
  500. if(!$synonyms){
  501. db_set_active($previous_db);
  502. return;
  503. }
  504. // iterate through each synonym and add it to the database
  505. foreach($syn_array as $syn){
  506. // skip this item if it's empty
  507. if(!$syn){ break; }
  508. // check to see if we have this accession number already in the database
  509. // if so then don't add it again. it messes up drupal if the insert fails.
  510. // It is possible for the accession number to be present and not the feature
  511. $synonym_sql = "SELECT synonym_id FROM {synonym} ".
  512. "WHERE name = '%s'";
  513. $synonym = db_fetch_object(db_query($synonym_sql,$syn));
  514. if(!$synonym){
  515. $synonym_isql = "INSERT INTO {synonym} (name,synonym_sgml,type_id) ".
  516. "VALUES ('%s','%s', ".
  517. " (SELECT cvterm_id ".
  518. " FROM {CVTerm} CVT ".
  519. " INNER JOIN CV ON CVT.cv_id = CV.cv_id ".
  520. " WHERE CV.name = 'feature_property' and CVT.name = 'synonym'))";
  521. if(!db_query($synonym_isql,$syn,$syn)){
  522. $error .= "Could not add synonym. ";
  523. }
  524. // now get the synonym we just added
  525. $synonym_sql = "SELECT synonym_id FROM {synonym} ".
  526. "WHERE name = '%s'";
  527. $synonym = db_fetch_object(db_query($synonym_sql,$syn));
  528. }
  529. // now add in our new sysnonym
  530. $feature_syn_isql = "INSERT INTO {feature_synonym} (synonym_id,feature_id,pub_id) ".
  531. "VALUES (%d,%d,1)";
  532. if(!db_query($feature_syn_isql,$synonym->synonym_id,$feature_id)){
  533. $error .= "Could not add synonyms to feature. ";
  534. }
  535. }
  536. // return to the drupal database
  537. db_set_active($previous_db);
  538. return $error;
  539. }
  540. /************************************************************************
  541. *
  542. */
  543. function chado_feature_add_gbaccession($accession,$feature_id){
  544. // use chado database
  545. $previous_db = db_set_active('chado');
  546. // remove any old accession from genbank dbEST
  547. $fdbxref_dsql = "DELETE FROM {feature_dbxref} ".
  548. "WHERE feature_id = %d and dbxref_id IN ".
  549. " (SELECT DBX.dbxref_id FROM {dbxref} DBX ".
  550. " INNER JOIN DB ON DB.db_id = DBX.db_id ".
  551. " INNER JOIN feature_dbxref FDBX ON DBX.dbxref_id = FDBX.dbxref_id ".
  552. " WHERE DB.name = 'DB:Genbank' and FDBX.feature_id = %d)";
  553. if(!db_query($fdbxref_dsql,$feature_id,$feature_id)){
  554. $error .= "Could not remove accession from feature. ";
  555. }
  556. // if we don't have an accession number to add then just return
  557. if(!$accession){
  558. db_set_active($previous_db);
  559. return;
  560. }
  561. // get the db_id
  562. $db_sql = "SELECT db_id FROM {DB} ".
  563. "WHERE name = 'DB:Genbank_est'";
  564. $db = db_fetch_object(db_query($db_sql));
  565. // check to see if we have this accession number already in the database
  566. // if so then don't add it again. it messes up drupal if the insert fails.
  567. // It is possible for the accession number to be present and not the feature
  568. $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
  569. "WHERE db_id = %d and accession = '%s'";
  570. $dbxref = db_fetch_object(db_query($dbxref_sql,$db->db_id,$accession));
  571. if(!$dbxref){
  572. // add the accession number
  573. $dbxref_isql = "INSERT INTO {dbxref} (db_id,accession) ".
  574. " VALUES (%d, '%s') ";
  575. if(!db_query($dbxref_isql,$db->db_id,$accession)){
  576. $error .= 'Could not add accession as a database reference ';
  577. }
  578. // get the dbxref_id for the just added accession number
  579. $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
  580. "WHERE db_id = %d and accession = '%s'";
  581. $dbxref = db_fetch_object(db_query($dbxref_sql,$db->db_id,$accession));
  582. }
  583. // associate the accession number with the feature
  584. $feature_dbxref_isql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) ".
  585. " VALUES (%d, %d) ";
  586. if(!db_query($feature_dbxref_isql,$feature_id,$dbxref->dbxref_id)){
  587. $error .= 'Could not add feature database reference. ';
  588. }
  589. db_set_active($previous_db);
  590. return $error;
  591. }
  592. /************************************************************************
  593. *
  594. */
  595. function chado_feature_form ($node,$param){
  596. $type = node_get_types('type', $node);
  597. $form = array();
  598. $feature = $node->feature;
  599. $synonyms = $node->synonyms;
  600. $analyses = $node->analyses;
  601. $references = $node->references;
  602. // We need to pass above variables for preview to show
  603. $form['feature'] = array(
  604. '#type' => 'value',
  605. '#value' => $feature
  606. );
  607. // This field is read when previewing a node
  608. $form['synonyms'] = array(
  609. '#type' => 'value',
  610. '#value' => $synonyms
  611. );
  612. // This field is read when previewing a node
  613. $form['analyses'] = array(
  614. '#type' => 'value',
  615. '#value' => $analyses
  616. );
  617. // This field is read when previewing a node
  618. $form['references'] = array(
  619. '#type' => 'value',
  620. '#value' => $references
  621. );
  622. // keep track of the feature id if we have one. If we do have one then
  623. // this would indicate an update as opposed to an insert.
  624. $form['feature_id'] = array(
  625. '#type' => 'value',
  626. '#value' => $feature->feature_id,
  627. );
  628. $form['title']= array(
  629. '#type' => 'textfield',
  630. '#title' => t('Unique Feature Name'),
  631. '#required' => TRUE,
  632. '#default_value' => $feature->featurename,
  633. '#description' => t('Enter a unique name for this feature'),
  634. '#weight' => 1,
  635. '#maxlength' => 255
  636. );
  637. // get the list of supported feature types
  638. $ftypes = array();
  639. $ftypes[''] = '';
  640. $supported_ftypes = split("[ \n]",variable_get('chado_feature_types','EST contig'));
  641. foreach($supported_ftypes as $ftype){
  642. $ftypes["$ftype"] = $ftype;
  643. }
  644. $form['feature_type'] = array (
  645. '#title' => t('Feature Type'),
  646. '#type' => t('select'),
  647. '#description' => t("Choose the feature type."),
  648. '#required' => TRUE,
  649. '#default_value' => $feature->cvname,
  650. '#options' => $ftypes,
  651. '#weight' => 2
  652. );
  653. // get the list of organisms
  654. $sql = "SELECT * FROM {Organism} ORDER BY genus, species";
  655. $previous_db = db_set_active('chado'); // use chado database
  656. $org_rset = db_query($sql);
  657. db_set_active($previous_db); // now use drupal database
  658. //
  659. $organisms = array();
  660. $organisms[''] = '';
  661. while($organism = db_fetch_object($org_rset)){
  662. $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
  663. }
  664. $form['organism_id'] = array (
  665. '#title' => t('Organism'),
  666. '#type' => t('select'),
  667. '#description' => t("Choose the organism with which this feature is associated "),
  668. '#required' => TRUE,
  669. '#default_value' => $feature->organism_id,
  670. '#options' => $organisms,
  671. '#weight' => 3,
  672. );
  673. // Get synonyms
  674. if ($synonyms) {
  675. if (is_array($synonyms)) {
  676. foreach ($synonyms as $synonym){
  677. $syn_text .= "$synonym->name\n";
  678. }
  679. } else {
  680. $syn_text = $synonyms;
  681. }
  682. }
  683. $form['synonyms']= array(
  684. '#type' => 'textarea',
  685. '#title' => t('Synonyms'),
  686. '#required' => FALSE,
  687. '#default_value' => $syn_text,
  688. '#description' => t('Enter alternate names (synonmys) for this feature to help in searching and identification. You may enter as many alternate names as needed separated by spaces or on different lines.'),
  689. '#weight' => 5,
  690. );
  691. $form['residues']= array(
  692. '#type' => 'textarea',
  693. '#title' => t('Residues'),
  694. '#required' => FALSE,
  695. '#default_value' => $feature->residues,
  696. '#description' => t('Enter the nucelotide sequences for this feature'),
  697. '#weight' => 6
  698. );
  699. $checked = '';
  700. if($feature->is_obsolete == 't'){
  701. $checked = '1';
  702. }
  703. $form['is_obsolete']= array(
  704. '#type' => 'checkbox',
  705. '#title' => t('Is Obsolete'),
  706. '#required' => FALSE,
  707. '#default_value' => $checked,
  708. '#description' => t('Check this box if this sequence should be retired and no longer included in further analysis.'),
  709. '#weight' => 8
  710. );
  711. return $form;
  712. }
  713. /************************************************************************
  714. *
  715. */
  716. function chado_feature_validate($node){
  717. $result = 0;
  718. // if this is an update, we want to make sure that a different feature for
  719. // the organism doesn't already have this uniquename. We don't want to give
  720. // two sequences the same uniquename
  721. if($node->feature_id){
  722. $sql = "SELECT * FROM {Feature} WHERE uniquename = '%s' ".
  723. " AND organism_id = %d AND NOT feature_id = %d";
  724. $previous_db = db_set_active('chado');
  725. $result = db_fetch_object(db_query($sql, $node->title,$node->organism_id,$node->feature_id));
  726. db_set_active($previous_db);
  727. if($result){
  728. form_set_error('title',t("Feature update cannot proceed. The feature name '$node->title' is not unique for this organism. Please provide a unique name for this feature. "));
  729. }
  730. }
  731. // if this is an insert then we just need to make sure this name doesn't
  732. // already exist for this organism if it does then we need to throw an error
  733. else {
  734. $sql = "SELECT * FROM {Feature} WHERE uniquename = '%s' AND organism_id = %d";
  735. $previous_db = db_set_active('chado');
  736. $result = db_fetch_object(db_query($sql, $node->title,$node->organism_id));
  737. db_set_active($previous_db);
  738. if($result){
  739. form_set_error('title',t("Feature insert cannot proceed. The feature name '$node->title' already exists for this organism. Please provide a unique name for this feature. "));
  740. }
  741. }
  742. // we want to remove all characters except IUPAC nucleotide characters from the
  743. // the residues. however, residues are not required so if blank then we'll skip
  744. // this step
  745. if($node->residues){
  746. $residues = preg_replace("/[^\w]/",'',$node->residues);
  747. if(!preg_match("/^[ACTGURYMKSWBDHVN]+$/i",$residues)){
  748. form_set_error('residues',t("The residues in feature $node->title contains more than the nucleotide IUPAC characters. Only the following characters are allowed: A,C,T,G,U,R,Y,M,K,S,W,B,D,H,V,N: '" . $residues ."'"));
  749. }
  750. }
  751. // we don't allow a genbank accession number for a contig
  752. if($node->feature_type == 'contig' and $node->gbaccession){
  753. form_set_error('gbaccession',t("Contigs cannot have a genbank accession number. Please change the feature type or remove the accession number"));
  754. }
  755. }
  756. /************************************************************************
  757. * When a node is requested by the user this function is called to allow us
  758. * to add auxiliary data to the node object.
  759. */
  760. function chado_feature_load($node){
  761. // get the feature_id for this node:
  762. $sql = 'SELECT feature_id FROM {chado_feature} WHERE vid = %d';
  763. $map = db_fetch_object(db_query($sql, $node->vid));
  764. $previous_db = db_set_active('chado'); // use chado database
  765. // get information about this organism and add it to the items in this node
  766. $sql = "SELECT F.feature_id, F.name as featurename, F.uniquename, ".
  767. "F.residues, F.seqlen, O.genus, O.species, O.common_name, ".
  768. " CVT.name as cvname, O.organism_id, F.type_id, F.is_obsolete ".
  769. "FROM {Feature} F ".
  770. " INNER JOIN Organism O ON F.organism_id = O.organism_id ".
  771. " INNER JOIN CVterm CVT ON F.type_id = CVT.cvterm_id ".
  772. "WHERE F.feature_id = %d";
  773. $feature = db_fetch_object(db_query($sql,$map->feature_id));
  774. $additions->feature = $feature;
  775. $additions->seqlen = $feature->seqlen;
  776. // get the feature synonyms
  777. $sql = "SELECT S.name ".
  778. "FROM {Feature_Synonym} FS ".
  779. " INNER JOIN Synonym S ".
  780. " ON FS.synonym_id = S.Synonym_id ".
  781. "WHERE FS.feature_id = %d";
  782. $results = db_query($sql,$map->feature_id);
  783. $synonyms = array();
  784. $i=0;
  785. while($synonym = db_fetch_object($results)){
  786. $synonyms[$i++] = $synonym;
  787. }
  788. $additions->synonyms = $synonyms;
  789. // get feature references in external databases
  790. $sql = "SELECT F.uniquename,F.Feature_id,DBX.accession,DB.description as dbdesc, ".
  791. " DB.db_id, DB.name as db_name, DB.urlprefix ".
  792. "FROM {Feature} F ".
  793. " INNER JOIN Feature_dbxref FDBX on F.feature_id = FDBX.feature_id ".
  794. " INNER JOIN Dbxref DBX on DBX.dbxref_id = FDBX.dbxref_id ".
  795. " INNER JOIN DB on DB.db_id = DBX.db_id ".
  796. "WHERE F.feature_id = %d";
  797. $results = db_query($sql,$map->feature_id);
  798. $references = array();
  799. $i=0;
  800. while($accession = db_fetch_object($results)){
  801. $references[$i++] = $accession;
  802. // we want to specifically pull out the genbank id
  803. if(preg_match("/Genbank_est/",$accession->db_name)){
  804. $additions->gbaccession = $accession;
  805. }
  806. }
  807. $additions->references = $references;
  808. db_set_active($previous_db); // now use drupal database
  809. // get organism node nid
  810. $sql = "SELECT nid FROM {chado_organism} WHERE organism_id = %d";
  811. $org_nid = db_result(db_query($sql, $additions->feature->organism_id));
  812. $additions->org_nid = $org_nid;
  813. return $additions;
  814. }
  815. /************************************************************************
  816. * This function customizes the view of the chado_feature node. It allows
  817. * us to generate the markup.
  818. */
  819. function chado_feature_view ($node, $teaser = FALSE, $page = FALSE) {
  820. if (!$teaser) {
  821. // use drupal's default node view:
  822. $node = node_prepare($node, $teaser);
  823. // if we're building the node for searching then
  824. // we want to handle this within the module and
  825. // not allow theme customization. We don't want to
  826. // index all items (such as DNA sequence).
  827. if($node->build_mode == NODE_BUILD_SEARCH_INDEX){
  828. $node->content['index_version'] = array(
  829. '#value' => theme('tripal_feature_search_index',$node),
  830. '#weight' => 1,
  831. );
  832. }
  833. else if($node->build_mode == NODE_BUILD_SEARCH_RESULT){
  834. $node->content['index_version'] = array(
  835. '#value' => theme('tripal_feature_search_results',$node),
  836. '#weight' => 1,
  837. );
  838. }
  839. else {
  840. // do nothing here, let the theme derived template handle display
  841. }
  842. }
  843. return $node;
  844. }
  845. /*******************************************************************************
  846. * Display feature information for associated organisms. This function also
  847. * provides contents for indexing
  848. */
  849. function tripal_feature_nodeapi(&$node, $op, $teaser, $page) {
  850. switch ($op) {
  851. // Note that this function only adds feature view to an organism node.
  852. // The view of a feature node is controled by the theme *.tpl file
  853. case 'view':
  854. // Set the node types for showing feature information
  855. $types_to_show = array('chado_organism', 'chado_library');
  856. // Abort if this node is not one of the types we should show.
  857. if (!in_array($node->type, $types_to_show, TRUE)) {
  858. break;
  859. }
  860. // Add feature to the content item if it's not a teaser
  861. if (!$teaser) {
  862. // Show feature browser
  863. $node->content['tripal_feature_browser'] = array(
  864. '#value' => theme('tripal_feature_browser', $node),
  865. '#weight' => 5
  866. );
  867. $node->content['tripal_feature_org_counts'] = array(
  868. '#value' => theme('tripal_feature_counts', $node),
  869. '#weight' => 4
  870. );
  871. }
  872. }
  873. }
  874. /************************************************************************
  875. * We need to let drupal know about our theme functions and their arguments.
  876. * We create theme functions to allow users of the module to customize the
  877. * look and feel of the output generated in this module
  878. */
  879. function tripal_feature_theme () {
  880. return array(
  881. 'tripal_feature_search_index' => array (
  882. 'arguments' => array('node'),
  883. ),
  884. 'tripal_feature_search_results' => array (
  885. 'arguments' => array('node'),
  886. ),
  887. 'tripal_feature_browser' => array (
  888. 'arguments' => array('node'),
  889. ),
  890. 'tripal_feature_counts' => array (
  891. 'arguments' => array('node'),
  892. )
  893. );
  894. }
  895. /*******************************************************************************
  896. * create a list of features for the organism and pie chart
  897. */
  898. function theme_tripal_feature_counts($node){
  899. // don't show the summary if the settings in the admin page is turned off
  900. $show_browser = variable_get('tripal_feature_summary_setting',array('show_feature_summary'));
  901. if(strcmp($show_browser,'show_feature_summary')!=0){
  902. return;
  903. }
  904. // get the feature counts. This is dependent on a materialized view
  905. // installed with the organism module
  906. $content = '';
  907. if ($node->organism_id && $node->type == 'chado_organism') {
  908. $sql = "SELECT * FROM {organism_feature_count} ".
  909. "WHERE organism_id = %d AND NOT feature_type = 'EST_match' ".
  910. "ORDER BY num_features desc";
  911. $features = array();
  912. $previous_db = db_set_active('chado'); // use chado database
  913. $results = db_query($sql,$node->organism_id);
  914. db_set_active($previous_db); // now use drupal database
  915. $feature = db_fetch_object($results); // retrieve the first result
  916. if ($feature) {
  917. $content .= "<div class=\"tripal_feature_summary-info-box\"><br>
  918. <div class=\"tripal_expandableBox\">".
  919. "<h3>Feature Summary</h3>".
  920. "</div>";
  921. $content .= "<div class=\"tripal_expandableBoxContent\">";
  922. $content .= "<table class=\"tripal_table_horz\">";
  923. $content .= " <tr>";
  924. $content .= " <th class=\"dbfieldname\">Type</th>";
  925. $content .= " <th class=\"dbfieldname\">Number</th>";
  926. $content .= " </tr>";
  927. do {
  928. $content .= "<tr>";
  929. $content .= " <td>$feature->feature_type</td>";
  930. $content .= " <td>". number_format($feature->num_features) . "</td>";
  931. $content .= "</tr>";
  932. } while($feature = db_fetch_object($results));
  933. $content .= "</table>";
  934. $content .= "
  935. <img class=\"tripal_cv_chart\" id=\"tripal_feature_cv_chart_$node->organism_id\" src=\"\" border=\"0\">
  936. ";
  937. $content .= "</div></div>";
  938. }
  939. }
  940. return $content;
  941. }
  942. /************************************************************************
  943. *
  944. */
  945. function tripal_feature_cv_chart($chart_id){
  946. // The CV module will create the JSON array necessary for buillding a
  947. // pie chart using jgChart and Google Charts. We have to pass to it
  948. // a table that contains count information, tell it which column
  949. // contains the cvterm_id and provide a filter for getting the
  950. // results we want from the table.
  951. $organism_id = preg_replace("/^tripal_feature_cv_chart_(\d+)$/","$1",$chart_id);
  952. $options = array(
  953. count_mview => 'organism_feature_count',
  954. cvterm_id_column => 'cvterm_id',
  955. count_column => 'num_features',
  956. size => '650x200',
  957. filter => "CNT.organism_id = $organism_id AND NOT feature_type = 'EST_match' ",
  958. );
  959. return $options;
  960. }
  961. /************************************************************************
  962. *
  963. */
  964. function tripal_feature_cv_tree($tree_id){
  965. // The CV module will create the JSON array necessary for buillding a
  966. // pie chart using jgChart and Google Charts. We have to pass to it
  967. // a table that contains count information, tell it which column
  968. // contains the cvterm_id and provide a filter for getting the
  969. // results we want from the table.
  970. $organism_id = preg_replace("/^tripal_feature_cv_tree_(\d+)$/","$1",$tree_id);
  971. $options = array(
  972. cv_id => tripal_cv_get_cv_id('sequence'),
  973. count_mview => 'organism_feature_count',
  974. cvterm_id_column => 'cvterm_id',
  975. count_column => 'num_features',
  976. filter => "CNT.organism_id = $organism_id",
  977. label => 'Features',
  978. );
  979. return $options;
  980. }
  981. /*******************************************************************************
  982. * create a simple paged feature browser
  983. */
  984. function theme_tripal_feature_browser($node){
  985. // don't show the browser if the settings in the admin page is turned off
  986. $show_browser = variable_get('tripal_feature_browse_setting',array('show_feature_browser'));
  987. if(strcmp($show_browser,'show_feature_browser')!=0){
  988. return;
  989. }
  990. if ($node->organism_id && $node->type == 'chado_organism') {
  991. # get the list of available sequence ontology terms for which
  992. # we will build drupal pages from features in chado. If a feature
  993. # is not one of the specified typse we won't build a node for it.
  994. $allowed_types = variable_get('chado_feature_types','EST contig');
  995. $allowed_types = preg_replace("/[\s\n\r]+/"," ",$allowed_types);
  996. $so_terms = split(' ',$allowed_types);
  997. $where_cvt = "";
  998. foreach ($so_terms as $term){
  999. $where_cvt .= "CVT.name = '$term' OR ";
  1000. }
  1001. $where_cvt = substr($where_cvt,0,strlen($where_cvt)-3); # strip trailing 'OR'
  1002. // get the features for this organism
  1003. $sql = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname ".
  1004. "FROM {feature} F ".
  1005. " INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
  1006. "WHERE organism_id = $node->organism_id and ($where_cvt) ".
  1007. "ORDER BY feature_id ASC";
  1008. // the counting SQL
  1009. $csql = "SELECT count(*) ".
  1010. "FROM {feature} F".
  1011. " INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
  1012. "WHERE organism_id = $node->organism_id and ($where_cvt) ".
  1013. "GROUP BY organism_id ";
  1014. $previous_db = db_set_active('chado'); // use chado database
  1015. $features = pager_query($sql,10,0,$csql);
  1016. db_set_active($previous_db); // now use drupal database
  1017. $content = "<br><div id=\"tripal_feature_box\" class=\"feature-info-box\">";
  1018. $content .= "<div class=\"tripal_expandableBox\">".
  1019. "<h3>Browse Features</h3>".
  1020. "</div>";
  1021. $content .= "<div class=\"tripal_expandableBoxContent\">";
  1022. $content .= "Below are the features associated with this organism.\n";
  1023. $content .= "<table class=\"tripal_table_horz\">";
  1024. $content .= " <tr>";
  1025. $content .= " <th>Feature Name</th>";
  1026. $content .= " <th>Type</th>";
  1027. $content .= " </tr>";
  1028. // prepare the query that will lookup node ids
  1029. $sql = "SELECT nid FROM {chado_feature} ".
  1030. "WHERE feature_id = %d";
  1031. while($feature = db_fetch_object($features)){
  1032. $node = db_fetch_object(db_query($sql,$feature->feature_id));
  1033. if($node){
  1034. $name= "<a href=\"" . url("node/$node->nid") . "\">$feature->name</a>";
  1035. } else {
  1036. $name= "$feature->name";
  1037. }
  1038. $content .= " <tr>";
  1039. $content .= " <td>$name</td>";
  1040. $content .= " <td>$feature->cvname</td>";
  1041. $content .= " </tr>";
  1042. }
  1043. $content .= "</table>";
  1044. $content .= theme('pager');
  1045. $content .= "</div></div>";
  1046. return $content;
  1047. }
  1048. }
  1049. /************************************************************************
  1050. * This function is an extension of the chado_feature_view by providing
  1051. * the markup for the feature object THAT WILL BE INDEXED.
  1052. */
  1053. function theme_tripal_feature_search_index ($node) {
  1054. $feature = $node->feature;
  1055. $content = '';
  1056. // get the accession prefix
  1057. $aprefix = variable_get('chado_feature_accession_prefix','ID');
  1058. $content .= "<h1>$feature->uniquename</h1>. ";
  1059. $content .= "<strong>$aprefix$feature->feature_id.</strong> ";
  1060. $content .= "$feature->cvname ";
  1061. $content .= "$feature->common_name ";
  1062. // add the synonyms of this feature to the text for searching
  1063. $synonyms = $node->synonyms;
  1064. if(count($synonyms) > 0){
  1065. foreach ($synonyms as $result){
  1066. $content .= "$result->name ";
  1067. }
  1068. }
  1069. return $content;
  1070. }
  1071. /************************************************************************
  1072. * This function is an extension of the chado_feature_view by providing
  1073. * the markup for the feature object THAT WILL BE INDEXED.
  1074. */
  1075. function theme_tripal_feature_search_results ($node) {
  1076. $feature = $node->feature;
  1077. $content = '';
  1078. // get the accession prefix
  1079. $aprefix = variable_get('chado_feature_accession_prefix','ID');
  1080. $content .= "Feature Name: <h1>$feature->uniquename</h1>. ";
  1081. $content .= "<strong>Accession: $aprefix$feature->feature_id.</strong>";
  1082. $content .= "Type: $feature->cvname. ";
  1083. $content .= "Organism: $feature->common_name. ";
  1084. // add the synonyms of this feature to the text for searching
  1085. $synonyms = $node->synonyms;
  1086. if(count($synonyms) > 0){
  1087. $content .= "Synonyms: ";
  1088. foreach ($synonyms as $result){
  1089. $content .= "$result->name, ";
  1090. }
  1091. }
  1092. return $content;
  1093. }
  1094. /************************************************************************
  1095. *
  1096. */
  1097. function tripal_feature_sync_features ($max_sync = 0, $job_id = NULL){
  1098. $i = 0;
  1099. // get the list of available sequence ontology terms for which
  1100. // we will build drupal pages from features in chado. If a feature
  1101. // is not one of the specified typse we won't build a node for it.
  1102. $allowed_types = variable_get('chado_feature_types','EST contig');
  1103. $allowed_types = preg_replace("/[\s\n\r]+/"," ",$allowed_types);
  1104. $so_terms = split(' ',$allowed_types);
  1105. $where_cvt = "";
  1106. foreach ($so_terms as $term){
  1107. $where_cvt .= "CVT.name = '$term' OR ";
  1108. }
  1109. $where_cvt = substr($where_cvt,0,strlen($where_cvt)-3); # strip trailing 'OR'
  1110. // get the list of organisms that are synced and only include features from
  1111. // those organisms
  1112. $orgs = tripal_organism_get_synced();
  1113. $where_org = "";
  1114. foreach($orgs as $org){
  1115. $where_org .= "F.organism_id = $org->organism_id OR ";
  1116. }
  1117. $where_org = substr($where_org,0,strlen($where_org)-3); # strip trailing 'OR'
  1118. // use this SQL statement to get the features that we're going to upload
  1119. $sql = "SELECT feature_id ".
  1120. "FROM {FEATURE} F ".
  1121. " INNER JOIN Cvterm CVT ON F.type_id = CVT.cvterm_id ".
  1122. "WHERE ($where_cvt) AND ($where_org) ".
  1123. "ORDER BY feature_id";
  1124. // get the list of features
  1125. $previous_db = db_set_active('chado'); // use chado database
  1126. $results = db_query($sql);
  1127. db_set_active($previous_db); // now use drupal database
  1128. // load into ids array
  1129. $count = 0;
  1130. $ids = array();
  1131. while($id = db_fetch_object($results)){
  1132. $ids[$count] = $id->feature_id;
  1133. $count++;
  1134. }
  1135. // make sure our vocabularies are set before proceeding
  1136. tripal_feature_set_vocabulary();
  1137. // pre-create the SQL statement that will be used to check
  1138. // if a feature has already been synced. We skip features
  1139. // that have been synced
  1140. $sql = "SELECT * FROM {chado_feature} WHERE feature_id = %d";
  1141. // Iterate through features that need to be synced
  1142. $interval = intval($count * 0.01);
  1143. foreach($ids as $feature_id){
  1144. // update the job status every 1% features
  1145. if($job_id and $i % $interval == 0){
  1146. tripal_job_set_progress($job_id,intval(($i/$count)*100));
  1147. }
  1148. // if we have a maximum number to sync then stop when we get there
  1149. // if not then just continue on
  1150. if($max_sync and $i == $max_sync){
  1151. return '';
  1152. }
  1153. if(!db_fetch_object(db_query($sql,$feature_id))){
  1154. tripal_feature_sync_feature ($feature_id);
  1155. }
  1156. $i++;
  1157. }
  1158. return '';
  1159. }
  1160. /************************************************************************
  1161. *
  1162. */
  1163. function tripal_feature_sync_feature ($feature_id){
  1164. global $user;
  1165. $create_node = 1; // set to 0 if the node exists and we just sync and not create
  1166. // get the accession prefix
  1167. $aprefix = variable_get('chado_feature_accession_prefix','ID');
  1168. // if we don't have a feature_id then return
  1169. if(!$feature_id){
  1170. drupal_set_message(t("Please provide a feature_id to sync"));
  1171. return '';
  1172. }
  1173. // get information about this feature
  1174. $fsql = "SELECT F.feature_id, F.name, F.uniquename,O.genus, ".
  1175. " O.species,CVT.name as cvname,F.residues,F.organism_id ".
  1176. "FROM {FEATURE} F ".
  1177. " INNER JOIN Cvterm CVT ON F.type_id = CVT.cvterm_id ".
  1178. " INNER JOIN Organism O ON F.organism_id = O.organism_ID ".
  1179. "WHERE F.feature_id = %d";
  1180. $previous_db = db_set_active('chado'); // use chado database
  1181. $feature = db_fetch_object(db_query($fsql,$feature_id));
  1182. db_set_active($previous_db); // now use drupal database
  1183. // check to make sure that we don't have any nodes with this feature name as a title
  1184. // but without a corresponding entry in the chado_feature table if so then we want to
  1185. // clean up that node. (If a node is found we don't know if it belongs to our feature or
  1186. // not since features can have the same name/title.)
  1187. $tsql = "SELECT * FROM {node} N ".
  1188. "WHERE title = '%s'";
  1189. $cnsql = "SELECT * FROM {chado_feature} ".
  1190. "WHERE nid = %d";
  1191. $nodes = db_query($tsql,$feature->name);
  1192. // cycle through all nodes that may have this title
  1193. while($node = db_fetch_object($nodes)){
  1194. $feature_nid = db_fetch_object(db_query($cnsql,$node->nid));
  1195. if(!$feature_nid){
  1196. drupal_set_message(t("$feature_id: A node is present but the chado_feature entry is missing... correcting"));
  1197. node_delete($node->nid);
  1198. }
  1199. }
  1200. // check if this feature already exists in the chado_feature table.
  1201. // if we have a chado feature, we want to check to see if we have a node
  1202. $cfsql = "SELECT * FROM {chado_feature} ".
  1203. "WHERE feature_id = %d";
  1204. $nsql = "SELECT * FROM {node} ".
  1205. "WHERE nid = %d";
  1206. $chado_feature = db_fetch_object(db_query($cfsql,$feature->feature_id));
  1207. if($chado_feature){
  1208. drupal_set_message(t("$feature_id: A chado_feature entry exists"));
  1209. $node = db_fetch_object(db_query($nsql,$chado_feature->nid));
  1210. if(!$node){
  1211. // if we have a chado_feature but not a node then we have a problem and
  1212. // need to cleanup
  1213. drupal_set_message(t("$feature_id: The node is missing, but has a chado_feature entry... correcting"));
  1214. $df_sql = "DELETE FROM {chado_feature} WHERE feature_id = %d";
  1215. db_query($df_sql,$feature_id);
  1216. } else {
  1217. drupal_set_message(t("$feature_id: A corresponding node exists"));
  1218. $create_node = 0;
  1219. }
  1220. }
  1221. // if we've encountered an error then just return.
  1222. if($error_msg = db_error()){
  1223. //print "$error_msg\n";
  1224. return '';
  1225. }
  1226. // if a drupal node does not exist for this feature then we want to
  1227. // create one. Note that the node_save call in this block
  1228. // will call the hook_submit function which
  1229. if($create_node){
  1230. drupal_set_message(t("$feature_id: Creating node $feature->name"));
  1231. $new_node = new stdClass();
  1232. $new_node->type = 'chado_feature';
  1233. $new_node->uid = $user->uid;
  1234. $new_node->title = "$feature->name";
  1235. $new_node->feature_id = $feature->feature_id;
  1236. $new_node->residues = $feature->residues;
  1237. $new_node->organism_id = $feature->organism_id;
  1238. $new_node->feature_type = $feature->cvname;
  1239. // validate the node and if okay then submit
  1240. node_validate($new_node);
  1241. if ($errors = form_get_errors()) {
  1242. foreach($errors as $key => $msg){
  1243. drupal_set_message($msg);
  1244. }
  1245. return $errors;
  1246. } else {
  1247. $node = node_submit($new_node);
  1248. node_save($node);
  1249. }
  1250. }
  1251. else {
  1252. $node = $chado_feature;
  1253. }
  1254. // set the taxonomy for this node
  1255. drupal_set_message(t("$feature_id ($node->nid): setting taxonomy"));
  1256. tripal_feature_set_taxonomy($node,$feature_id);
  1257. // reindex the node
  1258. drupal_set_message(t("$feature_id( $node->nid): indexing"));
  1259. tripal_feature_index_feature ($feature_id,$node->nid);
  1260. // remove any URL alias that may already exist and recreate
  1261. drupal_set_message(t("$feature_id ($node->nid): setting URL alias"));
  1262. db_query("DELETE FROM {url_alias} WHERE dst = '%s'", "$aprefix$feature_id");
  1263. path_set_alias("node/$node->nid","$aprefix$feature_id");
  1264. return '';
  1265. }
  1266. /************************************************************************
  1267. *
  1268. */
  1269. function tripal_feature_set_vocabulary (){
  1270. //include the file containing the required functions for adding taxonomy vocabs
  1271. module_load_include('inc', 'taxonomy', 'taxonomy.admin');
  1272. // get the vocabularies so that we make sure we don't recreate
  1273. // the vocabs that already exist
  1274. $vocabularies = taxonomy_get_vocabularies();
  1275. $ft_vid = NULL;
  1276. $op_vid = NULL;
  1277. $lb_vid = NULL;
  1278. $an_vid = NULL;
  1279. // These taxonomic terms are hard coded because we
  1280. // konw we have these relationships in the chado tables
  1281. // through foreign key relationships. The tripal
  1282. // modules that correspond to these chado "modules" don't
  1283. // need to be installed for the taxonomy to work.
  1284. foreach($vocabularies as $vocab){
  1285. if($vocab->name == 'Feature Type'){
  1286. $ft_vid = $vocab->vid;
  1287. }
  1288. if($vocab->name == 'Organism'){
  1289. $op_vid = $vocab->vid;
  1290. }
  1291. if($vocab->name == 'Library'){
  1292. $lb_vid = $vocab->vid;
  1293. }
  1294. if($vocab->name == 'Analysis'){
  1295. $an_vid = $vocab->vid;
  1296. }
  1297. }
  1298. if(!$ft_vid){
  1299. $form_state = array();
  1300. $values = array(
  1301. 'name' => t('Feature Type'),
  1302. 'nodes' => array('chado_feature' => 'chado_feature'),
  1303. 'description' => t('The feature type (or SO cvterm for this feature).'),
  1304. 'help' => t('Select the term that matches the feature '),
  1305. 'tags' => 0,
  1306. 'hierarchy' => 1,
  1307. 'relations' => 1,
  1308. 'multiple' => 0,
  1309. 'required' => 0,
  1310. 'weight' => 1,
  1311. );
  1312. drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
  1313. drupal_execute('taxonomy_form_vocabulary', $form_state);
  1314. }
  1315. if(!$op_vid){
  1316. $form_state = array();
  1317. $values = array(
  1318. 'name' => t('Organism'),
  1319. 'nodes' => array('chado_feature' => 'chado_feature'),
  1320. 'description' => t('The organism to which this feature belongs.'),
  1321. 'help' => t('Select the term that matches the feature '),
  1322. 'tags' => 0,
  1323. 'hierarchy' => 1,
  1324. 'relations' => 1,
  1325. 'multiple' => 0,
  1326. 'required' => 0,
  1327. 'weight' => 2,
  1328. );
  1329. drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
  1330. drupal_execute('taxonomy_form_vocabulary', $form_state);
  1331. }
  1332. if(!$lb_vid){
  1333. $form_state = array();
  1334. $values = array(
  1335. 'name' => t('Library'),
  1336. 'nodes' => array('chado_feature' => 'chado_feature'),
  1337. 'description' => t('Chado features associated with a library are assigned the term associated with the library'),
  1338. 'help' => t('Select the term that matches the feature '),
  1339. 'tags' => 0,
  1340. 'hierarchy' => 1,
  1341. 'relations' => 1,
  1342. 'multiple' => 0,
  1343. 'required' => 0,
  1344. 'weight' => 3,
  1345. );
  1346. drupal_execute('taxonomy_form_vocabulary', $form_state, $values);
  1347. drupal_execute('taxonomy_form_vocabulary', $form_state);
  1348. }
  1349. if(!$an_vid){
  1350. $form_state = array();
  1351. $values = array(
  1352. 'name' => t('Analysis'),
  1353. 'nodes' => array('chado_feature' => 'chado_feature'),
  1354. 'description' => t('Any analysis to which this feature belongs.'),
  1355. 'help' => t('Select the term that matches the feature '),
  1356. 'tags' => 0,
  1357. 'hierarchy' => 1,
  1358. 'relations' => 1,
  1359. 'multiple' => 1,
  1360. 'required' => 0,
  1361. 'weight' => 4,
  1362. );
  1363. drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
  1364. drupal_execute('taxonomy_form_vocabulary', $form_state);
  1365. }
  1366. }
  1367. /************************************************************************
  1368. *
  1369. */
  1370. function tripal_feature_del_vocabulary(){
  1371. //include the file containing the required functions for adding taxonomy vocabs
  1372. module_load_include('inc', 'taxonomy', 'taxonomy.admin');
  1373. // get the vocabularies
  1374. $vocabularies = taxonomy_get_vocabularies();
  1375. // These taxonomic terms are hard coded because we
  1376. // know we have these relationships in the chado tables
  1377. // through foreign key relationships. The tripal
  1378. // modules that correspond to these chado "modules" don't
  1379. // need to be installed for the taxonomy to work.
  1380. foreach($vocabularies as $vocab){
  1381. if($vocab->name == 'Feature Type'){
  1382. taxonomy_del_vocabulary($vocab->vid);
  1383. }
  1384. if($vocab->name == 'Organism'){
  1385. taxonomy_del_vocabulary($vocab->vid);
  1386. }
  1387. if($vocab->name == 'Library'){
  1388. taxonomy_del_vocabulary($vocab->vid);
  1389. }
  1390. if($vocab->name == 'Analysis'){
  1391. taxonomy_del_vocabulary($vocab->vid);
  1392. }
  1393. }
  1394. }
  1395. /************************************************************************
  1396. *
  1397. */
  1398. function tripal_features_set_taxonomy($max_sync = 0,$job_id = NULL){
  1399. // make sure our vocabularies are cleaned and reset before proceeding
  1400. tripal_feature_del_vocabulary();
  1401. tripal_feature_set_vocabulary();
  1402. // iterate through all drupal feature nodes and set the taxonomy
  1403. $results = db_query("SELECT * FROM {chado_feature}");
  1404. $nsql = "SELECT * FROM {node} ".
  1405. "WHERE nid = %d";
  1406. $i = 0;
  1407. // load into ids array
  1408. $count = 0;
  1409. $chado_features = array();
  1410. while($chado_feature = db_fetch_object($results)){
  1411. $chado_features[$count] = $chado_feature;
  1412. $count++;
  1413. }
  1414. // Iterate through features that need to be synced
  1415. $interval = intval($count * 0.01);
  1416. foreach($chado_features as $chado_feature){
  1417. // update the job status every 1% features
  1418. if($job_id and $i % $interval == 0){
  1419. tripal_job_set_progress($job_id,intval(($i/$count)*100));
  1420. }
  1421. $node = db_fetch_object(db_query($nsql,$chado_feature->nid));
  1422. tripal_feature_set_taxonomy($node,$chado_feature->feature_id);
  1423. $i++;
  1424. }
  1425. }
  1426. /************************************************************************
  1427. *
  1428. */
  1429. function tripal_feature_set_taxonomy ($node,$feature_id){
  1430. // iterate through the taxonomy classes that have been
  1431. // selected by the admin user and make sure we only set those
  1432. $tax_classes = variable_get('tax_classes', '');
  1433. $do_ft = 0;
  1434. $do_op = 0;
  1435. $do_lb = 0;
  1436. $do_an = 0;
  1437. foreach($tax_classes as $class){
  1438. if(strcmp($class ,'organism')==0){
  1439. $do_op = 1;
  1440. }
  1441. if(strcmp($class,'feature_type')==0){
  1442. $do_ft = 1;
  1443. }
  1444. if(strcmp($class,'library')==0){
  1445. $do_lb = 1;
  1446. }
  1447. if(strcmp($class,'analysis')==0){
  1448. $do_an = 1;
  1449. }
  1450. }
  1451. // get the list of vocabularies and find our two vocabularies of interest
  1452. $vocabularies = taxonomy_get_vocabularies();
  1453. $ft_vid = NULL;
  1454. $op_vid = NULL;
  1455. $lb_vid = NULL;
  1456. $an_vid = NULL;
  1457. foreach($vocabularies as $vocab){
  1458. if($vocab->name == 'Feature Type'){
  1459. $ft_vid = $vocab->vid;
  1460. }
  1461. if($vocab->name == 'Organism'){
  1462. $op_vid = $vocab->vid;
  1463. }
  1464. if($vocab->name == 'Library'){
  1465. $lb_vid = $vocab->vid;
  1466. }
  1467. if($vocab->name == 'Analysis'){
  1468. $an_vid = $vocab->vid;
  1469. }
  1470. }
  1471. // get the cvterm and the organism for this feature
  1472. $sql = "SELECT CVT.name AS cvname, O.genus, O.species ".
  1473. "FROM {CVTerm} CVT ".
  1474. " INNER JOIN Feature F on F.type_id = CVT.cvterm_id ".
  1475. " INNER JOIN Organism O ON F.organism_id = O.organism_id ".
  1476. "WHERE F.feature_id = $feature_id";
  1477. $previous_db = db_set_active('chado'); // use chado database
  1478. $feature = db_fetch_object(db_query($sql));
  1479. db_set_active($previous_db); // now use drupal database
  1480. // Set the feature type for this feature
  1481. if($do_ft && $ft_vid){
  1482. $tags["$ft_vid"] = "$feature->cvname";
  1483. }
  1484. // Set the organism for this feature type
  1485. if($do_op && $op_vid){
  1486. $tags["$op_vid"] = "$feature->genus $feature->species";
  1487. }
  1488. // get the library that this feature may belong to and add it as taxonomy
  1489. if($do_lb && $lb_vid){
  1490. $sql = "SELECT L.name ".
  1491. "FROM {Library} L ".
  1492. " INNER JOIN Library_feature LF ON LF.library_id = L.library_id ".
  1493. "WHERE LF.feature_id = %d ";
  1494. $previous_db = db_set_active('chado'); // use chado database
  1495. $library = db_fetch_object(db_query($sql,$feature_id));
  1496. db_set_active($previous_db); // now use drupal database
  1497. $tags["$lb_vid"] = "$library->name";
  1498. }
  1499. // get the analysis that this feature may belong to and add it as taxonomy
  1500. if($do_an && $an_vid){
  1501. $sql = "SELECT A.name ".
  1502. "FROM {Analysis} A ".
  1503. " INNER JOIN Analysisfeature AF ON AF.analysis_id = A.analysis_id ".
  1504. "WHERE AF.feature_id = $feature_id ";
  1505. $results = db_query($sql);
  1506. $previous_db = db_set_active('chado'); // use chado database
  1507. $analysis_terms = array();
  1508. while($analysis=db_fetch_object($results)){
  1509. // TODO -- how to set more than one taxonmy term ????
  1510. $analysis_terms[] = "$analysis->name";
  1511. // $tags["$an_vid"] = "$analysis->name";
  1512. }
  1513. $tags["$an_vid"] = $analysis_terms;
  1514. db_set_active($previous_db); // now use drupal database
  1515. }
  1516. // now add the taxonomy to the node
  1517. $terms['tags'] = $tags;
  1518. taxonomy_node_save($node,$terms);
  1519. // print "Setting $node->title: " . implode(", ",$tags) . "\n";
  1520. }
  1521. /************************************************************************
  1522. *
  1523. */
  1524. function tripal_features_reindex ($max_sync,$job_id = NULL){
  1525. $i = 0;
  1526. // We register a shutdown function to ensure that the nodes
  1527. // that are indexed will have proper entries in the search_totals
  1528. // table. Without these entries, the searching doesn't work
  1529. // properly. This function may run for quite a while since
  1530. // it must calculate the sum of the scores of all entries in
  1531. // the search_index table. In the case of common words like
  1532. // 'contig', this will take quite a while
  1533. register_shutdown_function('search_update_totals');
  1534. // use this SQL statement to get the features that we're going to index. This
  1535. // SQL statement is derived from the hook_search function in the Drupal API.
  1536. // Essentially, this is the SQL statement that finds all nodes that need
  1537. // reindexing, but adjusted to include the chado_feature
  1538. $sql = "SELECT N.nid, N.title, CF.feature_id ".
  1539. "FROM {node} N ".
  1540. " INNER JOIN chado_feature CF ON CF.nid = N.nid ";
  1541. $results = db_query($sql);
  1542. // load into ids array
  1543. $count = 0;
  1544. $chado_features = array();
  1545. while($chado_feature = db_fetch_object($results)){
  1546. $chado_features[$count] = $chado_feature;
  1547. $count++;
  1548. }
  1549. // Iterate through features that need to be synced
  1550. $interval = intval($count * 0.01);
  1551. foreach($chado_features as $chado_feature){
  1552. // update the job status every 1% features
  1553. if($job_id and $i % $interval == 0){
  1554. tripal_job_set_progress($job_id,intval(($i/$count)*100));
  1555. }
  1556. // sync only the max requested
  1557. if($max_sync and $i == $max_sync){
  1558. return '';
  1559. }
  1560. tripal_feature_index_feature ($chado_feature->feature_id,$chado_feature->nid);
  1561. $i++;
  1562. }
  1563. return '';
  1564. }
  1565. /************************************************************************
  1566. *
  1567. */
  1568. function tripal_feature_index_feature ($feature_id,$nid){
  1569. // return if we haven't been provided with a feature_id
  1570. if(!$feature_id){
  1571. return 0;
  1572. }
  1573. // if we only have a feature_id then let's find a corresponding
  1574. // node. If we can't find a node then return.
  1575. if(!$nid){
  1576. $nsql = "SELECT N.nid,N.title FROM {chado_feature} CF ".
  1577. " INNER JOIN {node} N ON N.nid = CF.nid ".
  1578. "WHERE CF.feature_id = %d";
  1579. $node = db_fetch_object(db_query($nsql,$feature_id));
  1580. if(!$node){
  1581. return 0;
  1582. }
  1583. $node = node_load($node->nid);
  1584. } else {
  1585. $node = node_load($nid);
  1586. }
  1587. // node load the noad, the comments and the taxonomy and
  1588. // index
  1589. $node->build_mode = NODE_BUILD_SEARCH_INDEX;
  1590. $node = node_build_content($node, FALSE, FALSE);
  1591. $node->body = drupal_render($node->content);
  1592. node_invoke_nodeapi($node, 'view', FALSE, FALSE);
  1593. $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
  1594. $node->body .= module_invoke('taxonomy','nodeapi', $node, 'update index');
  1595. // print "$node->title: $node->body\n";
  1596. search_index($node->nid,'node',$node->body);
  1597. return 1;
  1598. }
  1599. /************************************************************************
  1600. *
  1601. */
  1602. function tripal_features_cleanup($dummy = NULL, $job_id = NULL) {
  1603. // build the SQL statments needed to check if nodes point to valid features
  1604. $dsql = "SELECT * FROM {node} WHERE type = 'chado_feature' order by nid";
  1605. $nsql = "SELECT * FROM {node} WHERE nid = %d";
  1606. $csql = "SELECT * FROM {chado_feature} where nid = %d ";
  1607. $cfsql= "SELECT * FROM {chado_feature}";
  1608. $tsql = "SELECT * FROM {feature} F ".
  1609. " INNER JOIN CVTerm CVT ON F.type_id = CVT.cvterm_id ".
  1610. "WHERE feature_id = %d AND (";
  1611. $supported_ftypes = split("[ \n]",variable_get('chado_feature_types','EST contig'));
  1612. foreach($supported_ftypes as $ftype){
  1613. $tsql .= " CVT.name = '$ftype' OR ";
  1614. }
  1615. $tsql .= " 0=1) "; // add a 0=1 just as a filler so we don't have to remove a trailing 'OR'
  1616. // load into nodes array
  1617. $results = db_query($dsql);
  1618. $count = 0;
  1619. $nodes = array();
  1620. while($node = db_fetch_object($results)){
  1621. $nodes[$count] = $node;
  1622. $count++;
  1623. }
  1624. // load the chado_features into an array
  1625. $results = db_query($cfsql);
  1626. $cnodes = array();
  1627. while($node = db_fetch_object($results)){
  1628. $cnodes[$count] = $node;
  1629. $count++;
  1630. }
  1631. $interval = intval($count * 0.01);
  1632. // iterate through all of the chado_feature nodes and delete those that aren't valid
  1633. foreach($nodes as $nid){
  1634. // update the job status every 1% features
  1635. if($job_id and $i % $interval == 0){
  1636. tripal_job_set_progress($job_id,intval(($i/$count)*100));
  1637. }
  1638. // first check to see if the node has a corresponding entry
  1639. // in the chado_feature table. If not then delete the node.
  1640. $feature = db_fetch_object(db_query($csql,$nid->nid));
  1641. if(!$feature){
  1642. node_delete($nid->nid);
  1643. $message = "Missing in chado_feature table.... DELETING: $nid->nid\n";
  1644. watchdog('tripal_feature',$message,array(),WATCHDOG_WARNING);
  1645. continue;
  1646. }
  1647. // second check to see if the node is for a feature of an allowed type.
  1648. // if not, then delete the node. This check will also take care of the
  1649. // case when a node exists and an entry in the chado_feature table exists
  1650. // but no feature with a matching feature_id exists
  1651. $previous_db = db_set_active('chado'); // use chado database
  1652. $ftype = db_fetch_object(db_query($tsql,$feature->feature_id));
  1653. db_set_active($previous_db); // now use drupal database
  1654. if(!$ftype){
  1655. node_delete($nid->nid);
  1656. db_query("DELETE FROM {chado_feature} WHERE feature_id = $feature->feature_id");
  1657. $message = "Node of the wrong feature type.... DELETING: $nid->nid\n";
  1658. watchdog('tripal_feature',$message,array(),WATCHDOG_WARNING);
  1659. }
  1660. $i++;
  1661. }
  1662. // iterate through all of the chado_feature nodes and delete those that aren't valid
  1663. foreach($cnodes as $nid){
  1664. // update the job status every 1% features
  1665. if($job_id and $i % $interval == 0){
  1666. tripal_job_set_progress($job_id,intval(($i/$count)*100));
  1667. }
  1668. $node = db_fetch_object(db_query($nsql,$nid->nid));
  1669. if(!$node){
  1670. db_query("DELETE FROM {chado_feature} WHERE nid = $nid->nid");
  1671. $message = "chado_feature missing node.... DELETING: $nid->nid\n";
  1672. watchdog('tripal_feature',$message,array(),WATCHDOG_WARNING);
  1673. }
  1674. $i++;
  1675. }
  1676. return '';
  1677. }
  1678. /************************************************************************
  1679. *
  1680. */
  1681. function tripal_feature_bulkload(){
  1682. return drupal_get_form('tripal_feature_load_fasta_form');
  1683. }
  1684. /************************************************************************
  1685. *
  1686. */
  1687. function tripal_feature_load_fasta_form (&$form_state = NULL){
  1688. // get the list of organisms
  1689. $sql = "SELECT * FROM {Organism} ORDER BY genus,species";
  1690. $previous_db = db_set_active('chado'); // use chado database
  1691. $org_rset = db_query($sql);
  1692. db_set_active($previous_db); // now use drupal database
  1693. $organisms = array();
  1694. $organisms[''] = '';
  1695. while($organism = db_fetch_object($org_rset)){
  1696. $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
  1697. }
  1698. // get the list of supported feature types
  1699. $ftypes = array();
  1700. $ftypes[''] = '';
  1701. $supported_ftypes = split("[ \n]",variable_get('chado_feature_feature_types','EST contig'));
  1702. foreach($supported_ftypes as $ftype){
  1703. $ftypes["$ftype"] = $ftype;
  1704. }
  1705. // get the list of libraries
  1706. // TODO !!!! Use Ajax to filter this automatically based on the organism
  1707. // selected by the user. This will prevent mistakes from user input.
  1708. $sql = "SELECT * FROM {Library} L ".
  1709. " INNER JOIN Organism O ON L.organism_id = O.organism_id ".
  1710. "ORDER BY L.name";
  1711. $previous_db = db_set_active('chado'); // use chado database
  1712. $lib_rset = db_query($sql);
  1713. db_set_active($previous_db); // now use drupal database
  1714. $libraries = array();
  1715. $libraries[''] = '';
  1716. while($library = db_fetch_object($lib_rset)){
  1717. $libraries[$library->library_id] = "$library->name ($library->genus $library->species)";
  1718. }
  1719. $form['#attributes']['enctype'] = 'multipart/form-data';
  1720. $form['organism'] = array (
  1721. '#title' => t('Organism'),
  1722. '#type' => t('select'),
  1723. '#description' => t("Choose the organism with which these sequences are associated "),
  1724. '#required' => TRUE,
  1725. '#default_vaule' => '',
  1726. '#options' => $organisms,
  1727. '#weight' => 1,
  1728. );
  1729. $form['library'] = array (
  1730. '#title' => t('Library'),
  1731. '#type' => t('select'),
  1732. '#description' => t("Choose the library with from which these sequences are derived. Leave blank if not applicable."),
  1733. '#required' => FALSE,
  1734. '#default_vaule' => '',
  1735. '#options' => $libraries,
  1736. '#weight' => 2,
  1737. );
  1738. $form['ftype'] = array (
  1739. '#title' => t('Feature Type'),
  1740. '#type' => t('select'),
  1741. '#description' => t("Choose the category of sequences you are uploading. All sequences in the FASTA file will be imported as this type"),
  1742. '#required' => TRUE,
  1743. '#default_vaule' => '',
  1744. '#options' => $ftypes,
  1745. '#weight' => 3,
  1746. );
  1747. $form['fasta_file'] = array(
  1748. '#type' => t('file'),
  1749. '#title' => t('Fasta File'),
  1750. '#description' => t('Upload a FASTA file of sequences. The definition line should contain only the feature name. All other annotations should be removed. The file must not be larger than ' . file_upload_max_size() . ' bytes'),
  1751. '#weight' => 4,
  1752. );
  1753. $form['upload'] = array(
  1754. '#type' => 'submit',
  1755. '#value' => t('Upload File'),
  1756. '#weight' => 2,
  1757. '#executes_submit_callback' => TRUE,
  1758. '#weight' => 5,
  1759. );
  1760. return $form;
  1761. }
  1762. /************************************************************************
  1763. *
  1764. */
  1765. function tripal_feature_load_fasta_form_validate($form, &$form_state){
  1766. // TODO !!! check that the fasta file is valid
  1767. global $user;
  1768. // we need a path within the drupal installation to temporarily use as the destination
  1769. // after we upload, we'll move the file to analysis directory
  1770. $upload_url = file_directory_path() . "/chado_feature_bulk_upload/$user->uid";
  1771. // create the download directory. We do it this way rather than the
  1772. // file_check_directory because we don't want a drupal message presented
  1773. // the user when the directory is created.
  1774. if (!is_dir($upload_url)) {
  1775. mkdir($upload_url,0775,TRUE);
  1776. }
  1777. // upload the file and copy it to the proper location
  1778. $validators = array(); // we don't have any validators
  1779. if($file = file_save_upload('fasta_file',$validators,$upload_url)){
  1780. drupal_set_message("File $file->name uploaded succesfully");
  1781. } else {
  1782. form_set_error('fasta_file',t('Upload Failed'));
  1783. }
  1784. }
  1785. /************************************************************************
  1786. *
  1787. */
  1788. function tripal_feature_load_fasta_form_submit($form, &$form_state){
  1789. global $user;
  1790. // add a job to be executed
  1791. tripal_add_job ($job_name,$type,$callback,$uid);
  1792. }
  1793. /************************************************************************
  1794. *
  1795. */
  1796. function tripal_feature_return_fasta($feature,$desc){
  1797. $fasta = ">" . variable_get('chado_feature_accession_prefix','ID') . "$feature->feature_id|$feature->name";
  1798. $fasta .= " $desc\n";
  1799. $fasta .= wordwrap($feature->residues, 50, "\n", true);
  1800. $fasta .= "\n\n";
  1801. return $fasta;
  1802. }