edu.harvard.mcz.imagecapture.jobs.JobRecheckForTemplates.java Source code

Java tutorial

Introduction

Here is the source code for edu.harvard.mcz.imagecapture.jobs.JobRecheckForTemplates.java

Source

/**
 * JobRecheckForTemplates.java
 * edu.harvard.mcz.imagecapture
 * Copyright  2016 President and Fellows of Harvard College
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of Version 2 of the GNU General Public License
 * as published by the Free Software Foundation.
 *
 * 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Paul J. Morris
 */
package edu.harvard.mcz.imagecapture.jobs;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;

import edu.harvard.mcz.imagecapture.ImageCaptureProperties;
import edu.harvard.mcz.imagecapture.RunnableJobReportDialog;
import edu.harvard.mcz.imagecapture.ConfiguredBarcodePositionTemplateDetector;
import edu.harvard.mcz.imagecapture.PositionTemplate;
import edu.harvard.mcz.imagecapture.Singleton;
import edu.harvard.mcz.imagecapture.data.HigherTaxonLifeCycle;
import edu.harvard.mcz.imagecapture.data.ICImage;
import edu.harvard.mcz.imagecapture.data.ICImageLifeCycle;
import edu.harvard.mcz.imagecapture.data.LocationInCollection;
import edu.harvard.mcz.imagecapture.data.MetadataRetriever;
import edu.harvard.mcz.imagecapture.data.Specimen;
import edu.harvard.mcz.imagecapture.data.SpecimenLifeCycle;
import edu.harvard.mcz.imagecapture.data.UnitTrayLabel;
import edu.harvard.mcz.imagecapture.data.WorkFlowStatus;
import edu.harvard.mcz.imagecapture.exceptions.NoSuchTemplateException;
import edu.harvard.mcz.imagecapture.exceptions.OCRReadException;
import edu.harvard.mcz.imagecapture.exceptions.SaveFailedException;
import edu.harvard.mcz.imagecapture.exceptions.UnreadableFileException;
import edu.harvard.mcz.imagecapture.interfaces.CollectionReturner;
import edu.harvard.mcz.imagecapture.interfaces.DrawerNameReturner;
import edu.harvard.mcz.imagecapture.interfaces.RunStatus;
import edu.harvard.mcz.imagecapture.interfaces.RunnableJob;
import edu.harvard.mcz.imagecapture.interfaces.RunnerListener;
import edu.harvard.mcz.imagecapture.interfaces.TaxonNameReturner;

/** 
 * JobRecheckForTemplates, recheck image files that have no template identified, but should have one, 
 * and try to identify their template.  Can be run after a new template has been created.
 * 
 * @author Paul J. Morris
 *
 */
public class JobRecheckForTemplates implements RunnableJob, Runnable {

    private static final Log log = LogFactory.getLog(JobRecheckForTemplates.class);

    /**
     * Recheck all files with no template.
     */
    public static final int SCAN_ALL = 0;

    /**
     * Open a dialog and scan a specific directory.
     */
    public static final int SCAN_SELECT = 1;
    /**
     * From a file, scan a specific directory.
     */
    public static final int SCAN_SPECIFIC = 2;

    private int scan = SCAN_SELECT; // default scan a user selected directory
    Counter counter = null; // For reporting results
    private File startPointSpecific = null; // place to start for scan_specific
    private int runStatus = RunStatus.STATUS_NEW;
    private Date startDate = null;
    private int percentComplete = 0;

    private ArrayList<RunnerListener> listeners = null;

    /**
     * Default constructor, scan all
     */
    public JobRecheckForTemplates() {
        init(SCAN_ALL, null);
    }

