PageRenderTime 59ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/CXGN/Marker/Location.pm

https://github.com/sheenams/cxgn-corelibs
Perl | 452 lines | 413 code | 37 blank | 2 comment | 27 complexity | d6a494c23fc1d1db0eac4220a7db31b7 MD5 | raw file
  1. =head1 NAME
  2. CXGN::Marker::Location;
  3. =head1 AUTHOR
  4. John Binns <zombieite@gmail.com>
  5. =head1 DESCRIPTION
  6. Location object. It's a very simple match to the marker_location table in the database, but it has a little bit of intelligence too.
  7. =cut
  8. package CXGN::Marker::Location;
  9. use strict;
  10. use CXGN::Marker::Tools;
  11. use CXGN::DB::Connection;
  12. use CXGN::Tools::Text;
  13. use Carp;
  14. =head2 new
  15. my $location=CXGN::Marker::Location->new($dbh,$location_id);
  16. Takes a dbh and a location_id and returns an object representing little more than a row in the marker_location table.
  17. my $location=CXGN::Marker::Location->new($dbh);
  18. Takes a dbh and returns an empty object which can perform an insert into the marker_location table.
  19. =cut
  20. sub new
  21. {
  22. my $class=shift;
  23. my($dbh,$id)=@_;
  24. unless(CXGN::DB::Connection::is_valid_dbh($dbh))
  25. {
  26. croak"Invalid DBH";
  27. }
  28. my $self=bless({},$class);
  29. $self->{dbh}=$dbh;
  30. if($id)
  31. {
  32. my $q=$dbh->prepare
  33. ('
  34. select
  35. marker_id,
  36. location_id,
  37. lg_id,
  38. lg_name,
  39. marker_location.map_version_id,
  40. position,
  41. confidence_id,
  42. confidence_name as confidence,
  43. subscript
  44. from
  45. marker_experiment
  46. inner join marker_location using (location_id)
  47. inner join linkage_group using (lg_id)
  48. inner join marker_confidence using (confidence_id)
  49. where
  50. location_id=?
  51. ');
  52. $q->execute($id);
  53. my $hr=$q->fetchrow_hashref();
  54. while(my($key,$value)=each %$hr)
  55. {
  56. $self->{$key}=$value;
  57. }
  58. }
  59. return $self;
  60. }
  61. =head2 location_id
  62. my $id=$location->location_id();
  63. Gets location ID. Cannot set it since it is either retrieved from the database or sent in to the constructor.
  64. =cut
  65. #this is not a setter, since these ids are assigned by the database
  66. sub location_id
  67. {
  68. my $self=shift;
  69. return $self->{location_id};
  70. }
  71. =head2 marker_id, lg_name, map_version_id, position, confidence, subscript
  72. Getters/setters.
  73. =cut
  74. sub marker_id
  75. {
  76. my $self=shift;
  77. my($value)=@_;
  78. if($value) {
  79. unless($value=~/^\d+$/) {
  80. croak"Marker ID must be a number, not '$value'";
  81. }
  82. unless(CXGN::Marker::Tools::is_valid_marker_id($self->{dbh},$value)) {
  83. croak"Marker ID '$value' does not exist in the database";
  84. }
  85. $self->{marker_id}=$value;
  86. }
  87. return $self->{marker_id};
  88. }
  89. sub lg_name {
  90. my $self=shift;
  91. my($lg_name)=@_;
  92. if($lg_name) {
  93. unless($self->{map_version_id}) {
  94. croak"You must set this object's map_version_id before throwing around lg_names like that, else how can it know what map_version those lg_names are on?";
  95. }
  96. my $lg_id=CXGN::Marker::Tools::get_lg_id($self->{dbh},$lg_name,$self->{map_version_id});
  97. unless($lg_id)
  98. {
  99. croak"Linkage group '$lg_name' does not exist on map_version_id '$self->{map_version_id}'";
  100. }
  101. $self->{lg_id}=$lg_id;
  102. $self->{lg_name}=$lg_name;
  103. }
  104. return $self->{lg_name};
  105. }
  106. sub lg_id {
  107. my $self = shift;
  108. my $lg_id = shift;
  109. if ($lg_id) {
  110. unless ($self->{map_version_id}) {
  111. croak "You must set map_version_id before trying to set lg_id. Thanks!\n";
  112. }
  113. $self->{lg_id}=$lg_id;
  114. }
  115. return $self->{lg_id};
  116. }
  117. sub map_version_id
  118. {
  119. my $self=shift;
  120. my($map_version_id)=@_;
  121. if($map_version_id)
  122. {
  123. unless($map_version_id=~/^\d+$/)
  124. {
  125. croak"Map version ID must be an integer, not '$map_version_id'";
  126. }
  127. $self->{map_version_id}=$map_version_id;
  128. }
  129. return $self->{map_version_id};
  130. }
  131. sub position {
  132. my $self=shift;
  133. my($position)=@_;
  134. if ($self->{position} =~ /\-/) { # if position describes a range, such as a QTL
  135. print STDERR "RANGE DETECTED ($self->{position})\n";
  136. ($self->{position_north}, $self->{position_south}) = split "-", $self->{position};
  137. $self->{position} = ($self->{position_south} + $self->{position_north})/2;
  138. }
  139. if(defined($position)) {
  140. unless(CXGN::Tools::Text::is_number($position)) {
  141. print STDERR "Position must be a floating-point number, not '$position'";
  142. }
  143. $self->{position}=$position;
  144. }
  145. return $self->{position};
  146. }
  147. sub confidence {
  148. my $self=shift;
  149. my($confidence)=@_;
  150. if($confidence)
  151. {
  152. my $confidence_id;
  153. $confidence_id=CXGN::Marker::Tools::get_marker_confidence_id($self->{dbh},$confidence);
  154. unless(defined($confidence_id))
  155. {
  156. croak"Confidence ID not found for confidence '$confidence'";
  157. }
  158. $self->{confidence_id}=$confidence_id;
  159. $self->{confidence}=$confidence;
  160. }
  161. return $self->{confidence};
  162. }
  163. sub subscript
  164. {
  165. my $self=shift;
  166. my($subscript)=@_;
  167. if($subscript)
  168. {
  169. $subscript=uc($subscript);
  170. unless($subscript=~/^[ABC]$/)
  171. {
  172. croak"Subscript must be a 'A', 'B', or 'C', not '$subscript'";
  173. }
  174. $self->{subscript}=$subscript;
  175. }
  176. return $self->{subscript};
  177. }
  178. =head2 equals
  179. if($location1->equals($location2)){print"Location 1 and 2 are the same.";}
  180. Takes another location object and tells you if it is equivalent to the first location object.
  181. =cut
  182. sub equals
  183. {
  184. my $self=shift;
  185. my($other)=@_;
  186. if
  187. (
  188. $self->{marker_id}==$other->{marker_id}
  189. and $self->{lg_id}==$other->{lg_id}
  190. and $self->{map_version_id}==$other->{map_version_id}
  191. and $self->{position}==$other->{position}
  192. and $self->{confidence} eq $other->{confidence}
  193. and $self->{subscript} eq $other->{subscript}
  194. )
  195. {
  196. return 1;
  197. }
  198. return 0;
  199. }
  200. =head2 exists
  201. if($location->exists()){print"Location exists in database.";}
  202. Returns its location_id if location is already in the database, or undef if not. Mainly used by store_unless_exists.
  203. =cut
  204. sub exists
  205. {
  206. my $self=shift;
  207. unless($self->{marker_id})
  208. {
  209. croak"Cannot test for a location's existence without knowing which marker it goes with--store marker and set experiment's marker ID before storing locations";
  210. }
  211. unless($self->{lg_id})
  212. {
  213. croak"You really should have an lg_id set before testing for a location's existence";
  214. }
  215. unless($self->{map_version_id})
  216. {
  217. croak"You really should have a map_version_id set before testing for a location's existence";
  218. }
  219. unless(defined($self->{position}))
  220. {
  221. croak"You really should have a position set before testing for a location's existence";
  222. }
  223. unless(defined($self->{confidence_id}))
  224. {
  225. croak"You really should have a confidence_id set before testing for a location's existence";
  226. }
  227. if($self->{location_id})
  228. {
  229. #warn"I think it's pretty obvious that this location exists, since it seems to have been loaded from the database, or recently stored to the database--it already has an id of $self->{location_id}";
  230. return $self->{location_id};
  231. }
  232. my $dbh=$self->{dbh};
  233. my $q;
  234. $q=$dbh->prepare
  235. ('
  236. select
  237. distinct location_id
  238. from
  239. marker_location
  240. inner join marker_experiment using (location_id)
  241. where
  242. marker_id=?
  243. and lg_id=?
  244. and marker_location.map_version_id=?
  245. and position=?
  246. and confidence_id=?
  247. and not(subscript is distinct from ?)
  248. ');
  249. $q->execute($self->{marker_id},$self->{lg_id},$self->{map_version_id},$self->{position},$self->{confidence_id},$self->{subscript});
  250. my %found_location_ids;#a place to keep all location IDs that match for this marker, for use in error checking in a moment
  251. my($location_id)=$q->fetchrow_array();
  252. if($location_id)#if we found some matching locations for this marker
  253. {
  254. $self->{location_id}=$location_id;#get the ID of the existing row in the database so we know we've already been stored
  255. $found_location_ids{$location_id}=1;#make a note of the location ID found
  256. while(my($other_location_id)=$q->fetchrow_array())#grab all other location IDs
  257. {
  258. $found_location_ids{$other_location_id}=1;
  259. }
  260. if(keys(%found_location_ids)>1)#if we found more than one matching location ID, then the database data is not how we expect it to be
  261. {
  262. die"Multiple locations found like\n".$self->as_string()."Locations found: ".CXGN::Tools::Text::list_to_string(keys(%found_location_ids));
  263. }
  264. return $self->{location_id};
  265. }
  266. return;
  267. }
  268. =head2 exists_with_any_confidence
  269. Checks to see if a location exists, but not knowing its confidence. Used by CAPS loading scripts which know which location
  270. the PCR experiment maps to, but they do not know the confidence.
  271. $loc->exists_with_any_confidence() or die"Could not find location:\n".$loc->as_string()."in database--load locations first, before running this script";
  272. =cut
  273. sub exists_with_any_confidence
  274. {
  275. my $self=shift;
  276. unless($self->{marker_id})
  277. {
  278. croak"Cannot test for a location's existence without knowing which marker it goes with--store marker and set experiment's marker ID before storing locations";
  279. }
  280. unless($self->{lg_id})
  281. {
  282. croak"You really should have an lg_id set before testing for a location's existence";
  283. }
  284. unless($self->{map_version_id})
  285. {
  286. croak"You really should have a map_version_id set before testing for a location's existence";
  287. }
  288. unless(defined($self->{position}))
  289. {
  290. croak"You really should have a position set before testing for a location's existence";
  291. }
  292. if(defined($self->{confidence_id}))
  293. {
  294. croak"You have a confidence_id set--why not just use the 'exists' function instead?";
  295. }
  296. if($self->{location_id})
  297. {
  298. #warn"I think it's pretty obvious that this location exists, since it seems to have been loaded from the database, or recently stored to the database--it already has an id of $self->{location_id}";
  299. return $self->{location_id};
  300. }
  301. my $dbh=$self->{dbh};
  302. my $q;
  303. $q=$dbh->prepare
  304. ('
  305. select
  306. distinct location_id
  307. from
  308. marker_location
  309. inner join marker_experiment using (location_id)
  310. where
  311. marker_id=?
  312. and lg_id=?
  313. and map_version_id=?
  314. and position=?
  315. and not(subscript is distinct from ?)
  316. ');
  317. $q->execute($self->{marker_id},$self->{lg_id},$self->{map_version_id},$self->{position},$self->{subscript});
  318. my %found_location_ids;#a place to keep all location IDs that match for this marker, for use in error checking in a moment
  319. my($location_id)=$q->fetchrow_array();
  320. if($location_id)#if we found some matching locations for this marker
  321. {
  322. $self->{location_id}=$location_id;#get the ID of the existing row in the database so we know we've already been stored
  323. $found_location_ids{$location_id}=1;#make a note of the location ID found
  324. while(my($other_location_id)=$q->fetchrow_array())#grab all other location IDs
  325. {
  326. $found_location_ids{$other_location_id}=1;
  327. }
  328. if(keys(%found_location_ids)>1)#if we found more than one matching location ID, then the database data is not how we expect it to be
  329. {
  330. die"Multiple locations found like\n".$self->as_string()."Locations found: ".CXGN::Tools::Text::list_to_string(keys(%found_location_ids));
  331. }
  332. return $self->{location_id};
  333. }
  334. return;
  335. }
  336. =head2 store_unless_exists
  337. my $location_id,$existing_location_id,$new_location_id;
  338. $location_id=$new_location_id=$location->store_unless_exists();
  339. unless($location_id)
  340. {
  341. $location_id=$existing_location_id=$location->location_id();
  342. }
  343. Makes a database insert unless a similar row exists. Returns a location_id ONLY if a new insert was made. If a matching entry was found, location_id is now set, but not returned.
  344. =cut
  345. sub store_unless_exists
  346. {
  347. my $self=shift;
  348. if($self->exists()){return;}
  349. unless($self->{lg_id})
  350. {
  351. croak"No lg_id set";
  352. }
  353. unless($self->{map_version_id})
  354. {
  355. croak"No map_version_id set";
  356. }
  357. unless(defined($self->{position}))
  358. {
  359. croak"No position set";
  360. }
  361. unless(defined($self->{confidence_id}))
  362. {
  363. croak"No confidence set";
  364. }
  365. my $dbh=$self->{dbh};
  366. my $statement='insert into sgn.marker_location (lg_id,map_version_id,position,confidence_id,subscript, position_north, position_south) values (?,?,?,?,?,?,?)';
  367. my @values=($self->{lg_id},$self->{map_version_id},$self->{position},$self->{confidence_id},$self->{subscript}, $self->{position_north}, $self->{position_south});
  368. my $q=$dbh->prepare($statement);
  369. #print STDERR "$statement; (@values)\n";
  370. $q->execute(@values);
  371. $self->{location_id}=$dbh->last_insert_id('marker_location') or croak"Can't find last insert id for location ".$self->as_string();
  372. return($self->{location_id});
  373. }
  374. =head2 as_string
  375. print $location->as_string();
  376. Prints a location string for debugging.
  377. =cut
  378. sub as_string
  379. {
  380. my $self=shift;
  381. my $string="<location>\n";
  382. $string.="\tmarker_id: '$self->{marker_id}'\tsubscript: '$self->{subscript}'\n";
  383. $string.="\tlg_name: '$self->{lg_name}'\tlg_id: '$self->{lg_id}'\tposition: '$self->{position}'\n";
  384. $string.="\tlocation_id: '$self->{location_id}'\tmap_version_id: '$self->{map_version_id}'\n";
  385. $string.="\tconfidence: '$self->{confidence}'\tconfidence_id: '$self->{confidence_id}'\n";
  386. $string.="</location>\n";
  387. return $string;
  388. }
  389. 1;