Index: branches/eam_branches/ipp-20130307/ippTools/src/addtool.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/addtool.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/addtool.c	(revision 35350)
@@ -876,5 +876,5 @@
         psTrace("addtool", PS_LOG_INFO, "no rows found");
         psFree(output);
-        return true;
+        return false;
     }
 
Index: branches/eam_branches/ipp-20130307/ippTools/src/camtool.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/camtool.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/camtool.c	(revision 35350)
@@ -599,5 +599,5 @@
         psTrace("camtool", PS_LOG_INFO, "no rows found");
         psFree(output);
-        return true;
+        return false;
     }
 
Index: branches/eam_branches/ipp-20130307/ippTools/src/laptool.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/laptool.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/laptool.c	(revision 35350)
@@ -34,4 +34,12 @@
 static bool inactiveexpMode(pxConfig *config);
 
+// Groups
+static bool definegroupMode(pxConfig *config);
+static bool pendinggroupMode(pxConfig *config);
+static bool filtersforgroupMode(pxConfig *config);
+static bool updategroupMode(pxConfig *config);
+static bool revertgroupMode(pxConfig *config);
+static bool listgroupMode(pxConfig *config);
+
 # define MODECASE(caseName, func) \
   case caseName: \
@@ -67,4 +75,12 @@
     
     MODECASE(LAPTOOL_MODE_INACTIVEEXP,   inactiveexpMode);
+
+    MODECASE(LAPTOOL_MODE_DEFINEGROUP,   definegroupMode);
+    MODECASE(LAPTOOL_MODE_PENDINGGROUP,  pendinggroupMode);
+    MODECASE(LAPTOOL_MODE_FILTERSFORGROUP, filtersforgroupMode);
+    MODECASE(LAPTOOL_MODE_UPDATEGROUP,   updategroupMode);
+    MODECASE(LAPTOOL_MODE_REVERTGROUP,   revertgroupMode);
+    MODECASE(LAPTOOL_MODE_LISTGROUP,     listgroupMode);
+
   default:
     psAbort("invalid option (this should not happen)");
@@ -900,3 +916,443 @@
   return(true);
 }
