// $HeadURL: // http://seanderickson1@forge.abcd.harvard.edu/svn/screensaver/branches/lincs/ui-cleanup/core/src/main/java/edu/harvard/med/screensaver/io/screenresults/ScreenResultImporter.java // $ // $Id$ // // Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College. // // Screensaver is an open-source project developed by the ICCB-L and NSRB labs // at Harvard Medical School. This software is distributed under the terms of // the GNU General Public License. package edu.harvard.med.screensaver.io.screenresults; import java.io.File; import java.io.FileNotFoundException; import java.util.Iterator; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.ParseException; import org.apache.commons.lang.math.IntRange; import org.apache.log4j.Logger; import edu.harvard.med.screensaver.db.GenericEntityDAO; import edu.harvard.med.screensaver.io.CommandLineApplication; import edu.harvard.med.screensaver.io.ParseError; import edu.harvard.med.screensaver.io.ParseErrorsException; import edu.harvard.med.screensaver.io.workbook2.Workbook; import edu.harvard.med.screensaver.model.screens.Screen; import edu.harvard.med.screensaver.model.users.AdministratorUser; import edu.harvard.med.screensaver.service.EntityNotFoundException; import edu.harvard.med.screensaver.service.screenresult.ScreenResultDeleter; import edu.harvard.med.screensaver.service.screenresult.ScreenResultLoader; public class ScreenResultImporter extends CommandLineApplication { public ScreenResultImporter(String[] cmdLineArgs) { super(cmdLineArgs); } private static Logger log = Logger.getLogger(ScreenResultImporter.class); public static final int SHORT_OPTION = 0; public static final int LONG_OPTION = 1; static final String[] INPUT_FILE_OPTION = { "f", "input-file" }; static final String[] SCREEN_OPTION = { "s", "screen" }; static final String[] IMPORT_OPTION = { "i", "import" }; static final String[] WELLS_OPTION = { "w", "wells" }; static final String[] PLATE_NUMBER_START_OPTION = { "sp", "start-plate" }; static final String[] PLATE_NUMBER_END_OPTION = { "ep", "end-plate" }; static final String[] IGNORE_DUPLICATE_ERRORS_OPTION = { "ignoreDuplicates", "ignore-duplicate-well-errors" }; static final String[] INCREMENTAL_FLUSH_OPTION = { "incrementalFlush", "incremental-flush" }; static final String[] COMMENTS = { "c", "comments", "Comments to be recorded for this screen result data loading activity" }; static final String[] DELETE_EXISTING = { "d", "delete", "Delete existing screen result, if it exists" }; private static final String ERROR_ANNOTATED_WORKBOOK_FILE_EXTENSION = "errors.xls"; protected static final String SCREEN_RESULT_IMPORTER_SPRING_CONFIGURATION = "spring-context-screen-result-parser-app.xml"; @SuppressWarnings("static-access") public static void main(String[] args) { ScreenResultImporter app = new ScreenResultImporter(args); app.addCommandLineOption(OptionBuilder.hasArg() .withArgName("screen facility ID") .isRequired() .withDescription("the facility-assigned ID of the screen for which the screen result is being parsed") .withLongOpt(SCREEN_OPTION[LONG_OPTION]) .create(SCREEN_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.hasArg() .withArgName("file") .isRequired() .withDescription("the file location of the Excel workbook file holding the Screen Result metadata") .withLongOpt(INPUT_FILE_OPTION[LONG_OPTION]) .create(INPUT_FILE_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.hasArg() .withArgName("comments") .isRequired(false) .withDescription("comments to associate with the data loading activity") .withLongOpt(COMMENTS[LONG_OPTION]) .create(COMMENTS[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription("The first plate number to parse/import") .hasArg() .withArgName("#") .withLongOpt(PLATE_NUMBER_START_OPTION[LONG_OPTION]) .create(PLATE_NUMBER_START_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription("The last plate number to parse/import") .hasArg() .withArgName("#") .withLongOpt(PLATE_NUMBER_END_OPTION[LONG_OPTION]) .create(PLATE_NUMBER_END_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription("Import screen result into database if parsing is successful. " + "(By default, the parser only validates the input and then exits.)") .withLongOpt(IMPORT_OPTION[LONG_OPTION]) .create(IMPORT_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription("Ignore any subsequent duplicates of a well") .isRequired(false) .withLongOpt(IGNORE_DUPLICATE_ERRORS_OPTION[LONG_OPTION]) .create(IGNORE_DUPLICATE_ERRORS_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription("Set the incremental flushing option; this is necessary for conserving memory in large imports (default=\"true\")") .isRequired(false) .withArgName("value") .withLongOpt(INCREMENTAL_FLUSH_OPTION[LONG_OPTION]) .create(INCREMENTAL_FLUSH_OPTION[SHORT_OPTION])); app.addCommandLineOption(OptionBuilder.withDescription(DELETE_EXISTING[2]) .hasArg(false) .withLongOpt(DELETE_EXISTING[LONG_OPTION]) .create(DELETE_EXISTING[SHORT_OPTION])); app.processOptions(/* acceptDatabaseOptions= */true, /* acceptAdminUserOptions= */true); try { execute(app); } catch (ParseErrorsException e) { if (!e.getErrors().isEmpty()) { for (ParseError pe : e.getErrors()) { log.error("" + pe); } log.error("" + e.getErrors().size() + " errors found."); } System.exit(1); } catch (Exception e) { log.error("Failed to create the screen result", e); System.exit(1); } } private static void execute(ScreenResultImporter app) throws EntityNotFoundException, FileNotFoundException, ParseException, ParseErrorsException { File inputFile; GenericEntityDAO dao = (GenericEntityDAO) app.getSpringBean("genericEntityDao"); // if parse-only mode is requested, use a spring configuration that does not have a database dependency if (!app.isCommandLineFlagSet(IMPORT_OPTION[SHORT_OPTION])) { // TODO: this is a bug, since cannot instantiate the spring-application-context after getting the genericEntityDao bean (or any bean) - sde4 app.setSpringConfigurationResource(SCREEN_RESULT_IMPORTER_SPRING_CONFIGURATION); } inputFile = app.getCommandLineOptionValue(INPUT_FILE_OPTION[SHORT_OPTION], File.class); IntRange plateNumberRange = null; if (app.isCommandLineFlagSet(PLATE_NUMBER_START_OPTION[SHORT_OPTION]) && app.isCommandLineFlagSet(PLATE_NUMBER_END_OPTION[SHORT_OPTION])) { plateNumberRange = new IntRange(app.getCommandLineOptionValue(PLATE_NUMBER_START_OPTION[SHORT_OPTION], Integer.class), app.getCommandLineOptionValue(PLATE_NUMBER_END_OPTION[SHORT_OPTION], Integer.class)); log.info("will parse/load plates " + plateNumberRange); } final IntRange finalPlateNumberRange = plateNumberRange; String screenFacilityId = app.getCommandLineOptionValue(SCREEN_OPTION[SHORT_OPTION]); Screen screen = dao.findEntityByProperty(Screen.class, Screen.facilityId.getPropertyName(), screenFacilityId); if (screen == null) { throw new EntityNotFoundException(Screen.class, screenFacilityId); } Workbook workbook = new Workbook(inputFile); ScreenResultLoader screenResultLoader = (ScreenResultLoader) app.getSpringBean("screenResultLoader"); screenResultLoader.setIgnoreDuplicateErrors(app.isCommandLineFlagSet(IGNORE_DUPLICATE_ERRORS_OPTION[SHORT_OPTION])); boolean incrementalFlush = true; if (app.isCommandLineFlagSet(INCREMENTAL_FLUSH_OPTION[SHORT_OPTION])) { log.info("get incrementalFlush value"); incrementalFlush = app.getCommandLineOptionValue(INCREMENTAL_FLUSH_OPTION[SHORT_OPTION], Boolean.class); } log.info("incrementalFlush: " + incrementalFlush); AdministratorUser admin = app.findAdministratorUser(); deleteIfNecessary(app, screen, admin); String comments = app.getCommandLineOptionValue(COMMENTS[SHORT_OPTION]); screenResultLoader.parseAndLoad(screen, workbook, admin, comments, finalPlateNumberRange, incrementalFlush); } private static void deleteIfNecessary(ScreenResultImporter app, Screen screen, AdministratorUser admin) throws ParseException { boolean deleteExisting = app.isCommandLineFlagSet(DELETE_EXISTING[SHORT_OPTION]); if (deleteExisting && screen.getScreenResult() != null) { ScreenResultDeleter screenResultDeleter = (ScreenResultDeleter) app.getSpringBean("screenResultDeleter"); screenResultDeleter.deleteScreenResult(screen.getScreenResult(), admin); } } @SuppressWarnings("unchecked") private static void cleanErrorAnnotatedWorkbooks(File dir) { if (!dir.isDirectory()) { log.warn("cannot clean the directory '" + dir + "' since it is not a directory"); return; } log.info("cleaning directory " + dir); Iterator<File> iterator = org.apache.commons.io.FileUtils.iterateFiles(dir, new String[] { ERROR_ANNOTATED_WORKBOOK_FILE_EXTENSION, ".out" }, false); while (iterator.hasNext()) { File fileToDelete = (File) iterator.next(); log.info("deleting previously generated outputfile '" + fileToDelete + "'"); fileToDelete.delete(); } } }