blast_ui.form_per_program.inc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /**
  3. * @file
  4. * Forms in the file provide a per BLAST program interface to submitting BLASTs.
  5. *
  6. * In other words, it provides a form for blastn, one for blastp, etc. It does
  7. * this using a single form function for code reusability and depending upon
  8. * the $type passed in, it will execute additional hooks allowing for program
  9. * specific modifications & advanced options.
  10. */
  11. /**
  12. * This single form definition provides 4 different program-specific forms.
  13. */
  14. function blast_ui_per_blast_program_form($form, $form_state) {
  15. // CSS support to the form
  16. $form['#attached']['css'] = array(
  17. drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
  18. );
  19. // We are going to lay out this form as two choices: either look at a recent blast
  20. // or execute a new one. We want to theme accordingly so set a class to aid us in such.
  21. $form['#attributes'] = array('class' => array('blast-choice-form'));
  22. // @deepaksomanadh - Code added for edit and resubmit funcitonality
  23. // Approach: persist the form data and read it back using JobID
  24. $job_data = variable_get('job_data', '');
  25. if (isset($_GET['jid']) && isset($job_data)) {
  26. $jid = blast_ui_reveal_secret($_GET['jid']);
  27. }
  28. else {
  29. $job_data = array();
  30. $jid = 0;
  31. }
  32. // Determine the BLAST program.
  33. $query_type = $form_state['build_info']['args'][0];
  34. $db_type = $form_state['build_info']['args'][1];
  35. if ($query_type == 'nucleotide') {
  36. if ($db_type == 'nucleotide') {
  37. $blast_program = 'blastn';
  38. }
  39. elseif ($db_type == 'protein') {
  40. $blast_program = 'blastx';
  41. }
  42. }
  43. elseif ($query_type == 'protein') {
  44. if ($db_type == 'nucleotide') {
  45. $blast_program = 'tblastn';
  46. }
  47. elseif ($db_type == 'protein') {
  48. $blast_program = 'blastp';
  49. }
  50. }
  51. // Set the title to be more Researcher friendly.
  52. drupal_set_title(t(
  53. '@query to @db BLAST (@program)',
  54. array(
  55. '@query' => ucfirst($query_type),
  56. '@db' => ucfirst($db_type),
  57. '@program' => $blast_program
  58. )
  59. ));
  60. // Add the details about the specific BLAST choosen.
  61. $form['query_type'] = array(
  62. '#type' => 'hidden',
  63. '#value' => $query_type
  64. );
  65. $form['db_type'] = array(
  66. '#type' => 'hidden',
  67. '#value' => $db_type
  68. );
  69. $form['blast_program'] = array(
  70. '#type' => 'hidden',
  71. '#value' => $blast_program
  72. );
  73. // CHOOSE RECENT BLAST RESULTS
  74. //-----------------------------------
  75. // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
  76. $recent_jobs = get_recent_blast_jobs(array($blast_program));
  77. // If there are recent jobs then show a table of them.
  78. if ($recent_jobs) {
  79. $form['A'] = array(
  80. '#type' => 'fieldset',
  81. '#title' => 'See Results from a Recent BLAST',
  82. '#attributes' => array('class' => array('blast-choice')),
  83. '#collapsible' => TRUE,
  84. '#collapsed' => TRUE
  85. );
  86. $table = array(
  87. 'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
  88. 'rows' => array(),
  89. 'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
  90. 'sticky' => TRUE,
  91. );
  92. foreach ($recent_jobs as $job) {
  93. // Define a row for the current job.
  94. $table['rows'][] = array(
  95. $job['query_info'],
  96. $job['target'],
  97. $job['date'],
  98. l('See Results', $job['job_output_url'])
  99. );
  100. }
  101. $form['A']['job_table'] = array(
  102. '#type' => 'markup',
  103. '#markup' => theme('table', $table),
  104. );
  105. }
  106. // REQUEST A NEW BLAST
  107. //-----------------------------------
  108. $form['B'] = array(
  109. '#type' => 'fieldset',
  110. '#title' => 'Request a New BLAST',
  111. '#attributes' => array('class' => array('blast-choice')),
  112. '#collapsible' => TRUE,
  113. );
  114. // NUCLEOTIDE QUERY
  115. //.........................
  116. $form['B']['query'] = array(
  117. '#type' => 'fieldset',
  118. '#title' => t('Enter %type Query Sequence',
  119. array('%type' => ucfirst($query_type))),
  120. '#description' => t('Enter one or more queries in the top text box or use '
  121. . 'the browse button to upload a file from your local disk. The file may '
  122. . 'contain a single sequence or a list of sequences. In both cases, the '
  123. . 'data must be in <a href="@formaturl" target="_blank">FASTA format</a>.',
  124. array(
  125. '@formaturl' => 'http://www.ncbi.nlm.nih.gov/BLAST/blastcgihelp.shtml'
  126. )
  127. ),
  128. '#collapsible' => TRUE,
  129. '#collapsed' => FALSE,
  130. );
  131. // Checkbox to show an example.
  132. $form['B']['query']['example_sequence'] = array(
  133. '#type' => 'checkbox',
  134. '#title' => t('Show an Example Sequence'),
  135. '#prefix' => '<span style="float: right;">',
  136. '#suffix' => '</span>',
  137. '#ajax' => array(
  138. 'callback' => 'ajax_blast_ui_perprogram_example_sequence_callback',
  139. 'wrapper' => 'fasta-textarea',
  140. 'method' => 'replace',
  141. 'effect' => 'fade',
  142. ),
  143. );
  144. // Textfield for submitting a mult-FASTA query
  145. $form['B']['query']['FASTA'] = array(
  146. '#type' => 'textarea',
  147. '#title' => t('Enter FASTA sequence(s)'),
  148. '#description'=>t('Enter query sequence(s) in the text area.'),
  149. '#default_value' => isset($job_data[$jid]['fasta']) ? $job_data[$jid]['fasta'] : '',
  150. '#prefix' => '<div id="fasta-textarea">',
  151. '#suffix' => '</div>',
  152. );
  153. if (variable_get('blast_ui_allow_query_upload', TRUE)) {
  154. // Upload a file as an alternative to enter a query sequence
  155. $form['#attributes']['enctype'] = 'multipart/form-data';
  156. $form['B']['query']['UPLOAD'] = array(
  157. '#title' => 'Or upload your own query FASTA: ',
  158. '#type' => 'managed_file',
  159. '#description' => t('The file should be a plain-text FASTA
  160. (.fasta, .fna, .fa, .fas) file. In other words, it cannot have formatting as is the
  161. case with MS Word (.doc, .docx) or Rich Text Format (.rtf). It cannot be greater
  162. than %max_size in size. <strong>Don\'t forget to press the Upload button before
  163. attempting to submit your BLAST.</strong>',
  164. array(
  165. '%max_size' => round(file_upload_max_size() / 1024 / 1024,1) . 'MB'
  166. )
  167. ),
  168. '#upload_validators' => array(
  169. 'file_validate_extensions' => array('fasta fna fa fas'),
  170. 'file_validate_size' => array(file_upload_max_size()),
  171. ),
  172. );
  173. }
  174. // BLAST DATABASE
  175. //.........................
  176. $form['B']['DB'] = array(
  177. '#type' => 'fieldset',
  178. '#title' => t('Choose Search Target'),
  179. '#description' => t('Choose from one of the %type BLAST databases listed '
  180. . 'below. You can also use the browse button to upload a file from your '
  181. . 'local disk. The file may contain a single sequence or a list of '
  182. . 'sequences. ',
  183. array('%type' => $db_type)),
  184. '#collapsible' => TRUE,
  185. '#collapsed' => FALSE,
  186. );
  187. $options = get_blast_database_options($db_type);
  188. $form['B']['DB']['SELECT_DB'] = array(
  189. '#type' => 'select',
  190. '#title' => t('%type BLAST Databases:', array('%type' => ucfirst($db_type))),
  191. '#options' => $options,
  192. '#default_value' => isset($job_data[$jid]['db_option']) ? $job_data[$jid]['db_option'] : 0,
  193. );
  194. if (variable_get('blast_ui_allow_target_upload', FALSE)) {
  195. // Upload a file as an alternative to selecting an existing BLAST database
  196. $form['#attributes']['enctype'] = 'multipart/form-data';
  197. $form['B']['DB']['DBUPLOAD'] = array(
  198. '#title' => 'Or upload your own dataset: ',
  199. '#type' => 'managed_file',
  200. '#description' => t('The file should be a plain-text FASTA
  201. (.fasta, .fna, .fa) file. In other words, it cannot have formatting as is the
  202. case with MS Word (.doc, .docx) or Rich Text Format (.rtf). It cannot be greater
  203. than %max_size in size. <strong>Don\'t forget to press the Upload button before
  204. attempting to submit your BLAST.</strong>',
  205. array(
  206. '%max_size' => round(file_upload_max_size() / 1024 / 1024,1) . 'MB'
  207. )
  208. ),
  209. '#default_value' => variable_get($db_file_id, ''),
  210. '#upload_validators' => array(
  211. 'file_validate_extensions' => array('fasta fna fa'),
  212. 'file_validate_size' => array(file_upload_max_size()),
  213. ),
  214. );
  215. }
  216. // Advanced Options
  217. //.........................
  218. // These options will be different depending upon the program selected.
  219. // Therefore, allow for program-specific callbacks to populate these options.
  220. $form['B']['ALG'] = array(
  221. '#type' => 'fieldset',
  222. '#title' => t('Advanced Options'),
  223. '#collapsible' => TRUE,
  224. '#collapsed' => TRUE,
  225. );
  226. $advanced_options_form = 'blast_ui_' . $blast_program . '_advanced_options_form';
  227. if (function_exists($advanced_options_form)) {
  228. call_user_func_array($advanced_options_form, array(&$form, $form_state));
  229. }
  230. $form['B']['ALG'] = array_merge($form['B']['ALG'], $form['ALG']);
  231. unset($form['ALG']);
  232. // Submit
  233. //.........................
  234. $form['B']['submit'] = array(
  235. '#type' => 'submit',
  236. '#default_value' => ' BLAST ',
  237. );
  238. return $form;
  239. }
  240. /**
  241. * Validate the user options submitted via the above form.
  242. *
  243. * @see blast_ui_per_blast_program_form().
  244. */
  245. function blast_ui_per_blast_program_form_validate($form, &$form_state) {
  246. $blast_program = $form_state['values']['blast_program'];
  247. $type = $form_state['values']['query_type'];
  248. if ($type == 'nucleotide') {
  249. $molecule_type = 'nucleotides';
  250. }
  251. else {
  252. $molecule_type = 'amino acid residues';
  253. }
  254. // Validate Query
  255. //----------------
  256. // @todo: We are currently not validating uploaded files are valid FASTA.
  257. // First check to see if we have an upload & if so then validate it.
  258. $file = file_load($form_state['values']['UPLOAD']);
  259. // If the $file is populated then this is a newly uploaded, temporary file.
  260. if (is_object($file)) {
  261. $form_state['qFlag'] = 'upQuery';
  262. $form_state['upQuery_path'] = drupal_realpath($file->uri);
  263. }
  264. // Otherwise there was no file uploaded.
  265. // Check if there was a query sequence entered in the texfield.
  266. elseif (!empty($form_state['input']['FASTA'])) {
  267. // Check to ensure that the query sequence entered is valid FASTA.
  268. if (validate_fasta_sequence($type, $form_state['input']['FASTA'])){
  269. form_set_error('query', t('You need to provide a valid FASTA sequence '
  270. . 'for the query. It should contain a FASTA header/definition line '
  271. . 'followed by %molecule-type. For more information see the '
  272. . '<a href="@url" target="_blank">NCBI FASTA Specification</a>.',
  273. array(
  274. '%molecule-type' => $molecule_type,
  275. '@url' => 'http://www.ncbi.nlm.nih.gov/BLAST/blastcgihelp.shtml'
  276. )));
  277. }
  278. else {
  279. $form_state['qFlag'] = 'seqQuery';
  280. }
  281. }
  282. // Otherwise they didn't enter a query!!
  283. else {
  284. form_set_error('query', t('No query sequence given. Only raw sequence or '
  285. . 'sequence of type FASTA can be read. Enter sequence in the box provided '
  286. . 'or upload a plain text file.'));
  287. }
  288. // Validate Database
  289. //-------------------
  290. // @todo: We are currently not validating uploaded files are valid FASTA.
  291. // First check to see if we have an upload & if so then validate it.
  292. if (isset($form_state['values']['DBUPLOAD'])) {
  293. $file = file_load($form_state['values']['DBUPLOAD']);
  294. // If the $file is populated then this is a newly uploaded, temporary file.
  295. if (is_object($file)) {
  296. $form_state['dbFlag'] = 'upDB';
  297. $form_state['upDB_path'] = drupal_realpath($file->uri);
  298. }
  299. // Otherwise there was no file uploaded
  300. // Check if there was a database choosen from the list instead
  301. elseif (!empty($form_state['values']['SELECT_DB'])) {
  302. $form_state['dbFlag'] = 'blastdb';
  303. }
  304. // Otherwise they didn't select a database!!
  305. else {
  306. form_set_error('DB', t('No database selected. Either choose a database '
  307. . 'from the list or upload one of your own.'));
  308. }
  309. }
  310. // Otherwise there was no file uploaded
  311. // Check if there was a database choosen from the list instead
  312. elseif (!empty($form_state['values']['SELECT_DB'])) {
  313. $form_state['dbFlag'] = 'blastdb';
  314. }
  315. // Otherwise they didn't select a database!!
  316. else {
  317. form_set_error('DB', t('No database selected. Either choose a database '
  318. . 'from the list or upload one of your own.'));
  319. }
  320. // Validate Advanced Options
  321. //---------------------------
  322. $advanced_options_form_validate = 'blast_ui_' . $blast_program . '_advanced_options_form_validate';
  323. if (function_exists($advanced_options_form_validate)) {
  324. call_user_func_array(
  325. $advanced_options_form_validate,
  326. array(&$form, $form_state)
  327. );
  328. }
  329. }//blast_ui_per_blast_program_form_validate
  330. /**
  331. * Process the user options submitted via the blast program form.
  332. *
  333. * @see blast_ui_per_blast_program_form().
  334. */
  335. function blast_ui_per_blast_program_form_submit($form, &$form_state) {
  336. $error = FALSE;
  337. $blast_program = $form_state['values']['blast_program'];
  338. if ($form_state['values']['db_type'] == 'nucleotide') {
  339. $mdb_type = 'nucl';
  340. }
  341. else {
  342. $mdb_type = 'prot';
  343. }
  344. // If the query was submitted via the texrfield then create a file containing it
  345. if (isset($form_state['qFlag'])) {
  346. if ($form_state['qFlag'] == 'seqQuery') {
  347. $seq_content = $form_state['values']['FASTA'];
  348. $query = '/tmp/' . date('YMd_His') . '_query.fasta';
  349. file_put_contents ($query , $seq_content);
  350. }
  351. elseif ($form_state['qFlag'] == 'upQuery') {
  352. $query = $form_state['upQuery_path'];
  353. }
  354. }
  355. // If the BLAST database was uploaded then use it to run the BLAST
  356. if ($form_state['dbFlag'] == 'upDB') {
  357. // Since we only support using the -db flag (not -subject) we need to create a
  358. // blast database for the FASTA uploaded.
  359. // NOTE: We can't support subject because we need to generate the ASN.1+ format
  360. // to provide multiple download type options from the same BLAST
  361. $blastdb_with_path = $form_state['upDB_path'];
  362. $result = NULL;
  363. exec("makeblastdb -in $blastdb_with_path -dbtype $mdb_type -parse_seqids 2>&1", $result);
  364. // Check that the BLAST database was made correctly.
  365. $result = implode('<br />', $result);
  366. if (preg_match('/Error/', $result)) {
  367. drupal_set_message('Unable to generate a BLAST database from your uploaded
  368. FASTA sequence. Please check that your file is a valid FASTA file and that if
  369. your sequence headers include pipes (i.e.: | ) they adhere to '
  370. . l('NCBI standards.', 'http://www.ncbi.nlm.nih.gov/books/NBK21097/table/A632/?report=objectonly', array('attributes' => array('target' => '_blank'))),
  371. 'error'
  372. );
  373. $error = TRUE;
  374. }
  375. }//upload target db
  376. // Otherwise, we are using one of the website provided BLAST databases so form the
  377. // BLAST command accordingly
  378. elseif ($form_state['dbFlag'] == 'blastdb') {
  379. $selected_db = $form_state['values']['SELECT_DB'];
  380. $blastdb_node = node_load($selected_db);
  381. $blastdb_name = $blastdb_node->db_name;
  382. $blastdb_with_path = $blastdb_node->db_path;
  383. }
  384. // Now let each program process its own advanced options.
  385. $advanced_options = array();
  386. $advanced_options_form_submit = 'blast_ui_' . $blast_program . '_advanced_options_form_submit';
  387. if (function_exists($advanced_options_form_submit)) {
  388. $advanced_options = call_user_func_array(
  389. $advanced_options_form_submit,
  390. array($form['B'], $form_state)
  391. );
  392. }
  393. else {
  394. $advanced_options = array('none' => 0);
  395. }
  396. // Set path to a BLAST target file to check for its existence
  397. if ($mdb_type == 'nucl' && (preg_match('/\.[pn]al/', $blastdb_with_path) == 0)) {
  398. // Suffix may be .nsq or .nal
  399. if (is_readable("$blastdb_with_path.nsq")) {
  400. $blastdb_with_suffix = "$blastdb_with_path.nsq";
  401. }
  402. else if (is_readable("$blastdb_with_path.nal")) {
  403. $blastdb_with_suffix = "$blastdb_with_path.nal";
  404. }
  405. }
  406. else if ($mdb_type == 'prot' && (preg_match('/\.[pn]al/', $blastdb_with_path) == 0)) {
  407. // Suffix may be .psq or .pal
  408. if (is_readable("$blastdb_with_path.psq")) {
  409. $blastdb_with_suffix = "$blastdb_with_path.psq";
  410. }
  411. else if (is_readable("$blastdb_with_path.pal")) {
  412. $blastdb_with_suffix = "$blastdb_with_path.pal";
  413. }
  414. }
  415. else {
  416. $blastdb_with_suffix = $blastdb_with_path;
  417. }
  418. // Actually submit the BLAST Tripal Job
  419. if (is_readable($blastdb_with_suffix)) {
  420. // BLAST target exists.
  421. global $user;
  422. $output_filestub = date('YMd_His');
  423. $job_args = array(
  424. 'program' => $blast_program,
  425. 'query' => $query,
  426. 'database' => $blastdb_with_path,
  427. 'output_filename' => $output_filestub,
  428. 'options' => $advanced_options
  429. );
  430. $job_id = tripal_add_job(
  431. t('BLAST (@program): @query', array('@program' => $blast_program, '@query' => $query)),
  432. 'blast_job',
  433. 'run_BLAST_tripal_job',
  434. $job_args,
  435. $user->uid
  436. );
  437. $job_data = variable_get('job_data', '');
  438. $seq_rows = explode(PHP_EOL, $seq_content);
  439. foreach($seq_rows as $row) {
  440. if (strpos($row, ">") !== FALSE) {
  441. $query_def[] = trim($row, "> \t\n\r\0\x0B");
  442. }
  443. }
  444. $job_data[$job_id] =
  445. array(
  446. 'program' => $blast_program,
  447. 'job_url' => current_path(),
  448. 'fasta' => $seq_content,
  449. 'query_def' => $query_def,
  450. 'db_name' => $blastdb_node->db_name,
  451. 'db_option' => $selected_db,
  452. 'options' => $advanced_options,
  453. );
  454. variable_set('job_data', $job_data);
  455. //@deepaksomanadh create session and save the recent jobs in respective session
  456. if (session_status() === PHP_SESSION_NONE) {
  457. session_start();
  458. }
  459. $sid = session_id();
  460. $job_encode_id = blast_ui_make_secret($job_id);
  461. $job_url = "blast/report/$job_encode_id";
  462. $all_jobs = $_SESSION['all_jobs'];
  463. $session_jobs = $all_jobs[$sid];
  464. $session_jobs[$job_id] =
  465. array(
  466. 'job_output_url'=> $job_url,
  467. 'query_defs' => $query_def,
  468. 'target' => $blastdb_name,
  469. 'program' => $blast_program,
  470. 'date' => date('Y-M-d h:i:s'),
  471. );
  472. $all_jobs[$sid] = $session_jobs;
  473. $_SESSION['all_jobs'] = $all_jobs;
  474. // Comment out this line to run BLAST by hand via the command:
  475. // drush trp-run-jobs --username=admin
  476. tripal_jobs_launch(1, $job_id);
  477. //Encode the job_id
  478. $job_encode_id = blast_ui_make_secret($job_id);
  479. // Redirect to the BLAST results page
  480. drupal_goto("blast/report/$job_encode_id");
  481. }//BLAST target is readable
  482. // BLAST target is unreadable
  483. else {
  484. $dbfile_uploaded_msg = ($form_state['dbFlag'] == 'upDB')
  485. ? 'The BLAST database was submitted via user upload.'
  486. : 'Existing BLAST Database was chosen.';
  487. tripal_report_error(
  488. 'blast_ui',
  489. TRIPAL_ERROR,
  490. "BLAST database %db unaccessible. %msg",
  491. array('%db' => $blastdb_with_path, '%msg' => $dbfile_uploaded_msg)
  492. );
  493. $msg = "$dbfile_uploaded_msg BLAST database '$blastdb_with_path' is unaccessible. ";
  494. $msg .= "Please contact the site administrator.";
  495. drupal_set_message($msg, 'error');
  496. }
  497. }
  498. /**
  499. * AJAX: Replace the sequence textarea with one containing an example.
  500. */
  501. function ajax_blast_ui_perprogram_example_sequence_callback($form, $form_state) {
  502. $sequence_type = $form_state['values']['query_type'];
  503. // Choose the example sequence based on the sequence type of the query.
  504. if ($sequence_type == 'nucleotide') {
  505. $example_sequence = variable_get('blast_ui_nucleotide_example_sequence', 'sample');
  506. }
  507. elseif ($sequence_type == 'protein') {
  508. $example_sequence = variable_get('blast_ui_protein_example_sequence', 'sample');
  509. }
  510. else {
  511. $example_sequence = 'unknown query type';
  512. }
  513. // If the Show Example checkbox is true then put the example in the textfield
  514. if ($form_state['values']['example_sequence']) {
  515. // Set the value to be the example sequence (set in the admin form).
  516. $form['B']['query']['FASTA']['#value'] = $example_sequence;
  517. }
  518. // Otherwise we want to remove the already displayed example.
  519. else {
  520. $form['B']['query']['FASTA']['#value'] = '';
  521. }
  522. return $form['B']['query']['FASTA'];
  523. }