fasta_loader.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <?php
  2. /**
  3. * @defgroup fasta_loader FASTA Feature Loader
  4. * @{
  5. * Provides fasta loading functionality. Creates features based on their specification in a fasta file.
  6. * @}
  7. * @ingroup tripal_feature
  8. */
  9. /**
  10. *
  11. *
  12. * @ingroup fasta_loader
  13. */
  14. function tripal_feature_fasta_load_form (){
  15. $form['fasta_file']= array(
  16. '#type' => 'textfield',
  17. '#title' => t('FASTA File'),
  18. '#description' => t('Please enter the full system path for the FASTA file, or a path within the Drupal
  19. installation (e.g. /sites/default/files/xyz.obo). The path must be accessible to the
  20. server on which this Drupal instance is running.'),
  21. '#required' => TRUE,
  22. '#weight' => 1
  23. );
  24. // get the list of organisms
  25. $sql = "SELECT * FROM {organism} ORDER BY genus, species";
  26. $previous_db = tripal_db_set_active('chado'); // use chado database
  27. $org_rset = db_query($sql);
  28. tripal_db_set_active($previous_db); // now use drupal database
  29. $organisms = array();
  30. $organisms[''] = '';
  31. while($organism = db_fetch_object($org_rset)){
  32. $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
  33. }
  34. $form['organism_id'] = array (
  35. '#title' => t('Organism'),
  36. '#type' => t('select'),
  37. '#description' => t("Choose the organism to which these sequences are associated "),
  38. '#required' => TRUE,
  39. '#options' => $organisms,
  40. '#weight' => 2,
  41. );
  42. $form['type']= array(
  43. '#type' => 'textfield',
  44. '#title' => t('Sequence Type'),
  45. '#required' => TRUE,
  46. '#description' => t('Please enter the Sequence Ontology term that describes the sequences in the FASTA file.'),
  47. '#weight' => 3
  48. );
  49. // get the list of organisms
  50. $sql = "SELECT L.library_id, L.name, CVT.name as type
  51. FROM {library} L
  52. INNER JOIN {cvterm} CVT ON L.type_id = CVT.cvterm_id
  53. ORDER BY name";
  54. $previous_db = tripal_db_set_active('chado'); // use chado database
  55. $lib_rset = db_query($sql);
  56. tripal_db_set_active($previous_db); // now use drupal database
  57. $libraries = array();
  58. $libraries[''] = '';
  59. while($library = db_fetch_object($lib_rset)){
  60. $libraries[$library->library_id] = "$library->name ($library->type)";
  61. }
  62. // $form['library_id'] = array (
  63. // '#title' => t('Library'),
  64. // '#type' => t('select'),
  65. // '#description' => t("Choose the library to which these sequences are associated "),
  66. // '#required' => FALSE,
  67. // '#options' => $libraries,
  68. // '#weight' => 5,
  69. // );
  70. $form['update']= array(
  71. '#type' => 'checkbox',
  72. '#title' => t('Insert and update'),
  73. '#required' => FALSE,
  74. '#description' => t('By default only new features are inserted. Select this checkbox to update
  75. features that already exists with the contents from the FASTA file.'),
  76. '#weight' => 6
  77. );
  78. $form['analysis'] = array(
  79. '#type' => 'fieldset',
  80. '#title' => t('Analysis Used to Derive Features'),
  81. '#weight'=> 6,
  82. '#collapsed' => TRUE
  83. );
  84. $form['analysis']['desc'] = array(
  85. '#type' => 'markup',
  86. '#value' => t("Why specify an analysis for a data load? All data comes
  87. from some place, even if downloaded from Genbank. By specifying
  88. analysis details for all data uploads, it allows an end user to reproduce the
  89. data set, but at least indicates the source of the data."),
  90. );
  91. // get the list of organisms
  92. $sql = "SELECT * FROM {analysis} ORDER BY name";
  93. $previous_db = tripal_db_set_active('chado'); // use chado database
  94. $org_rset = db_query($sql);
  95. tripal_db_set_active($previous_db); // now use drupal database
  96. $analyses = array();
  97. $analyses[''] = '';
  98. while($analysis = db_fetch_object($org_rset)){
  99. $analyses[$analysis->analysis_id] = "$analysis->name ($analysis->program $analysis->programversion, $analysis->sourcename)";
  100. }
  101. $form['analysis']['analysis_id'] = array (
  102. '#title' => t('Analysis'),
  103. '#type' => t('select'),
  104. '#description' => t("Choose the analysis to which these features are associated "),
  105. '#required' => TRUE,
  106. '#options' => $analyses,
  107. );
  108. // Advanced Options
  109. $form['advanced'] = array(
  110. '#type' => 'fieldset',
  111. '#title' => t('Advanced Options'),
  112. '#weight'=> 7,
  113. '#collapsed' => TRUE
  114. );
  115. $form['advanced']['re_help']= array(
  116. '#type' => 'item',
  117. '#value' => t('A regular expression is an advanced method for extracting information from a string of text.
  118. By default, this loader will use the first word in the definition line for each sequence in the FASTA file
  119. as the uniquename for the sequences. If this is not desired, you may use the following regular
  120. expressions to define the postions of the unique name.'),
  121. '#weight' => 0
  122. );
  123. $form['advanced']['re_name']= array(
  124. '#type' => 'textfield',
  125. '#title' => t('Regular expression for the name'),
  126. '#required' => FALSE,
  127. '#description' => t('Enter the regular expression that will extract the feature name from the FASTA definition line. For example, for a defintion line with a name and uniquename separated by a bar \'|\' (>seqname|uniquename), the regular expression would be, "^(.*?)\|.*$"'),
  128. '#weight' => 1
  129. );
  130. $form['advanced']['re_uname']= array(
  131. '#type' => 'textfield',
  132. '#title' => t('Regular expression for the unique name'),
  133. '#required' => FALSE,
  134. '#description' => t('Enter the regular expression that will extract the unique feature name for each feature from the FASTA definition line. This name must be unique for the organism.'),
  135. '#weight' => 2
  136. );
  137. // Advanced database cross-reference optoins
  138. $form['advanced']['db'] = array(
  139. '#type' => 'fieldset',
  140. '#title' => t('External Database Reference'),
  141. '#weight'=> 6,
  142. '#collapsed' => TRUE
  143. );
  144. $form['advanced']['db']['re_accession']= array(
  145. '#type' => 'textfield',
  146. '#title' => t('Regular expression for the accession'),
  147. '#required' => FALSE,
  148. '#description' => t('Enter the regular expression that will extract the accession for the external database for each feature from the FASTA definition line.'),
  149. '#weight' => 2
  150. );
  151. // get the list of databases
  152. $sql = "SELECT * FROM {db} ORDER BY name";
  153. $previous_db = tripal_db_set_active('chado'); // use chado database
  154. $db_rset = db_query($sql);
  155. tripal_db_set_active($previous_db); // now use drupal database
  156. $dbs = array();
  157. $dbs[''] = '';
  158. while($db = db_fetch_object($db_rset)){
  159. $dbs[$db->db_id] = "$db->name";
  160. }
  161. $form['advanced']['db']['db_id'] = array (
  162. '#title' => t('External Database'),
  163. '#type' => t('select'),
  164. '#description' => t("Plese choose an external database for which these sequences have a cross reference."),
  165. '#required' => FALSE,
  166. '#options' => $dbs,
  167. '#weight' => 1,
  168. );
  169. $form['advanced']['relationship'] = array(
  170. '#type' => 'fieldset',
  171. '#title' => t('Relationships'),
  172. '#weight'=> 6,
  173. '#collapsed' => TRUE
  174. );
  175. $rels = array();
  176. $rels[''] = '';
  177. $rels['part_of'] = 'part of';
  178. $rels['derives_from'] = 'produced by';
  179. // Advanced references options
  180. $form['advanced']['relationship']['rel_type']= array(
  181. '#title' => t('Relationship Type'),
  182. '#type' => t('select'),
  183. '#description' => t("Use this option to create associations, or relationships between the
  184. features of this FASTA file and existing features in the database. For
  185. example, to associate a FASTA file of peptides to existing genes or transcript sequence,
  186. select the type 'produced by'. For a CDS sequences select the type 'part of'"),
  187. '#required' => FALSE,
  188. '#options' => $rels,
  189. '#weight' => 5,
  190. );
  191. $form['advanced']['relationship']['re_subject']= array(
  192. '#type' => 'textfield',
  193. '#title' => t('Regular expression for the parent'),
  194. '#required' => FALSE,
  195. '#description' => t('Enter the regular expression that will extract the unique
  196. name needed to identify the existing sequence for which the
  197. relationship type selected above will apply.'),
  198. '#weight' => 6
  199. );
  200. $form['advanced']['relationship']['parent_type']= array(
  201. '#type' => 'textfield',
  202. '#title' => t('Parent Type'),
  203. '#required' => FALSE,
  204. '#description' => t('Please enter the Sequence Ontology term for the parent. For example
  205. if the FASTA file being loaded is a set of proteins that are
  206. products of genes, then use the SO term \'gene\' or \'transcript\' or equivalent. However,
  207. this type must match the type for already loaded features.'),
  208. '#weight' => 7
  209. );
  210. $form['button'] = array(
  211. '#type' => 'submit',
  212. '#value' => t('Import FASTA file'),
  213. '#weight' => 10,
  214. );
  215. return $form;
  216. }
  217. /**
  218. *
  219. *
  220. * @ingroup fasta_loader
  221. */
  222. function tripal_feature_fasta_load_form_validate($form, &$form_state){
  223. $fasta_file = trim($form_state['values']['fasta_file']);
  224. $organism_id = $form_state['values']['organism_id'];
  225. $type = trim($form_state['values']['type']);
  226. $update = trim($form_state['values']['update']);
  227. $library_id = $form_state['values']['library_id'];
  228. $re_name = trim($form_state['values']['re_name']);
  229. $re_uname = trim($form_state['values']['re_uname']);
  230. $re_accession = trim($form_state['values']['re_accession']);
  231. $db_id = $form_state['values']['db_id'];
  232. $rel_type = $form_state['values']['rel_type'];
  233. $re_subject = trim($form_state['values']['re_subject']);
  234. $parent_type = trim($form_state['values']['parent_type']);
  235. // check to see if the file is located local to Drupal
  236. $dfile = $_SERVER['DOCUMENT_ROOT'] . base_path() . $fasta_file;
  237. if(!file_exists($dfile)){
  238. // if not local to Drupal, the file must be someplace else, just use
  239. // the full path provided
  240. $dfile = $fasta_file;
  241. }
  242. if(!file_exists($dfile)){
  243. form_set_error('fasta_file',t("Cannot find the file on the system. Check that the file exists or that the web server has permissions to read the file."));
  244. }
  245. // make sure if a relationship is specified that all fields are provided.
  246. if(($rel_type or $parent_type) and !$re_subject){
  247. form_set_error('re_subject',t("Please provide a regular expression for the parent"));
  248. }
  249. if(($rel_type or $re_subject) and !$parent_type){
  250. form_set_error('parent_type',t("Please provide a SO term for the parent"));
  251. }
  252. if(($parent_type or $re_subject) and !$rel_type){
  253. form_set_error('rel_type',t("Please select a relationship type"));
  254. }
  255. // make sure if a database is specified that all fields are provided
  256. if($db_id and !$re_accession){
  257. form_set_error('re_accession',t("Please provide a regular expression for the accession"));
  258. }
  259. if($re_accession and !$db_id){
  260. form_set_error('db_id',t("Please select a database"));
  261. }
  262. // check to make sure the types exists
  263. $cvtermsql = "SELECT CVT.cvterm_id
  264. FROM {cvterm} CVT
  265. INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
  266. LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
  267. WHERE cv.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
  268. $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$type,$type));
  269. if(!$cvterm){
  270. form_set_error('type',t("The Sequence Ontology (SO) term selected for the sequence type is not available in the database. Please check spelling or select another."));
  271. }
  272. if($rel_type){
  273. $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$parent_type,$parent_type));
  274. if(!$cvterm){
  275. form_set_error('parent_type',t("The Sequence Ontology (SO) term selected for the parent relationship is not available in the database. Please check spelling or select another."));
  276. }
  277. }
  278. // check to make sure the 'relationship' and 'sequence' ontologies are loaded
  279. $form_state['storage']['dfile'] = $dfile;
  280. }
  281. /**
  282. *
  283. *
  284. * @ingroup fasta_loader
  285. */
  286. function tripal_feature_fasta_load_form_submit ($form, &$form_state){
  287. global $user;
  288. $dfile = $form_state['storage']['dfile'];
  289. $organism_id = $form_state['values']['organism_id'];
  290. $type = trim($form_state['values']['type']);
  291. $update = trim($form_state['values']['update']);
  292. $library_id = $form_state['values']['library_id'];
  293. $re_name = trim($form_state['values']['re_name']);
  294. $re_uname = trim($form_state['values']['re_uname']);
  295. $re_accession = trim($form_state['values']['re_accession']);
  296. $db_id = $form_state['values']['db_id'];
  297. $rel_type = $form_state['values']['rel_type'];
  298. $re_subject = trim($form_state['values']['re_subject']);
  299. $parent_type = trim($form_state['values']['parent_type']);
  300. $analysis_id = $form_state['values']['analysis_id'];
  301. $args = array($dfile,$organism_id,$type,$library_id,$re_name,$re_uname,
  302. $re_accession,$db_id,$rel_type,$re_subject,$parent_type,$update,
  303. $user->uid,$analysis_id);
  304. tripal_add_job("Import FASTA file: $dfile",'tripal_feature',
  305. 'tripal_feature_load_fasta',$args,$user->uid);
  306. }
  307. /**
  308. *
  309. *
  310. * @ingroup fasta_loader
  311. */
  312. function tripal_feature_load_fasta($dfile, $organism_id, $type,
  313. $library_id, $re_name, $re_uname, $re_accession, $db_id, $rel_type,
  314. $re_subject, $parent_type, $update,$uid, $analysis_id, $job = NULL)
  315. {
  316. print "Opening FASTA file $dfile\n";
  317. $lines = file($dfile,FILE_SKIP_EMPTY_LINES);
  318. $i = 0;
  319. $name = '';
  320. $residues = '';
  321. $num_lines = sizeof($lines);
  322. $interval = intval($num_lines * 0.01);
  323. if($interval == 0){
  324. $interval = 1;
  325. }
  326. foreach ($lines as $line_num => $line) {
  327. $i++; // update the line count
  328. // update the job status every 1% features
  329. if($job and $i % $interval == 0){
  330. tripal_job_set_progress($job,intval(($i/$num_lines)*100));
  331. }
  332. // get the name, uniquename, accession and relationship subject from
  333. // the definition line
  334. if(preg_match('/^>/',$line)){
  335. // if we have a feature name then we are starting a new sequence
  336. // and we need to insert this one
  337. if($name){
  338. tripal_feature_fasta_loader_insert_feature($name,$uname,$db_id,
  339. $accession,$subject,$rel_type,$parent_type,$library_id,$organism_id,$type,
  340. $source,$residues,$update,$re_name);
  341. $residues = '';
  342. $name = '';
  343. }
  344. $line = preg_replace("/^>/",'',$line);
  345. if($re_name){
  346. if(!preg_match("/$re_name/",$line,$matches)){
  347. print "Regular expression for the feature name finds nothing\n";
  348. }
  349. $name = trim($matches[1]);
  350. } else {
  351. preg_match("/^\s*(.*?)[\s\|].*$/",$line,$matches);
  352. $name = trim($matches[1]);
  353. }
  354. if($re_uname){
  355. preg_match("/$re_uname/",$line,$matches);
  356. $uname = trim($matches[1]);
  357. } else {
  358. preg_match("/^\s*(.*?)[\s\|].*$/",$line,$matches);
  359. $uname = trim($matches[1]);
  360. }
  361. preg_match("/$re_accession/",$line,$matches);
  362. $accession = trim($matches[1]);
  363. preg_match("/$re_subject/",$line,$matches);
  364. $subject = trim($matches[1]);
  365. // print "Name: $name, UName: $uname, Accession: $accession, Subject: $subject\n";
  366. }
  367. else {
  368. $residues .= trim($line);
  369. }
  370. }
  371. // now load the last sequence in the file
  372. tripal_feature_fasta_loader_insert_feature($name,$uname,$db_id,
  373. $accession,$subject,$rel_type,$parent_type,$library_id,$organism_id,$type,
  374. $source,$residues,$update,$re_name);
  375. return '';
  376. }
  377. /**
  378. *
  379. *
  380. * @ingroup fasta_loader
  381. */
  382. function tripal_feature_fasta_loader_insert_feature($name,$uname,$db_id,$accession,
  383. $parent,$rel_type,$parent_type,$library_id,$organism_id,$type,
  384. $source,$residues,$update,$re_name)
  385. {
  386. $previous_db = tripal_db_set_active('chado');
  387. // first get the type for this sequence
  388. $cvtermsql = "SELECT CVT.cvterm_id
  389. FROM {cvterm} CVT
  390. INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
  391. LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
  392. WHERE cv.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
  393. $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$type,$type));
  394. if(!$cvterm){
  395. print "ERROR: cannot find the term type: '$type'\n";
  396. return 0;
  397. }
  398. // check to see if this feature already exists
  399. $feature_sql = "SELECT * FROM {feature}
  400. WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
  401. $feature = db_fetch_object(db_query($feature_sql,$organism_id,$uname,$cvterm->cvterm_id));
  402. if(!$feature){
  403. // now insert the feature
  404. $sql = "INSERT INTO {feature} (organism_id, name, uniquename, residues, seqlen, md5checksum,type_id,is_analysis,is_obsolete)
  405. VALUES(%d,'%s','%s','%s',%d, '%s', %d, %s, %s)";
  406. $result = db_query($sql,$organism_id,$name,$uname,$residues,strlen($residues),
  407. md5($residues),$cvterm->cvterm_id,'false','false');
  408. if(!$result){
  409. print "ERROR: failed to insert feature '$name ($uname)'\n";
  410. return 0;
  411. } else {
  412. print "Inserted feature $name ($uname)\n";
  413. }
  414. } else {
  415. if($update){
  416. // we do not want to wipe out the name if the user did not intend for this to
  417. // happen. The uniquename must match the sequence but the name may not.
  418. // so, we'll only update the name if the users specified an 're_name' regular
  419. // expression.
  420. if($re_name){
  421. $sql = "UPDATE {feature}
  422. SET name = '%s', residues = '%s', seqlen = '%s', md5checksum = '%s'
  423. WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
  424. $result = db_query($sql,$name,$residues,strlen($residues),md5($residues),$organism_id,$uname,$cvterm->cvterm_id);
  425. } else {
  426. $sql = "UPDATE {feature}
  427. SET residues = '%s', seqlen = '%s', md5checksum = '%s'
  428. WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
  429. $result = db_query($sql,$residues,strlen($residues),md5($residues),$organism_id,$uname,$cvterm->cvterm_id);
  430. }
  431. if(!$result){
  432. print "ERROR: failed to update feature '$name ($uname)'\n";
  433. return 0;
  434. } else {
  435. print "Updated feature $name ($uname)\n";
  436. }
  437. } else {
  438. print "WARNING: feature already exists, skipping: '$name ($uname)'\n";
  439. }
  440. }
  441. // now get the feature
  442. $feature = db_fetch_object(db_query($feature_sql,$organism_id,$uname,$cvterm->cvterm_id));
  443. if(!$feature){
  444. print "Something bad has happened: $organism_id, $uname, $cvterm->cvterm_id\n";
  445. return 0;
  446. }
  447. // now add the database cross reference
  448. if($db_id){
  449. // check to see if this accession reference exists, if not add it
  450. $dbxrefsql = "SELECT * FROM {dbxref} WHERE db_id = %s and accession = '%s'";
  451. $dbxref = db_fetch_object(db_query($dbxrefsql,$db_id,$accession));
  452. if(!$dbxref){
  453. $sql = "INSERT INTO {dbxref} (db_id,accession) VALUES (%d,'%s')";
  454. $result = db_query($sql,$db_id,$accession);
  455. if(!$result){
  456. print "WARNING: could not add external database acession: '$name accession: $accession'\n";
  457. }
  458. $dbxref = db_fetch_object(db_query($dbxrefsql,$db_id,$accession));
  459. }
  460. // check to see if the feature dbxref record exists if not, then add it
  461. $fdbxrefsql = "SELECT * FROM {feature_dbxref} WHERE feature_id = %d and dbxref_id = %d";
  462. $fdbxref = db_fetch_object(db_query($fdbxrefsql,$feature->feature_id,$dbxref->dbxref_id));
  463. if(!$fdbxref){
  464. $sql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) VALUES (%d,%d)";
  465. $result = db_query($sql,$feature->feature_id,$dbxref->dbxref_id);
  466. if(!$result){
  467. print "WARNING: could not associate database cross reference with feature: '$name accession: $accession'\n";
  468. } else {
  469. print "Added database crossreference $name ($uname) -> $accession\n";
  470. }
  471. }
  472. }
  473. // now add in the relationship if one exists. First, get the parent type for the relationship
  474. // then get the parent feature
  475. if($rel_type){
  476. $parentcvterm = db_fetch_object(db_query($cvtermsql,'sequence',$parent_type,$parent_type));
  477. $relcvterm = db_fetch_object(db_query($cvtermsql,'relationship',$rel_type,$rel_type));
  478. $parent_feature = db_fetch_object(db_query($feature_sql,$organism_id,$parent,$parentcvterm->cvterm_id));
  479. if($parent_feature){
  480. // check to see if the relationship already exists
  481. $sql = "SELECT * FROM {feature_relationship} WHERE subject_id = %d and object_id = %d and type_id = %d";
  482. $rel = db_fetch_object(db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id));
  483. if($rel){
  484. print "WARNING: relationship already exists, skipping '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
  485. } else {
  486. $sql = "INSERT INTO {feature_relationship} (subject_id,object_id,type_id)
  487. VALUES (%d,%d,%d)";
  488. $result = db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id);
  489. if(!$result){
  490. print "WARNING: failed to insert feature relationship '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
  491. } else {
  492. print "Inserted relationship relationship: '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
  493. }
  494. }
  495. }
  496. else {
  497. print "WARNING: cannot establish relationship '$uname' ($type) $rel_type '$parent' ($parent_type): Cannot find the parent\n";
  498. }
  499. }
  500. tripal_db_set_active($previous_db);
  501. }