org.mitre.ptmatchadapter.format.SimplePatientCsvFormat.java Source code

Java tutorial

Introduction

Here is the source code for org.mitre.ptmatchadapter.format.SimplePatientCsvFormat.java

Source

/**
 * PtMatchAdapter - a patient matching system adapter
 * Copyright (C) 2016 The MITRE Corporation.  ALl rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mitre.ptmatchadapter.format;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.Pointer;
import org.hl7.fhir.instance.model.ContactPoint;
import org.hl7.fhir.instance.model.HumanName;
import org.hl7.fhir.instance.model.Identifier;
import org.hl7.fhir.instance.model.Patient;
import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.instance.model.ContactPoint.ContactPointUse;
import org.mitre.ptmatchadapter.util.ContactPointUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Formats a Patient FHIR Resource as a line of comma-separated values.
 * 
 * The field order is:
 * <ul>
 * <li>id</li>
 * <li>identifiers</li>
 * <li>name parts</li>
 * <li>gender</li>
 * <li>date of birth</li>
 * <li>mobile phone number</li>
 * <li>work phone</li>
 * <li>home phone</li>
 * <li>work email address</li>
 * <li>home email address</li>
 * </ul>
 * 
 * @author Michael Los, mel@mitre.org
 *
 */
public class SimplePatientCsvFormat {
    private static final Logger LOG = LoggerFactory.getLogger(SimplePatientCsvFormat.class);

    private static final int INITIAL_ROW_LENGTH = 500;
    private static final String COMMA = ",";
    private static final String DOUBLE_QUOTE = "\"";
    private static final String UNDERSCORE = "_";

    private String[] identifierSystems = { "SSN" };

    private String[] nameUses = { "", "official", "usual" };
    private String[] nameParts = { TEXT_NAME_PART, "family", "suffix", "given" };

    public static final String TEXT_NAME_PART = "text";

    private ContactPointSystem[] telecomSystems = { ContactPointSystem.PHONE, ContactPointSystem.EMAIL };
    private ContactPointUse[] telecomUses = { ContactPointUse.WORK, ContactPointUse.HOME };

    public final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * Returns column titles as a comma-separated strings.
     * 
     * @return String of comma-separated column titles
     */
    public String getHeaders() {
        final StringBuilder sb = new StringBuilder(INITIAL_ROW_LENGTH);

        // resource Id
        sb.append("id");
        sb.append(COMMA);

        // Identifiers
        for (String sysName : identifierSystems) {
            sb.append(DOUBLE_QUOTE);
            sb.append("identifier_");
            sb.append(sysName);
            sb.append(DOUBLE_QUOTE);
            sb.append(COMMA);
        }
        // Name fields. Title formed as name_use_part (e.g., name_official_family)
        String sep = "";
        for (String use : nameUses) {
            for (String part : nameParts) {
                sb.append(sep);
                sb.append("name_");
                sb.append(use);
                sb.append(UNDERSCORE);
                sb.append(part);
                sep = COMMA;
            }
        }

        sb.append(COMMA);
        sb.append("gender");

        sb.append(COMMA);
        sb.append("DOB");

        sb.append(COMMA);
        sb.append("telecom_phone_mobile");

        for (ContactPointSystem system : telecomSystems) {
            for (ContactPointUse use : telecomUses) {
                sb.append(COMMA);
                sb.append("telecom_");
                sb.append(system.toString().toLowerCase());
                sb.append(UNDERSCORE);
                sb.append(use.toString().toLowerCase());
            }
        }

        return sb.toString();
    }

