Index: branches/pap/ippTools/src/Makefile.am
===================================================================
--- branches/pap/ippTools/src/Makefile.am	(revision 28483)
+++ branches/pap/ippTools/src/Makefile.am	(revision 28484)
@@ -26,5 +26,6 @@
 	warptool \
 	receivetool \
-	pubtool
+	pubtool \
+	diffphottool
 
 pkginclude_HEADERS = \
@@ -71,5 +72,6 @@
 	staticskytool.h \
 	warptool.h \
-	pubtool.h
+	pubtool.h \
+	diffphottool.h
 
 lib_LTLIBRARIES = libpxtools.la
@@ -272,4 +274,10 @@
     pubtoolConfig.c
 
+diffphottool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+diffphottool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+diffphottool_SOURCES = \
+    diffphottool.c \
+    diffphottoolConfig.c
+
 clean-local:
 	-rm -f TAGS
Index: branches/pap/ippTools/src/addtool.c
===================================================================
--- branches/pap/ippTools/src/addtool.c	(revision 28483)
+++ branches/pap/ippTools/src/addtool.c	(revision 28484)
@@ -40,4 +40,15 @@
 static bool maskedMode(pxConfig *config);
 static bool unblockMode(pxConfig *config);
+static bool addminidvodbrunMode(pxConfig *config);
+static bool updateminidvodbrunMode(pxConfig *config);
+static bool listminidvodbrunMode(pxConfig *config);
+static bool flipminidvodbrunMode(pxConfig *config);
+static bool checkminidvodbrunaddrunMode(pxConfig *config);
+static bool addminidvodbprocessedMode(pxConfig *config);
+static bool listminidvodbprocessedMode(pxConfig *config);
+static bool revertminidvodbprocessedMode(pxConfig *config);
+static bool updateminidvodbprocessedMode(pxConfig *config);
+
+
 
 # define MODECASE(caseName, func) \
@@ -69,4 +80,14 @@
         MODECASE(ADDTOOL_MODE_MASKED,               maskedMode);
         MODECASE(ADDTOOL_MODE_UNBLOCK,              unblockMode);
+        MODECASE(ADDTOOL_MODE_ADDMINIDVODBRUN,      addminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_UPDATEMINIDVODBRUN,   updateminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_LISTMINIDVODBRUN,     listminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_FLIPMINIDVODBRUN,     flipminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN, checkminidvodbrunaddrunMode);
+        MODECASE(ADDTOOL_MODE_ADDMINIDVODBPROCESSED,addminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_LISTMINIDVODBPROCESSED,listminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,revertminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED,updateminidvodbprocessedMode);
+
         default:
             psAbort("invalid option (this should not happen)");
@@ -102,4 +123,5 @@
     PXOPT_COPY_STR(config->args, where,  "-reduction", "camRun.reduction", "==");
 
+
     if (!psListLength(where->list)) {
         psFree(where);
@@ -114,8 +136,13 @@
     PXOPT_LOOKUP_STR(reduction,   config->args, "-set_reduction", false, false);
     PXOPT_LOOKUP_STR(note,        config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_name,  config->args, "-set_minidvodb_name", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-set_minidvodb_group", false, false);
     PXOPT_LOOKUP_BOOL(image_only, config->args, "-image_only", false);
+    PXOPT_LOOKUP_BOOL(minidvodb,  config->args, "-set_minidvodb", false);
     PXOPT_LOOKUP_BOOL(destreaked, config->args, "-destreaked", false);
     PXOPT_LOOKUP_BOOL(pretend,    config->args, "-pretend", false);
     PXOPT_LOOKUP_BOOL(simple,     config->args, "-simple", false);
+
+
 
     // find the cam_id of all the exposures that we want to queue up.
@@ -245,8 +272,11 @@
                                dvodb       ? dvodb     : row->dvodb,
                                note        ? note      : NULL,
-                               image_only
+                               image_only,
+                               minidvodb,
+                               minidvodb_group,
+                               minidvodb_name
         )) {
             if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
+                psError(PS_ERR_UNKNOWN, false, "database error sfg");
             }
             psError(PS_ERR_UNKNOWN, false,
@@ -291,5 +321,5 @@
 
     // pxUpdateRun gets parameters from config->args and runs the update query
-    bool result = pxUpdateRun(config, where, &query, "addRun", "add_id", 
+    bool result = pxUpdateRun(config, where, &query, "addRun", "add_id",
         "addProcessedExp", false);
 
@@ -374,4 +404,6 @@
     PXOPT_LOOKUP_STR(path_base,     config->args, "-path_base", false, false);
     PXOPT_LOOKUP_F32(dtime_addstar, config->args, "-dtime_addstar", false, false);
+    PXOPT_LOOKUP_STR(dvodb_path, config->args, "-dvodb_path", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-minidvodb_name", false, false);
     PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
 
@@ -399,4 +431,5 @@
         return false;
     }
+
     psFree(query);
 
@@ -424,4 +457,5 @@
         dtime_addstar,
         path_base,
+        dvodb_path,
         fault
         );
@@ -437,4 +471,22 @@
         return false;
     }
+
+    //if there is a minidvodb_name, set it in addRun (it's not known until it is processed)
+    if (minidvodb_name) {
+      psString setName = NULL;
+      psStringAppend (&setName, "UPDATE addRun set minidvodb_name = '%s' where add_id = %" PRId64, minidvodb_name, row->add_id);
+      if (!p_psDBRunQuery(config->dbh, setName)) {
+        if (!psDBRollback(config->dbh)) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+
+        return false;
+      }
+    }
+
+
+
+
 
     // since there is only one exp per 'new' set addRun.state = 'full'
@@ -522,5 +574,5 @@
 
     if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+      psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
         return false;
@@ -576,6 +628,10 @@
 
     {
-        psString query = pxDataGet("addtool_reset_faulted_runs.sql");
+        psString query = pxDataGet("addtool_revertprocessedexp.sql");
         if (!query) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
             psFree(where);
@@ -602,36 +658,4 @@
         psFree(query);
     }
-
-    {
-        psString query = pxDataGet("addtool_revertprocessedexp.sql");
-        if (!query) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
-            psFree(where);
-            return false;
-        }
-
-        // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
-        if (where && psListLength(where->list)) {
-            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-            psStringAppend(&query, " AND %s", whereClause);
-            psFree(whereClause);
-        }
-
-        if (!p_psDBRunQuery(config->dbh, query)) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(query);
-            psFree(where);
-            return false;
-        }
-        psFree(query);
-    }
     psFree(where);
 
@@ -733,2 +757,818 @@
     return true;
 }
