tripal_bulk_loader.loader.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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['submit'] = array(
  19. '#type' => 'submit',
  20. '#value' => 'Submit Job'
  21. );
  22. return $form;
  23. }
  24. /**
  25. * Add Loader Job Form (Submit)
  26. */
  27. function tripal_bulk_loader_add_loader_job_form_submit ($form, $form_state) {
  28. global $user;
  29. if (preg_match('/Submit Job/', $form_state['values']['op'])) {
  30. //Submit Tripal Job
  31. $job_args[1] = $form_state['values']['nid'];
  32. if (is_readable($form_state['values']['file'])) {
  33. $fname = basename($form_state['values']['file']);
  34. tripal_add_job("Bulk Loading Job: $fname",'tripal_bulk_loader', 'tripal_bulk_loader_load_data', $job_args, $user->uid);
  35. } else {
  36. drupal_set_message("Can not open ".$form_state['values']['file'].". Job not scheduled.");
  37. }
  38. }
  39. }
  40. /**
  41. * Tripal Bulk Loader
  42. *
  43. * This is the function that's run by tripal_launch_jobs to bulk load chado data.
  44. * @param $nid
  45. * The Node ID of the bulk loading job node to be loaded. All other needed data is expected to be
  46. * in the node (ie: template ID and file)
  47. *
  48. * Note: Instead of returning a value this function updates the tripal_bulk_loader.status and
  49. * Enters errors into tripal_bulk_loader_errors if they are encountered.
  50. */
  51. function tripal_bulk_loader_load_data($nid) {
  52. $node = node_load($nid);
  53. print "Template: ".$node->template->name." (".$node->template_id.")\n";
  54. print "File: ".$node->file."\n";
  55. // Prep Work ==================================================================================
  56. // get base table and it's table_description array
  57. $base_table = $node->template->template_array['module'];
  58. $base_table_desc = module_invoke_all('chado_'.$base_table.'_schema');
  59. $related_tables = module_invoke_all('tripal_bulk_loader_'.$base_table.'_related_tables');
  60. //print "Base Table: ".print_r($base_table,TRUE)."\nTable DEscription: ".print_r($base_table_desc, TRUE)."\nTemplate array: ".print_r($node->template->template_array,TRUE)."\n";
  61. // get a default values array to be passed into tripal_core_chado_insert
  62. // and get a mapping between table.field and spreadsheet column
  63. // also determine relationship between each table and base table
  64. $default_values_array = array();
  65. $field2column = array();
  66. $relationships2base = array();
  67. $all_tables = array();
  68. foreach ($node->template->template_array as $table => $table_array) {
  69. if (is_array($table_array)) {
  70. $all_tables[$table] = $table;
  71. foreach ($table_array['field'] as $field_array) {
  72. if (preg_match('/table field/', $field_array['type'])) {
  73. $default_values_array[$table][$field_array['field']] = '';
  74. $field2column[$table][$field_array['field']] = $field_array['spreadsheet column'];
  75. } elseif (preg_match('/constant/', $field_array['type'])) {
  76. $default_values_array[$table][$field_array['field']] = $field_array['constant value'];
  77. } else {
  78. print 'WARNING: Unsupported type: '. $field_array['type'] . ' for ' . $table . '.' . $field_array['field']."!\n";
  79. }
  80. }
  81. // Determine what relation is between this table and the base table-----------------------
  82. // This is used later to link the various records
  83. //print "===\n";
  84. // Is there a foreign key to this table in the base table?
  85. // ie: feature.type_id (base) for cvterm (current)
  86. //print "A) Is there fkey to ".$table." table in ".$base_table."?\n";
  87. if ($related_tables['Foreign Key Relations'][$table]) {
  88. //print "YES!\n";
  89. $relationships2base['foreign key'][$table] = $base_table_desc['foreign keys'][$table];
  90. }
  91. // Is there a foreign key in this table to the base table?
  92. // ie: featureloc.feature_id (current) for feature (base)
  93. //print "B) Does ".$table." contain a fkey to ".$base_table."?\n";
  94. if ($related_tables['Direct Relations'][$table]) {
  95. $table_desc = module_invoke_all('chado_'.$table.'_schema');
  96. //print "YES!\n";
  97. $relationships2base['direct'][$table] = $table_desc['foreign keys'][$base_table];
  98. }
  99. // Is there a linking table which links this table with the base table
  100. // ie: analysisfeature (current) links analysis and feature (base)
  101. //print "C) Is ".$table." a linking table?\n";
  102. if ($related_tables['Indirect Relations'][$table]) {
  103. //print "YES!\n";
  104. $table_desc = module_invoke_all('chado_'.$table.'_schema');
  105. $relationships2base['indirect'][$table] = $table_desc['foreign keys'];
  106. }
  107. }
  108. }
  109. //print "\nDefault Values Array: ".print_r($default_values_array, TRUE)."\n";
  110. //print "\nField to Column Mapping: ".print_r($field2column, TRUE)."\n";
  111. //print "\nRelationships to Base Table: ".print_r($relationships2base, TRUE)."\n";
  112. // Parse File adding records as we go ========================================================
  113. $file_handle = fopen($node->file, 'r');
  114. if (preg_match('/(t|true|1)/', $node->file_has_header)) { fgets($file_handle, 4096); }
  115. $num_records = 0;
  116. $num_lines;
  117. while (!feof($file_handle)) {
  118. $line = array();
  119. $raw_line = fgets($file_handle, 4096);
  120. $line = preg_split("/\t/", $raw_line);
  121. $num_lines++;
  122. // Contains constants set above
  123. $tables = $all_tables;
  124. $values = $default_values_array;
  125. // Insert base record-----------------------------------------------------------------------
  126. foreach ($relationships2base['foreign key'] as $table => $desc) {
  127. // Ensure foreign key is present
  128. $values[$table] = tripal_bulk_loader_supplement_values_array ($values[$table], $line, $field2column[$table]);
  129. $success = tripal_bulk_loader_ensure_record ($table, $values[$table]);
  130. if ($success) {
  131. $num_records++;
  132. }
  133. //Add to base values array
  134. foreach ($desc['columns'] as $foreign_key => $primary_key) {
  135. $values[$base_table][$foreign_key] = $values[$table];
  136. }
  137. unset($tables[$table]);
  138. }
  139. // base table
  140. $values[$base_table] = tripal_bulk_loader_supplement_values_array ($values[$base_table], $line, $field2column[$base_table]);
  141. $success = tripal_bulk_loader_ensure_record ($base_table, $values[$base_table]);
  142. //print 'Base Table values array: '.print_r($values[$base_table], TRUE)."\n";
  143. if (!$success) {
  144. print "ERROR: Unable to insert base record where base table: ".$base_table.
  145. " and values array: ".tripal_bulk_loader_flatten_array($values[$base_table])."\n";
  146. } else {
  147. $num_records++;
  148. }
  149. unset($tables[$base_table]);
  150. // Insert all tables with direct relationship to base ----------------------------------------
  151. foreach ($relationships2base['direct'] as $table => $desc) {
  152. //Add base to values array
  153. foreach ($desc['columns'] as $foreign_key => $primary_key) {
  154. $values[$table][$foreign_key] = $values[$base_table];
  155. }
  156. // Supplement and Add record
  157. $values[$table] = tripal_bulk_loader_supplement_values_array ($values[$table], $line, $field2column[$table]);
  158. $success = tripal_bulk_loader_ensure_record ($table, $values[$table]);
  159. if ($success) {
  160. $num_records++;
  161. }
  162. unset($tables[$table]);
  163. }
  164. // Add in all other tables -----------------------------------------------------------------
  165. foreach ($tables as $table) {
  166. // Don't insert if its an indirect relationship linking table
  167. if (!$relationships2base['indirect'][$table]) {
  168. // Supplement and Add record
  169. $values[$table] = tripal_bulk_loader_supplement_values_array ($values[$table], $line, $field2column[$table]);
  170. $success = tripal_bulk_loader_ensure_record ($table, $values[$table]);
  171. if ($success) {
  172. $num_records++;
  173. }
  174. }
  175. }
  176. // Add in indirect relationships -----------------------------------------------------------
  177. foreach ($relationships2base['indirect'] as $table => $desc) {
  178. // Add foreign keys to values array
  179. foreach ($desc as $subtable => $subdesc) {
  180. foreach ($subdesc['columns'] as $foreign_key => $primary_key) {
  181. $values[$table][$foreign_key] = $values[$subtable];
  182. }
  183. }
  184. // Supplement and Add record
  185. $values[$table] = tripal_bulk_loader_supplement_values_array ($values[$table], $line, $field2column[$table]);
  186. $success = tripal_bulk_loader_ensure_record ($table, $values[$table]);
  187. if ($success) {
  188. $num_records++;
  189. }
  190. }
  191. } //end of file
  192. print "\nNumber of Records Inserted:".$num_records."\n";
  193. print "Number of Lines in File:".$num_lines."\n\n";
  194. }
  195. /**
  196. * This function adds the file data to the values array
  197. *
  198. * @param $values
  199. * The default values array -contains all constants
  200. * @param $line
  201. * An array of values for the current line
  202. * @param $field2column
  203. * An array mapping values fields to line columns
  204. * @return
  205. * Supplemented values array
  206. */
  207. function tripal_bulk_loader_supplement_values_array ($values, $line, $field2column) {
  208. foreach ($values as $field => $value) {
  209. $column = $field2column[$field] - 1;
  210. if ($line[$column] OR (!$values[$field])) {
  211. $values[$field] = $line[$column];
  212. }
  213. }
  214. return $values;
  215. }
  216. /**
  217. * This function first ensures the record doesn't already exist and then inserts it
  218. *
  219. * @param $table
  220. * The table the record should be present in
  221. * @param $values
  222. * The values array used for selecting and/or inserting the record
  223. * @return
  224. * TRUE or FALSE based on presence or absence of record
  225. */
  226. function tripal_bulk_loader_ensure_record ($table, $values) {
  227. // get flattened values array for printing errors
  228. $flattened_values = tripal_bulk_loader_flatten_array($values);
  229. // check if record exists
  230. $has_record = tripal_core_chado_select($table, array_keys($values), $values, array('has_record' => TRUE));
  231. if ($has_record) {
  232. print "\tWARNING: Record already exists in $table where ".implode(',',$flattened_values).".\n";
  233. return true;
  234. } else {
  235. // if record doesn't exist then insert it
  236. $success = tripal_core_chado_insert($table, $values);
  237. if (!$success) {
  238. print "ERROR: Unable to insert the following record into $table: ".implode(',',$flattened_values)."\n";
  239. return false;
  240. } else {
  241. return true;
  242. }
  243. }
  244. }
  245. /**
  246. * Flattens an array up to two levels
  247. * Used for printing of arrays without taking up much space
  248. */
  249. function tripal_bulk_loader_flatten_array ($values) {
  250. $flattened_values = array();
  251. foreach ($values as $k => $v) {
  252. if (is_array($v)) {
  253. $vstr = array();
  254. foreach ($v as $vk => $vv) {
  255. if (strlen($vv) > 20) {
  256. $vstr[] = $vk .'=>'. substr($vv, 0, 20) . '...';
  257. } else {
  258. $vstr[] = $vk .'=>'. $vv;
  259. }
  260. }
  261. $v = '{'. implode(',',$vstr) .'}';
  262. } elseif (strlen($v) > 20) {
  263. $v = substr($v, 0, 20) . '...';
  264. }
  265. $flattened_values[] = $k .'=>'. $v;
  266. }
  267. return $flattened_values;
  268. }