    /**
     * Create a recheck for templates job to bring up dialog to pick a specific directory  
     * on which to recheck image records for templates.
     * <BR>
     * Behavior:
     * <BR>
     * whatToScan=SCAN_ALL, all records having no template and a linked specimen are rechecked.
     * whatToScan=SCAN_SELECT, startAt is used as starting point for directory chooser dialog.
     * whatToScan=SCAN_SPECIFIC, startAt is used as starting point for repeat (if null falls back to SCAN_SELECT).
     * <BR> 
     *
     * @param whatToScan one of SCAN_SPECIFIC, SCAN_SELECT
     * @param startAt null or a directory starting point.
     */
    public JobRecheckForTemplates(int whatToScan, File startAt) {
        init(whatToScan, startAt);
    }

    public void init(int whatToScan, File startAt) {
        listeners = new ArrayList<RunnerListener>();
        scan = SCAN_SELECT;
        // store startPoint as base for dialog if SCAN_SELECT, or directory to scan if SCAN_SPECIFIC
        if (startAt != null && startAt.canRead()) {
            startPointSpecific = startAt;
        }
        if (whatToScan == SCAN_ALL) {
            scan = SCAN_ALL;
            startPointSpecific = null;
        }
        if (whatToScan == SCAN_SPECIFIC) {
            if ((startAt != null) && startAt.canRead()) {
                scan = SCAN_SPECIFIC;
            } else {
                scan = SCAN_SELECT;
            }
        }
        runStatus = RunStatus.STATUS_NEW;
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#cancel()
     */
    @Override
    public boolean cancel() {
        runStatus = RunStatus.STATUS_TERMINATED;
        log.debug("JobCleanDirectory " + this.toString() + "  recieved cancel signal");
        return false;
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#getStatus()
     */
    @Override
    public int getStatus() {
        return runStatus;
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#percentComplete()
     */
    @Override
    public int percentComplete() {
        return percentComplete;
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#registerListener(edu.harvard.mcz.imagecapture.interfaces.RunnerListener)
     */
    @Override
    public boolean registerListener(RunnerListener jobListener) {
        return listeners.add(jobListener);
    }

    private List<ICImage> getFileList() {
        List<ICImage> files = new ArrayList<ICImage>();
        if (scan != SCAN_ALL) {
            String pathToCheck = "";
            // Find the path in which to include files.
            File imagebase = null; // place to start the scan from, imagebase directory for SCAN_ALL
            File startPoint = null;
            // If it isn't null, retrieve the image base directory from properties, and test for read access.
            if (Singleton.getSingletonInstance().getProperties().getProperties()
                    .getProperty(ImageCaptureProperties.KEY_IMAGEBASE) == null) {
                JOptionPane.showMessageDialog(Singleton.getSingletonInstance().getMainFrame(),
                        "Can't start scan.  Don't know where images are stored.  Set imagbase property.",
                        "Can't Scan.", JOptionPane.ERROR_MESSAGE);
            } else {
                imagebase = new File(Singleton.getSingletonInstance().getProperties().getProperties()
                        .getProperty(ImageCaptureProperties.KEY_IMAGEBASE));
                if (imagebase != null) {
                    if (imagebase.canRead()) {
                        startPoint = imagebase;
                    } else {
                        // If it can't be read, null out imagebase
                        imagebase = null;
                    }
                }
                if (scan == SCAN_SPECIFIC && startPointSpecific != null && startPointSpecific.canRead()) {
                    // A scan start point has been provided, don't launch a dialog.
                    startPoint = startPointSpecific;
                }
                if (imagebase == null || scan == SCAN_SELECT) {
                    // launch a file chooser dialog to select the directory to scan
                    final JFileChooser fileChooser = new JFileChooser();
                    fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                    if (scan == SCAN_SELECT && startPointSpecific != null && startPointSpecific.canRead()) {
                        fileChooser.setCurrentDirectory(startPointSpecific);
                    } else {
                        if (Singleton.getSingletonInstance().getProperties().getProperties()
                                .getProperty(ImageCaptureProperties.KEY_LASTPATH) != null) {
                            fileChooser
                                    .setCurrentDirectory(new File(Singleton.getSingletonInstance().getProperties()
                                            .getProperties().getProperty(ImageCaptureProperties.KEY_LASTPATH)));
                        }
                    }
                    int returnValue = fileChooser.showOpenDialog(Singleton.getSingletonInstance().getMainFrame());
                    if (returnValue == JFileChooser.APPROVE_OPTION) {
                        File file = fileChooser.getSelectedFile();
                        log.debug("Selected base directory: " + file.getName() + ".");
                        startPoint = file;
                    } else {
                        //TODO: handle error condition
                        log.error("Directory selection cancelled by user.");
                    }
                }

                // Check that startPoint is or is within imagebase.
                if (!ImageCaptureProperties.isInPathBelowBase(startPoint)) {
                    String base = Singleton.getSingletonInstance().getProperties().getProperties()
                            .getProperty(ImageCaptureProperties.KEY_IMAGEBASE);
                    log.error("Tried to scan directory (" + startPoint.getPath()
                            + ") outside of base image directory (" + base + ")");
                    String message = "Can't scan and database files outside of base image directory (" + base + ")";
                    JOptionPane.showMessageDialog(Singleton.getSingletonInstance().getMainFrame(), message,
                            "Can't Scan outside image base directory.", JOptionPane.YES_NO_OPTION);
                } else {
                    if (!startPoint.canRead()) {
                        JOptionPane.showMessageDialog(Singleton.getSingletonInstance().getMainFrame(),
                                "Can't start scan.  Unable to read selected directory: " + startPoint.getPath(),
                                "Can't Scan.", JOptionPane.YES_NO_OPTION);
                    } else {
                        pathToCheck = ImageCaptureProperties.getPathBelowBase(startPoint);

                        // retrieve a list of image records in the selected directory
                        ICImageLifeCycle ils = new ICImageLifeCycle();
                        ICImage pattern = new ICImage();
                        pattern.setPath(pathToCheck);
                        pattern.setTemplateId(PositionTemplate.TEMPLATE_NO_COMPONENT_PARTS);
                        files = ils.findByExample(pattern);
                        if (files != null) {
                            log.debug(files.size());
                        }
                    }
                }
            }
        } else {
            try {
                // retrieve a list of all image records with no template
                ICImageLifeCycle ils = new ICImageLifeCycle();
                files = ils.findNotDrawerNoTemplateImages();
                if (files != null) {
                    log.debug(files.size());
                }
            } catch (HibernateException e) {
                log.error(e.getMessage());
                runStatus = RunStatus.STATUS_FAILED;
                String message = "Error loading the list of images with no templates. " + e.getMessage();
                JOptionPane.showMessageDialog(Singleton.getSingletonInstance().getMainFrame(), message,
                        "Error loading image records.", JOptionPane.YES_NO_OPTION);
            }
        }

        log.debug("Found " + files.size() + " Image files without templates in directory to check.");

        return files;
    }

    private void recheckForTemplates(ICImage image) {
        if (image.getSpecimen() != null) {
            File imageFile = new File(
                    ImageCaptureProperties.assemblePathWithBase(image.getPath(), image.getFilename()));
            counter.incrementFilesSeen();

            ConfiguredBarcodePositionTemplateDetector detector = new ConfiguredBarcodePositionTemplateDetector();
            try {
                ICImageLifeCycle ils = new ICImageLifeCycle();
                String templateName = detector.detectTemplateForImage(imageFile);
                if (templateName != null && !templateName.equals(PositionTemplate.TEMPLATE_NO_COMPONENT_PARTS)) {
                    // update the image record with this template.
                    image.setTemplateId(templateName);
                    ils.attachDirty(image);
                    counter.incrementFilesUpdated();
                } else if (templateName != null
                        && templateName.equals(PositionTemplate.TEMPLATE_NO_COMPONENT_PARTS)) {
                    RunnableJobError error = new RunnableJobError(image.getFilename(), image.getRawBarcode(), "",
                            image.getRawExifBarcode(), "No Template Found.", null, null, null,
                            RunnableJobError.TYPE_NO_TEMPLATE);
                    counter.appendError(error);
                }
            } catch (UnreadableFileException e) {
                log.error(e.getMessage());
                RunnableJobError error = new RunnableJobError(image.getFilename(), image.getRawBarcode(), "",
                        image.getRawExifBarcode(), "Unreadable File Exception checking for template.", null, null,
                        null, RunnableJobError.TYPE_NO_TEMPLATE);
                counter.appendError(error);
            } catch (SaveFailedException e) {
                log.error(e.getMessage(), e);
                RunnableJobError error = new RunnableJobError(image.getFilename(), image.getRawBarcode(), "",
                        image.getRawExifBarcode(), "Save Failed Exception saving new template.", null, null, null,
                        RunnableJobError.TYPE_SAVE_FAILED);
                counter.appendError(error);
            }

        } else {
            log.debug(image.getPath() + image.getFilename() + " Has no attached image.");
        }
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#start()
     */
    @Override
    public void start() {
        startDate = new Date();
        Singleton.getSingletonInstance().getJobList().addJob((RunnableJob) this);
        counter = new Counter();
        // Obtain a list of image file records for the selected directory.
        List<ICImage> files = getFileList();
        log.debug("ReckeckForTemplatesJob started" + this.toString());
        int i = 0;
        while (i < files.size() && runStatus != RunStatus.STATUS_TERMINATED
                && runStatus != RunStatus.STATUS_FAILED) {
            // Find out how far along the process is
            Float seen = 0.0f + i;
            Float total = 0.0f + files.size();
            percentComplete = (int) ((seen / total) * 100);
            setPercentComplete(percentComplete);
            // Repeat the OCR for the present file.
            recheckForTemplates(files.get(i));
            i++;
        }
        if (runStatus != RunStatus.STATUS_TERMINATED) {
            setPercentComplete(100);
        }
        Singleton.getSingletonInstance().getMainFrame().notifyListener(runStatus, this);
        report();
        done();
    }

    private void setPercentComplete(int aPercentage) {
        //set value
        percentComplete = aPercentage;
        //notify listeners
        Singleton.getSingletonInstance().getMainFrame().notifyListener(percentComplete, this);
        Iterator<RunnerListener> i = listeners.iterator();
        while (i.hasNext()) {
            i.next().notifyListener(percentComplete, this);
        }
    }

    /**
     * Cleanup when job is complete.
     */
    private void done() {
        Singleton.getSingletonInstance().getJobList().removeJob((RunnableJob) this);
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#stop()
     */
    @Override
    public boolean stop() {
        // TODO Auto-generated method stub
        return false;
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        start();
    }

    private void report() {
        String report = "Results of template check on Image files missing templates (WholeImageOnly).\n";
        report += "Found  " + counter.getFilesSeen() + " image file database records without templates.\n";
        report += "Updated " + counter.getFilesUpdated() + " image records to a template.\n";
        Singleton.getSingletonInstance().getMainFrame().setStatusMessage("Check for templates complete.");
        RunnableJobReportDialog errorReportDialog = new RunnableJobReportDialog(
                Singleton.getSingletonInstance().getMainFrame(), report, counter.getErrors(),
                "Recheck Files for Templates Results");
        errorReportDialog.setVisible(true);
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#getName()
     */
    @Override
    public String getName() {
        return "Recheck for Templates for images that are " + PositionTemplate.TEMPLATE_NO_COMPONENT_PARTS;
    }

    /* (non-Javadoc)
     * @see edu.harvard.mcz.imagecapture.interfaces.RunnableJob#getStartTime()
     */
    @Override
    public Date getStartTime() {
        return startDate;
    }

}