PageRenderTime 68ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/CXGN/Marker/Location.pm

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