+
+static bool addminidvodbrunMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  // required
+
+  PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-set_minidvodb_group", true, false);
+  PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-set_mergedvodb_path", true, false);
+
+  //optional
+  PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-set_minidvodb_name", false, false);
+  PXOPT_LOOKUP_STR(minidvodb_path, config->args, "-set_minidvodb_path", false, false);
+  PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psString minidvodbpath = "NULL";
+
+  // I don't know how to get around the complaints of minidvodb_path can't be null. this 'fixes' it, but someone smarter can fix it properly.
+  if (minidvodb_path) {
+    minidvodbpath = minidvodb_path;
+  }
+
+  if (!minidvodbRunInsert(config->dbh,
+            0, // job_id
+            minidvodb_name,
+            minidvodb_group,
+            minidvodbpath,
+            mergedvodb_path,
+            "new",
+            0
+            )) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psU64 affected = psDBAffectedRows(config->dbh);
+  if (affected != 1) {
+    psError(PS_ERR_UNKNOWN, false,
+            "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+    return false;
+  }
+
+  psS64 minidvodb_id = psDBLastInsertID(config->dbh);
+  printf("%" PRId64 "\n", minidvodb_id);
+
+
+  if (!minidvodb_name) {
+    psStringAppend(&minidvodb_name, "%s.%" PRIu64,minidvodb_group,minidvodb_id);
+  }
+
+  if (!minidvodb_path) {
+    psStringAppend(&minidvodb_path,"%s/%s",mergedvodb_path,minidvodb_name);
+  }
+
+  if (minidvodb_path) {
+    psStringAppend(&minidvodb_path,"/%s",minidvodb_name);
+  }
+
+
+  psString query = NULL;
+
+  psStringAppend(&query, "UPDATE minidvodbRun SET minidvodb_path = '%s', minidvodb_name = '%s' where minidvodb_id = %" PRIu64";", minidvodb_path, minidvodb_name, minidvodb_id);
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  return true;
+}
+
+static bool updateminidvodbrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id",     "minidvodb_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_name",     "minidvodb_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-state",     "state", "==");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_path", "minidvodb_path", "==");
+    PXOPT_COPY_STR(config->args, where, "-mergedvodb_path", "mergedvodb_path", "==");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_group",     "minidvodb_group", "==");
+
+    PXOPT_LOOKUP_STR(minidvodb_name,  config->args, "-set_minidvodb_name", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_path,  config->args, "-set_minidvodb_path", false, false);
+    PXOPT_LOOKUP_STR(state,  config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(mergedvodb_path,  config->args, "-set_mergedvodb_path", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_group,  config->args, "-set_minidvodb_group", false, false);
+
+
+    if (!psListLength(where->list)) {
+      psFree(where);
+      psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+      return false;
+    }
+
+    psString query = psStringCopy("UPDATE minidvodbRun SET ");
+    int cnt = 0;
+    psString comma = ",";
+    if (minidvodb_name) {
+      psStringAppend(&query, " minidvodb_name = '%s'", minidvodb_name);
+      cnt++;
+    }
+
+    if (minidvodb_path) {
+      if (cnt) {
+          psStringAppend(&query, "%s", comma);
+      }
+
+      psStringAppend(&query, " minidvodb_path = '%s'", minidvodb_path);
+      cnt++;
+    }
+
+    if (state) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " state = '%s'", state);
+      cnt++;
+    }
+
+    if (mergedvodb_path) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " mergedvodb_path = '%s'", mergedvodb_path);
+      cnt++;
+    }
+
+    if (minidvodb_group) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " minidvodb_group = '%s'", minidvodb_group);
+      cnt++;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+
+    psFree(query);
+    psFree(where);
+
+    return true;
+
+}
+
+static bool flipminidvodbrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_LOOKUP_STR(minidvodb_group,  config->args, "-minidvodb_group",true, false);
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group",     "minidvodb_group", "==");
+
+//this flips the new - > active
+// and the active - > waiting in one action
+
+
+// the first query looks to find things that are new and where all the fields are filled (ie, ready to be flipped to active)
+
+// start a transaction eraly so it will contain any row level locks
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psString firstquery = psStringCopy("SELECT * from  minidvodbRun where state = 'new' and minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL and mergedvodb_path IS NOT NULL");
+
+  psString firstwhereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&firstquery, " AND %s", firstwhereClause);
+
+
+
+  if (!p_psDBRunQuery(config->dbh, firstquery)) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    return false;
+  }
+  psFree(firstquery);
+
+ //we don't care what the stuff is that is found, just that there is stuff. This is a check to see that there is something in the 'new' state, before flipping (so that if there is nothing in new, it won't flip the active to waiting.  the flipminidvo is just to make it easy to flip from new -> active.
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("addtool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+
+  //ok, there's something, so flip active -> waiting
+
+  psString query = psStringCopy("UPDATE minidvodbRun SET state = 'waiting' WHERE state = 'active' ");
+
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " AND %s", whereClause);
+
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    return false;
+  }
+
+
+  //now flip new -> active
+  psString query2 = psStringCopy("UPDATE minidvodbRun SET state = 'active' WHERE state = 'new' AND minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL and mergedvodb_path IS NOT NULL ");
+  psStringAppend(&query2, " AND minidvodb_group = '%s' limit 1;", minidvodb_group);
+  if (!p_psDBRunQuery(config->dbh, query2)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psFree(query);
+    return false;
+  }
+
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psFree(query);
+  return false;
+  }
+ psFree(query2);
+ psFree(where);
+
+ return true;
+
+}
+
+
+static bool checkminidvodbrunaddrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  psMetadata *where = psMetadataAlloc();
+
+  //this checks to see if a minidvod_group/name is has completed addRun processing
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbRun.minidvodb_name", "==");
+  PXOPT_COPY_STR(config->args, where, "-state", "minidvodbRun.state", "==");
+
+
+  PXOPT_LOOKUP_U64(limit,      config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(all_addrun_states, config->args, "-all_addrun_states", false);
+  //this doesn't care what state the addRun is in (useful for counting addRuns)
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+  if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+  }
+
+  psString query = pxDataGet("addtool_checkminidvodbaddrun.sql");
+
+  if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+  }
+
+
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+  }
+
+  if (!all_addrun_states) {
+    psStringAppend(&query, " AND (cnt2 = cnt) ");
+  }
+
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, " %s", limitString);
+    psFree(limitString);
+  }
+
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("addtool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  if (!ippdbPrintMetadatas(stdout, output, "minidvodbRun", !simple)) {
+    psError(PS_ERR_UNKNOWN, false, "failed to print array");
+    psFree(output);
+    return false;
+  }
+
+  psFree(output);
+
+  return true;
+}
+
+
+
+static bool listminidvodbrunMode(pxConfig *config) {
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbRun.minidvodb_name", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbRun.minidvodb_id", "==");
+
+    PXOPT_COPY_STR(config->args, where, "-state", "minidvodbRun.state", "==");
+    PXOPT_LOOKUP_U64(limit,      config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+
+    if (!psListLength(where->list)) {
+      psFree(where);
+      psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+      return false;
+    }
+
+    psString query = pxDataGet("addtool_find_minidvodbrun.sql");
+
+    if (!query) {
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+      psStringAppend(&query, " WHERE %s", whereClause);
+      psFree(whereClause);
+    }
+
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+
+    psFree(query);
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("addtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "minidvodbRun",  !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+
+    psFree(output);
+
+return true;
+}
+
+static bool addminidvodbprocessedMode(pxConfig *config) {
+
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+  PXOPT_LOOKUP_U64(minidvodb_id, config->args, "-minidvodb_id", true, false);
+  PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-mergedvodb_path", true, false);
+  PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-minidvodb_group", true, false);
+
+    // optional
+  PXOPT_LOOKUP_U64(merge_order,     config->args, "-merge_order", false, false);
+  PXOPT_LOOKUP_F32(dtime_relphot, config->args, "-dtime_relphot", false, false);
+  PXOPT_LOOKUP_F32(dtime_resort, config->args, "-dtime_resort", false, false);
+  PXOPT_LOOKUP_F32(dtime_merge, config->args, "-dtime_merge", false, false);
+  PXOPT_LOOKUP_TIME(epoch, config->args, "-epoch", false, false);
+  PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
+    //generate restrictions
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id",   "minidvodbRun.minidvodb_id",   "==");
+
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+
+  psString query = pxDataGet("addtool_find_pendingmergeprocess.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+  // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+  if (psListLength(where->list)) {
+    psString whereClaus = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClaus);
+    psFree(whereClaus);
+  }
+  psFree(where);
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("addtool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  minidvodbRunRow *pendingRow = minidvodbRunObjectFromMetadata(output->data[0]);
+  psFree(output);
+  minidvodbProcessedRow *row = minidvodbProcessedRowAlloc(
+               pendingRow->minidvodb_id,
+               merge_order,
+               dtime_resort,
+               dtime_relphot,
+               dtime_merge,
+               epoch,
+               mergedvodb_path,
+               fault
+               );
+
+  if (!minidvodbProcessedInsertObject(config->dbh, row)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(row);
+    psFree(pendingRow);
+    return false;
+  }
+
+
+
+
+  //this finds the # of merged things (for the merge order)
+  psString query3 = NULL;
+  psStringAppend(&query3, "select count(*) from minidvodbRun join minidvodbProcessed using (minidvodb_id) where state = 'merged' and minidvodb_group = '%s';", minidvodb_group);
+
+  if (!p_psDBRunQuery(config->dbh, query3)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query3);
+    return false;
+  }
+  psArray *output2 = p_psDBFetchResult(config->dbh);
+  if (!output2) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output2)) {
+    psTrace("addtool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  bool status;
+  psS64 m_order = psMetadataLookupS64(&status, output2->data[0], "count(*)");
+  if (!status) {
+
+    psAbort("failed to lookup value for count column");
+    return false;
+  }
+  psString final = NULL;
+  psStringAppend(&final, "%" PRIu64, m_order);
+    //return false;
+  psFree(query3);
+  psFree(output2);
+
+
+
+  //update the merge_order
+
+  psString query4 = NULL;
+  psStringAppend(&query4, "update minidvodbProcessed set merge_order = %"PRIu64,m_order);
+  psStringAppend(&query4," where minidvodb_id = %" PRIu64,  minidvodb_id);
+  printf("%s", query4);
+  if (!p_psDBRunQuery(config->dbh, query4)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query4);
+    return false;
+  }
+  //
+psFree(query4);
+
+
+
+// since there is only one exp per 'new' set mindvodbRun.state = 'merged'
+
+  psString query2 = NULL ;
+  psStringAppend(&query2, "UPDATE minidvodbRun SET state = 'merged' WHERE minidvodb_id = %'" PRIu64, row->minidvodb_id);
+
+  if (!p_psDBRunQuery(config->dbh, query2)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query2);
+    return false;
+  }
+
+
+
+
+
+
+
+
+  psFree(row);
+  psFree(pendingRow);
+
+
+
+
+
+  //commit the changes
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+
+  //print the merge_order (why not!)
+  printf("%s", final);
+  psFree(final);
+
+  return true;
+}
+
+
+
+static bool listminidvodbprocessedMode(pxConfig *config) {
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbProcessed.minidvodb_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbProcessed.minidvodb_name", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbProcessed.minidvodb_group", "==");
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+  if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+  psString query = pxDataGet("addtool_find_minidvodbprocessed.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+
+// we either add AND (condition) or WHERE (condition):
+    if (where->list && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " AND minidvodbProcessed.fault != 0");
+    }
+    if (where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " AND minidvodbProcessed.fault = 0");
+    }
+    if (!where->list && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " WHERE minidvodbProcessed.fault != 0");
+    }
+    if (!where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " WHERE minidvodbProcessed.fault = 0");
+    }
+    psFree(where);
+
+    // order by epoch
+    psStringAppend(&query, " ORDER BY minidvodb_id");
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error ");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "minidvodbProcessed", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+return true;
+}
+
+
+
+static bool revertminidvodbprocessedMode(pxConfig *config) {
+  psMetadata *where = psMetadataAlloc();
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbProcessed.minidvodb_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "addRun.minidvodb_group", "==");
+
+  if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+
+  if (!psDBTransaction(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(where);
+      return false;
+  }
+
+  {
+    psString query = pxDataGet("addtool_revertminidvodbprocessed.sql");
+    if (!query) {
+      // rollback
+      if (!psDBRollback(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+      }
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      psFree(where);
+      return false;
+    }
+
+    // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+      psStringAppend(&query, " AND %s", whereClause);
+      psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      // rollback
+      if (!psDBRollback(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+      }
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      psFree(where);
+            return false;
+    }
+    psFree(query);
+  }
+  psFree(where);
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  return true;
+}
+
+
+
+static bool updateminidvodbprocessedMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  psMetadata *where = psMetadataAlloc();
+
+  PXOPT_LOOKUP_U64(minidvodb_id,  config->args, "-minidvodb_id", true, false);
+  PXOPT_LOOKUP_U64(merge_order,  config->args, "-set_merge_order", false, false);
+  PXOPT_LOOKUP_S16(fault,  config->args, "-set_fault", false, false);
+  PXOPT_LOOKUP_F32(dtime_relphot,  config->args, "-set_dtime_relphot", false, false);
+  PXOPT_LOOKUP_F32(dtime_resort,  config->args, "-set_dtime_resort", false, false);
+  PXOPT_LOOKUP_F32(dtime_merge,  config->args, "-set_dtime_merge", false, false);
+
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id",     "minidvodbProcessed.minidvodb_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name",     "minidvodbRun.minidvodb_name", "==");
+
+
+  if (!psListLength(where->list)) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+
+  psString query = psStringCopy("UPDATE minidvodbProcessed JOIN minidvodbRun USING (minidvodb_id) SET ");
+  int cnt = 0;
+  psString comma = ",";
+  if (fault) {
+    psStringAppend(&query, " fault = %d", fault);
+  cnt++;
+  }
+
+  if (merge_order) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+
+    psStringAppend(&query, " merge_order = %" PRId64, merge_order);
+    cnt++;
+  }
+
+  if (dtime_relphot) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_relphot = %f", dtime_relphot);
+    cnt++;
+  }
+
+  if (dtime_resort) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_resort = %f", dtime_resort);
+    cnt++;
+  }
+
+  if (dtime_merge) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_merge = %f", dtime_merge);
+    cnt++;
+ }
+
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " WHERE %s", whereClause);
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+   psError(PS_ERR_UNKNOWN, false, "database error");
+   psFree(query);
+   return false;
+  }
+
+  psFree(query);
+  psFree(where);
+
+  return true;
+}
+
+
Index: branches/pap/ippTools/src/addtool.h
===================================================================
--- branches/pap/ippTools/src/addtool.h	(revision 28483)
+++ branches/pap/ippTools/src/addtool.h	(revision 28484)
@@ -39,5 +39,14 @@
     ADDTOOL_MODE_DONECLEANUP,
     ADDTOOL_MODE_EXPORTRUN,
-    ADDTOOL_MODE_IMPORTRUN
+    ADDTOOL_MODE_IMPORTRUN,
+    ADDTOOL_MODE_ADDMINIDVODBRUN,
+    ADDTOOL_MODE_UPDATEMINIDVODBRUN,
+    ADDTOOL_MODE_LISTMINIDVODBRUN,
+    ADDTOOL_MODE_FLIPMINIDVODBRUN,
+    ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN,
+    ADDTOOL_MODE_ADDMINIDVODBPROCESSED,
+    ADDTOOL_MODE_LISTMINIDVODBPROCESSED,
+    ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,
+    ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED
 } addtoolMode;
 
Index: branches/pap/ippTools/src/addtoolConfig.c
===================================================================
--- branches/pap/ippTools/src/addtoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/addtoolConfig.c	(revision 28484)
@@ -63,5 +63,7 @@
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",           0, "do not actually modify the database", false);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",            0, "use the simple output format", false);
-
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb",            0, "use minidvodb", false);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb_group", 0,   "define minidvodb_group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb_name", 0,   "define minidvodb_bname", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_data_group", 0,   "define new data_group", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
@@ -84,4 +86,5 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "define new note", NULL);
+
     // -pendingexp
     psMetadata *pendingexpArgs = psMetadataAlloc();
@@ -98,4 +101,6 @@
     psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-add_id", 0,            "define addtool ID (required)", 0);
     psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", NULL);