    /**
     * Returns supported Patient properties as a string of comma-separated values.
     * Values of String fields are enclosed by double-quotes.
     * 
     * @param patient
     *          the patient resource to serialize to a CSV string
     * @return
     *          comma-delimited values of fields associated with the Patient
     */
    public String toCsv(Patient patient) {
        final StringBuilder sb = new StringBuilder(INITIAL_ROW_LENGTH);
        JXPathContext patientCtx = JXPathContext.newContext(patient);

        try {
            // resource id (logical id only)
            sb.append(patient.getIdElement().getIdPart());
        } catch (NullPointerException e) {
            // check for null by exception since it is unexpected. 
            // Privacy concern keeps me from logging anything about this patient.
            LOG.error("Patient has null identifier element. This is unexpected!");
        }
        sb.append(COMMA);

        // identifiers of interest
        for (String sysName : identifierSystems) {
            Pointer ptr = patientCtx.getPointer("identifier[system='" + sysName + "']");
            Identifier id = (Identifier) ptr.getValue();
            if (id != null) {
                sb.append(id.getValue());
            }
            sb.append(COMMA);
        }

        // Extract Name Parts of interest
        for (String use : nameUses) {
            Pointer ptr;
            if (use.isEmpty()) {
                ptr = patientCtx.getPointer("name[not(use)]");
            } else {
                ptr = patientCtx.getPointer("name[use='" + use + "']");
            }
            HumanName name = (HumanName) ptr.getValue();
            if (name != null) {
                JXPathContext nameCtx = JXPathContext.newContext(ptr.getValue());
                for (String part : nameParts) {
                    sb.append(DOUBLE_QUOTE);
                    if (TEXT_NAME_PART.equals(part)) {
                        Object val = nameCtx.getValue(part);
                        if (val != null) {
                            sb.append(val.toString());
                        }
                    } else {
                        // other supported parts return lists of string types
                        Object namePart = nameCtx.getValue(part);
                        if (namePart instanceof List<?>) {
                            List<StringType> partList = (List<StringType>) namePart;
                            if (partList.size() > 0) {
                                sb.append(partList.get(0).getValue());
                            }
                        }
                    }
                    sb.append(DOUBLE_QUOTE);
                    sb.append(COMMA);
                }
            } else {
                // add blank sections for the name parts
                for (int i = 0; i < nameParts.length; i++) {
                    sb.append(COMMA);
                }
            }
        }

        // Gender
        sb.append(patient.getGender().toString());
        sb.append(COMMA);

        // Date of Birth
        Date dob = patient.getBirthDate();
        if (dob != null) {
            sb.append(dateFormat.format(dob));
        }

        sb.append(COMMA);
        sb.append(buidContactInfo(patient));

        return sb.toString();
    }

    /**
     * Concatenate contact point information as a string of comma-separated
     * values.
     * 
     * @param patient
     * @return String containing comma-separated list of contact information
     */
    private String buidContactInfo(Patient patient) {
        final StringBuilder sb = new StringBuilder(100);

        List<ContactPoint> matches = ContactPointUtil.find(patient.getTelecom(), ContactPointSystem.PHONE,
                ContactPointUse.MOBILE);

        if (matches.size() > 0) {
            sb.append(matches.get(0).getValue());
        }

        for (ContactPointSystem system : telecomSystems) {
            for (ContactPointUse use : telecomUses) {
                matches = ContactPointUtil.find(patient.getTelecom(), system, use);
                sb.append(COMMA);
                if (matches.size() > 0) {
                    sb.append(matches.get(0).getValue());
                }
            }
        }

        return sb.toString();
    }

    /**
     * @param nameParts
     *          the nameParts to set
     */
    public final void setNameParts(String[] nameParts) {
        this.nameParts = new String[nameParts.length];
        for (int i = 0; i < nameParts.length; i++) {
            this.nameParts[i] = nameParts[i].toLowerCase();
        }
    }

    /**
     * @return the nameUses
     */
    public final String[] getNameUses() {
        return nameUses;
    }

    /**
     * @param nameUses
     *          the nameUses to set
     */
    public final void setNameUses(String[] nameUses) {
        this.nameUses = new String[nameUses.length];
        for (int i = 0; i < nameUses.length; i++) {
            this.nameUses[i] = nameUses[i].toLowerCase();
        }
    }

}