Package ke.go.moh.oec.mpi.match

Source Code of ke.go.moh.oec.mpi.match.PersonMatch

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is OpenEMRConnect.
*
* The Initial Developer of the Original Code is International Training &
* Education Center for Health (I-TECH) <http://www.go2itech.org/>
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
package ke.go.moh.oec.mpi.match;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import ke.go.moh.oec.Fingerprint;
import ke.go.moh.oec.Person;
import ke.go.moh.oec.PersonIdentifier;
import ke.go.moh.oec.lib.Mediator;
import ke.go.moh.oec.mpi.Scorecard;
import ke.go.moh.oec.mpi.SiteCandidate;

/**
* Represents a Person for matching.
*
* @author Jim Grace
*/
public class PersonMatch {

    /**
     * Stores all the data that doesn't need to be pre-processed
     * for approximate matching.
     */
    private Person person;
    /** Database person_id (primary key) for this person */
    private int DbPersonId;
    /** Person's three names. */
    private PersonNamesMatch self;
    /** Mother's three names. */
    private PersonNamesMatch mother;
    /** Father's three names. */
    private PersonNamesMatch father;
    /** Head of Compound's three names. */
    private PersonNamesMatch compoundHead;
    /** The person's date of birth. */
    private DateMatch birthdateMatch;
    /** Any other name by which a person is commonly known. */
    private NameMatch otherNameMatch;
    /** The name of the clan to which a person belongs. */
    private NameMatch clanNameMatch;
    /** The name of the village in which the person lives. */
    private NameMatch villageNameMatch;
    /** A list containing each FingerprintMatch for this person. */
    private List<FingerprintMatch> fingerprintMatchList;
    /** A list of possible site candidates if the site corresponding to an ID is uncertain. */
    private Set<SiteCandidate> siteCandidateSet;

    /**
     * Construct a PersonMatch from a Person object.
     * <p>
     * Information about the person is extracted and stored ahead of time for quick matching.
     * For persons coming from the database, this information is extracted when all the
     * database values are loaded into memory. Then a database value can be compared
     * more quickly with multiple searches. For the person search terms,
     * this information is extracted before comparing the search terms with all
     * the database values. Then a search term can be compared more quickly with
     * multiple database values.
     *
     * @param p the Person to use in matching.
     */
    public PersonMatch(Person p) {
        person = p;
        birthdateMatch = new DateMatch(p.getBirthdate());
        self = new PersonNamesMatch(p.getFirstName(), p.getMiddleName(), p.getLastName());
        mother = new PersonNamesMatch(p.getMothersFirstName(), p.getMothersMiddleName(), p.getMothersLastName());
        father = new PersonNamesMatch(p.getFathersFirstName(), p.getFathersMiddleName(), p.getFathersLastName());
        compoundHead = new PersonNamesMatch(p.getCompoundHeadFirstName(), p.getCompoundHeadMiddleName(), p.getCompoundHeadLastName());
        otherNameMatch = NameMatch.getNameMatch(p.getOtherName());
        clanNameMatch = NameMatch.getNameMatch(p.getClanName());
        villageNameMatch = NameMatch.getNameMatch(p.getVillageName());
        if (p.getFingerprintList() != null) {
            fingerprintMatchList = new ArrayList<FingerprintMatch>();
            for (Fingerprint f : p.getFingerprintList()) {
                FingerprintMatch mf = new FingerprintMatch(f);
                fingerprintMatchList.add(mf);
            }
        }
    }

    public int getDbPersonId() {
        return DbPersonId;
    }

    public void setDbPersonId(int DbPersonId) {
        this.DbPersonId = DbPersonId;
    }

    public DateMatch getBirthdateMatch() {
        return birthdateMatch;
    }

    public void setBirthdateMatch(DateMatch birthdateMatch) {
        this.birthdateMatch = birthdateMatch;
    }

    public NameMatch getClanNameMatch() {
        return clanNameMatch;
    }

    public void setClanNameMatch(NameMatch clanNameMatch) {
        this.clanNameMatch = clanNameMatch;
    }

    public PersonNamesMatch getCompoundHead() {
        return compoundHead;
    }