+     psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-dvodb_path", 0,            "define base output location", NULL);
+      psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-minidvodb_name", 0,            "define minidvodb_name", NULL);
     psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-dtime_addstar", 0, "define elapsed time for DVO insertion (seconds)", NAN);
     psMetadataAddS16(addprocessedexpArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code", 0);
@@ -142,4 +147,79 @@
     psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
 
+    // -addminidvodbruns
+    psMetadata *addminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_name",        0, "define minidvodb_name", NULL);
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_group",        0, "define minidvodb_group (required)", NULL);
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_path",        0, "define path for minidvodb", NULL);
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define path for the merged dvodb (required)", NULL);
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_state",        0, "define state", NULL);
+
+    // -updateminidvodbruns
+    psMetadata *updateminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddU64(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id ", 0);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by minidvodb_name (LIKE)", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name (LIKE)", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_path",        0, "search by path for minidvodb", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-mergedvodb_path",        0, "search by path for the merged dvodb", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-state",        0, "search by state", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_name",        0, "define minidvodb_name", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_path",        0, "define path for minidvodb", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define path for minidvodb", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_group",        0, "define path for the merged dvodb", NULL);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_state",        0, "define state", NULL);
+    
+    // -listminidvodbrunArgs
+    psMetadata *listminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddU64(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name (LIKE)", NULL);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "search by minidvodb.minidvodb_group", NULL);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-state",        0, "search by state", NULL);
+    psMetadataAddU64(listminidvodbrunArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listminidvodbrunArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    
+    //psMetadataAddBool(listminidvodbrunArgs, PS_LIST_TAIL, "-finished_addrun",        0, "limit to minidvodbs with completed addRuns (none in new state)", false);
+    psMetadata *flipminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddStr(flipminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "for the supplied minidvodb_group (required): flip the current 'new' to 'active', the current 'active' to 'waiting'", NULL);
+    
+    psMetadata *checkminidvodbrunaddrunArgs = psMetadataAlloc();
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "for the supplied minidvodb_group (required): check if the addRun stage is complete (all in addRun.state = full) ", NULL);
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-minidvodb_name",     0,    "for the supplied minidvodb_name: check if the addRun stage is complete (all in addRun.state = full) ", NULL);
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-state",     0,    "limit by minidvodbRun state ", NULL);
+    psMetadataAddU64(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-all_addrun_states",        0, "list all minidvodbRun.minidvodb_names, not just ones that have complete addRuns", false);
+
+    psMetadata *addminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(addminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id", 0,    "define minidvodb_id (required)", 0);
+    psMetadataAddU64(addminidvodbprocessedArgs, PS_LIST_TAIL, "-merge_order",    0,    "define merge order", 0);
+    psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_relphot",  0,    "define elapsed time for relphot (seconds)", NAN);
+    psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_resort", 0,    "define elapsed time for resort (seconds)", NAN);
+    psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", NAN);
+    psMetadataAddTime(addminidvodbprocessedArgs, PS_LIST_TAIL, "-epoch",         0,    "time merge is finished", NULL);
+    psMetadataAddStr(addminidvodbprocessedArgs, PS_LIST_TAIL, "-mergedvodb_path",0,    "path of merged dvodb", NULL);
+    psMetadataAddStr(addminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",0,    "minidvodb_group", NULL);
+    psMetadataAddS16(addminidvodbprocessedArgs, PS_LIST_TAIL, "-fault",          0,    "set fault code", 0);
+
+    psMetadata *listminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name", NULL);
+    psMetadataAddStr(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by minidvodb.minidvodb_group", NULL);
+    psMetadataAddU64(listminidvodbprocessedArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listminidvodbprocessedArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(listminidvodbprocessedArgs, PS_LIST_TAIL, "-faulted",        0, "limit to faulted state", false);
+
+    psMetadata *revertminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(revertminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(revertminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by addRun.minidvodb_group", NULL);
+
+    psMetadata *updateminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name", NULL);
+    psMetadataAddS16(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_fault",  0,            "set fault code", 0);
+    psMetadataAddU64(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_merge_order",    0,    "define merge order", 0);
+    psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_relphot",  0,    "define elapsed time for relphot (seconds)", 0);
+    psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_resort", 0,    "define elapsed time for resort (seconds)", 0);
+    psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", 0);
+    
     psMetadata *argSets = psMetadataAlloc();
     psMetadata *modes = psMetadataAlloc();
@@ -154,5 +234,16 @@
     PXOPT_ADD_MODE("-block",                "set a label block",                    ADDTOOL_MODE_BLOCK,         blockArgs);
     PXOPT_ADD_MODE("-masked",               "show blocked labels",                  ADDTOOL_MODE_MASKED,        maskedArgs);
-    PXOPT_ADD_MODE("-unblock",              "remove a label block",                 ADDTOOL_MODE_UNBLOCK,       unblockArgs);
+    PXOPT_ADD_MODE("-addminidvodbrun",      "create minidvodbs ",                   ADDTOOL_MODE_ADDMINIDVODBRUN, addminidvodbrunArgs);
+    PXOPT_ADD_MODE("-updateminidvodbrun",   "change minidvodb properties",          ADDTOOL_MODE_UPDATEMINIDVODBRUN,     updateminidvodbrunArgs);
+    PXOPT_ADD_MODE("-listminidvodbrun",     "list minidvodbs",                      ADDTOOL_MODE_LISTMINIDVODBRUN,       listminidvodbrunArgs);
+    PXOPT_ADD_MODE("-flipminidvodbrun",     "flip minidvodbs",                      ADDTOOL_MODE_FLIPMINIDVODBRUN,       flipminidvodbrunArgs);
+    PXOPT_ADD_MODE("-checkminidvodbrunaddrun", "check minidvodbs to see if addRuns are completed", ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN,       checkminidvodbrunaddrunArgs);
+    PXOPT_ADD_MODE("-addminidvodbprocessed","add a processed minidvodb",            ADDTOOL_MODE_ADDMINIDVODBPROCESSED,  addminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-listminidvodbprocessed","list processed minidvodbs",           ADDTOOL_MODE_LISTMINIDVODBPROCESSED, listminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-revertminidvodbprocessed","revert processed minidvobs",        ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,     revertminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-updateminidvodbprocessed","change processed minidvodb properties",ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED,  updateminidvodbprocessedArgs);
+ 
+ 
+
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: branches/pap/ippTools/src/camtool.c
===================================================================
--- branches/pap/ippTools/src/camtool.c	(revision 28483)
+++ branches/pap/ippTools/src/camtool.c	(revision 28484)
@@ -130,4 +130,8 @@
     PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
 
+    // default
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
     // find the exp_id of all the exposures that we want to queue up.
     psString query = pxDataGet("camtool_find_chip_id.sql");
@@ -161,4 +165,15 @@
     if (!psArrayLength(output)) {
         psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "chipRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
         psFree(output);
         return true;
@@ -292,5 +307,5 @@
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
-    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    psString query = pxDataGet("camtool_pendingexp.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -305,4 +320,6 @@
     }
     psFree(where);
+
+    psStringAppend(&query, "\nORDER BY priority DESC, cam_id");
 
     // treat limit == 0 as "no limit"
@@ -503,5 +520,5 @@
 
 /*     psTrace("czw.test",1,"Received versions: pslib %s psmodules %s psphot %s psastro %s ppstats %s ppImage %s streaks %s\n", */
-/* 	    ver_pslib,ver_psmodules,ver_psphot,ver_psastro,ver_ppstats,ver_ppimage,ver_streaks); */
+/*          ver_pslib,ver_psmodules,ver_psphot,ver_psastro,ver_ppstats,ver_ppimage,ver_streaks); */
     psString software_ver = NULL;
     if ((ver_pslib)&&(ver_psmodules)) {
@@ -531,5 +548,5 @@
     PXOPT_COPY_S64(config->args, where, "-cam_id",   "camRun.cam_id",   "==");
 
-    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    psString query = pxDataGet("camtool_addprocessedexp.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -637,16 +654,16 @@
         path_base,
         fault,
-	software_ver,
-	maskfrac_ref_npix,
-	maskfrac_ref_static,
-	maskfrac_ref_dynamic,
-	maskfrac_ref_magic,
-	maskfrac_ref_advisory,
-	maskfrac_max_npix,
-	maskfrac_max_static,
-	maskfrac_max_dynamic,
-	maskfrac_max_magic,
-	maskfrac_max_advisory,
-	quality
+        software_ver,
+        maskfrac_ref_npix,
+        maskfrac_ref_static,
+        maskfrac_ref_dynamic,
+        maskfrac_ref_magic,
+        maskfrac_ref_advisory,
+        maskfrac_max_npix,
+        maskfrac_max_static,
+        maskfrac_max_dynamic,
+        maskfrac_max_magic,
+        maskfrac_max_advisory,
+        quality
         );
 
@@ -677,8 +694,8 @@
     if (!pxSetRunSoftware(config, "camRun", "cam_id", cam_id, software_ver)) {
       if (!psDBRollback(config->dbh)) {
-	psError(PS_ERR_UNKNOWN, false, "database error");
+        psError(PS_ERR_UNKNOWN, false, "database error");
       }
       psError(PS_ERR_UNKNOWN, false, "failed to set camRun.software_ver for cam_id: %" PRId64,
-	      cam_id);
+              cam_id);
       psFree(output);
       return(false);
@@ -687,15 +704,15 @@
     if (maskfrac_ref_npix) {
       if (!pxCamSetRunMaskfrac(config, "camRun", "cam_id",cam_id,
-			       (float) maskfrac_ref_npix, maskfrac_ref_static,
-			       maskfrac_ref_dynamic, maskfrac_ref_magic, maskfrac_ref_advisory,
-			       (float) maskfrac_max_npix, maskfrac_max_static,
-			       maskfrac_max_dynamic, maskfrac_max_magic, maskfrac_max_advisory)) {
-	if (!psDBRollback(config->dbh)) {
-	  psError(PS_ERR_UNKNOWN, false, "database error");
-	}
-	psError(PS_ERR_UNKNOWN, false, "failed to set camRun.maskstats for cam_id: %" PRId64,
-		cam_id);
-	psFree(output);
-	return(false);
+                               (float) maskfrac_ref_npix, maskfrac_ref_static,
+                               maskfrac_ref_dynamic, maskfrac_ref_magic, maskfrac_ref_advisory,
+                               (float) maskfrac_max_npix, maskfrac_max_static,
+                               maskfrac_max_dynamic, maskfrac_max_magic, maskfrac_max_advisory)) {
+        if (!psDBRollback(config->dbh)) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to set camRun.maskstats for cam_id: %" PRId64,
+                cam_id);
+        psFree(output);
+        return(false);
       }
     }
@@ -709,10 +726,11 @@
         return false;
     }
-    
+
     psFree(row);
 
     // EAM:  NULL for end_stage means go as far as possible
     // Also, we can run fake even if tess_id is not defined
-    if (pendingRow->end_stage && psStrcasestr(pendingRow->end_stage, "cam")) {
+    // but stop if quality is non-zero.
+    if ((quality > 0) || (pendingRow->end_stage && psStrcasestr(pendingRow->end_stage, "cam"))) {
         psFree(pendingRow);
         if (!psDBCommit(config->dbh)) {
@@ -893,6 +911,10 @@
 
     {
-        psString query = pxDataGet("camtool_reset_faulted_runs.sql");
+        psString query = pxDataGet("camtool_revertprocessedexp.sql");
         if (!query) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
             psFree(where);
@@ -919,36 +941,4 @@
         psFree(query);
     }
-
-    {
-        psString query = pxDataGet("camtool_revertprocessedexp.sql");
-        if (!query) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
-            psFree(where);
-            return false;
-        }
-
-        // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
-        if (where && psListLength(where->list)) {
-            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-            psStringAppend(&query, " AND %s", whereClause);
-            psFree(whereClause);
-        }
-
-        if (!p_psDBRunQuery(config->dbh, query)) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(query);
-            psFree(where);
-            return false;
-        }
-        psFree(query);
-    }
     psFree(where);
 
Index: branches/pap/ippTools/src/camtoolConfig.c
===================================================================
--- branches/pap/ippTools/src/camtoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/camtoolConfig.c	(revision 28484)
@@ -65,4 +65,6 @@
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dist_group",     0, "define dist group", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_note",           0, "define note", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",           0, "do not actual modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",            0, "use the simple output format", false);
 
     // -updaterun
@@ -83,4 +85,5 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "define new note", NULL);
+
     // -pendingexp
     psMetadata *pendingexpArgs = psMetadataAlloc();
Index: branches/pap/ippTools/src/diffphottool.c
===================================================================
--- branches/pap/ippTools/src/diffphottool.c	(revision 28484)
+++ branches/pap/ippTools/src/diffphottool.c	(revision 28484)
@@ -0,0 +1,553 @@
+/*
+ * diffphottool.c
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "diffphottool.h"
+
+static bool definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool inputMode(pxConfig *config);
+static bool pendingMode(pxConfig *config);
+static bool doneMode(pxConfig *config);
+static bool advanceMode(pxConfig *config);
+static bool revertMode(pxConfig *config);
+static bool dataMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = diffphottoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DIFFPHOTTOOL_MODE_DEFINERUN, definerunMode);
+        MODECASE(DIFFPHOTTOOL_MODE_UPDATERUN, updaterunMode);
+        MODECASE(DIFFPHOTTOOL_MODE_INPUT,     inputMode);
+        MODECASE(DIFFPHOTTOOL_MODE_PENDING,   pendingMode);
+        MODECASE(DIFFPHOTTOOL_MODE_DONE,      doneMode);
+        MODECASE(DIFFPHOTTOOL_MODE_ADVANCE,   advanceMode);
+        MODECASE(DIFFPHOTTOOL_MODE_REVERT,    revertMode);
+        MODECASE(DIFFPHOTTOOL_MODE_DATA,      dataMode);
+
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // Options
+    PXOPT_LOOKUP_STR(set_workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(set_data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(set_reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(set_note, config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-set_registered", false, false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // Selections
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs(config, where, "-label", "diffRun.label", "LIKE");
+    pxAddLabelSearchArgs(config, where, "-data_group", "diffRun.data_group", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-comment", "rawExp.comment", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "rawExp.dateobs", "<=");
+
+
+    psString query = pxDataGet("diffphottool_definerun.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "Unable to run query: %s", query);
+        psFree(where);
+        return false;
+    }
+    psFree(where);
+
+    psArray *results = p_psDBFetchResult(config->dbh); // Results of query
+    if (!results) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!psArrayLength(results)) {
+        psTrace("diffphottool", 1, "no rows found");
+        psFree(results);
+        return true;
+    }
+
+    if (pretend) {
+        if (!ippdbPrintMetadatas(stdout, results, "diffPhotRun", !simple)) {
+            psError(psErrorCodeLast(), false, "failed to print array");
+            psFree(results);
+            return false;
+        }
+        psFree(results);
+        return true;
+    }
+
+    for (int i = 0; i < results->n; i++) {
+        psMetadata *row = results->data[i]; // Output row from query
+        bool mdok;                          // Status of MD lookup
+        psS64 diff_id = psMetadataLookupS64(&mdok, row, "diff_id");
+        const char *workdir = psMetadataLookupStr(&mdok, row, "workdir");
+        const char *label = psMetadataLookupStr(&mdok, row, "data_group");
+        const char *data_group = psMetadataLookupStr(&mdok, row, "data_group");
+        const char *reduction = psMetadataLookupStr(&mdok, row, "reduction");
+        const char *note = psMetadataLookupStr(&mdok, row, "note");
+
+        diffPhotRunRow *run = diffPhotRunRowAlloc(0, diff_id, "new",
+                                                  set_workdir ? set_workdir : workdir,
+                                                  set_label ? set_label : label,
+                                                  set_data_group ? set_data_group : data_group,
+                                                  set_reduction ? set_reduction : reduction,
+                                                  registered,
+                                                  set_note ? set_note : note);
+        if (!diffPhotRunInsertObject(config->dbh, run)) {
+            psError(psErrorCodeLast(), false, "database error");
+            if (!psDBRollback(config->dbh)) {
+                psError(psErrorCodeLast(), false, "database error");
+            }
+            psFree(run);
+            psFree(results);
+            return false;
+        }
+
+        run->diff_phot_id = psDBLastInsertID(config->dbh);
+
+        if (!diffPhotRunPrintObject(stdout, run, !simple)) {
+            psError(psErrorCodeLast(), false, "failed to print object");
+            psFree(run);
+            psFree(results);
+            return false;
+        }
+        psFree(run);
+    }
+    psFree(results);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "data_group", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE diffPhotRun");
+
+    // pxUpdateRun gets parameters from config->args and updates
+    bool result = pxUpdateRun(config, where, &query, "diffPhotRun", "diff_phot_id", "diffPhotSkyfile", true);
+
+    psFree(query);
+    psFree(where);
+
+    return result;
+}
+
+
+static bool inputMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_input.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nWHERE %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffSkyfile", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "diffPhotRun.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_pending.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffPhotRun", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+
+static bool doneMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(diff_phot_id, config->args, "-diff_phot_id", true, false); // required
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-quality", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", true, false);
+    PXOPT_LOOKUP_F32(dtime_script, config->args, "-dtime_script", false, false);
+    PXOPT_LOOKUP_STR(ver_pslib, config->args, "-ver_pslib", false, false);
+    PXOPT_LOOKUP_STR(ver_psmodules, config->args, "-ver_psmodules", false, false);
+    PXOPT_LOOKUP_STR(ver_ppstats, config->args, "-ver_ppstats", false, false);
+    PXOPT_LOOKUP_STR(ver_psphot, config->args, "-ver_psphot", false, false);
+    PXOPT_LOOKUP_S64(magicked, config->args, "-magicked", false, false);
+
+    psString version = pxMergeCodeVersions(ver_pslib, ver_psmodules);
+    version = pxMergeCodeVersions(version, ver_ppstats);
+    version = pxMergeCodeVersions(version, ver_psphot);
+
+    if (!diffPhotSkyfileInsert(config->dbh, diff_phot_id, skycell_id, path_base, dtime_script, hostname,
+                               fault, quality, version, magicked)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool advanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "diffPhotRun.label", "==");
+
+    psString query = pxDataGet("diffphottool_advance.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = psStringCopy("");
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&whereClause, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, whereClause)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+    psFree(whereClause);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    for (int i = 0; i < output->n; i++) {
+        psMetadata *row = output->data[i]; // Row of interest
+        psS64 diff_phot_id = psMetadataLookupS64(NULL, row, "diff_phot_id");
+
+        const char *query = "UPDATE diffPhotRun SET state = 'full' WHERE diff_phot_id = %" PRId64;
+        if (!p_psDBRunQueryF(config->dbh, query, diff_phot_id)) {
+            psError(psErrorCodeLast(), false,
+                    "failed to change state for diff_phot_id %" PRId64, diff_phot_id);
+            if (!psDBRollback(config->dbh)) {
+                psError(psErrorCodeLast(), false, "database error");
+            }
+            psFree(output);
+            return false;
+        }
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool revertMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "fault", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "label", "LIKE");
+
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+    if (!psListLength(where->list) && !all) {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        psFree(where);
+        return false;
+    }
+
+    psString query = pxDataGet("diffphottool_revert.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool dataMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_data.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nWHERE %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffPhotSkyfile", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
Index: branches/pap/ippTools/src/diffphottool.h
===================================================================
--- branches/pap/ippTools/src/diffphottool.h	(revision 28484)
+++ branches/pap/ippTools/src/diffphottool.h	(revision 28484)
@@ -0,0 +1,39 @@
+/*
+ * diffphottool.h
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DIFFPHOTTOOL_H
+#define DIFFPHOTTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DIFFPHOTTOOL_MODE_NONE           = 0x0,
+    DIFFPHOTTOOL_MODE_DEFINERUN,
+    DIFFPHOTTOOL_MODE_UPDATERUN,
+    DIFFPHOTTOOL_MODE_INPUT,
+    DIFFPHOTTOOL_MODE_PENDING,
+    DIFFPHOTTOOL_MODE_DONE,
+    DIFFPHOTTOOL_MODE_ADVANCE,
+    DIFFPHOTTOOL_MODE_REVERT,
+    DIFFPHOTTOOL_MODE_DATA,
+} diffphottoolMode;
+
+pxConfig *diffphottoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // DIFFPHOTTOOL_H
Index: branches/pap/ippTools/src/diffphottoolConfig.c
===================================================================
--- branches/pap/ippTools/src/diffphottoolConfig.c	(revision 28484)
+++ branches/pap/ippTools/src/diffphottoolConfig.c	(revision 28484)
@@ -0,0 +1,161 @@
+/*
+ * diffphottoolConfig.c
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "diffphottool.h"
+
+pxConfig *diffphottoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(psErrorCodeLast(), false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label", 0, "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_reduction", 0, "define reduction class", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-set_registered", 0, "time detrend run was registered", now);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_data_group", 0, "define data group", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_note", 0, "define note", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label (LIKE comparison, multiple OK)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by data_group", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-comment", 0, "search for comment (LIKE)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-filter", 0, "search for filter", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_begin", 0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_end", 0, "search for exposures by time (<)", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-pretend",  0, "do not actually modify the database", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0, "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0, "search by label (LIKE comparison)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group", 0, "search by data_group (LIKE comparison)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0, "define new value for label", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0, "define new state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_data_group", 0, "define new data_group", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0, "define new note", NULL);
+
+    // -input
+    psMetadata *inputArgs = psMetadataAlloc();
+    psMetadataAddS64(inputArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(inputArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", NULL);
+    psMetadataAddU64(inputArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(inputArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -pending
+    psMetadata *pendingArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", 0);
+    psMetadataAddU64(pendingArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -done
+    psMetadata *doneArgs = psMetadataAlloc();
+    psMetadataAddS64(doneArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "define diffphot ID (required)", 0);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-skycell_id", 0, "define skycell of file (required)", NULL);
+    psMetadataAddS16(doneArgs, PS_LIST_TAIL, "-fault", 0, "set fault code", 0);
+    psMetadataAddS16(doneArgs, PS_LIST_TAIL, "-quality", 0, "set quality", 0);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-path_base", 0, "define base output location (required)", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-hostname", 0, "set hostname (required)", NULL);
+    psMetadataAddF32(doneArgs, PS_LIST_TAIL, "-dtime_script", 0, "define elapsed time in script (seconds)", NAN);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_pslib", 0, "define psLib version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_psmodules", 0, "define psModules version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_ppstats", 0, "define ppStats version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_psphot", 0, "define psphot version", NULL);
+    psMetadataAddS64(doneArgs, PS_LIST_TAIL, "-magicked", 0, "define magicked state", 0);
+
+    // -advance
+    psMetadata *advanceArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "select by diffphot ID", 0);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "select by label", NULL);
+    psMetadataAddS32(advanceArgs, PS_LIST_TAIL, "-limit", 0, "limit number of results", 0);
+
+    // -revert
+    psMetadata *revertArgs = psMetadataAlloc();
+    psMetadataAddS64(revertArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell_id", NULL);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", NULL);
+    psMetadataAddS16(revertArgs, PS_LIST_TAIL, "-fault", 0, "search by fault code", 0);
+    psMetadataAddBool(revertArgs, PS_LIST_TAIL, "-all", 0, "allow no search terms", 0);
+
+    // -data
+    psMetadata *dataArgs = psMetadataAlloc();
+    psMetadataAddS64(dataArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(dataArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", NULL);
+    psMetadataAddU64(dataArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(dataArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definerun", "", DIFFPHOTTOOL_MODE_DEFINERUN, definerunArgs);
+    PXOPT_ADD_MODE("-updaterun", "", DIFFPHOTTOOL_MODE_UPDATERUN, updaterunArgs);
+    PXOPT_ADD_MODE("-input",     "", DIFFPHOTTOOL_MODE_INPUT,     inputArgs);
+    PXOPT_ADD_MODE("-pending",   "", DIFFPHOTTOOL_MODE_PENDING,   pendingArgs);
+    PXOPT_ADD_MODE("-done",      "", DIFFPHOTTOOL_MODE_DONE,      doneArgs);
+    PXOPT_ADD_MODE("-advance",   "", DIFFPHOTTOOL_MODE_ADVANCE,   advanceArgs);
+    PXOPT_ADD_MODE("-revert",    "", DIFFPHOTTOOL_MODE_REVERT,    revertArgs);
+    PXOPT_ADD_MODE("-data",      "", DIFFPHOTTOOL_MODE_DATA,      dataArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: branches/pap/ippTools/src/difftool.c
===================================================================
--- branches/pap/ippTools/src/difftool.c	(revision 28483)
+++ branches/pap/ippTools/src/difftool.c	(revision 28484)
@@ -44,4 +44,6 @@
 static bool definewarpwarpMode(pxConfig *config);
 static bool definestackstackMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupskyfileMode(pxConfig *config);
@@ -93,4 +95,6 @@
         MODECASE(DIFFTOOL_MODE_DEFINEWARPWARP,        definewarpwarpMode);
         MODECASE(DIFFTOOL_MODE_DEFINESTACKSTACK,      definestackstackMode);
+	MODECASE(DIFFTOOL_MODE_TOSUMMARY,             tosummaryMode);
+	MODECASE(DIFFTOOL_MODE_ADDSUMMARY,            addsummaryMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
@@ -2250,4 +2254,135 @@
 }
 
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-warp_id",    "diffSkyfile.warp_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "diffSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "diffRun.state", "==");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs",  ">=");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_end",   "rawExp.dateobs",  "<=");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "rawExp.filter", "LIKE");
+  PXOPT_COPY_S64(config->args, where, "-magicked", "diffSkyfile.magicked", "==");
+  pxAddLabelSearchArgs (config, where, "-label",   "diffRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "diffRun.data_group", "LIKE");
+
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("difftool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  psString where2 = NULL;
+  if (!pxmagicAddWhere(config, &where2, "diffSkyfile")) {
+    psError(psErrorCodeLast(), false, "pxMagicAddWhere failed");
+    return false;
+  }
+  
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all && !where2) {
+    psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+    return false;
+  }
+  
+  if (where2) {
+    if (psListLength(where->list)) {
+      psStringAppend(&query, " %s", where2);
+    } else {
+      psStringAppend(&query, " AND 1 %s", where2);
+    }
+  }
+  psFree(where);
+
+  // treat limit == 0 as "no limit"
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, " %s", limitString);
+    psFree(limitString);
+  }
+  
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psErrorCode err = psErrorCodeLast();
+    switch (err) {
+    case PS_ERR_DB_CLIENT:
+      psError(PXTOOLS_ERR_SYS, false, "database error");
+    case PS_ERR_DB_SERVER:
+      psError(PXTOOLS_ERR_PROG, false, "database error");
+    default:
+      psError(PXTOOLS_ERR_PROG, false, "unknown error");
+    }
+    
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("difftool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "diffRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("difftool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, diff_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
+
 static bool pendingcleanuprunMode(pxConfig *config)
 {
Index: branches/pap/ippTools/src/difftool.h
===================================================================
--- branches/pap/ippTools/src/difftool.h	(revision 28483)
+++ branches/pap/ippTools/src/difftool.h	(revision 28484)
@@ -32,4 +32,6 @@
     DIFFTOOL_MODE_ADDDIFFSKYFILE,
     DIFFTOOL_MODE_ADVANCE,
+    DIFFTOOL_MODE_TOSUMMARY,
+    DIFFTOOL_MODE_ADDSUMMARY,
     DIFFTOOL_MODE_DIFFSKYFILE,
     DIFFTOOL_MODE_REVERTDIFFSKYFILE,
Index: branches/pap/ippTools/src/difftoolConfig.c
===================================================================
--- branches/pap/ippTools/src/difftoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/difftoolConfig.c	(revision 28484)
@@ -323,4 +323,33 @@
     psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-diff_id", 0,           "search by diff ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-tess_id",  0,          "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state",  0,            "search by state", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-exp_id",  0,           "search by exposure ID", 0);
+    psMetadataAddStr(tosummaryArgs , PS_LIST_TAIL, "-exp_name",  0,        "search by exposure name", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "search by warp ID", 0);
+    
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_begin", 0,    "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_end", 0,      "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-filter", 0,           "search for filter", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-magicked", 0,         "search by magic id", 0);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by diffRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by diffRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-dist_group",  PS_META_DUPLICATE_OK, "search by diffRun dist_group (LIKE comparison)", NULL);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL,  "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL,  "-not_destreaked", 0, "search for runs that have not been destreaked", false);
+    
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+    
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL,  "-diff_id", 0,           "search by diff ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0, "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0, "set summary path base", NULL);
+
     // -pendingcleanupskyfile
     psMetadata *pendingcleanupskyfileArgs = psMetadataAlloc();
@@ -380,5 +409,4 @@
     psMetadata *importrunArgs = psMetadataAlloc();
     psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
-
 
 
@@ -415,4 +443,6 @@
     PXOPT_ADD_MODE("-toscrubbedskyfile", "set skyfile as scrubbed", DIFFTOOL_MODE_TOSCRUBBEDSKYFILE, toscrubbedskyfileArgs);
     PXOPT_ADD_MODE("-tofullskyfile", "set skyfile as full", DIFFTOOL_MODE_TOFULLSKYFILE, tofullskyfileArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", DIFFTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", DIFFTOOL_MODE_ADDSUMMARY, addsummaryArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: branches/pap/ippTools/src/disttool.c
===================================================================
--- branches/pap/ippTools/src/disttool.c	(revision 28483)
+++ branches/pap/ippTools/src/disttool.c	(revision 28484)
@@ -148,5 +148,5 @@
     PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
 
-    if (use_alternate) { 
+    if (use_alternate) {
         if (strcmp(stage, "raw")) {
             psError(PXTOOLS_ERR_SYS, true, "alternate inputs only supported for raw stage");
@@ -299,17 +299,17 @@
       query = pxDataGet("disttool_definebyquery_SSdiff.sql");
       if (!query) {
-	psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
-	psFree(where);
-	return(false);
+        psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+        psFree(where);
+        return(false);
       }
 
       if (label) {
-	psStringAppend(&query, " AND (diffRun.label = '%s') ", label);
+        psStringAppend(&query, " AND (diffRun.label = '%s') ", label);
       }
       if (dist_group) {
-	psStringAppend(&query, " AND (diffRun.dist_group = '%s') ", dist_group);
+        psStringAppend(&query, " AND (diffRun.dist_group = '%s') ", dist_group);
       }
 
-      no_magic = true;    
+      no_magic = true;
     } else {
         psError(PS_ERR_UNKNOWN, true, "unknown value for stage: %s", stage);
@@ -470,5 +470,14 @@
     PXOPT_COPY_STR(config->args, where, "-data_group", "distRun.data_group", "LIKE");
     PXOPT_COPY_STR(config->args, where, "-dist_group", "distTarget.dist_group", "==");
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_TIME(config->args, where, "-time_stamp_begin", "distRun.time_stamp", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-time_stamp_end", "distRun.time_stamp", "<=");
+
+    PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+    PXOPT_LOOKUP_BOOL(full, config->args, "-full", false);
+    if (clean && full) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, true, "-clean and -full are contradictory parameters");
+        return false;
+    }
 
     if (!psListLength(where->list)) {
@@ -491,4 +500,5 @@
     }
 
+    psString extraWhere = NULL;
     psString query = psStringCopy("UPDATE distRun join distTarget using(target_id, stage) SET distRun.time_stamp = UTC_TIMESTAMP()");
 
@@ -498,4 +508,8 @@
     if (state) {
         psStringAppend(&query, " , distRun.state = '%s'", state);
+        if (!strcmp(state, "goto_cleaned")) {
+            // don't queue for clean up if run has already already cleaned
+            psStringAppend(&extraWhere, " AND (distRun.state != 'cleaned' AND distRun.state != 'goto_cleaned')");
+        }
     }
 
@@ -521,4 +535,13 @@
     psFree(whereClause);
     psFree(where);
+
+    if (extraWhere) {
+        psStringAppend(&query, "%s", extraWhere);
+    }
+    if (clean) {
+        psStringAppend(&query, " AND (distRun.clean)");
+    } else if (full) {
+        psStringAppend(&query, " AND (!distRun.clean)");
+    }
 
     if (!p_psDBRunQuery(config->dbh, query)) {
@@ -1680,5 +1703,5 @@
         return false;
     }
-        
+
 
     PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
Index: branches/pap/ippTools/src/disttoolConfig.c
===================================================================
--- branches/pap/ippTools/src/disttoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/disttoolConfig.c	(revision 28484)
@@ -79,5 +79,10 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group",     0, "limit updates to data_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-dist_group",     0, "limit updates to data_group", NULL);
+    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-time_stamp_begin", 0, "limit updates by time_stamp (>=)", NULL); 
+    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-time_stamp_end", 0, "limit updates by time_stamp (<=)", NULL); 
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL,"-clean",     0, "limit updates to clean distRuns", false);
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL,"-full",      0, "limit updates to not clean distRuns", false);
     psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-fault",      0, "define fault code", 0);
+
     // -revertrun
     psMetadata *revertrunArgs = psMetadataAlloc();
Index: branches/pap/ippTools/src/dqstatstool.c
===================================================================
--- branches/pap/ippTools/src/dqstatstool.c	(revision 28483)
+++ branches/pap/ippTools/src/dqstatstool.c	(revision 28484)
@@ -85,5 +85,5 @@
 
   if (label) {
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "camRun.label", "LIKE"); // define using cam label
   }
   // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
@@ -654,5 +654,15 @@
 	psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find max value for %s",colname);
 	return(false);
-      }      
+      }
+      psString filter = psMetadataLookupStr(&status,rule,"FILTER");
+      if (!status) {
+	status = true;
+      }
+      if (filter) {
+	psString imfilter = psMetadataLookupStr(&status,tableRow,"FILTER");
+	if (strcmp(filter,imfilter) != 0) {
+	  continue;
+	}
+      }
       // Not happy with this being set to a F32. Can this ever be something else?
       psF32 value      = psMetadataLookupF32(&status,tableRow,colname);
Index: branches/pap/ippTools/src/flatcorr.c
===================================================================
--- branches/pap/ippTools/src/flatcorr.c	(revision 28483)
+++ branches/pap/ippTools/src/flatcorr.c	(revision 28484)
@@ -671,5 +671,8 @@
                 row->dvodb,
                 NULL,       // note is not propagated
-                0)) {
+                0,
+		0,  //The minidvodb stuff is off
+		NULL,
+		NULL)) {
             if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
Index: branches/pap/ippTools/src/magictool.c
===================================================================
--- branches/pap/ippTools/src/magictool.c	(revision 28483)
+++ branches/pap/ippTools/src/magictool.c	(revision 28484)
@@ -1367,4 +1367,5 @@
             (strncmp(state, "new", 4) == 0)
             || (strncmp(state, "full", 5) == 0)
+            || (strncmp(state, "drop", 5) == 0)
             || (strncmp(state, "reg", 4) == 0)
         )
Index: branches/pap/ippTools/src/pstamptool.c
===================================================================
--- branches/pap/ippTools/src/pstamptool.c	(revision 28483)
+++ branches/pap/ippTools/src/pstamptool.c	(revision 28484)
@@ -271,4 +271,5 @@
         uri,
         NULL,   // outdir
+        NULL,   // timestamp
         0       // fault
         )) {
@@ -547,4 +548,9 @@
     PXOPT_LOOKUP_BOOL(clearfault,config->args, "-clearfault",    false);
 
+    if (!state && !label && !outProduct && !fault && !uri && !outdir && !name && !reqType && !clearfault) {
+        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
+        return false;
+    }
+
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-req_id",     "req_id", "==");
@@ -552,4 +558,6 @@
     PXOPT_COPY_S32(config->args, where, "-fault",      "fault", "==");
     PXOPT_COPY_STR(config->args, where, "-state",      "state", "==");
+    PXOPT_COPY_TIME(config->args, where, "-timestamp_begin", "timestamp", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-timestamp_end", "timestamp", "<=");
     pxAddLabelSearchArgs(config, where, "-label",      "pstampRequest.label", "LIKE");
     if (!psListLength(where->list)) {
@@ -559,11 +567,9 @@
     }
 
-    psString query = psStringCopy("UPDATE pstampRequest SET");
+    psString query = psStringCopy("UPDATE pstampRequest SET timestamp = UTC_TIMESTAMP()");
 
     psString stateCheck = NULL;
-    char c = ' ';
     if (state) {
-        psStringAppend(&query, "%c state = '%s'", c, state);
-        c = ',';
+        psStringAppend(&query, ", state = '%s'", state);
         if (!strcmp(state, "goto_cleaned")) {
             psStringAppend(&stateCheck, " AND state != 'cleaned'");
@@ -571,14 +577,11 @@
     }
     if (label) {
-        psStringAppend(&query, "%c label = '%s'", c, label);
-        c = ',';
+        psStringAppend(&query, ", label = '%s'", label);
     }
     if (outProduct) {
-        psStringAppend(&query, "%c outProduct = '%s'", c, outProduct);
-        c = ',';
+        psStringAppend(&query, ", outProduct = '%s'", outProduct);
     }
     if (outdir) {
-        psStringAppend(&query, "%c outdir = '%s'", c, outdir);
-        c = ',';
+        psStringAppend(&query, ", outdir = '%s'", outdir);
     }
     if (clearfault) {
@@ -587,25 +590,16 @@
             return false;
         }
-        psStringAppend(&query, "%c fault = 0", c);
-        c = ',';
+        psStringAppend(&query, ", fault = 0");
     } else if (fault) {
-        psStringAppend(&query, "%c fault = %d", c, fault);
-        c = ',';
+        psStringAppend(&query, ", fault = %d", fault);
     }
     if (uri) {
-        psStringAppend(&query, "%c uri = '%s'", c, uri);
-        c = ',';
+        psStringAppend(&query, ", uri = '%s'", uri);
     }
     if (name) {
-        psStringAppend(&query, "%c name = '%s'", c, name);
-        c = ',';
+        psStringAppend(&query, ", name = '%s'", name);
     }
     if (reqType) {
-        psStringAppend(&query, "%c reqType = '%s'", c, reqType);
-        c = ',';
-    }
-    if (c != ',') {
-        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
-        return false;
+        psStringAppend(&query, ", reqType = '%s'", reqType);
     }
 
@@ -876,8 +870,9 @@
 
     PXOPT_LOOKUP_S64(job_id,    config->args, "-job_id", false, false);
+    PXOPT_LOOKUP_S64(req_id,    config->args, "-req_id", false, false);
     PXOPT_LOOKUP_S64(dep_id,    config->args, "-dep_id", false, false);
 
-    if (!job_id && !dep_id) {
-        psError(PS_ERR_UNKNOWN, true, "at least -job_id or -dep_id is required");
+    if (!job_id && !req_id && !dep_id) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -job_id -req_id or -dep_id is required");
         return false;
     }
@@ -894,5 +889,7 @@
 
     PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
     PXOPT_COPY_S64(config->args, where, "-dep_id", "dep_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-state",  "pstampJob.state", "==");
 
     psString query = pxDataGet("pstamptool_updatejob.sql");
Index: branches/pap/ippTools/src/pstamptoolConfig.c
===================================================================
--- branches/pap/ippTools/src/pstamptoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/pstamptoolConfig.c	(revision 28484)
@@ -103,4 +103,6 @@
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-state", 0,        "search by state", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by pstampJob label (LIKE comparision)", NULL);
+    psMetadataAddTime(updatereqArgs, PS_LIST_TAIL, "-timestamp_begin", 0, "search by timestamp (>=)", NULL);
+    psMetadataAddTime(updatereqArgs, PS_LIST_TAIL, "-timestamp_end", 0, "search by timestamp (<=)", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_state", 0,        "new state", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_label", 0,        "new label", NULL);
@@ -158,6 +160,8 @@
     // -updatejob
     psMetadata *updatejobArgs = psMetadataAlloc();
-    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-job_id", 0,            "req_id for which to change state", 0);
-    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-dep_id", 0,            "dep_id for which to change state", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-req_id", 0,            "req_id of jobs to update", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-job_id", 0,            "job_id of jobs to update", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-dep_id", 0,            "dep_id of jobs to update", 0);
+    psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-state", 0,            "current state of jobs to update", 0);
     psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-set_state", 0,            "new state", NULL);
     psMetadataAddS16(updatejobArgs, PS_LIST_TAIL, "-set_fault", 0,            "new result", 0);
Index: branches/pap/ippTools/src/pubtool.c
===================================================================
--- branches/pap/ippTools/src/pubtool.c	(revision 28483)
+++ branches/pap/ippTools/src/pubtool.c	(revision 28484)
@@ -151,37 +151,97 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+    psMetadata *diffWhere = psMetadataAlloc(); // WHERE conditions for diffs
+    psMetadata *camWhere = psMetadataAlloc(); // WHERE conditions for cams
+    psMetadata *diffphotWhere = psMetadataAlloc(); // WHERE conditions for diffphots
 
     // required
 
     // optional
-    PXOPT_COPY_S64(config->args, where, "-client_id", "client_id", "==");
-    pxAddLabelSearchArgs(config, where, "-label", "label", "=="); // define using newExp label
+    PXOPT_COPY_S64(config->args, diffWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, diffWhere, "-label", "diffRun.label", "==");
+    pxAddLabelSearchArgs(config, diffWhere, "-data_group", "diffRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, diffWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, diffWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, diffWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, diffWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_COPY_S64(config->args, camWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, camWhere, "-label", "camRun.label", "==");
+    pxAddLabelSearchArgs(config, camWhere, "-data_group", "camRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, camWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, camWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, camWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, camWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_COPY_S64(config->args, diffphotWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, diffphotWhere, "-label", "diffphotRun.label", "==");
+    pxAddLabelSearchArgs(config, diffphotWhere, "-data_group", "diffphotRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, diffphotWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, diffphotWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, diffphotWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, diffphotWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psString query = pxDataGet("pubtool_definerun.sql"); // Query to run
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "Failed to retreive SQL statement");
-        psFree(where);
-        return false;
+        psFree(diffWhere);
+        psFree(camWhere);
+        return false;
+    }
+
+    if (!rerun) {
+        psStringAppend(&query, "\nWHERE publishRun.client_id IS NULL");
+    }
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
     }
 
     if (!psDBTransaction(config->dbh)) {
         psError(PS_ERR_UNKNOWN, false, "Database error");
-        psFree(where);
-        return false;
-    }
-
-    psString whereClause = psStringCopy(""); // Additional constraints to add to query
-    if (psListLength(where->list)) {
-        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&whereClause, "\n AND %s", clause);
+        psFree(diffWhere);
+        psFree(camWhere);
+        psFree(diffphotWhere);
+        return false;
+    }
+
+    psString whereDiff = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(diffWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(diffWhere, NULL);
+        psStringAppend(&whereDiff, "\n AND %s", clause);
         psFree(clause);
     }
-    psFree(where);
-
-    if (!p_psDBRunQueryF(config->dbh, query, whereClause, whereClause)) {
+    psFree(diffWhere);
+
+    psString whereCam = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(camWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(camWhere, NULL);
+        psStringAppend(&whereCam, "\n AND %s", clause);
+        psFree(clause);
+    }
+    psFree(camWhere);
+
+    psString whereDiffphot = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(diffphotWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(diffphotWhere, NULL);
+        psStringAppend(&whereDiffphot, "\n AND %s", clause);
+        psFree(clause);
+    }
+    psFree(diffphotWhere);
+
+    if (!p_psDBRunQueryF(config->dbh, query, whereDiff, whereCam, whereDiffphot)) {
         psError(PS_ERR_UNKNOWN, false, "Database error");
         psFree(query);
-        psFree(whereClause);
+        psFree(whereDiff);
+        psFree(whereCam);
+        psFree(whereDiffphot);
         if (!psDBRollback(config->dbh)) {
             psError(PS_ERR_UNKNOWN, false, "Database error");
@@ -190,5 +250,7 @@
     }
     psFree(query);
-    psFree(whereClause);
+    psFree(whereDiff);
+    psFree(whereCam);
+    psFree(whereDiffphot);
 
     psArray *output = p_psDBFetchResult(config->dbh); // Output of SELECT statement
@@ -206,11 +268,21 @@
     }
 
+    if (pretend) {
+        if (!ippdbPrintMetadatas(stdout, output, "publishRun", !simple)) {
+            psError(psErrorCodeLast(), false, "Failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
     for (int i = 0; i < output->n; i++) {
         psMetadata *row = output->data[i]; // Row of interest
         psS64 client = psMetadataLookupS64(NULL, row, "client_id"); // Client identifier
         psS64 stage = psMetadataLookupS64(NULL, row, "stage_id");   // Stage identifier
-        char *label = psMetadataLookupStr(NULL, row, "src_label");   // label from correct source
-
-        if (!publishRunInsert(config->dbh, 0, client, stage, label, "new")) {
+        const char *label = psMetadataLookupStr(NULL, row, "src_label");   // label from correct source
+
+        if (!publishRunInsert(config->dbh, 0, client, stage, set_label ? set_label : label, "new")) {
             psError(PS_ERR_UNKNOWN, false, "Unable to add fileset");
             psFree(output);
@@ -241,5 +313,5 @@
     PXOPT_COPY_STR(config->args, where, "-stage", "publishClient.stage", "==");
     PXOPT_COPY_STR(config->args, where, "-comment", "publishClient.comment", "LIKE");
-    PXOPT_COPY_STR(config->args, where, "-label", "publishRun.label", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "publishRun.label", "==");
 
     // optional
Index: branches/pap/ippTools/src/pubtoolConfig.c
===================================================================
--- branches/pap/ippTools/src/pubtoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/pubtoolConfig.c	(revision 28484)
@@ -62,6 +62,16 @@
     // -definerun
     psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label", 0, "set label", NULL);
     psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-client_id", 0, "search by client_id", 0);
     psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "set and search by label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by data_group", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_begin", 0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_end", 0, "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(definerunArgs,  PS_LIST_TAIL, "-filter", 0, "search for filter", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-obs_mode", 0, "search by observation mode", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-rerun", 0, "Re-run publish?", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-pretend", 0, "Pretend to define?", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple",  0, "use simple output format?", false);
+    psMetadataAddU64(definerunArgs, PS_LIST_TAIL, "-limit",  0, "limit result set", 0);
 
     // -pending
@@ -70,5 +80,5 @@
     psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-stage", 0, "search on source", NULL);
     psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-comment", 0, "search on comment (LIKE)", NULL);
-    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", 0, "search on label", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search on label", NULL);
     psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple",  0, "use simple output format?", false);
     psMetadataAddU64(pendingArgs, PS_LIST_TAIL, "-limit",  0, "limit result set", 0);
Index: branches/pap/ippTools/src/pxadd.c
===================================================================
--- branches/pap/ippTools/src/pxadd.c	(revision 28483)
+++ branches/pap/ippTools/src/pxadd.c	(revision 28484)
@@ -130,5 +130,8 @@
                        char *dvodb,
                        char *note,
-                       bool image_only)
+                       bool image_only,
+		       bool minidvodb,
+		       char *minidvodb_group,
+		       char *minidvodb_name)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -158,7 +161,10 @@
                          note     ? note     : "NULL",
                          image_only,
+			 minidvodb,
+			 minidvodb_group,
+			 minidvodb_name,
                          (long long) cam_id
     )) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+      psError(PS_ERR_UNKNOWN, false, "database error %s", query);
         return false;
     }
Index: branches/pap/ippTools/src/pxadd.h
===================================================================
--- branches/pap/ippTools/src/pxadd.h	(revision 28483)
+++ branches/pap/ippTools/src/pxadd.h	(revision 28484)
@@ -38,5 +38,8 @@
 		       char *dvodb,
 		       char *note,
-		       bool image_only);
+		       bool image_only,
+		       bool minidvodb,
+		       char *minidvodb_group,
+		       char *minidvodb_name);
 
 #endif // PXADD_H
Index: branches/pap/ippTools/src/pxchip.c
===================================================================
--- branches/pap/ippTools/src/pxchip.c	(revision 28483)
+++ branches/pap/ippTools/src/pxchip.c	(revision 28484)
@@ -40,5 +40,5 @@
     psMetadataAddStr(md,  PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
     psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_begin",      0, "search for exposures by time (>=)", NULL);
-    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",        0, "search for exposures by time (<)", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",        0, "search for exposures by time (<=)", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_tag",            0, "search by exp_tag", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
Index: branches/pap/ippTools/src/pxtools.c
===================================================================
--- branches/pap/ippTools/src/pxtools.c	(revision 28483)
+++ branches/pap/ippTools/src/pxtools.c	(revision 28484)
@@ -260,10 +260,11 @@
         psMetadataItem *item = NULL;
         while ((item = psListGetAndIncrement(iter))) {
+            psMetadataItem *new = psMetadataItemCopy(item);
             // need to change the name and comment
-            psFree (item->name);
-            item->name = psStringCopy (field);
-            psFree (item->comment);
-            item->comment = psStringCopy (op);
-            if (!psMetadataAddItem(where, item, PS_LIST_TAIL, PS_META_DUPLICATE_OK)) {
+            psFree (new->name);
+            new->name = psStringCopy (field);
+            psFree (new->comment);
+            new->comment = psStringCopy (op);
+            if (!psMetadataAddItem(where, new, PS_LIST_TAIL, PS_META_DUPLICATE_OK)) {
                 psError(psErrorCodeLast(), false, "failed to add item %s", field);
                 psFree(where);
@@ -271,5 +272,5 @@
             }
         }
-        psFree (iter);
+        psFree(iter);
     }
     return true;
@@ -298,4 +299,14 @@
     PXOPT_LOOKUP_STR(data_group, config->args,  "-set_data_group", false, false);
     PXOPT_LOOKUP_STR(note, config->args,        "-set_note", false, false);
+
+    if ((state)&&(!strcmp(state, "update"))) {
+        fprintf(stderr, "'-updaterun -set_state update' is not supported.");
+        if (!strcmp(runTable, "chipRun")) {
+            fprintf(stderr, " Use -setimfiletoupdate.\n");
+        } else {
+            fprintf(stderr, " Use -setskyfiletoupdate.\n");
+        }
+        exit(1);
+    }
 
     psString dist_group = NULL;
Index: branches/pap/ippTools/src/stacktool.c
===================================================================
--- branches/pap/ippTools/src/stacktool.c	(revision 28483)
+++ branches/pap/ippTools/src/stacktool.c	(revision 28484)
@@ -39,5 +39,8 @@
 static bool addsumskyfileMode(pxConfig *config);
 static bool sumskyfileMode(pxConfig *config);
+static bool sassskyfileMode(pxConfig *config);
 static bool revertsumskyfileMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupskyfileMode(pxConfig *config);
@@ -75,5 +78,8 @@
         MODECASE(STACKTOOL_MODE_ADDSUMSKYFILE,         addsumskyfileMode);
         MODECASE(STACKTOOL_MODE_SUMSKYFILE,            sumskyfileMode);
+	MODECASE(STACKTOOL_MODE_SASSSKYFILE,            sassskyfileMode);
         MODECASE(STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileMode);
+	MODECASE(STACKTOOL_MODE_TOSUMMARY,             tosummaryMode);
+	MODECASE(STACKTOOL_MODE_ADDSUMMARY,            addsummaryMode);
         MODECASE(STACKTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
         MODECASE(STACKTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
@@ -102,4 +108,84 @@
     exit(exit_status);
 }
+//stackAssociationRow *association = pxStackAssociationDefine(data_group,tess_id,filter,skycell_id);
+stackAssociationRow *pxStackAssociationDefine(pxConfig *config, psS64 stack_id) {
+  psString select = pxDataGet("stacktool_associationdefine_select.sql");
+  if (!select) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  psString idString = NULL;
+  psStringAppend(&idString, "%" PRId64, stack_id);
+  // Copy string to get around the issue with psStringSubstitute not believing
+  // that select is a psString.
+  psString rep = psStringCopy(select);
+  psFree(select);
+  select = rep;
+  psStringSubstitute(&select, idString, "@STACK_ID@");
+  psFree(idString);
+
+  if (!p_psDBRunQuery(config->dbh, select)) {
+    psError(PS_ERR_UNKNOWN,false, "database error");
+    psFree(select);
+    return(NULL);
+  }
+  psFree(select);
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psErrorCode err = psErrorCodeLast();
+    switch (err) {
+    case PS_ERR_DB_CLIENT:
+      psError(PXTOOLS_ERR_SYS, false, "database error");
+    case PS_ERR_DB_SERVER:
+      psError(PXTOOLS_ERR_PROG, false, "database error");
+    default:
+      psError(PXTOOLS_ERR_PROG, false, "unknown error");
+    }
+    return(NULL);
+  }
+  if (psArrayLength(output) != 1) {
+    psWarning("stacktool: incorrect number of rows found");
+    psFree(output);
+    return(NULL);
+  }
+  psMetadata *outrow = psMetadataAlloc();
+  for (long i = 0; i < output->n; i++) {
+    psMetadata *row = output->data[i];
+
+    printf("%" PRId64 " %s %s %s %s\n",psMetadataLookupS64(NULL,row,"sass_id"),
+	   psMetadataLookupStr(NULL,row,"data_group"),
+	   psMetadataLookupStr(NULL,row,"tess_id"),
+	   psMetadataLookupStr(NULL,row,"filter"),
+	   psMetadataLookupStr(NULL,row,"projection_cell"));
+
+    if (psMetadataLookupS64(NULL,row,"sass_id") == PS_MAX_S64) {
+      psMetadataAddS64(outrow,PS_LIST_TAIL,"sass_id",PS_META_REPLACE,"",0);
+    }
+    else {
+      psMetadataAddS64(outrow,PS_LIST_TAIL,"sass_id",PS_META_REPLACE,"",psMetadataLookupS64(NULL,row,"sass_id"));
+    }
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"data_group",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"data_group"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"tess_id",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"tess_id"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"filter",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"filter"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"projection_cell",PS_META_REPLACE,"",
+		     psMetadataLookupStr(NULL,row,"projection_cell"));
+  }
+  printf("%" PRId64 " %s %s %s %s\n",psMetadataLookupS64(NULL,outrow,"sass_id"),
+	 psMetadataLookupStr(NULL,outrow,"data_group"),
+	 psMetadataLookupStr(NULL,outrow,"tess_id"),
+	 psMetadataLookupStr(NULL,outrow,"filter"),
+	 psMetadataLookupStr(NULL,outrow,"projection_cell"));
+  
+
+  psFree(output);
+  stackAssociationRow *sassRow = stackAssociationObjectFromMetadata(outrow);
+  psFree(outrow);
+  return(sassRow);
+}
+      
+      
+  
+					      
 
 
@@ -151,4 +237,6 @@
     PXOPT_COPY_F32(config->args,  where, "-select_iq_m4_min",          "camProcessedExp.iq_m4", ">=");
     PXOPT_COPY_F32(config->args,  where, "-select_iq_m4_max",          "camProcessedExp.iq_m4", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_zpt_obs_min",        "camProcessedExp.zpt_obs", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_zpt_obs_max",        "camProcessedExp.zpt_obs", "<=");
 
     PXOPT_COPY_STR(config->args,  where, "-select_exp_type",           "rawExp.exp_type", "==");
@@ -370,5 +458,5 @@
             tess_id,
             filter,
-	    NULL, // software_ver
+            NULL, // software_ver
             note);
 
@@ -395,4 +483,48 @@
         psFree(run);
 
+	//CZW Add an association entry here.
+	// Define the requested association, and insert it if it doesn't already exist
+	stackAssociationRow *association = pxStackAssociationDefine(config,stack_id);
+	psS64 sass_id;
+	if (!association->sass_id) {
+	  psTrace("stacktool.association",2,"No required Association found. Adding.");
+
+	  if (!stackAssociationInsertObject(config->dbh,association)) {
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	    psFree(output);
+	    psFree(run);
+	    psFree(insert);
+	    psFree(list);
+	    psFree(association);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	  sass_id = psDBLastInsertID(config->dbh);
+	  association->sass_id = sass_id;
+	}
+	// Insert the map entry for this row.
+	stackAssociationMapRow *maprow = stackAssociationMapRowAlloc(sass_id,stack_id);
+	if (!stackAssociationMapInsertObject(config->dbh,maprow)) {
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  psError(PS_ERR_UNKNOWN, false, "database error");
+	  psFree(output);
+	  psFree(run);
+	  psFree(insert);
+	  psFree(list);
+	  psFree(association);
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  return(false);
+	}
+	
+	
         // Create a suitable insertion query for this run
         psString thisInsert = psStringCopy(insert);
@@ -527,5 +659,5 @@
         tess_id,
         filter,
-	NULL, // software_ver
+        NULL, // software_ver
         note);
 
@@ -552,4 +684,6 @@
     run->stack_id = psDBLastInsertID(config->dbh);
 
+    //CZW Add an association entry here.
+    
     // insert the stackInputSkyfile rows
     psListIterator *iter = psListIteratorAlloc(warp_ids->data.list, 0, false);
@@ -603,7 +737,8 @@
 #endif
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-stack_id",  "stack_id",   "==");
-    PXOPT_COPY_STR(config->args, where, "-label",     "label",     "==");
-    PXOPT_COPY_STR(config->args, where, "-state",     "state",     "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id",  "stackRun.stack_id",   "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "stackRun.label",     "==");
+    PXOPT_COPY_STR(config->args, where, "-state",     "stackRun.state",     "==");
+    PXOPT_COPY_STR(config->args, where, "-sass_id",   "stackAssociationMap.sass_id",  "==");
     if (!psListLength(where->list)) {
         psFree(where);
@@ -612,4 +747,5 @@
     }
 
+    //CZW join against stackAssociationMap
     psString query = psStringCopy("UPDATE stackRun");
 
@@ -854,5 +990,5 @@
 
     psTrace("czw.test",1,"Received versions: pslib %s psmodules %s psphot %s ppstats %s ppstack %s streaks %s\n",
-	    ver_pslib,ver_psmodules,ver_psphot,ver_ppstats,ver_ppstack,ver_streaks);
+            ver_pslib,ver_psmodules,ver_psphot,ver_ppstats,ver_ppstack,ver_streaks);
     psString software_ver = NULL;
     if ((ver_pslib)&&(ver_psmodules)) {
@@ -871,5 +1007,5 @@
       software_ver = pxMergeCodeVersions(software_ver,ver_streaks);
     }
-    
+
     // default values
     PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
@@ -911,5 +1047,5 @@
                                good_frac,
                                fault,
-			       software_ver,
+                               software_ver,
                                quality
           )) {
@@ -922,13 +1058,13 @@
 
     if (fault == 0) {
-	// Set stackRun software if we are finished.
-	if (!pxSetRunSoftware(config, "stackRun", "stack_id", stack_id, software_ver)) {
-	  if (!psDBRollback(config->dbh)) {
-	    psError(PS_ERR_UNKNOWN, false, "database error");
-	  }
-	  psError(PS_ERR_UNKNOWN, false, "failed to set stackRun.software_ver for stack_id: %" PRId64,
-		  stack_id);
-	  return(false);
-	}
+        // Set stackRun software if we are finished.
+        if (!pxSetRunSoftware(config, "stackRun", "stack_id", stack_id, software_ver)) {
+          if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+          }
+          psError(PS_ERR_UNKNOWN, false, "failed to set stackRun.software_ver for stack_id: %" PRId64,
+                  stack_id);
+          return(false);
+        }
 
         if (!setstackRunState(config, stack_id, "full")) {
@@ -1038,23 +1174,27 @@
 }
 
-
-static bool revertsumskyfileMode(pxConfig *config)
+static bool sassskyfileMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
-    pxAddLabelSearchArgs(config, where, "-label", "stackRun.label", "==");
-    PXOPT_COPY_S16(config->args, where, "-fault", "stackSumSkyfile.fault", "==");
-
-    if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
-        psFree(where);
-        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
-        return false;
-    }
-
-    // Delete product
-    psString delete = pxDataGet("stacktool_revertsumskyfile_delete.sql");
-    if (!delete) {
+    PXOPT_COPY_S64(config->args, where, "-sass_id", "stackAssociation.sass_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "stackAssociation.tess_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-projection_cell", "stackAssociation.projection_cell", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "stackAssociation.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "stackAssociation.data_group", "LIKE");
+
+//  The following three selectors are incompatible with the sql so omit them
+//    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpRun.warp_id", "==");
+//     PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+//    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("stacktool_sassskyfile.sql");
+    if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
@@ -1063,88 +1203,11 @@
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&delete, " AND %s", whereClause);
+        psStringAppend(&query, " WHERE %s", whereClause);
         psFree(whereClause);
-    }
-
-    if (!p_psDBRunQuery(config->dbh, delete)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(delete);
-        psFree(where);
-        return false;
-    }
-    psFree(delete);
-
-    int numRows = psDBAffectedRows(config->dbh); // Number of row affected
-    psLogMsg("stacktool", PS_LOG_INFO, "Deleted %d rows", numRows);
-
-    psFree(where);
-
-    return true;
-}
-
-
-static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state)
-{
-    PS_ASSERT_PTR_NON_NULL(state, false);
-
-    // check that state is a valid string value
-    if (!pxIsValidState(state)) {
-        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
-        return false;
-    }
-
-    char *query = "UPDATE stackRun SET state = '%s' WHERE stack_id = %"PRId64;
-    if (!p_psDBRunQueryF(config->dbh, query, state, stack_id)) {
-        psError(PS_ERR_UNKNOWN, false,
-                "failed to change state for stack_id %"PRId64, stack_id);
-        return false;
-    }
-
-    return true;
-}
-
-#ifdef notdef
-static bool setstackRunStateByLabel(pxConfig *config, const char *label, const char *state)
-{
-    PS_ASSERT_PTR_NON_NULL(state, false);
-
-    // check that state is a valid string value
-    if (!pxIsValidState(state)) {
-        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
-        return false;
-    }
-
-    char *query = "UPDATE stackRun SET state = '%s' WHERE label = '%s'";
-    if (!p_psDBRunQueryF(config->dbh, query, state, label)) {
-        psError(PS_ERR_UNKNOWN, false,
-                "failed to change state for label %s", label);
-        return false;
-    }
-
-    return true;
-}
-#endif
-
-static bool pendingcleanuprunMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, NULL);
-
-    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
-    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
-
-    psMetadata *where = psMetadataAlloc();
-    pxAddLabelSearchArgs (config, where, "-label", "stackRun.label", "==");
-
-    psString query = pxDataGet("stacktool_pendingcleanuprun.sql");
-    if (!query) {
-        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    if (where && psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&query, " AND %s", whereClause);
-        psFree(whereClause);
-    }
+    } else if (!all) {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        return false;
+    }
+
     psFree(where);
 
@@ -1165,5 +1228,14 @@
     psArray *output = p_psDBFetchResult(config->dbh);
     if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
         return false;
     }
@@ -1174,31 +1246,233 @@
     }
 
+    if (psArrayLength(output)) {
+        if (!ippdbPrintMetadatas(stdout, output, "stackSumSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertsumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "stackRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "stackSumSkyfile.fault", "==");
+
+    if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    // Delete product
+    psString delete = pxDataGet("stacktool_revertsumskyfile_delete.sql");
+    if (!delete) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&delete, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, delete)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(delete);
+        psFree(where);
+        return false;
+    }
+    psFree(delete);
+
+    int numRows = psDBAffectedRows(config->dbh); // Number of row affected
+    psLogMsg("stacktool", PS_LOG_INFO, "Deleted %d rows", numRows);
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE stackRun SET state = '%s' WHERE stack_id = %"PRId64;
+    if (!p_psDBRunQueryF(config->dbh, query, state, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for stack_id %"PRId64, stack_id);
+        return false;
+    }
+
+    return true;
+}
+
+#ifdef notdef
+static bool setstackRunStateByLabel(pxConfig *config, const char *label, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE stackRun SET state = '%s' WHERE label = '%s'";
+    if (!p_psDBRunQueryF(config->dbh, query, state, label)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for label %s", label);
+        return false;
+    }
+
+    return true;
+}
+#endif
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-stack_id",    "stackSumSkyfile.warp_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-sass_id",     "stackAssociationMap.sass_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "stackSumSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "stackRun.state", "==");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "stackRun.filter", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-label",   "stackRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "stackRun.data_group", "LIKE");
+  
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("stacktool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all) {
+    psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+    return false;
+  }
+  
+  psFree(where);
+
+  // treat limit == 0 as "no limit"
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, " %s", limitString);
+    psFree(limitString);
+  }
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psErrorCode err = psErrorCodeLast();
+    switch (err) {
+    case PS_ERR_DB_CLIENT:
+      psError(PXTOOLS_ERR_SYS, false, "database error");
+    case PS_ERR_DB_SERVER:
+      psError(PXTOOLS_ERR_PROG, false, "database error");
+    default:
+      psError(PXTOOLS_ERR_PROG, false, "unknown error");
+    }
+    
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("stacktool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupRun", !simple)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
-
-    psFree(output);
-
-    return true;
-}
-
-static bool pendingcleanupskyfileMode(pxConfig *config)
+    if (!ippdbPrintMetadatas(stdout, output, "stackRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(sass_id, config->args, "-sass_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("stacktool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, sass_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
-    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psMetadata *where = psMetadataAlloc();
-    if (stack_id) {
-        PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
-    }
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackRun.stack_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-sass_id", "stackAssociationMap.sass_id", "==");
+    
     pxAddLabelSearchArgs (config, where, "-label", "stackRun.label", "==");
 
-    psString query = pxDataGet("stacktool_pendingcleanupskyfile.sql");
+    psString query = pxDataGet("stacktool_pendingcleanuprun.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -1239,5 +1513,5 @@
 
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupSkyfile", !simple)) {
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupRun", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
         psFree(output);
@@ -1250,16 +1524,19 @@
 }
 
-
-static bool donecleanupMode(pxConfig *config)
+static bool pendingcleanupskyfileMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
-
-    psString query = pxDataGet("stacktool_donecleanup.sql");
+    if (stack_id) {
+        PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+    }
+    pxAddLabelSearchArgs (config, where, "-label", "stackRun.label", "==");
+
+    psString query = pxDataGet("stacktool_pendingcleanupskyfile.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -1300,4 +1577,66 @@
 
     // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupSkyfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_STR(config->args, where, "-sass_id", "sass_id", "==");
+
+    psString query = pxDataGet("stacktool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
     if (!ippdbPrintMetadatas(stdout, output, "stackDoneCleanup", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
@@ -1329,4 +1668,5 @@
 }
 
+//CZW I have not added sass information to the export/import run modes yet.
 bool exportrunMode(pxConfig *config)
 {
Index: branches/pap/ippTools/src/stacktool.h
===================================================================
--- branches/pap/ippTools/src/stacktool.h	(revision 28483)
+++ branches/pap/ippTools/src/stacktool.h	(revision 28484)
@@ -33,5 +33,8 @@
     STACKTOOL_MODE_ADDSUMSKYFILE,
     STACKTOOL_MODE_SUMSKYFILE,
+    STACKTOOL_MODE_SASSSKYFILE,
     STACKTOOL_MODE_REVERTSUMSKYFILE,
+    STACKTOOL_MODE_TOSUMMARY,
+    STACKTOOL_MODE_ADDSUMMARY,
     STACKTOOL_MODE_PENDINGCLEANUPRUN,
     STACKTOOL_MODE_PENDINGCLEANUPSKYFILE,
Index: branches/pap/ippTools/src/stacktoolConfig.c
===================================================================
--- branches/pap/ippTools/src/stacktoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/stacktoolConfig.c	(revision 28484)
@@ -88,4 +88,6 @@
     psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_iq_m4_min", 0, "define min iq_m4", NAN);
     psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_iq_m4_max", 0, "define max iq_m4", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_zpt_obs_min", 0, "define min zero point", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_zpt_obs_max", 0, "define max zero point", NAN);
     psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-random", 0, "use this number of random elements", 0);
     psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-min_num", 0, "minimum number of inputs", 0);
@@ -197,4 +199,16 @@
     psMetadataAddBool(sumskyfileArgs, PS_LIST_TAIL, "-all",  0,            "enable search without arguments", false);
 
+    // -sassskyfile
+    psMetadata *sassskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(sassskyfileArgs, PS_LIST_TAIL, "-sass_id", 0,           "search by stack association ID", 0);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-tess_id", 0,            "search by tess ID", 0);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-projection_cell", 0,         "search by projection cell", 0);
+
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-data_group", 0,        "search by stackAssociation.data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-filter", 0,            "search by filter (LIKE comparison)", NULL);
+    psMetadataAddU64(sassskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(sassskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(sassskyfileArgs, PS_LIST_TAIL, "-all",  0,            "enable search without arguments", false);
+
     // -revertsumskyfile
     psMetadata *revertsumskyfileArgs= psMetadataAlloc();
@@ -204,4 +218,25 @@
     psMetadataAddBool(revertsumskyfileArgs, PS_LIST_TAIL, "-all",  0, "allow no search terms", 0);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-stack_id", 0,  "search by stack ID", 0);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-sass_id", 0,  "search by stack association ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-tess_id", 0,   "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state", 0,     "search by state", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-filter", 0,    "search by filter", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by stackRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by stackRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-dist_group", PS_META_DUPLICATE_OK, "search by stackRun dist_group (LIKE comparison)", NULL);
+
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL, "-sass_id", 0,      "set stack Association ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0, "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0,     "set summary path base", NULL);		     
+    
     // -pendingcleanuprun
     psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
@@ -252,4 +287,5 @@
     PXOPT_ADD_MODE("-addsumskyfile",   "", STACKTOOL_MODE_ADDSUMSKYFILE,   addsumskyfileArgs);
     PXOPT_ADD_MODE("-sumskyfile",      "list results of stackRun", STACKTOOL_MODE_SUMSKYFILE,      sumskyfileArgs);
+    PXOPT_ADD_MODE("-sassskyfile",      "list results of stackAssociation", STACKTOOL_MODE_SASSSKYFILE,      sassskyfileArgs);
     PXOPT_ADD_MODE("-revertsumskyfile","", STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileArgs);
     PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", STACKTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
@@ -257,4 +293,6 @@
     PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     STACKTOOL_MODE_DONECLEANUP,          donecleanupArgs);
     PXOPT_ADD_MODE("-updatesumskyfile",      "update fault code for sumskyfile",     STACKTOOL_MODE_UPDATESUMSKYFILE,          updatesumskyfileArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", STACKTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", STACKTOOL_MODE_ADDSUMMARY, addsummaryArgs);
     PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", STACKTOOL_MODE_EXPORTRUN, exportrunArgs);
     PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           STACKTOOL_MODE_IMPORTRUN, importrunArgs);
Index: branches/pap/ippTools/src/warptool.c
===================================================================
--- branches/pap/ippTools/src/warptool.c	(revision 28483)
+++ branches/pap/ippTools/src/warptool.c	(revision 28484)
@@ -48,4 +48,6 @@
 static bool maskedMode(pxConfig *config);
 static bool unblockMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupwarpMode(pxConfig *config);
@@ -101,4 +103,6 @@
         MODECASE(WARPTOOL_MODE_MASKED,             maskedMode);
         MODECASE(WARPTOOL_MODE_UNBLOCK,            unblockMode);
+	MODECASE(WARPTOOL_MODE_TOSUMMARY,          tosummaryMode);
+	MODECASE(WARPTOOL_MODE_ADDSUMMARY,         addsummaryMode);
         MODECASE(WARPTOOL_MODE_PENDINGCLEANUPRUN,  pendingcleanuprunMode);
         MODECASE(WARPTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupwarpMode);
@@ -426,5 +430,5 @@
     }
 
-    psString query = psStringCopy("UPDATE warpRun JOIN warpSkyfile USING(warp_id) JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id)");
+    psString query = psStringCopy("UPDATE warpRun");
 
     // pxUpdateRun gets parameters from config->args and updates
@@ -1548,4 +1552,133 @@
 }
 
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-warp_id",    "warpSkyfile.warp_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "warpSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "warpRun.state", "==");
+  PXOPT_COPY_S64(config->args, where, "-exp_id",     "rawExp.exp_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-exp_name",   "rawExp.exp_name", "==");
+  PXOPT_COPY_S64(config->args, where, "-fake_id",    "fakeRun.fake_id", "==");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs",  ">=");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_end",   "rawExp.dateobs",  "<=");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "rawExp.filter", "LIKE");
+  PXOPT_COPY_S64(config->args, where, "-magicked", "warpSkyfile.magicked", "==");
+  pxAddLabelSearchArgs (config, where, "-label",   "warpRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "warpRun.data_group", "LIKE");
+
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("warptool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  psString where2 = NULL;
+  if (!pxmagicAddWhere(config, &where2, "warpSkyfile")) {
+    psError(psErrorCodeLast(), false, "pxMagicAddWhere failed");
+    return false;
+  }
+  
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all && !where2) {
+    psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+    return false;
+  }
+  
+  if (where2) {
+    psStringAppend(&query, " %s", where2);
+  }
+  psFree(where);
+
+  // treat limit == 0 as "no limit"
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, " %s", limitString);
+    psFree(limitString);
+  }
+  
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psErrorCode err = psErrorCodeLast();
+    switch (err) {
+    case PS_ERR_DB_CLIENT:
+      psError(PXTOOLS_ERR_SYS, false, "database error");
+    case PS_ERR_DB_SERVER:
+      psError(PXTOOLS_ERR_PROG, false, "database error");
+    default:
+      psError(PXTOOLS_ERR_PROG, false, "unknown error");
+    }
+    
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("warptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("warptool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, warp_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
 static bool pendingcleanuprunMode(pxConfig *config)
 {
Index: branches/pap/ippTools/src/warptool.h
===================================================================
--- branches/pap/ippTools/src/warptool.h	(revision 28483)
+++ branches/pap/ippTools/src/warptool.h	(revision 28484)
@@ -44,4 +44,6 @@
     WARPTOOL_MODE_MASKED,
     WARPTOOL_MODE_UNBLOCK,
+    WARPTOOL_MODE_TOSUMMARY,
+    WARPTOOL_MODE_ADDSUMMARY,
     WARPTOOL_MODE_PENDINGCLEANUPRUN,
     WARPTOOL_MODE_PENDINGCLEANUPSKYFILE,
Index: branches/pap/ippTools/src/warptoolConfig.c
===================================================================
--- branches/pap/ippTools/src/warptoolConfig.c	(revision 28483)
+++ branches/pap/ippTools/src/warptoolConfig.c	(revision 28484)
@@ -321,4 +321,32 @@
     psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "search by warp ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-tess_id",  0,          "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state",  0,            "search by state", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure tag", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", 0);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-fake_id", 0,           "search by phase 3 version of exposure tag", 0);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_begin", 0,    "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_end", 0,      "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-filter", 0,           "search for exposures by filter", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-magicked", 0,         "search by magic id", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-label",  PS_META_DUPLICATE_OK, "search by warpRun label", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-data_group",  PS_META_DUPLICATE_OK, "search by warpRun data_group", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-dist_group",  PS_META_DUPLICATE_OK, "search by warpRun dist_group", NULL);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-not_destreaked", 0, "search for runs that have not been destreaked", false);
+    
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "set warp ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0,   "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0,       "set summary path base", NULL);
+    
     // -pendingcleanuprun
     psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
@@ -430,4 +458,7 @@
     PXOPT_ADD_MODE("-updateskyfile", "update fault code for skyfile", WARPTOOL_MODE_UPDATESKYFILE, updateskyfileArgs);
     PXOPT_ADD_MODE("-setskyfiletoupdate", "set cleaned skyfile to be updated", WARPTOOL_MODE_SETSKYFILETOUPDATE, setskyfiletoupdateArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", WARPTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", WARPTOOL_MODE_ADDSUMMARY, addsummaryArgs);
+
     PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", WARPTOOL_MODE_EXPORTRUN, exportrunArgs);
     PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           WARPTOOL_MODE_IMPORTRUN, importrunArgs);
