Print

Print


Author: [log in to unmask]
Date: Tue Dec  1 15:42:26 2015
New Revision: 3997

Log:
Rewrite tag command so run range overlaps are handled correctly by using a disambiguation action (last created by default).

Modified:
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/ConditionsRecord.java
    java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/TagCommand.java

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/ConditionsRecord.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/ConditionsRecord.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/api/ConditionsRecord.java	Tue Dec  1 15:42:26 2015
@@ -8,6 +8,7 @@
 import org.hps.conditions.database.ConditionsRecordConverter;
 import org.hps.conditions.database.Converter;
 import org.hps.conditions.database.Field;
+import org.hps.conditions.database.MultipleCollectionsAction;
 import org.hps.conditions.database.Table;
 
 /**
@@ -233,6 +234,33 @@
         public final ConditionsRecordCollection sortedByUpdated() {
             return (ConditionsRecordCollection) this.sorted(new UpdatedComparator());
         }
+        
+        /**
+         * Find a unique record using the selected action for disambiguating conditions with the same key.
+         * @param key the name of the key
+         * @param action the disambiguation action
+         * @return the unique conditions record or <code>null</code> if does not exist
+         */
+        public ConditionsRecord findUniqueRecord(String key, MultipleCollectionsAction action) {
+            ConditionsRecord record = null;
+            ConditionsRecordCollection keyRecords = this.findByKey(key);
+            if (keyRecords.size() > 0) {
+                if (keyRecords.size() == 1) {
+                    record = keyRecords.get(0);
+                } else {
+                    if (action.equals(MultipleCollectionsAction.LAST_UPDATED)) {
+                        record = sortedByUpdated().get(this.size() - 1);
+                    } else if (action.equals(MultipleCollectionsAction.LAST_CREATED)) {
+                        record = sortedByCreated().get(this.size() - 1);
+                    } else if (action.equals(MultipleCollectionsAction.LATEST_RUN_START)) {
+                        record = sortedByRunStart().get(this.size() - 1);
+                    } else if (action.equals(MultipleCollectionsAction.ERROR)) {
+                        throw new RuntimeException("Multiple ConditionsRecord object found for conditions key " + key + ".");
+                    }
+                }
+            }
+            return record;
+        }
     }
 
     /**

Modified: java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/TagCommand.java
 =============================================================================
--- java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/TagCommand.java	(original)
+++ java/branches/jeremy-dev/conditions/src/main/java/org/hps/conditions/cli/TagCommand.java	Tue Dec  1 15:42:26 2015
@@ -1,10 +1,9 @@
 package org.hps.conditions.cli;
 
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.logging.Level;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.logging.Logger;
 
 import org.apache.commons.cli.CommandLine;
@@ -16,17 +15,16 @@
 import org.hps.conditions.api.ConditionsTag;
 import org.hps.conditions.api.ConditionsTag.ConditionsTagCollection;
 import org.hps.conditions.api.DatabaseObjectException;
-import org.hps.conditions.api.TableMetaData;
 import org.hps.conditions.api.TableRegistry;
+import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.conditions.database.MultipleCollectionsAction;
+import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
 
 /**
  * Create a conditions system tag.
  * <p>
  * The tag groups together conditions records from the <i>conditions</i> database table with a run validity range that 
  * is between a specified starting and ending run.
- * <p>
- * Tagging will not disambiguate overlapping conditions, which is done at run-time based on the current run number.
  *
  * @author Jeremy McCormick, SLAC
  */
@@ -41,20 +39,29 @@
      * Defines command options.
      */
     private static Options OPTIONS = new Options();