    public void setCompoundHead(PersonNamesMatch compoundHead) {
        this.compoundHead = compoundHead;
    }

    public PersonNamesMatch getFather() {
        return father;
    }

    public void setFather(PersonNamesMatch father) {
        this.father = father;
    }

    public List<FingerprintMatch> getFingerprintMatchList() {
        return fingerprintMatchList;
    }

    public void setFingerprintMatchList(List<FingerprintMatch> fingerprintMatchList) {
        this.fingerprintMatchList = fingerprintMatchList;
    }

    public PersonNamesMatch getMother() {
        return mother;
    }

    public void setMother(PersonNamesMatch mother) {
        this.mother = mother;
    }

    public NameMatch getOtherNameMatch() {
        return otherNameMatch;
    }

    public void setOtherNameMatch(NameMatch otherNameMatch) {
        this.otherNameMatch = otherNameMatch;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public PersonNamesMatch getSelf() {
        return self;
    }

    public void setSelf(PersonNamesMatch self) {
        this.self = self;
    }

    public Set<SiteCandidate> getSiteCandidateSet() {
        return siteCandidateSet;
    }

    public void setSiteCandidateSet(Set<SiteCandidate> siteCandidateSet) {
        this.siteCandidateSet = siteCandidateSet;
    }

    public NameMatch getVillageNameMatch() {
        return villageNameMatch;
    }

    public void setVillageNameMatch(NameMatch villageNameMatch) {
        this.villageNameMatch = villageNameMatch;
    }

    /**
     * Allocate and fill a scorecard that describes this person match.
     * <p>
     * Note that this matches everything about the person except fingerprints.
     * Fingerprints are handled separately because they require a different
     * prepared fingerprint context for the search terms for each concurrent thread.
     *
     * @param p The person to match with (either as a search term or a database entry).
     * @return The scorecard for this person match.
     */
    public Scorecard scorePersonMatch(PersonMatch p) {
        Scorecard s = new Scorecard();
        if ((p.getPerson().getAliveStatus() == Person.AliveStatus.yes && person.getDeathdate() != null)
                || (person.getAliveStatus()) == Person.AliveStatus.yes && p.getPerson().getDeathdate() != null) {
        } else {
            scoreSex(s, person.getSex(), p.getPerson().getSex());
            birthdateMatch.score(s, p.birthdateMatch);
            self.score(s, p.self);
            mother.score(s, p.mother);
            father.score(s, p.father);
            compoundHead.score(s, p.compoundHead);
            otherNameMatch.score(s, p.otherNameMatch);
            clanNameMatch.score(s, p.clanNameMatch);
            villageNameMatch.score(s, p.villageNameMatch);
            scorePersonIdentifiers(s, p.getPerson());
        }
        return s;
    }

    /**
     * Score a comparison of two genders.
     *
     * @param s The scorecard in which to record the score.
     * @param sex1 One gender to compare.
     * @param sex2 The other gender to compare.
     */
    private void scoreSex(Scorecard s, Person.Sex sex1, Person.Sex sex2) {
        if (sex1 == null) {
            s.addScore(Scorecard.SEARCH_TERM_MISSING_WEIGHT, 0, Scorecard.SearchTerm.MISSING);
        } else if (sex2 == null) {
            s.addScore(Scorecard.MPI_VALUE_MISSING_WEIGHT, 0);
        } else {
            double score = 0.0; // Score if sex doesn't match.
            double weight = Scorecard.SEX_MISS_WEIGHT; // Weight if sex doesn't match.
            if (sex1.ordinal() == sex2.ordinal()) {
                score = 1.0; // Score if sex matches.
                weight = Scorecard.SEX_MATCH_WEIGHT; // Weignt if sex matches.
            }
            s.addScore(score, weight);
            if (Mediator.testLoggerLevel(Level.FINEST)) {
                Mediator.getLogger(DateMatch.class.getName()).log(Level.FINEST,
                        "Score {0},{1} total {2},{3},{4} comparing {5} with {6}",
                        new Object[]{score, weight, s.getTotalScore(), s.getTotalWeight(), s.getSearchTermScore(), sex1.name(), sex2.name()});
            }
        }
    }

    /**
     * Score a comparison between two lists of person identifiers.
     * <p>
     * Note that when identifiers do not match, no score is added to the scorecard.
     * This is because people may have different identifiers, and a
     * non-match is not considered to reduce the overall average score.
     * But a positive match is given a high score.
     *
     * @param s The scorecard in which to record the score.
     * @param p1 One person identifier list to compare.
     * @param p2 The other person identifier list to compare.
     */
    private void scorePersonIdentifiers(Scorecard s, Person p) {
        Person pSearch = this.getPerson();
        List<PersonIdentifier> list1 = pSearch.getPersonIdentifierList();
        List<PersonIdentifier> list2 = p.getPersonIdentifierList();
        if (list1 == null) {
            s.addScore(Scorecard.SEARCH_TERM_MISSING_WEIGHT, 0, Scorecard.SearchTerm.MISSING);
        } else if (list2 == null) {
            s.addScore(Scorecard.MPI_VALUE_MISSING_WEIGHT, 0);
        } else {
            for (PersonIdentifier pi1 : list1) {
                for (PersonIdentifier pi2 : list2) {
                    if (pi1.getIdentifierType() == pi2.getIdentifierType()) {
                        if (identifierNeedsSite(pi1) && this.getSiteCandidateSet() != null) {
                            for (SiteCandidate sc : this.getSiteCandidateSet()) {
                                String identifier = sc.getSitePersonIdentifier();
                                if (identifier.equals(pi2.getIdentifier())) {
                                    recordIdentifierMatchScore(s, identifier);
                                    s.setSiteName(sc.getSiteMatch().getSiteName());
                                    return; // EARLY RETURN, match found, no more looping needed.
                                }
                            }
                        }
                        if (pi1.getIdentifier().equals(pi2.getIdentifier())) {
                            recordIdentifierMatchScore(s, pi1.getIdentifier());
                            return; // EARLY RETURN, match found, no more looping needed.
                        }
                    }
                }
            }
            // If we fall through the end of the loop, it means there was no identifier match.
            s.addScore(0.0, Scorecard.ID_MISS_WEIGHT);
            if (Mediator.testLoggerLevel(Level.FINEST)) {
                Mediator.getLogger(PersonMatch.class.getName()).log(Level.FINEST,
                        "Score {0},{1} total {2},{3},{4} no identifier matched with person {5}",
                        new Object[]{0.0, Scorecard.ID_MISS_WEIGHT, s.getTotalScore(),
                            s.getSearchTermScore(), s.getTotalWeight(), p.getPersonGuid()});
            }
        }
    }

    /**
     * Records an identifier match in the scorecard.
     *
     * @param s Scorecard in which to record the match.
     * @param matchingIdentifier matching identifier (for tracing in the log).
     */
    private void recordIdentifierMatchScore(Scorecard s, String matchingIdentifier) {
        final double PERSON_IDENTIFIER_SCORE = 1.0;
        s.addScore(PERSON_IDENTIFIER_SCORE, Scorecard.ID_MATCH_WEIGHT);
        if (Mediator.testLoggerLevel(Level.FINEST)) {
            Mediator.getLogger(PersonMatch.class.getName()).log(Level.FINEST,
                    "Score {0},{1} total {2},{3},{4} matching identifier {5}",
                    new Object[]{PERSON_IDENTIFIER_SCORE, Scorecard.ID_MATCH_WEIGHT, s.getTotalScore(),
                        s.getSearchTermScore(), s.getTotalWeight(), matchingIdentifier});
        }
    }

    /**
     * Tests to see if a person identifier needs a site name.
     *
     * @param pi PersonIdentifier to test
     * @return true if the PersonIdentifier needs a site name to be complete,
     * or false if the PersonIdentifier already includes a site name.
     */
    public static boolean identifierNeedsSite(PersonIdentifier pi) {
        boolean returnValue = false;
        if (pi.getIdentifierType() == PersonIdentifier.Type.cccLocalId) {
            String identifier = pi.getIdentifier();
            if (!identifier.matches("^[0-9]{5}-*.")) {
                returnValue = true;
            }
        }
        return returnValue;
    }
}
TOP

Related Classes of ke.go.moh.oec.mpi.match.PersonMatch

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.