Index: trunk/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm
===================================================================
--- trunk/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 27139)
+++ trunk/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 27140)
@@ -19,4 +19,6 @@
 our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
 
+
+use PS::IPP::PStamp::RequestFile qw( :standard );
 
 use IPC::Cmd 0.36 qw( can_run run );
@@ -43,9 +45,10 @@
     my $mjd_max  = shift;
     my $filter   = shift;
-    my $data_group    = shift;
+    my $data_group = shift;
     my $verbose  = shift;
 
 
     # we die in response to bad data in request files
+    # The wrapper script is responsible for updating the database
     die "Unknown req_type: $req_type" 
         if ($req_type ne "byid") and
@@ -65,4 +68,15 @@
     if (isnull($tess_id)) {
         $tess_id = undef;
+    }
+    if (isnull($data_group)) {
+        $data_group = undef;
+    }
+    if (isnull($filter)) {
+        $filter = undef;
+    }
+
+    if ($req_type eq "bycoord") {
+        my $results = lookup_bycoord($ipprc, $image_db, $img_type, $tess_id, $component, $need_magic, $x, $y, $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
+        return $results;
     }
 
@@ -113,11 +127,4 @@
             return undef;
         }
-
-    } elsif ($req_type eq "bycoord") {
-
-        if ($img_type eq "stack") {
-            print STDERR "Error bycoord lookup not implemented for stage stack yet\n";
-            return undef;
-        }
     } elsif ($req_type eq "byskycell") {
         if (($img_type eq "raw") or ($img_type eq "chip")) {
@@ -131,5 +138,6 @@
     }
 
-    my $results = lookup($ipprc, $image_db, $req_type, $img_type, $id, $tess_id, $component, $need_magic, $x, $y, $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
+    my $results = lookup($ipprc, $image_db, $req_type, $img_type, $id, $tess_id, $component, $need_magic,
+        $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
 
     return $results;
@@ -146,6 +154,4 @@
     my $component= shift;
     my $need_magic = shift;
-    my $x        = shift;
-    my $y        = shift;
     my $dateobs_begin = shift;
     my $dateobs_end   = shift;
@@ -267,10 +273,4 @@
         die "tess_id and component are required for byskycell" if !$tess_id or ! $skycell_id;
         $command .= " -tess_id $tess_id -skycell_id $skycell_id";
-    } elsif ($req_type eq "bycoord") {
-        $command .= " -radius 3.0 -ra $x -decl $y";
-        if ($img_type ne "raw") {
-            $command .= " -tess_id $tess_id" if $tess_id;
-            $command .= " -skycell_id $skycell_id" if $skycell_id;
-        }
     } else {
         die "Unknown req_type supplied: $req_type";
@@ -515,15 +515,23 @@
 
 sub lookup_bycoord {
-    my $ipprc    = shift;
-    my $image_db = shift;
-    my $x        = shift;
-    my $y        = shift;
+    my $ipprc      = shift;
+    my $image_db   = shift;
+    my $img_type   = shift;
+    my $tess_id    = shift;
+    my $component  = shift;
+    my $need_magic = shift;
+    my $ra         = shift;
+    my $dec        = shift;
     my $dateobs_begin  = shift;
-    my $dateobs_end  = shift;
-    my $filter   = shift;
-    my $verbose  = shift;
+    my $dateobs_end    = shift;
+    my $filter     = shift;
+    my $data_group = shift;
+    my $verbose    = shift;
 
     my $missing_tools;
     my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    my $camtool = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
+    my $dvoImagesAtCoords = can_run('dvoImagesAtCoords') or (warn "Can't find dvoImagesAtCoords" and $missing_tools = 1);
+    my $whichimage = can_run('whichimage') or (warn "Can't find whichimage" and $missing_tools = 1);
     if ($missing_tools) {
         warn("Can't find required tools.");
@@ -531,50 +539,228 @@
     }
 
-    # XXX TODO:
-    # Full lookups by coordinate require looking at the DVO database. However, 
-    # dateobs and filter which is all that MOPS has asked for.
-
-    my $args;
-    if (!isnull($dateobs_begin)) {
-        $args .= " -dateobs_begin $dateobs_begin";
-    }
-    if (!isnull($dateobs_end)) {
-        $args .= " -dateobs_end $dateobs_end";
-    }
-    if (!isnull($filter)) {
-        $args .= " -filter $filter";
-    }
-
-    if (!$args) {
-        # avoid returning every exposure in the DB
-        print STDERR "no query arguments provided for bycoord\n";
-        return undef;
-    }
-
-    # XXX TODO: This query doesn't work Something appears to be out of
-    # sync about the times I'm passing and what regtool and or the DB expect
-    # the query I used in the C version of locateimages used
-    # WHERE dateobs >= FROM_UNIXTIME(%ld) AND dateobs <= FROM_UNIXTIME(%ld + exp_time)
-
-    my $command = "$regtool -processedexp -dbname $image_db $args";
-
-    # run the tool and parse the output
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-                run(command => $command, verbose => $verbose);
-    unless ($success) {
-        # not sure if we should die here
-        print STDERR @$stderr_buf;
-        return undef;
-    }
-    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
-
-    my $output = join "", @$stdout_buf;
-    if (!$output) {
-        print STDERR "no output returned from $command\n" if $verbose;
-        return undef;
-    }
-    my $images = parse_md_fast($mdcParser, $output) or die ("Unable to parse metadata config doc");
-
-    return $images;
+    my $results = ();
+    if (($img_type eq "raw") or ($img_type eq "chip")) {
+
+        my $runs = lookup_runs_by_camid_and_coords($ipprc, $image_db, $img_type,
+            $ra, $dec, $need_magic, $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
+
+        foreach my $run (@$runs) {
+            next if $component and ($run->{component} ne $component);
+            my $these_results = lookup($ipprc, $image_db, "byid", $img_type, $run->{id},
+                $tess_id, $run->{component}, $need_magic, 
+                $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
+
+            next if !$these_results;
+            push @$results, @$these_results;
+        }
+        
+    } else {
+        # this should be checked elsewhere
+        die "unexpected image type $img_type" if ($img_type ne "warp") 
+                                              and ($img_type ne "stack") and ($img_type ne "diff");
+
+        my $skycells = lookup_skycell_by_coords($ipprc, $tess_id, $component, $ra, $dec, $verbose);
+
+        foreach my $skycell (@$skycells) {
+            my $these_results = lookup($ipprc, $image_db, "byskycell", $img_type, undef,
+                $skycell->{tess_id}, $skycell->{component}, $need_magic, 
+                $dateobs_begin, $dateobs_end, $filter, $data_group, $verbose);
+
+            next if !$these_results;
+            push @$results, @$these_results;
+        }
+    }
+
+    return $results;
+}
+
+# lookup_runs_by_camid_and_coords
+# given an ra, dec, and optionally other paramters, find camera runs for exposures
+# that are within some distance of the coordinates
+sub lookup_runs_by_camid_and_coords {
+    my $ipprc      = shift;
+    my $image_db   = shift;
+    my $img_type   = shift;
+    my $ra         = shift;
+    my $dec        = shift;
+    my $need_magic = shift;
+    my $dateobs_begin  = shift;
+    my $dateobs_end = shift;
+    my $filter     = shift;
+    my $data_group = shift;
+    my $verbose    = shift;
+
+    my $camruns;
+    my $missing_tools;
+    my $camtool = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
+    my $dvoImagesAtCoords = can_run('dvoImagesAtCoords') or (warn "Can't find dvoImagesAtCoords" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    {
+        my $command = "$camtool -dbname $image_db -processedexp";
+           $command .= " -ra $ra -decl $dec -radius 3";
+           $command .= " -dateobs_begin $dateobs_begin" if $dateobs_begin;
+           $command .= " -dateobs_end   $dateobs_end"   if $dateobs_end  ;
+           $command .= " -filter $filter" if $filter;
+           # NOTE: we are applying the data_group to the camera run.
+           # If we're looking for chip stage images there is no guarentee that 
+           # chipRun.data_group = camRun.data_group. In practice this is almost
+           # always the case. If this turns out to be a problem we can defer
+           # the data_group test to when we look up the chipProcessedImfiles
+           $command .= " -data_group $data_group" if $data_group;
+           $command .= " -destreaked" if $need_magic;
+        # run the tool and parse the output
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+        unless ($success) {
+            print STDERR @$stderr_buf;
+            return undef;
+        }
+        my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+        my $output = join "", @$stdout_buf;
+        if (!$output) {
+            print STDERR "no output returned from $command\n" if $verbose;
+            return undef;
+        }
+        $camruns = parse_md_fast($mdcParser, $output);
+    }
+    if (!$camruns) {
+        return undef;
+    }
+    my $runs;
+    foreach my $camRun (@$camruns) {
+        next if $camRun->{quality};
+        next if $camRun->{fault};
+        # XXX Use file rule
+        my $astrom = $camRun->{path_base} . ".smf";
+        my $astrom_resolved = $ipprc->file_resolve($astrom);
+        next if !$astrom_resolved;
+
+        my $command = "$dvoImagesAtCoords -astrom $astrom_resolved $ra $dec";
+        # run the tool and parse the output
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+        unless ($success) {
+            my $result_code = $error_code >> 8;
+            if ($result_code == $PSTAMP_NO_OVERLAP) {
+                print STDERR "no overlap for $astrom\n" if $verbose;
+                next;
+            }
+            # unexpected result code
+            die "unexpected result code: $result_code from $command\n";
+        }
+
+        my $output = join "", @$stdout_buf;
+        if (!$output) {
+            # this shouldn't happen when dvoImagesAtCoord exits with zero status
+            die "no output returned from $command\n";
+        }
+
+        # dvoImagesAtCoords should return a single line
+        # rownum ra dec class_id
+        my @lines = split "\n", $output;
+        my $n = scalar @lines;
+        if ($n != 1) {
+            print STDERR "unexpected number of lines returned by dvoImagesAtCoords: $n\n";
+
+            # XXX: There is a bug in dvo where each component is listed twice
+            # When that gets fixed remove the conditional and just die
+            die "unexpected number lines returned by dvoImagesAtCoords: $n"
+                if ($n != 2) or ($lines[0] ne $lines[1]);
+        }
+
+        my (undef, $ra_out, $dec_out, $class_id) = split " ", $lines[0];
+        if (!$class_id) {
+            die "unexpected output from dvoImagesAtCoords: $lines[0]";
+        }
+        # build the hash to return
+        my $run = {
+            exp_id    => $camRun->{exp_id},
+            exp_name  => $camRun->{exp_name},
+            chip_id   => $camRun->{chip_id},
+            cam_id    => $camRun->{cam_id},
+            astrom    => $astrom_resolved,
+            class_id  => $class_id,
+            component => $class_id
+        };
+        if ($img_type eq "chip") {
+            $run->{id} = $run->{chip_id};
+        } else {
+            $run->{id} = $run->{exp_id};
+        }
+        push @$runs, $run;
+    }
+
+    return $runs;
+}
+
+sub lookup_skycell_by_coords {
+    my $ipprc      = shift;
+    my $requested_tess_id    = shift;
+    my $requested_skycell  = shift;
+    my $ra         = shift;
+    my $dec        = shift;
+    my $verbose    = shift;
+
+    my $missing_tools;
+    my $whichimage = can_run('whichimage') or (warn "Can't find whichimage" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    $requested_tess_id = "" if isnull($requested_tess_id);
+    $requested_skycell = "" if isnull($requested_skycell);
+
+    my @lines;
+    {
+        my $command = "$whichimage $ra $dec";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+        unless ($success) {
+            print STDERR @$stderr_buf;
+            return undef;
+        }
+
+        my $output = join "", @$stdout_buf;
+        if (!$output) {
+            print STDERR "no output returned from $command\n" if $verbose;
+            return undef;
+        }
+        @lines = split "\n", $output;
+        if (!scalar @lines) {
+            # can this happen? if $output is not null?
+            print STDERR "no output returned from $command\n" if $verbose;
+            return undef;
+        }
+    }
+    my $runs;
+    foreach my $line (@lines) {
+        my ($ra_out, $dec_out, $tess_id, $skycell_id) = split " ", $line;
+        die "unexpected output from whichimage" if !$tess_id or !$skycell_id;
+
+        if ($requested_tess_id) {
+            next if ($requested_tess_id ne $tess_id);
+        } else {
+            # skip these obsolete tesselations unless they were explicitly asked for
+            # should I do this?
+            next if $tess_id eq "FIXNS";
+            next if $tess_id eq "ALLSKY";
+        }
+        next if $requested_skycell and ($skycell_id ne $requested_skycell);
+
+        # build the hash to return
+        my $run = {
+            tess_id   => $tess_id,
+            component => $skycell_id,
+        };
+        push @$runs, $run;
+    }
+
+    return $runs;
 }
 
@@ -707,5 +893,5 @@
     my $val = shift;
 
-    return (!defined($val) or (lc($val) eq "null"));
+    return (!defined($val) or ($val eq "") or (lc($val) eq "null"));
 }
 
Index: trunk/pstamp/scripts/pstampparse.pl
===================================================================
--- trunk/pstamp/scripts/pstampparse.pl	(revision 27139)
+++ trunk/pstamp/scripts/pstampparse.pl	(revision 27140)
@@ -218,4 +218,5 @@
     }
 
+    # user requested us to search all components. Set to ""
     $search_component = "" if $search_component eq "all";
 
@@ -265,10 +266,4 @@
             print STDERR "center must be specified in sky coordintes for bycoord" if $verbose;
             insertFakeJobForRow($row, 1, $PSTAMP_INVALID_REQUEST);
-            $num_jobs++;
-            next;
-        }
-        if ($stage eq "stack") {
-            print STDERR "lookup bycoord is not yet implemented for stage stack" if $verbose;
-            insertFakeJobForRow($row, 1, $PSTAMP_NOT_IMPLEMENTED);
             $num_jobs++;
             next;
