tripal_bulk_loader.loader.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <?php
  2. /**
  3. * Add Loader Job Form
  4. *
  5. * This form is meant to be included on the node page to allow users to submit/re-submit
  6. * loading jobs
  7. */
  8. function tripal_bulk_loader_add_loader_job_form ($form_state, $node) {
  9. $form = array();
  10. $form['nid'] = array(
  11. '#type' => 'hidden',
  12. '#value' => $node->nid,
  13. );
  14. $form['file'] = array(
  15. '#type' => 'hidden',
  16. '#value' => $node->file
  17. );
  18. $form['job_id'] = array(
  19. '#type' => 'hidden',
  20. '#value' => $node->job_id,
  21. );
  22. $form['submit'] = array(
  23. '#type' => 'submit',
  24. '#value' => ($node->job_id) ? 'Re-Submit Job' : 'Submit Job',
  25. );
  26. $form['submit-cancel'] = array(
  27. '#type' => ($node->job_id)? 'submit' : 'hidden',
  28. '#value' => 'Cancel Job',
  29. );
  30. return $form;
  31. }
  32. /**
  33. * Add Loader Job Form (Submit)
  34. */
  35. function tripal_bulk_loader_add_loader_job_form_submit ($form, $form_state) {
  36. global $user;
  37. if (preg_match('/Submit Job/', $form_state['values']['op'])) {
  38. //Submit Tripal Job
  39. $job_args[1] = $form_state['values']['nid'];
  40. if (is_readable($form_state['values']['file'])) {
  41. $fname = basename($form_state['values']['file']);
  42. $job_id = tripal_add_job("Bulk Loading Job: $fname",'tripal_bulk_loader', 'tripal_bulk_loader_load_data', $job_args, $user->uid);
  43. // add job_id to bulk_loader node
  44. $success = db_query("UPDATE {tripal_bulk_loader} SET job_id=%d WHERE nid=%d", $job_id, $form_state['values']['nid']);
  45. } else {
  46. drupal_set_message("Can not open ".$form_state['values']['file'].". Job not scheduled.");
  47. }
  48. } elseif (preg_match('/Re-Submit Job/', $form_state['values']['op'])) {
  49. tripal_jobs_rerun($form_state['values']['job_id']);
  50. } elseif (preg_match('/Cancel Job/', $form_state['values']['op'])) {
  51. tripal_jobs_cancel($form_state['values']['job_id']);
  52. }
  53. }
  54. /**
  55. * Tripal Bulk Loader
  56. *
  57. * This is the function that's run by tripal_launch_jobs to bulk load chado data.
  58. * @param $nid
  59. * The Node ID of the bulk loading job node to be loaded. All other needed data is expected to be
  60. * in the node (ie: template ID and file)
  61. *
  62. * Note: Instead of returning a value this function updates the tripal_bulk_loader.status and
  63. * Enters errors into tripal_bulk_loader_errors if they are encountered.
  64. */
  65. function tripal_bulk_loader_load_data($nid) {
  66. $node = node_load($nid);
  67. print "Template: ".$node->template->name." (".$node->template_id.")\n";
  68. print "File: ".$node->file."\n";
  69. // Prep Work ==================================================================================
  70. // Generate default values array
  71. $default_data = array();
  72. $field2column = array();
  73. $record2priority = array();
  74. foreach ($node->template->template_array as $priority => $record_array) {
  75. if (!is_array($record_array)) { continue; }
  76. foreach ($record_array['fields'] as $field_index => $field_array) {
  77. $default_data[$priority]['table'] = $record_array['table'];
  78. $default_data[$priority]['mode'] = ($record_array['mode']) ? $record_array['mode'] : 'insert';
  79. $default_data[$priority]['record_id'] = $record_array['record_id'];
  80. $record2priority[$record_array['record_id']] = $priority;
  81. $default_data[$priority]['required'][$field_array['field']] = $field_array['required'];
  82. if (preg_match('/table field/', $field_array['type'])) {
  83. $default_data[$priority]['values_array'][$field_array['field']] = '';
  84. $default_data[$priority]['need_further_processing'] = TRUE;
  85. $field2column[$priority][$field_array['field']] = $field_array['spreadsheet column'];
  86. } elseif (preg_match('/constant/', $field_array['type'])) {
  87. $default_data[$priority]['values_array'][$field_array['field']] = $field_array['constant value'];
  88. } elseif (preg_match('/foreign key/', $field_array['type'])) {
  89. $default_data[$priority]['values_array'][$field_array['field']] = array();
  90. $default_data[$priority]['values_array'][$field_array['field']]['foreign record'] = $field_array['foreign key'];
  91. $default_data[$priority]['need_further_processing'] = TRUE;
  92. } else {
  93. print 'WARNING: Unsupported type: '. $field_array['type'] . ' for ' . $table . '.' . $field_array['field']."!\n";
  94. }
  95. } // end of foreach field
  96. } //end of foreach record
  97. //print "\nDefault Values Array: ".print_r($default_data, TRUE)."\n";
  98. //print "\nField to Column Mapping: ".print_r($field2column, TRUE)."\n";
  99. // Parse File adding records as we go ========================================================
  100. $file_handle = fopen($node->file, 'r');
  101. if (preg_match('/(t|true|1)/', $node->file_has_header)) { fgets($file_handle, 4096); }
  102. $num_records = 0;
  103. $num_lines = 0;
  104. $num_errors = 0;
  105. while (!feof($file_handle)) {
  106. $line = array();
  107. $raw_line = fgets($file_handle, 4096);
  108. $raw_line = trim($raw_line);
  109. if (preg_match('/^\s*$/', $raw_line)) { continue; } // skips blank lines
  110. $line = preg_split("/\t/", $raw_line);
  111. $num_lines++;
  112. $data = $default_data;
  113. foreach ($data as $priority => $table_data) {
  114. $table = $table_data['table'];
  115. $values = $table_data['values_array'];
  116. if ($table_data['need_further_processing']) {
  117. $values = tripal_bulk_loader_add_spreadsheetdata_to_values ($values, $line, $field2column[$priority]);
  118. $values = tripal_bulk_loader_add_foreignkey_to_values($values, $data, $record2priority);
  119. }
  120. if (!$values) {
  121. $msg = $table_data['record_id'].' ('.$table_data['mode'].') Aborted due to error in previous record.';
  122. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  123. print "ERROR: ".$msg."\n";
  124. $data[$priority]['error'] = TRUE;
  125. }
  126. if (preg_match('/optional/', $table_array['mode'])) {
  127. // Check all required fields are set
  128. $table_desc = module_invoke_all('chado_'.$table.'_schema');
  129. $fields = $table_desc['fields'];
  130. foreach($fields as $field => $def){
  131. // a field is considered missing if it cannot be null and there is no default
  132. // value for it or it is of type 'serial'
  133. if($def['not null'] == 1 and !array_key_exists($field,$insert_values) and !isset($def['default']) and strcmp($def['type'],serial)!=0){
  134. $msg = $table_data['record_id'].' ('.$table_data['mode'].') Missing Database Required Value: '.$table.'.'.$field;
  135. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  136. print "ERROR: ".$msg."\n";
  137. $data[$priority]['error'] = TRUE;
  138. }
  139. }
  140. } //end of if optional record
  141. // Check required fields are present
  142. foreach ($table_data['required'] as $field => $required) {
  143. if ($required) {
  144. if (!isset($values[$field])) {
  145. $msg = $table_data['record_id'].' ('.$table_data['mode'].') Missing Template Required Value: '.$table.'.'.$field;
  146. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  147. print "ERROR: ".$msg."\n";
  148. $data[$priority]['error'] = TRUE;
  149. }
  150. }
  151. }
  152. if ($data[$priority]['error']) {
  153. continue;
  154. }
  155. // add new values array into the data array
  156. $data[$priority]['values_array'] = $values;
  157. // first check if it already exists
  158. $exists = tripal_core_chado_select($table, array_keys($values), $values, array('has_record'=>TRUE));
  159. //print "Number of Records:".$exists.".\n";
  160. if ($exists === 1) {
  161. if (!preg_match('/select/',$table_data['mode'])) {
  162. $msg = $table_data['record_id'].' ('.$table_data['mode'].') Record already exists in '.$table.' where values:'.print_r($values,TRUE);
  163. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  164. print "ERROR: ".$msg."\n";
  165. $data[$priority]['error'] = TRUE;
  166. }
  167. } elseif ($exists > 1) {
  168. $msg = $table_data['record_id'].' ('.$table_data['mode'].') 2+ Records exists in '.$table.' where values:'.print_r($values,TRUE);
  169. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  170. print "ERROR: ".$msg."\n";
  171. $data[$priority]['error'] = TRUE;
  172. } else {
  173. // if it doesn't exist already then insert it
  174. if (!preg_match('/select/',$table_data['mode'])) {
  175. $success = tripal_core_chado_insert($table, $values);
  176. if (!$success) {
  177. $msg = $table_data['record_id'].' ('.$table_data['mode'].') Unable to insert record into '.$table.' where values:'.print_r($values,TRUE);
  178. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_ERROR);
  179. print "ERROR: ".$msg."\n";
  180. $data[$priority]['error'] = TRUE;
  181. } else {
  182. //print "Inserted ".$table_data['record_id']." into ".$table."\n";
  183. }
  184. } else {
  185. $msg = $table_data['record_id'].' ('.$table_data['mode'].') No Matching record in '.$table.' where values:'.print_r($values,TRUE);
  186. watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING);
  187. print "ERROR: ".$msg."\n";
  188. $data[$priority]['error'] = TRUE;
  189. }
  190. }// end of if/not record exists
  191. } // end of foreach table in default data array
  192. } //end of foreach line of file
  193. }
  194. /**
  195. * This function adds the file data to the values array
  196. *
  197. * @param $values
  198. * The default values array -contains all constants
  199. * @param $line
  200. * An array of values for the current line
  201. * @param $field2column
  202. * An array mapping values fields to line columns
  203. * @return
  204. * Supplemented values array
  205. */
  206. function tripal_bulk_loader_add_spreadsheetdata_to_values ($values, $line, $field2column) {
  207. foreach ($values as $field => $value) {
  208. if (is_array($value)) { continue; }
  209. $column = $field2column[$field] - 1;
  210. if (preg_match('/\w+/',$line[$column])) {
  211. $values[$field] = $line[$column];
  212. } else {
  213. unset($values[$field]);
  214. }
  215. }
  216. return $values;
  217. }
  218. /**
  219. * Handles foreign keys in the values array.
  220. *
  221. * Specifically, if the value for a field is an array then it is assumed that the array contains
  222. * the name of the record whose values array should be substituted here. Thus the foreign
  223. * record is looked up and the values array is substituted in.
  224. *
  225. */
  226. function tripal_bulk_loader_add_foreignkey_to_values($values, $data, $record2priority) {
  227. foreach ($values as $field => $value) {
  228. if (is_array($value)) {
  229. $foreign_record = $value['foreign record'];
  230. $foreign_priority = $record2priority[$foreign_record];
  231. $foreign_values = $data[$foreign_priority]['values_array'];
  232. //add to current values array
  233. if (!$data[$foreign_priority]['error']) {
  234. $values[$field] = $foreign_values;
  235. } else {
  236. return FALSE;
  237. }
  238. }
  239. }
  240. return $values;
  241. }
  242. /**
  243. * Flattens an array up to two levels
  244. * Used for printing of arrays without taking up much space
  245. */
  246. function tripal_bulk_loader_flatten_array ($values) {
  247. $flattened_values = array();
  248. foreach ($values as $k => $v) {
  249. if (is_array($v)) {
  250. $vstr = array();
  251. foreach ($v as $vk => $vv) {
  252. if (strlen($vv) > 20) {
  253. $vstr[] = $vk .'=>'. substr($vv, 0, 20) . '...';
  254. } else {
  255. $vstr[] = $vk .'=>'. $vv;
  256. }
  257. }
  258. $v = '{'. implode(',',$vstr) .'}';
  259. } elseif (strlen($v) > 20) {
  260. $v = substr($v, 0, 20) . '...';
  261. }
  262. $flattened_values[] = $k .'=>'. $v;
  263. }
  264. return implode(', ',$flattened_values);
  265. }