org.ojbc.adapters.analyticaldatastore.processor.IncidentReportProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.ojbc.adapters.analyticaldatastore.processor.IncidentReportProcessor.java

Source

/*
 * Unless explicitly acquired and licensed from Licensor under another license, the contents of
 * this file are subject to the Reciprocal Public License ("RPL") Version 1.5, or subsequent
 * versions as allowed by the RPL, and You may not copy or use this file in either source code
 * or executable form, except in compliance with the terms and conditions of the RPL
 *
 * All software distributed under the RPL is provided strictly on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND LICENSOR HEREBY DISCLAIMS ALL SUCH
 * WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the RPL for specific language
 * governing rights and limitations under the RPL.
 *
 * http://opensource.org/licenses/RPL-1.5
 *
 * Copyright 2012-2015 Open Justice Broker Consortium
 */
package org.ojbc.adapters.analyticaldatastore.processor;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ojbc.adapters.analyticaldatastore.dao.IncidentCircumstance;
import org.ojbc.adapters.analyticaldatastore.dao.IncidentType;
import org.ojbc.adapters.analyticaldatastore.dao.model.Arrest;
import org.ojbc.adapters.analyticaldatastore.dao.model.Charge;
import org.ojbc.adapters.analyticaldatastore.dao.model.Incident;
import org.ojbc.adapters.analyticaldatastore.personid.IdentifierGenerationStrategy;
import org.ojbc.adapters.analyticaldatastore.util.AnalyticalDataStoreUtils;
import org.ojbc.util.xml.XmlUtils;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class IncidentReportProcessor extends AbstractReportRepositoryProcessor {

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

    private static final String PATH_TO_LEXS_DATA_ITEM_PACKAGE = "//lexspd:doPublish/lexs:PublishMessageContainer/lexs:PublishMessage/lexs:DataItemPackage";

    private static final String PATH_TO_LEXS_DIGEST = PATH_TO_LEXS_DATA_ITEM_PACKAGE + "/lexs:Digest";

    private static final String INCIDENT_REPORT_ROOT_ELEMENT_NAME = "IncidentReport";

    private static final String INCIDENT_REPORT_UPDATE_ROOT_ELEMENT_NAME = "IncidentReportUpdate";

    @Transactional
    public void processReport(Document incidentReport) throws Exception {
        Incident incident = new Incident();

        String rootElemntName = incidentReport.getDocumentElement().getLocalName();
        if (INCIDENT_REPORT_ROOT_ELEMENT_NAME.equals(rootElemntName)) {
            incident.setRecordType('N');
        } else if (INCIDENT_REPORT_UPDATE_ROOT_ELEMENT_NAME.equals(rootElemntName)) {
            incident.setRecordType('U');
        }

        String reportingAgencyName = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DIGEST
                + "/lexsdigest:EntityOrganization/nc:Organization[@s:id= " + PATH_TO_LEXS_DIGEST
                + " /lexsdigest:Associations/nc:ActivityReportingOrganizationAssociation[nc:ActivityReference/@s:ref="
                + PATH_TO_LEXS_DIGEST
                + "/lexsdigest:EntityActivity/nc:Activity[nc:ActivityCategoryText='Incident']/@s:id]/nc:OrganizationReference/@s:ref]/nc:OrganizationName");
        log.debug("Agency Name: " + reportingAgencyName);

        String reportingAgencyORI = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                + "/lexs:PackageMetadata/lexs:DataOwnerMetadata/lexs:DataOwnerIdentifier/lexs:ORI");
        log.debug("Agency ORI: " + reportingAgencyORI);

        Integer reportingAgencyId = null;

        if (StringUtils.isNotBlank(reportingAgencyORI)) {
            reportingAgencyId = analyticalDatastoreDAO.searchForAgenyIDbyAgencyORI(reportingAgencyORI);

            if (reportingAgencyId == null) {
                throw new Exception("Valid Agency ORI required for incident.  Agency Name is: "
                        + reportingAgencyName + ", Agency ORI is: " + reportingAgencyORI);
            }

            incident.setReportingAgencyID(reportingAgencyId);
        } else {
            throw new Exception("Valid Agency ORI required for incident.  Agency Name is: "
                    + StringUtils.trimToEmpty(reportingAgencyName) + ", Agency ORI is: "
                    + StringUtils.trimToEmpty(reportingAgencyORI));
        }

        String incidentCaseNumber = XmlUtils.xPathStringSearch(incidentReport,
                PATH_TO_LEXS_DATA_ITEM_PACKAGE + "/lexs:PackageMetadata/lexs:DataItemID");
        log.debug("Incident Case Number: " + incidentCaseNumber);

        if (StringUtils.isNotBlank(incidentCaseNumber)) {
            incident.setIncidentCaseNumber(incidentCaseNumber);
        }

        //Check to see if incident(s) already exists
        List<Incident> incidents = analyticalDatastoreDAO
                .searchForIncidentsByIncidentNumberAndReportingAgencyID(incidentCaseNumber, reportingAgencyId);

        //if incidents exist, delete them prior to inserting a new one
        if (incidents.size() > 1) {
            throw new IllegalStateException(
                    "Error condition. Duplicate records with same incident number and agency ID exists in database");
        }

        Integer incidentIDToReplace = null;

        if (incidents.size() == 1) {
            incidentIDToReplace = incidents.get(0).getIncidentID();
            incident.setIncidentID(incidentIDToReplace);
            log.debug("Incident ID to replace: " + incidentIDToReplace);

            analyticalDatastoreDAO.deleteIncident(incidents.get(0).getIncidentID());
        }

        String reportingSystem = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                + "/lexs:PackageMetadata/lexs:DataOwnerMetadata/lexs:DataOwnerIdentifier/lexs:SystemID");
        incident.setReportingSystem(reportingSystem);

        //Look for either incident date/time or incident date field
        String incidentDateTimeAsString = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DIGEST
                + "/lexsdigest:EntityActivity/nc:Activity/nc:ActivityDateRange/nc:StartDate/nc:DateTime");
        log.debug("Incident Date/Time: " + incidentDateTimeAsString);

        if (StringUtils.isNotEmpty(incidentDateTimeAsString)) {
            Calendar incidentDateTimeCal = DatatypeConverter.parseDateTime(incidentDateTimeAsString);
            Date incidentDateTime = incidentDateTimeCal.getTime();

            if (incidentDateTime != null) {
                incident.setIncidentDate(incidentDateTime);
            }

            Time incidentTime = new Time(incidentDateTime.getTime());
            log.debug("Incident Time: " + incidentTime.toString());

            if (incidentTime != null) {
                incident.setIncidentTime(incidentTime);
            }
        } else {
            String incidentDateAsString = XmlUtils.xPathStringSearch(incidentReport,
                    PATH_TO_LEXS_DIGEST + "/lexsdigest:EntityActivity/nc:Activity/nc:ActivityDate/nc:Date");
            log.debug("Incident Date: " + incidentDateAsString);

            Calendar incidentDateCal = DatatypeConverter.parseDate(incidentDateAsString);
            Date incidentDate = incidentDateCal.getTime();

            if (incidentDate != null) {
                incident.setIncidentDate(incidentDate);
            }

        }

        String mapHorizontalCoordinateText = XmlUtils.xPathStringSearch(incidentReport,
                PATH_TO_LEXS_DATA_ITEM_PACKAGE
                        + "/lexs:StructuredPayload/inc-ext:IncidentReport/inc-ext:Location/nc:LocationMapLocation/nc:MapHorizontalCoordinateText");

        if (StringUtils.isNotBlank(mapHorizontalCoordinateText)) {
            //TODO: put this into a strategy
            try {
                mapHorizontalCoordinateText = updatedCoordinate(mapHorizontalCoordinateText);
                log.debug("Map horizontal coordinate text: " + mapHorizontalCoordinateText);

                BigDecimal longitude = new BigDecimal(mapHorizontalCoordinateText);
                incident.setIncidentLocationLongitude(longitude);
            } catch (Exception ex) {
                log.warn("Unable to set map horizontal text coordinate");
            }
        }

        String mapVerticalCoordinateText = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                + "/lexs:StructuredPayload/inc-ext:IncidentReport/inc-ext:Location/nc:LocationMapLocation/nc:MapVerticalCoordinateText");

        if (StringUtils.isNotBlank(mapVerticalCoordinateText)) {
            //TODO: put this into a strategy
            try {
                mapVerticalCoordinateText = updatedCoordinate(mapVerticalCoordinateText);
                log.debug("Map vertical coordinate text: " + mapVerticalCoordinateText);

                BigDecimal latitude = new BigDecimal(mapVerticalCoordinateText);
                incident.setIncidentLocationLatitude(latitude);
            } catch (Exception ex) {
                log.warn("Unable to set map vertical text coordinate");
            }
        }

        String incidentLocationReference = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DIGEST
                + "/lexsdigest:Associations/lexsdigest:IncidentLocationAssociation/nc:LocationReference/@s:ref");

        if (StringUtils.isNotEmpty(incidentLocationReference)) {
            Node locationNode = XmlUtils.xPathNodeSearch(incidentReport, PATH_TO_LEXS_DIGEST
                    + "/lexsdigest:EntityLocation/nc:Location[@s:id='" + incidentLocationReference + "']");

            String streetFullText = XmlUtils.xPathStringSearch(locationNode,
                    "nc:LocationAddress/nc:StructuredAddress/nc:LocationStreet/nc:StreetFullText");
            log.debug("Street Full Text: " + streetFullText);

            if (StringUtils.isNotBlank(streetFullText)) {
                incident.setIncidentLocationStreetAddress(streetFullText);
            } else {
                String streetNumberText = XmlUtils.xPathStringSearch(locationNode,
                        "nc:LocationAddress/nc:StructuredAddress/nc:LocationStreet/nc:StreetNumberText");
                log.debug("Street Number Text: " + streetNumberText);

                String streetName = XmlUtils.xPathStringSearch(locationNode,
                        "nc:LocationAddress/nc:StructuredAddress/nc:LocationStreet/nc:StreetName");
                log.debug("Street Name Text: " + streetName);

                String streetCategoryText = XmlUtils.xPathStringSearch(locationNode,
                        "nc:LocationAddress/nc:StructuredAddress/nc:LocationStreet/nc:StreetCategoryText");
                log.debug("Street Category Text: " + streetCategoryText);

                streetFullText = streetNumberText + streetName + streetFullText;
                log.debug("Street Full Text built from number, name and category: " + streetFullText);

                if (StringUtils.isNotBlank(streetFullText)) {
                    incident.setIncidentLocationStreetAddress(streetFullText);
                }

            }

            String cityTown = XmlUtils.xPathStringSearch(locationNode,
                    "nc:LocationAddress/nc:StructuredAddress/nc:LocationCityName");
            log.debug("City/Town: " + cityTown);

            if (StringUtils.isNotBlank(cityTown)) {
                incident.setIncidentLocationTown(cityTown);
            }

        }

        Integer incidentPk = analyticalDatastoreDAO.saveIncident(incident);

        //Add Incident Description Text
        processIncidentType(incidentReport, incidentPk);

        //Save circumstance codes
        processCircumstanceCodes(incidentReport, incidentPk);

        processArrests(incidentReport, incidentPk, reportingSystem);

    }

    private void processIncidentType(Document incidentReport, Integer incidentPk) throws Exception {
        NodeList incidentNatureTextNodes = XmlUtils.xPathNodeListSearch(incidentReport,
                PATH_TO_LEXS_DATA_ITEM_PACKAGE
                        + "/lexs:StructuredPayload/inc-ext:IncidentReport/inc-ext:Incident/inc-ext:IncidentNatureText");

        if (incidentNatureTextNodes == null || incidentNatureTextNodes.getLength() == 0) {
            log.debug("No incident nature text nodes in document");
            return;
        }

        for (int i = 0; i < incidentNatureTextNodes.getLength(); i++) {
            IncidentType incidentType = new IncidentType();

            incidentType.setIncidentID(incidentPk);
            incidentType.setIncidentDescriptionText(incidentNatureTextNodes.item(i).getTextContent());

            analyticalDatastoreDAO.saveIncidentType(incidentType);

        }

    }

    private void processCircumstanceCodes(Document incidentReport, Integer incidentPk) throws Exception {

        NodeList circumstanceCodeNodes = XmlUtils.xPathNodeListSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                + "/lexs:StructuredPayload/inc-ext:IncidentReport/inc-ext:Offense/inc-ext:OffenseModifierText");

        if (circumstanceCodeNodes == null || circumstanceCodeNodes.getLength() == 0) {
            log.debug("No circumstance codes in document");
            return;
        }

        for (int i = 0; i < circumstanceCodeNodes.getLength(); i++) {
            IncidentCircumstance incidentCircumstance = new IncidentCircumstance();

            incidentCircumstance.setIncidentID(incidentPk);
            incidentCircumstance.setIncidentCircumstanceText(circumstanceCodeNodes.item(i).getTextContent());

            analyticalDatastoreDAO.saveIncidentCircumstance(incidentCircumstance);

        }

    }

    protected void processArrests(Document incidentReport, Integer incidentPk, String reportingSystem)
            throws Exception {
        NodeList arrestSubjectAssocationNodes = XmlUtils.xPathNodeListSearch(incidentReport,
                PATH_TO_LEXS_DIGEST + "/lexsdigest:Associations/lexsdigest:ArrestSubjectAssociation");

        if (arrestSubjectAssocationNodes == null || arrestSubjectAssocationNodes.getLength() == 0) {
            log.debug("No Arrest nodes in document");
            return;
        }

        for (int i = 0; i < arrestSubjectAssocationNodes.getLength(); i++) {
            Arrest arrest = new Arrest();

            arrest.setIncidentID(incidentPk);
            arrest.setReportingSystem(reportingSystem);

            if (arrestSubjectAssocationNodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                String personId = XmlUtils.xPathStringSearch(arrestSubjectAssocationNodes.item(i),
                        "nc:PersonReference/@s:ref");
                log.debug("Arrestee Person ID: " + personId);

                String arrestId = XmlUtils.xPathStringSearch(arrestSubjectAssocationNodes.item(i),
                        "nc:ActivityReference/@s:ref");
                log.debug("Arrest ID: " + arrestId);

                Node personNode = XmlUtils.xPathNodeSearch(incidentReport, PATH_TO_LEXS_DIGEST
                        + "/lexsdigest:EntityPerson/lexsdigest:Person[@s:id='" + personId + "']");

                Map<String, Object> arrestee = AnalyticalDataStoreUtils.retrieveMapOfPersonAttributes(personNode);
                String personIdentifierKey = identifierGenerationStrategy.generateIdentifier(arrestee);
                log.debug("Arrestee person identifier keys: " + personIdentifierKey);

                String personBirthDateAsString = (String) arrestee
                        .get(IdentifierGenerationStrategy.BIRTHDATE_FIELD);
                String personRace = (String) arrestee.get("personRace");

                if (StringUtils.isBlank(personRace)) {
                    personRace = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                            + "/lexs:StructuredPayload/ndexia:IncidentReport/ndexia:Person/ndexia:PersonAugmentation[lexslib:SameAsDigestReference/@lexslib:ref='"
                            + personId + "']/ndexia:PersonRaceCode");
                    log.debug("Person race retrieved from ndex payload: " + personRace);
                }

                String personSex = (String) arrestee.get(IdentifierGenerationStrategy.SEX_FIELD);

                int personPk = savePerson(personBirthDateAsString, personSex, personRace, personIdentifierKey);
                arrest.setPersonID(personPk);

                Date arrestDateTime = returnArrestDate(incidentReport, arrestSubjectAssocationNodes.item(i));

                arrest.setArrestDate(arrestDateTime);

                Time arrestTime = new Time(arrestDateTime.getTime());
                log.debug("Arrest Time: " + arrestTime.toString());

                arrest.setArrestTime(arrestTime);

                String arrestingAgency = returnArrestingAgency(incidentReport);
                log.debug("Arresting Agency: " + arrestingAgency);

                if (StringUtils.isNotBlank(arrestingAgency)) {
                    arrest.setArrestingAgencyName(arrestingAgency);
                }

                //Save arrest
                int arrestPk = analyticalDatastoreDAO.saveArrest(arrest);

                //Save Charges
                processArrestCharge(incidentReport, arrestPk, arrestId);
            }
        }
    }

    private void processArrestCharge(Document incidentReport, int arrestPk, String arrestId) throws Exception {
        //get lexsdigest:ArrestOffenseAssociation nodes using arrest id
        NodeList arrestOffenseAssociationNodes = XmlUtils.xPathNodeListSearch(incidentReport,
                PATH_TO_LEXS_DIGEST + "/lexsdigest:Associations/lexsdigest:ArrestOffenseAssociation");

        //Get all offense IDs by looping through association
        for (int i = 0; i < arrestOffenseAssociationNodes.getLength(); i++) {
            String offenseId = XmlUtils.xPathStringSearch(arrestOffenseAssociationNodes.item(i),
                    "nc:ActivityReference[2]/@s:ref");
            log.debug("Arrest Node, Offense ID: " + offenseId);

            String offenseCategoryCodeText = XmlUtils.xPathStringSearch(incidentReport,
                    PATH_TO_LEXS_DATA_ITEM_PACKAGE
                            + "/lexs:StructuredPayload/inc-ext:IncidentReport/inc-ext:Offense[lexslib:SameAsDigestReference/@lexslib:ref='"
                            + offenseId + "']/inc-ext:OffenseCategoryCodeText");

            if (StringUtils.isBlank(offenseCategoryCodeText)) {
                offenseCategoryCodeText = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                        + "/lexs:StructuredPayload/ndexia:IncidentReport/ndexia:Offense[ndexia:ActivityAugmentation/lexslib:SameAsDigestReference/@lexslib:ref='"
                        + offenseId + "']/ndexia:OffenseCode");
            }

            log.debug("Offense Category Code Text (Statute): " + offenseCategoryCodeText);

            String offenseDescriptionText1 = XmlUtils.xPathStringSearch(incidentReport,
                    PATH_TO_LEXS_DATA_ITEM_PACKAGE
                            + "/lexs:StructuredPayload/ndexia:IncidentReport/ndexia:Offense[ndexia:ActivityAugmentation/lexslib:SameAsDigestReference/@lexslib:ref='"
                            + offenseId + "']/jxdm40:Statute/jxdm40:StatuteCodeIdentification/nc:IdentificationID");

            if (StringUtils.isBlank(offenseDescriptionText1)) {
                offenseDescriptionText1 = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DATA_ITEM_PACKAGE
                        + "/lexs:StructuredPayload/ndexia:IncidentReport/ndexia:Offense[ndexia:ActivityAugmentation/lexslib:SameAsDigestReference/@lexslib:ref='"
                        + offenseId + "']/ndexia:OffenseText");
            }

            log.debug("Offense Description Text 1 (Statute): " + offenseDescriptionText1);

            Charge charge = new Charge();
            charge.setArrestID(arrestPk);

            if (StringUtils.isNotBlank(offenseCategoryCodeText)) {
                charge.setOffenseDescriptionText(offenseCategoryCodeText);
            }

            if (StringUtils.isNotBlank(offenseDescriptionText1)) {
                charge.setOffenseDescriptionText1(offenseDescriptionText1.trim());
            }

            analyticalDatastoreDAO.saveCharge(charge);
        }

    }

    protected Date returnArrestDate(Document incidentReport, Node arrestSubjectAssocationNode) throws Exception {
        String arrestActivityReference = XmlUtils.xPathStringSearch(arrestSubjectAssocationNode,
                "nc:ActivityReference/@s:ref");
        log.debug("Arrest Activity Reference: " + arrestActivityReference);

        Node arrestNode = XmlUtils.xPathNodeSearch(incidentReport, PATH_TO_LEXS_DIGEST
                + "/lexsdigest:EntityActivity/nc:Activity[@s:id='" + arrestActivityReference + "']");

        String arrestDateTimeAsString = XmlUtils.xPathStringSearch(arrestNode, "nc:ActivityDate/nc:DateTime");

        Calendar arrestDateTimeCal = DatatypeConverter.parseDateTime(arrestDateTimeAsString);
        Date arrestDateTime = arrestDateTimeCal.getTime();
        return arrestDateTime;
    }

    private String returnArrestingAgency(Node incidentReport) throws Exception {
        String arrestingOfficerRefernce = XmlUtils.xPathStringSearch(incidentReport, PATH_TO_LEXS_DIGEST
                + "/lexsdigest:Associations/lexsdigest:ArrestOfficerAssociation/nc:PersonReference/@s:ref");
        log.debug("Arresting Officer Reference: " + arrestingOfficerRefernce);

        String arrestAgencyReference = XmlUtils.xPathStringSearch(incidentReport,
                PATH_TO_LEXS_DIGEST + "/lexsdigest:Associations/nc:PersonAssignedUnitAssociation/nc:PersonReference"
                        + "[@s:ref='" + arrestingOfficerRefernce
                        + "']/following-sibling::nc:OrganizationReference/@s:ref");
        log.debug("Arresting Agency Reference: " + arrestAgencyReference);

        String arrestingAgency = XmlUtils.xPathStringSearch(incidentReport,
                PATH_TO_LEXS_DIGEST + "/lexsdigest:EntityOrganization/nc:Organization[@s:id='"
                        + arrestAgencyReference + "']/nc:OrganizationName");
        log.debug("Arresting Agency: " + arrestingAgency);

        return arrestingAgency;
    }

    protected String updatedCoordinate(String coordinateText) {

        if (coordinateText.startsWith("-")) {
            coordinateText = new StringBuilder(coordinateText).insert(3, ".").toString();
        } else {
            coordinateText = new StringBuilder(coordinateText).insert(2, ".").toString();
        }

        return coordinateText;
    }

}