-
+// ---------------------------
+// Group level (a collection of completed lapRuns for a given lapSequence projection cell and collection of filters
+
+static bool definegroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  PXOPT_LOOKUP_S64(seq_id,          config->args, "-seq_id",    true, false);
+  PXOPT_LOOKUP_STR(label,           config->args, "-set_label", true, false);
+
+  PXOPT_LOOKUP_BOOL(pretend,        config->args, "-pretend",    false);
+  PXOPT_LOOKUP_BOOL(simple,         config->args, "-simple",    false);
+  PXOPT_LOOKUP_U64(limit,           config->args, "-limit",     false, false);
+
+  // the following insures that the -filter argument has been set up properly (adapted from pxAddLabelSearchArgs)
+  psMetadataItem *item = psMetadataLookup(config->args, "-filter");
+  psAssert (item, "-filter argument not found in config->args");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "%s should be a multi container", "filter");
+  psAssert (item->data.list->n, "%s should at least have a place-holder", "filter");
+  psMetadataItem *entry = (psMetadataItem *)item->data.list->head->data;
+  psAssert (entry, "%s should at least have a place-holder", "filter");
+  // end of checking
+
+  // Now if the ony entry is the place-holder then the user supplied no -filter arguments
+  // which are required
+  if (!entry->data.str) {
+    psError(PXTOOLS_ERR_ARGUMENTS, true, "at least one -filter is required");
+    return false;
+  }
+
+  int nFilters = item->data.list->n;
+
+  psString query = psStringCopy("SELECT seq_id, tess_id, projection_cell,\n");
+
+  // We construct a query joining completed lap runs with the same lapSequence.seq_id,
+  // tess_id, and projection_cell 
+
+  // select the lap_ids for the various filters. These are only used for debugging.
+  psStringAppend(&query, "lap_id_%d", 0);
+  for (int i = 1; i < nFilters; i++) {
+    psStringAppend(&query, ", lap_id_%d", i);
+  }
+
+
+  // sub query for each supplied filter
+  char * lapRunForFilter = "(\nSELECT seq_id, tess_id, projection_cell, lap_id as 'lap_id_%d'\n"
+    "FROM lapRun\n"
+    "WHERE filter LIKE '%s'\n"
+    "   AND lapRun.seq_id = %"PRId64"\n"
+    "   AND (lapRun.state = 'done' or lapRun.state = 'full')\n"
+    " ) as lap_%d\n";
+
+  // loop over supplied filters and flesh out the query using the format above
+  psListIterator *iter = psListIteratorAlloc (item->data.list, PS_LIST_HEAD, true);
+  psMetadataItem *filterItem = NULL;
+  int i = -1;
+  while ((filterItem = psListGetAndIncrement(iter))) {
+    ++i;
+    if (i == 0) {
+      psStringAppend(&query, "\nFROM\n");
+    } else {
+      psStringAppend(&query, "\nJOIN\n");
+    }
+
+    psString filter = filterItem->data.str;
+    psStringAppend(&query, lapRunForFilter, i, filter, seq_id, i);
+
+    if (i != 0) {
+      psStringAppend(&query, "USING (seq_id, tess_id, projection_cell)\n");
+    }
+  }
+  // now join to lapGroup
+  psStringAppend(&query, "\nLEFT JOIN lapGroup USING(seq_id, tess_id, projection_cell)\n");
+
+  // we only want projection cells which do not already of an entry
+  psStringAppend(&query, "\nWHERE lapGroup.projection_cell IS NULL\n");
+  psStringAppend(&query, "\nORDER by projection_cell\n");
+
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, "\n %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("laptool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+  }
+
+  if (pretend) {
+    if (!ippdbPrintMetadatas(stdout, output, "new_lapGroups", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+    return true;
+  }
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+  psString last_projection_cell = NULL;
+  for (long i = 0; i < psArrayLength(output); i++) {
+    psMetadata *row = output->data[i];
+
+    psString tess_id = psMetadataLookupStr(NULL, row, "tess_id");
+    psString projection_cell = psMetadataLookupStr(NULL, row, "projection_cell");
+    if (last_projection_cell && !strcmp(last_projection_cell, projection_cell)) {
+        // duplicate lap runs for a filter will generate multiple rows
+        // Since we care about projection_cells not lapRuns per se this is not problem.
+        // Skip any duplicates.
+        continue;
+    }
+    last_projection_cell = projection_cell;
+
+    lapGroupRow *group = lapGroupRowAlloc(
+                                  seq_id,
+				  tess_id,
+				  projection_cell,
+				  "new",  // state
+                                  label,
+                                  now,    // registered
+				  0       // fault
+				  );
+    if (!group) {
+      psError(PS_ERR_UNKNOWN, false, "failed to alloc lapGroup object");
+      psFree(output);
+      psFree(now);
+      return(false);
+    }
+
+
+    if (!lapGroupInsertObject(config->dbh, group)) {
+      if (!psDBRollback(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+      }
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(output);
+      return(true);
+    }
+  }
+
+  // point of no return
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psFree(now);
+  psFree(output);
+
+  return(true);  
+}
+
+static bool pendinggroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-seq_id", "seq_id", "==");
+  pxAddLabelSearchArgs(config, where, "-label", "lapRun.label", "==");
+  
+  psString query = pxDataGet("laptool_pendinggroup.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+    return false;
+  }
+
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  }
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, "\n %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("laptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  if (psArrayLength(output)) {
+    if (!ippdbPrintMetadatas(stdout, output, "lapGroup", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+
+  psFree(output);
+
+  return true;
+}
+static bool filtersforgroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-seq_id", "seq_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id", "tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-projection_cell", "projection_cell", "==");
+  
+  psString query = pxDataGet("laptool_filtersforgroup.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+    return false;
+  }
+
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " WHERE %s", whereClause);
+  psFree(whereClause);
+
+  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("laptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  if (psArrayLength(output)) {
+    if (!ippdbPrintMetadatas(stdout, output, "filters", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+
+  psFree(output);
+
+  return true;
+}
+
+static bool updategroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  // check that all of the keys that define a lapGroup are supplied
+  PXOPT_LOOKUP_S64(seq_id, config->args, "-seq_id", true, false);
+  PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-seq_id", "seq_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id", "tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-projection_cell", "projection_cell", "==");
+
+  psMetadata *values = psMetadataAlloc();
+  PXOPT_COPY_STR(config->args, values, "-set_state", "state", "==");
+  PXOPT_COPY_STR(config->args, values, "-set_label", "label", "==");
+  PXOPT_COPY_S16(config->args, values, "-set_fault", "fault", "==");
+
+  if (!psListLength(values->list)) {
+    psFree(values);
+    psFree(where);
+    psError(PXTOOLS_ERR_ARGUMENTS, true, "must set at least one column");
+    return false;
+  }
+
+  long rows = psDBUpdateRows(config->dbh, "lapGroup", where, values);
+  if (rows < 1) {
+    psFree(values);
+    psError(PXTOOLS_ERR_SYS, true, "failed to update lapGroup for %" PRId64 " %s %s", seq_id, tess_id, projection_cell);
+    return false;
+  }
+  psFree(values);
+
+  return(true);
+}
+
+static bool revertgroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  psMetadata *where = psMetadataAlloc();
+  pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+  PXOPT_COPY_S64(config->args, where, "-seq_id", "seq_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id", "tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-projection_cell", "projection_cell", "==");
+
+  if (!psListLength(where->list)) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search arguments are required");
+    return false;
+  }
+
+  psString query = pxDataGet("laptool_revertgroup.sql");
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " AND %s", whereClause);
+  psFree(whereClause);
+  psFree(where);
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psU64 affected = psDBAffectedRows(config->dbh);
+  psLogMsg("laptool", PS_LOG_INFO, "Updated %" PRIu64 " lapGroups", affected);
+
+  return true;
+}
+
+static bool listgroupMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  psError(PXTOOLS_ERR_SYS, true, "not yet implemented");
+  return false;
+
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-seq_id", "seq_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-seq_name",   "name",   "LIKE");
+
+  psString query = pxDataGet("laptool_listsequence.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retrieve 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) {
+    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("laptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  if (psArrayLength(output)) {
+    if (!ippdbPrintMetadatas(stdout, output, "lapSequence", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+
+  psFree(output);
+
+  return true;
+}
Index: branches/eam_branches/ipp-20130307/ippTools/src/laptool.h
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/laptool.h	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/laptool.h	(revision 35350)
@@ -20,5 +20,11 @@
   LAPTOOL_MODE_UPDATEEXP,
   LAPTOOL_MODE_DIFFCHECK,
-  LAPTOOL_MODE_INACTIVEEXP
+  LAPTOOL_MODE_INACTIVEEXP,
+  LAPTOOL_MODE_DEFINEGROUP,
+  LAPTOOL_MODE_PENDINGGROUP,
+  LAPTOOL_MODE_FILTERSFORGROUP,
+  LAPTOOL_MODE_UPDATEGROUP,
+  LAPTOOL_MODE_REVERTGROUP,
+  LAPTOOL_MODE_LISTGROUP,
 } laptoolMode;
 
Index: branches/eam_branches/ipp-20130307/ippTools/src/laptoolConfig.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/laptoolConfig.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/laptoolConfig.c	(revision 35350)
@@ -97,5 +97,4 @@
   ADD_OPT(S64, updaterunArgs, "-set_quick_sass_id",           "set quick stack sass_id", 0);
   ADD_OPT(S64, updaterunArgs, "-set_final_sass_id",           "set final stack sass_id", 0);
-  
   
   // -pendingexp
@@ -150,4 +149,52 @@
   ADD_OPT(U64, inactiveexpArgs, "-limit",                     "limit result set to N items", 0);
   
+  // -definegroup
+  psMetadata *definegroupArgs = psMetadataAlloc();
+  ADD_OPT(S64, definegroupArgs, "-seq_id",                    "search by lap sequence ID (required)", 0);
+  ADD_OPT(Str, definegroupArgs, "-set_label",                 "set label (required)", 0);
+
+  psMetadataAddStr(definegroupArgs,  PS_LIST_TAIL, 
+                                "-filter", PS_META_DUPLICATE_OK, 
+                                                              "search by filter (LIKE comparison, multiple OK)", NULL);
+  ADD_OPT(Bool,definegroupArgs, "-pretend",                   "do not actuallym modify the database", false);
+  ADD_OPT(Bool,definegroupArgs, "-simple",                    "use the simple output format", false);
+  ADD_OPT(U64, definegroupArgs, "-limit",                     "limit result set to N items", 0);
+
+  // -pendinggroup
+  psMetadata *pendinggroupArgs = psMetadataAlloc();
+  psMetadataAddStr(pendinggroupArgs,  PS_LIST_TAIL, 
+                                "-label", PS_META_DUPLICATE_OK, 
+                                                              "search by label (multiple OK)", NULL);
+  ADD_OPT(S64, pendinggroupArgs, "-seq_id",                    "search by lap sequence ID (required)", 0);
+
+  ADD_OPT(Bool,pendinggroupArgs, "-simple",                    "use the simple output format", false);
+  ADD_OPT(U64, pendinggroupArgs, "-limit",                     "limit result set to N items", 0);
+
+  // -updategroup
+  psMetadata *updategroupArgs = psMetadataAlloc();
+  ADD_OPT(S64, updategroupArgs, "-seq_id",                    "search by lap sequence ID (required)", 0);
+  ADD_OPT(Str, updategroupArgs, "-tess_id",                   "search by tess_id (required)", 0);
+  ADD_OPT(Str, updategroupArgs, "-projection_cell",           "search by projection_cell (required)", 0);
+  ADD_OPT(Str, updategroupArgs, "-set_state",                 "set state", 0);
+  ADD_OPT(S16, updategroupArgs, "-set_fault",                 "set fault code", INT16_MAX);
+  ADD_OPT(Str, updategroupArgs, "-set_label",                 "set label", 0);
+
+  // -revertgroup
+  psMetadata *revertgroupArgs = psMetadataAlloc();
+  psMetadataAddStr(revertgroupArgs,  PS_LIST_TAIL, 
+                                "-label", PS_META_DUPLICATE_OK, 
+                                                              "search by label (multiple OK)", NULL);
+  ADD_OPT(S64, revertgroupArgs, "-seq_id",                    "search by lap sequence ID", 0);
+  ADD_OPT(Str, revertgroupArgs, "-tess_id",                   "search by tess_id", 0);
+  ADD_OPT(Str, revertgroupArgs, "-projection_cell",           "search by projection_cell", 0);
+  ADD_OPT(S16, revertgroupArgs, "-fault",                     "fault code", INT16_MAX);
+
+  // -filtersforgroup
+  psMetadata *filtersforgroupArgs = psMetadataAlloc();
+  ADD_OPT(S64, filtersforgroupArgs, "-seq_id",                    "search by lap sequence ID (required)", 0);
+  ADD_OPT(Str, filtersforgroupArgs, "-tess_id",                   "search by tess_id (required)", 0);
+  ADD_OPT(Str, filtersforgroupArgs, "-projection_cell",           "search by projection_cell (required)", 0);
+  ADD_OPT(Bool,filtersforgroupArgs, "-simple",                    "use the simple output format", false);
+
   
   psMetadata *argSets = psMetadataAlloc();
@@ -166,4 +213,9 @@
   PXOPT_ADD_MODE("-diffcheck",               "", LAPTOOL_MODE_DIFFCHECK,        diffcheckArgs);
   PXOPT_ADD_MODE("-inactiveexp",             "", LAPTOOL_MODE_INACTIVEEXP,      inactiveexpArgs);
+  PXOPT_ADD_MODE("-definegroup",             "", LAPTOOL_MODE_DEFINEGROUP,      definegroupArgs);
+  PXOPT_ADD_MODE("-pendinggroup",            "", LAPTOOL_MODE_PENDINGGROUP,     pendinggroupArgs);
+  PXOPT_ADD_MODE("-filtersforgroup",         "", LAPTOOL_MODE_FILTERSFORGROUP,  filtersforgroupArgs);
+  PXOPT_ADD_MODE("-updategroup",             "", LAPTOOL_MODE_UPDATEGROUP,      updategroupArgs);
+  PXOPT_ADD_MODE("-revertgroup",             "", LAPTOOL_MODE_REVERTGROUP,      revertgroupArgs);
   
   if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: branches/eam_branches/ipp-20130307/ippTools/src/pstamptool.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/pstamptool.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/pstamptool.c	(revision 35350)
@@ -287,4 +287,9 @@
     PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id", false, false);
 
+    PXOPT_LOOKUP_STR(username,    config->args, "-username",  false, false);
+    PXOPT_LOOKUP_S64(proj_id,     config->args, "-proj_id", false, false);
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
     if (!pstampRequestInsert(config->dbh,
         0,      // req_id
@@ -297,5 +302,8 @@
         uri,
         NULL,   // outdir
-        NULL,   // timestamp
+        username,
+        proj_id,
+        now,    // registered
+        now,    // timestamp
         0       // fault
         )) {
@@ -444,5 +452,7 @@
     PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
     PXOPT_COPY_S64(config->args, where, "-not_req_id", "req_id", "!=");
-    PXOPT_COPY_STR(config->args, where, "-name", "name", "==");
+    PXOPT_COPY_STR(config->args, where, "-name", "name", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-username", "username", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
 
     PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
@@ -450,5 +460,5 @@
 
     if (!psListLength(where->list)) {
-        psError(PS_ERR_UNKNOWN, true, "-req_id or -name must be supplied");
+        psError(PS_ERR_UNKNOWN, true, "search paramters are required");
         return false;
     }
@@ -482,8 +492,8 @@
     if (!psArrayLength(output)) {
         psTrace("pstamptool", PS_LOG_INFO, "request not found");
-        // This causes main to exit with PS_EXIT_DATA_ERROR which the script is looking for
+        // This causes main to exit with PS_EXIT_DATA_ERROR which the pstamp scripts are looking for
         psError(PXTOOLS_ERR_CONFIG, true, "request not found");
         psFree(output);
-        // we return false so that the caller can determine that a request does not exist
+        // we return false so that the caller can easily determine that a request does not exist
         return false;
     }
@@ -567,12 +577,13 @@
     PXOPT_LOOKUP_STR(label,      config->args, "-set_label",      false, false);
     PXOPT_LOOKUP_STR(outProduct, config->args, "-set_outProduct", false, false);
-    PXOPT_LOOKUP_S16(fault,      config->args, "-set_fault",          false, false);
+    PXOPT_LOOKUP_S16(fault,      config->args, "-set_fault",      false, false);
     PXOPT_LOOKUP_STR(uri,        config->args, "-set_uri",        false, false);
-    PXOPT_LOOKUP_STR(outdir,     config->args, "-set_outdir",        false, false);
+    PXOPT_LOOKUP_STR(outdir,     config->args, "-set_outdir",     false, false);
     PXOPT_LOOKUP_STR(name,       config->args, "-set_name",       false, false);
+    PXOPT_LOOKUP_STR(username,   config->args, "-set_username",   false, false);
     PXOPT_LOOKUP_STR(reqType,    config->args, "-set_reqType",    false, false);
-    PXOPT_LOOKUP_BOOL(clearfault,config->args, "-clearfault",    false);
-
-    if (!state && !label && !outProduct && !fault && !uri && !outdir && !name && !reqType && !clearfault) {
+    PXOPT_LOOKUP_BOOL(clearfault,config->args, "-clearfault",     false);
+
+    if (!state && !label && !outProduct && !fault && !uri && !outdir && !name && !username && !reqType && !clearfault) {
         psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
         return false;
@@ -584,6 +595,7 @@
     PXOPT_COPY_S32(config->args, where, "-fault",      "fault", "==");
     PXOPT_COPY_STR(config->args, where, "-state",      "state", "==");
-    PXOPT_COPY_STR(config->args, where, "-reqType",     "reqType", "==");
-    PXOPT_COPY_STR(config->args, where, "-name",     "name", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-reqType",    "reqType", "==");
+    PXOPT_COPY_STR(config->args, where, "-name",       "name", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-username",   "username", "==");
     PXOPT_COPY_TIME(config->args, where, "-timestamp_begin", "timestamp", ">=");
     PXOPT_COPY_TIME(config->args, where, "-timestamp_end", "timestamp", "<=");
Index: branches/eam_branches/ipp-20130307/ippTools/src/pstamptoolConfig.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/pstamptoolConfig.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/pstamptoolConfig.c	(revision 35350)
@@ -74,5 +74,7 @@
     psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-uri", 0,    "define request file uri (required)", NULL);
     psMetadataAddS64(addreqArgs, PS_LIST_TAIL, "-ds_id", 0,  "define request ds_id", 0);
+    psMetadataAddS64(addreqArgs, PS_LIST_TAIL, "-proj_id", 0, "define request proj_id", 0);
     psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-name", 0,   "define request name", NULL);
+    psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-username", 0, "define user name", NULL);
     psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-label", 0,  "define request label", NULL);
 
@@ -88,4 +90,6 @@
     psMetadataAddS64(listreqArgs, PS_LIST_TAIL, "-req_id", 0,            "list by req_id", 0);
     psMetadataAddStr(listreqArgs, PS_LIST_TAIL, "-name", 0,              "list by name", NULL);
+    psMetadataAddStr(listreqArgs, PS_LIST_TAIL, "-username", 0,          "list by user name", NULL);
+    psMetadataAddStr(listreqArgs, PS_LIST_TAIL, "-state", 0,              "list by state", NULL);
     psMetadataAddS64(listreqArgs, PS_LIST_TAIL, "-not_req_id", 0,        "req_id to not list", 0);
     psMetadataAddU64(listreqArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
@@ -105,5 +109,6 @@
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-state", 0,        "search by state", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-reqType", 0,      "search by reqType", NULL);
-    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-name", 0,      "search by reqType (LIKE comparsion)", NULL);
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-name", 0,         "search by reqType (LIKE comparsion)", NULL);
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-username", 0,     "search by username", 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);
@@ -116,4 +121,5 @@
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_uri", 0,          "new uri", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_name", 0,         "new name", NULL);
+    psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_username", 0,     "new username", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_reqType", 0,      "new reqType", NULL);
     psMetadataAddBool(updatereqArgs, PS_LIST_TAIL, "-clearfault", 0,      "set fault to zero", false);
Index: branches/eam_branches/ipp-20130307/ippTools/src/releasetool.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/releasetool.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/releasetool.c	(revision 35350)
@@ -1270,4 +1270,7 @@
         }
         if (!psArrayLength(exposures)) {
+            fprintf(stderr, "no exposures found for lap_id %" PRId64 "\n", lap_id);
+#ifdef notdef
+            continue;
             psFree(now);
             psFree(output);
@@ -1278,4 +1281,5 @@
             }
             return false;
+#endif
         }
 
@@ -1301,5 +1305,5 @@
         psFree(exposures);
 
-        // set state of relGroup to 'new'
+        // update the state of relGroup to 'new'
         if (!p_psDBRunQueryF(config->dbh, "UPDATE relGroup set state = 'new' WHERE group_id = %d", group_id)) {
             psError(PS_ERR_UNKNOWN, false, "database error");
@@ -1325,6 +1329,78 @@
 static bool updaterelgroupMode(pxConfig *config)
 {
-    psError(PS_ERR_UNKNOWN, true, "not yet implemented");
-    return false;
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S32(group_id, config->args,      "-group_id", true, false);
+
+    PXOPT_LOOKUP_STR(state,    config->args,    "-set_state",  false, false);
+    PXOPT_LOOKUP_STR(exp_list_path, config->args, "-set_exp_list_path", false, false);
+    PXOPT_LOOKUP_STR(new_label, config->args,   "-set_label", false, false);
+    PXOPT_LOOKUP_S16(fault,    config->args,    "-set_fault",  false, false);
+    PXOPT_LOOKUP_BOOL(clearfault, config->args, "-clearfault",  false);
+
+    if (!state && !exp_list_path && !new_label && !fault && !clearfault) {
+        psError(PXTOOLS_ERR_CONFIG, true, "must set at least one column\n");
+        return false;
+    }
+
+    if (fault && clearfault) {
+        psError(PXTOOLS_ERR_CONFIG, true, "cannot set and clear fault at same time\n");
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-group_id", "relGroup.group_id", "==");
+    // XXX TODO if label is enabled (for changing label or state for a block) we should
+    // disallow setting some parameters such as exp_list_path
+    //    PXOPT_COPY_S64(config->args, where, "-label", "relGroup.label", "==");
+    // make sure that we have enough parameters to identify the relGroup to change
+    if (!psListLength(where->list)){
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required\n");
+        psFree(where);
+        return false;
+    }
+
+    psString sep = "";
+    psString comma = ", ";
+    psString query = psStringCopy("UPDATE relGroup SET");
+    if (state) {
+        psStringAppend(&query, "%s relGroup.state = '%s'", sep, state);
+        sep = comma;
+    }
+    if (exp_list_path) {
+        psStringAppend(&query, "%s relGroup.exp_list_path = '%s'", sep, exp_list_path);
+        sep = comma;
+    }
+    if (new_label) {
+        psStringAppend(&query, "%s relGroup.label = '%s'", sep, new_label);
+        sep = comma;
+    }
+    if (fault) {
+        psStringAppend(&query, "%s relGroup.fault = %d", sep, fault);
+        sep = comma;
+    }
+    if (clearfault) {
+        psStringAppend(&query, "%s relGroup.fault = 0", sep);
+        sep = comma;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    psLogMsg("releasetool", PS_LOG_INFO, "Updated %" PRIu64 " ippReleases", affected);
+
+
+    psFree(query);
+
+    return true;
 }
 
Index: branches/eam_branches/ipp-20130307/ippTools/src/releasetoolConfig.c
===================================================================
--- branches/eam_branches/ipp-20130307/ippTools/src/releasetoolConfig.c	(revision 35267)
+++ branches/eam_branches/ipp-20130307/ippTools/src/releasetoolConfig.c	(revision 35350)
@@ -217,5 +217,5 @@
         // parameters of the relGroup
     psMetadataAddStr(definerelgroupArgs, PS_LIST_TAIL, "-set_group_type", 0,  "define group_type (required)", NULL);
-    psMetadataAddStr(definerelgroupArgs, PS_LIST_TAIL, "-set_group_name", 0,  "define group_name (required)", NULL);
+    psMetadataAddStr(definerelgroupArgs, PS_LIST_TAIL, "-set_group_name", 0,  "define group_name", NULL);
     psMetadataAddStr(definerelgroupArgs, PS_LIST_TAIL, "-set_label", 0, "define relgroup label (required)", NULL);
     psMetadataAddStr(definerelgroupArgs,  PS_LIST_TAIL, "-set_state", 0,      "define state", NULL);
@@ -240,5 +240,4 @@
     psMetadata *updaterelgroupArgs = psMetadataAlloc();
 
-        // set the target release
     psMetadataAddS32(updaterelgroupArgs,  PS_LIST_TAIL, "-group_id", 0,   "select by group ID (required)", 0);
 