+    
+    private MultipleCollectionsAction multipleCollectionsAction = MultipleCollectionsAction.LAST_CREATED; 
+    
+    private static String getMultipleCollectionsActionString() {
+        StringBuffer sb = new StringBuffer();
+        for (MultipleCollectionsAction action : MultipleCollectionsAction.values()) {
+            sb.append(action.name() + " ");
+        }
+        sb.setLength(sb.length() - 1);
+        return sb.toString();
+    }
 
     /**
      * Define all command options.
      */
     static {
-        OPTIONS.addOption(new Option("h", false, "Show help for tag command"));
-        OPTIONS.addOption(new Option("t", true, "Conditions tag name"));
-        OPTIONS.addOption(new Option("s", true, "Starting run number (required)"));
-        OPTIONS.getOption("s").setRequired(true);
-        OPTIONS.addOption(new Option("e", true, "Ending run number (default is unlimited)"));
-        OPTIONS.getOption("t").setRequired(true);
-        OPTIONS.addOption(new Option("m", true,
-                "MultipleCollectionsAction to use for disambiguation (default is LAST_CREATED)"));
-        OPTIONS.addOption(new Option("d", false, "Don't prompt before making tag (be careful!)"));
+        OPTIONS.addOption(new Option("h", "help", false, "Show help for tag command"));
+        OPTIONS.addOption(new Option("t", "tag", true, "Conditions tag name"));
+        OPTIONS.addOption(new Option("s", "run-start", true, "Starting run number (required)"));
+        OPTIONS.addOption(new Option("e", "run-end", true, "Ending run number (required)"));
+        OPTIONS.addOption(new Option("m", "multiple", true, 
+                "set run overlap handling (" + getMultipleCollectionsActionString() + ")"));
+        OPTIONS.addOption(new Option("d", false, "Don't prompt before making tag (careful!)"));
     }
 
     /**
@@ -103,6 +110,11 @@
         } else {
             throw new RuntimeException("Missing required -t argument with the tag name.");
         }
+        
+        // Check if tag exists already.
+        if (getManager().getAvailableTags().contains(tagName)) {
+            throw new RuntimeException("The tag '" + tagName + "' already exists in the database.");
+        }
 
         // Starting run number (required).
         int runStart = -1;
@@ -110,19 +122,16 @@
             runStart = Integer.parseInt(commandLine.getOptionValue("s"));
             LOGGER.config("run start set to " + runStart);
         } else {
-            throw new RuntimeException("missing require -s argument with starting run number");
-        }
-
-        // Ending run number (max integer is default).
-        int runEnd = Integer.MAX_VALUE;
+            throw new RuntimeException("Missing required -s argument with starting run number.");
+        }
+
+        // Ending run number (required).
+        int runEnd = -1;
         if (commandLine.hasOption("e")) {
             runEnd = Integer.parseInt(commandLine.getOptionValue("e"));
             LOGGER.config("run end set to " + runEnd);
-        }
-
-        // Run end must be greater than or equal to run start.
-        if (runEnd < runStart) {
-            throw new IllegalArgumentException("runEnd < runStart");
+        } else {
+            throw new RuntimeException("Missing required -e argument with starting run number.");
         }
 
         // Action for disambiguating overlapping collections (default is to use the most recent creation date).
@@ -131,20 +140,24 @@
             multipleCollectionsAction = MultipleCollectionsAction
                     .valueOf(commandLine.getOptionValue("m").toUpperCase());
         }
-        LOGGER.config("multiple collections action set tco " + multipleCollectionsAction);
+        LOGGER.config("run overlaps will be disambiguated using " + multipleCollectionsAction);
 
         // Whether to prompt before tagging (default is yes).
         boolean promptBeforeTagging = true;
         if (commandLine.hasOption("d")) {
             promptBeforeTagging = false;
         }
-        LOGGER.config("prompt before tagging: " + promptBeforeTagging);
+        LOGGER.config("prompt before tagging = " + promptBeforeTagging);
 
         // Conditions system configuration.
         this.getManager().setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");
 
         // Find all the applicable conditions records by their run number ranges.
         ConditionsRecordCollection tagConditionsRecordCollection = this.findConditionsRecords(runStart, runEnd);
+        
+        if (tagConditionsRecordCollection.size() == 0) {
+            throw new RuntimeException("No records found for tag.");
+        }
 
         LOGGER.info("found " + tagConditionsRecordCollection.size() + " conditions records for the tag");
 
@@ -152,8 +165,8 @@
         final ConditionsTagCollection conditionsTagCollection = this.createConditionsTagCollection(
                 tagConditionsRecordCollection, tagName);
 
-        LOGGER.info("created " + conditionsTagCollection.size() + " tag records ..." + '\n' + conditionsTagCollection);
-
+        printConditionsRecords(tagConditionsRecordCollection);
+        
         // Prompt user to verify tag creation.
         boolean createTag = true;
         if (promptBeforeTagging) {
@@ -178,68 +191,85 @@
 
         LOGGER.info("done!");
     }
-   
-    /**
-     * Find all the conditions records that are applicable for the given run range.
-     * <p>
-     * Overlapping run numbers in conditions with the same key are not disambiguated.
-     * This must be done in the user's job at runtime; usually the most recently created 
-     * conditions record will be used if multiple one's are applicable to the current run.
-     *
-     * @param runStart the start run
-     * @param runEnd the end run (must be greater than or equal to <code>runStart</code>)
-     * @return the conditions records that fall in the run range
+    
+    /**
+     * Print information about conditions records in the tag to the log.
+     * 
+     * @param collection the conditions tag collection
+     */
+    private void printConditionsRecords(ConditionsRecordCollection records) {
+        StringBuffer sb = new StringBuffer();
+        Set<String> keys = new TreeSet<String>(records.getConditionsKeys());
+        for (String key : keys) {
+            ConditionsRecordCollection keyRecords = records.findByKey(key);
+            keyRecords.sortByKey();
+            for (ConditionsRecord record : keyRecords) {
+                sb.append("conditions_id: " + record.getRowId() + ", name: " + record.getName() + ", collection_id: "
+                        + record.getCollectionId() + ", run_start: " + record.getRunStart() 
+                        + ", run_end: " + record.getRunEnd() + ", notes: " + record.getNotes() + '\n');
+                
+            }
+        }        
+        LOGGER.info("including " + records.size() + " records in tag ..." + '\n' + sb.toString());
+    }
+     
+    /**
+     * Scan through a run range to find conditions records for the tag.
+     * 
+     * @param runStart the starting run number
+     * @param runEnd the ending run number
+     * @return the conditions records for the tag
      */
     private ConditionsRecordCollection findConditionsRecords(final int runStart, final int runEnd) {
-        if (runStart > runEnd) {
-            throw new IllegalArgumentException("runStart > runEnd");
-        }
-        if (runStart < 0) {
-            throw new IllegalArgumentException("invalid runStart: " + runStart);
-        }
-        if (runEnd < 0) {
-            throw new IllegalArgumentException("invalid runEnd: " + runEnd);
-        }
-        final Connection connection = this.getManager().getConnection();
-        final ConditionsRecordCollection conditionsRecordCollection = new ConditionsRecordCollection();
-        final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("conditions");
-        PreparedStatement statement = null;
-        try {
-            /*
-             * SQL statement handles 3 cases: 
-             * 1) condition's run_start in range 
-             * 2) condition's run_end in range 
-             * 3) condition's run_start and run_end enclose the range
-             */
-            statement = connection
-                    .prepareStatement("SELECT id FROM conditions WHERE (run_start >= ? and run_start <= ?) or (run_end >= ? and run_end <= ?)"
-                            + " or (run_start <= ? and run_end >= ?)");
-            statement.setInt(1, runStart);
-            statement.setInt(2, runEnd);
-            statement.setInt(3, runStart);
-            statement.setInt(4, runEnd);
-            statement.setInt(5, runStart);
-            statement.setInt(6, runEnd);
-
-            final ResultSet resultSet = statement.executeQuery();
-            while (resultSet.next()) {
-                final ConditionsRecord record = new ConditionsRecord();
-                record.setConnection(connection);
-                record.setTableMetaData(tableMetaData);
-                record.select(resultSet.getInt(1));
-                conditionsRecordCollection.add(record);
-            }
-        } catch (DatabaseObjectException | ConditionsObjectException | SQLException e) {
-            throw new RuntimeException(e);
-        } finally {
+        if (runStart < 0 ) {
+            throw new IllegalArgumentException("The run start " + runStart + " is invalid.");
+        }
+        if (runEnd < 0 ) {
+            throw new IllegalArgumentException("The run end " + runEnd + " is invalid.");
+        }
+        if (runStart > runEnd ) {
+            throw new IllegalArgumentException("The run start is greater than the run end.");
+        }
+        DatabaseConditionsManager dbManager = this.getManager();
+        if (dbManager.isFrozen()) {
+            dbManager.unfreeze();
+        }
+        if (!dbManager.getActiveTags().isEmpty()) {
+            dbManager.clearTags();
+        }
+        final String detectorName = "HPS-dummy-detector";
+        ConditionsRecordCollection tagRecords = new ConditionsRecordCollection();
+        Set<Integer> ids = new HashSet<Integer>();
+        for (int run = runStart; run <= runEnd; run++) {
+            LOGGER.info("loading run " + run);
             try {
-                if (statement != null) {
-                    statement.close();
+                dbManager.setDetector(detectorName, run);
+            } catch (ConditionsNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+            ConditionsRecordCollection runRecords = dbManager.getConditionsRecords();
+            Set<String> keys = runRecords.getConditionsKeys();
+            LOGGER.fine("run has " + runRecords.size() + " conditions records");
+            for (String key : keys) {
+                ConditionsRecord record = runRecords.findUniqueRecord(key, this.multipleCollectionsAction);
+                if (record == null) {
+                    throw new RuntimeException("Missing expected unique condition record for " + key + ".");
                 }
-            } catch (final SQLException e) {
-                e.printStackTrace();
-            }
-        }
-        return conditionsRecordCollection;
+                if (!ids.contains(record.getRowId())) {
+                    try {
+                        LOGGER.fine("adding conditions to tag ..." + '\n' + record.toString());
+                        tagRecords.add(record);
+                        ids.add(record.getRowId());
+                    } catch (ConditionsObjectException e) {
+                        throw new RuntimeException(e);
+                    }
+                } else {
+                    LOGGER.fine("Conditions record with row id " + record.getRowId() + " is already in the tag.");
+                }
+            }
+            LOGGER.info("done processing run " + run);
+        }
+        LOGGER.info("Found " + tagRecords.size() + " conditions records for tag.");
+        return tagRecords;
     }
 }