/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.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://license.openmrs.org
*
* 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.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.migration;
import java.io.IOException;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Location;
import org.openmrs.Patient;
import org.openmrs.PatientIdentifier;
import org.openmrs.PatientIdentifierType;
import org.openmrs.PatientProgram;
import org.openmrs.PatientState;
import org.openmrs.Person;
import org.openmrs.PersonName;
import org.openmrs.Program;
import org.openmrs.ProgramWorkflow;
import org.openmrs.ProgramWorkflowState;
import org.openmrs.Relationship;
import org.openmrs.RelationshipType;
import org.openmrs.Role;
import org.openmrs.User;
import org.openmrs.api.LocationService;
import org.openmrs.api.PatientService;
import org.openmrs.api.PersonService;
import org.openmrs.api.ProgramWorkflowService;
import org.openmrs.api.UserService;
import org.openmrs.api.context.Context;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class MigrationHelper {
protected final static Log log = LogFactory.getLog(MigrationHelper.class);
static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parseDate(String s) throws ParseException {
if (s == null || s.length() == 0) {
return null;
} else {
if (s.length() == 10) {
s += " 00:00:00";
}
return df.parse(s);
}
}
public static Document parseXml(String xml) throws ParserConfigurationException {
DocumentBuilder builder = factory.newDocumentBuilder();
try {
return builder.parse(new InputSource(new StringReader(xml)));
}
catch (IOException ex) {
return null;
}
catch (SAXException e) {
return null;
}
}
private static void findNodesNamed(Node node, String lookForName, Collection<Node> ret) {
if (node.getNodeName().equals(lookForName)) {
ret.add(node);
} else {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); ++i) {
findNodesNamed(list.item(i), lookForName, ret);
}
}
}
/**
* Takes XML like: <something> <user date_changed="2001-03-06 08:46:53.0"
* date_created="2001-03-06 08:46:53.0" username="hamish@mit.edu" first_name="Hamish"
* last_name="Fraser" user_id="2001"/> </something> Returns the number of users added
*/
public static int importUsers(Document document) throws ParseException {
int ret = 0;
Random rand = new Random();
UserService us = Context.getUserService();
List<Node> toAdd = new ArrayList<Node>();
findNodesNamed(document, "user", toAdd);
for (Node node : toAdd) {
Element e = (Element) node;
String username = e.getAttribute("username");
if (username == null || username.length() == 0) {
throw new IllegalArgumentException("each <user /> element must define a user_name attribute");
}
if (us.getUserByUsername(username) != null) {
continue;
}
User user = new User();
user.setPerson(new Person());
PersonName pn = new PersonName(e.getAttribute("first_name"), "", e.getAttribute("last_name"));
user.addName(pn);
user.setUsername(username);
user.setDateCreated(parseDate(e.getAttribute("date_created")));
user.setDateChanged(parseDate(e.getAttribute("date_changed")));
// Generate a temporary password: 8-12 random characters
String pass = null;
{
int length = rand.nextInt(4) + 8;
char[] password = new char[length];
for (int x = 0; x < length; x++) {
int randDecimalAsciiVal = rand.nextInt(93) + 33;
password[x] = (char) randDecimalAsciiVal;
}
pass = new String(password);
}
us.saveUser(user, pass);
++ret;
}
return ret;
}
/**
* Takes XML like: <something> <location name="Cerca-la-Source"/> </something> returns the
* number of locations added
*/
public static int importLocations(Document document) {
int ret = 0;
LocationService ls = Context.getLocationService();
List<Node> toAdd = new ArrayList<Node>();
findNodesNamed(document, "location", toAdd);
for (Node node : toAdd) {
Element e = (Element) node;
String name = e.getAttribute("name");
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("each <location /> element must define a name attribute");
}
if (ls.getLocation(name) != null) {
continue;
}
Location location = new Location();
location.setName(name);
ls.saveLocation(location);
++ret;
}
return ret;
}
/**
* Takes a list of Strings of the format RELATIONSHIP:<user last name>,<user first
* name>,<relationship type name>,<patient identifier type name>,<identifier> so if user hfraser
* if the cardiologist of the patient with patient_id 8039 in PIH's old emr, then:
* RELATIONSHIP:hfraser,Cardiologist,HIV-EMRV1,8039 (the "RELATIONSHIP:" is not actually
* necessary. Anything before and including the first : will be dropped If autoCreateUsers is
* true, and no user exists with the given username, one will be created. If autoAddRole is
* true, then whenever a user is auto-created, if a role exists with the same name as
* relationshipType.name, then the user will be added to that role
*/
public static int importRelationships(Collection<String> relationships, boolean autoCreateUsers, boolean autoAddRole) {
PatientService ps = Context.getPatientService();
UserService us = Context.getUserService();
PersonService personService = Context.getPersonService();
List<Relationship> relsToAdd = new ArrayList<Relationship>();
Random rand = new Random();
for (String s : relationships) {
if (s.indexOf(":") >= 0)
s = s.substring(s.indexOf(":") + 1);
String[] ss = s.split(",");
if (ss.length < 5)
throw new IllegalArgumentException("The line '" + s + "' is in the wrong format");
String userLastName = ss[0];
String userFirstName = ss[1];
String username = (userFirstName + userLastName).replaceAll(" ", "");
String relationshipType = ss[2];
String identifierType = ss[3];
String identifier = ss[4];
User user = null;
{ // first try looking for non-voided users
List<User> users = us.getUsersByName(userFirstName, userLastName, false);
if (users.size() == 1)
user = users.get(0);
else if (users.size() > 1) {
throw new IllegalArgumentException("Found " + users.size() + " users named '" + userLastName + ", "
+ userFirstName + "'");
}
}
if (user == null) {
// next try looking for voided users
List<User> users = us.getUsersByName(userFirstName, userLastName, false);
if (users.size() == 1)
user = users.get(0);
else if (users.size() > 1) {
throw new IllegalArgumentException("Found " + users.size() + " voided users named '" + userLastName
+ ", " + userFirstName + "'");
}
}
if (user == null && autoCreateUsers) {
user = new User();
user.setPerson(new Person());
PersonName pn = new PersonName(userFirstName, "", userLastName);
user.addName(pn);
user.setUsername(username);
// Generate a temporary password: 8-12 random characters
String pass = null;
{
int length = rand.nextInt(4) + 8;
char[] password = new char[length];
for (int x = 0; x < length; x++) {
int randDecimalAsciiVal = rand.nextInt(93) + 33;
password[x] = (char) randDecimalAsciiVal;
}
pass = new String(password);
}
if (autoAddRole) {
Role role = us.getRole(relationshipType);
if (role != null)
user.addRole(role);
}
us.saveUser(user, pass);
}
if (user == null)
throw new IllegalArgumentException("Can't find user '" + userLastName + ", " + userFirstName + "'");
Person person = personService.getPerson(user.getUserId());
RelationshipType relationship = personService.getRelationshipTypeByName(relationshipType);
PatientIdentifierType pit = ps.getPatientIdentifierTypeByName(identifierType);
List<PatientIdentifier> found = ps.getPatientIdentifiers(identifier, Collections.singletonList(pit), null, null,
null);
if (found.size() != 1)
throw new IllegalArgumentException("Found " + found.size() + " patients with identifier '" + identifier
+ "' of type " + identifierType);
Person relative = personService.getPerson(found.get(0).getPatient().getPatientId());
Relationship rel = new Relationship();
rel.setPersonA(person);
rel.setRelationshipType(relationship);
rel.setPersonB(relative);
relsToAdd.add(rel);
}
int addedSoFar = 0;
for (Relationship rel : relsToAdd) {
personService.saveRelationship(rel);
++addedSoFar;
}
return addedSoFar;
}
public static int importProgramsAndStatuses(List<String> programWorkflow) throws ParseException {
ProgramWorkflowService pws = Context.getProgramWorkflowService();
PatientService ps = Context.getPatientService();
List<PatientProgram> patientPrograms = new ArrayList<PatientProgram>();
//List<PatientState> patientStates = new ArrayList<PatientState>();
Map<String, PatientProgram> knownPatientPrograms = new HashMap<String, PatientProgram>();
Map<String, Program> programsByName = new HashMap<String, Program>();
for (Program program : pws.getAllPrograms()) {
programsByName.put(program.getConcept().getName(Context.getLocale(), false).getName(), program);
}
for (String s : programWorkflow) {
// ENROLLMENT:HIVEMR-V1,9266,IMB HIV PROGRAM,2005-08-25,
log.debug(s);
if (s.startsWith("ENROLLMENT:")) {
s = s.substring(s.indexOf(":") + 1);
String[] temp = s.split(",");
PatientIdentifierType pit = ps.getPatientIdentifierTypeByName(temp[0]);
String identifier = temp[1];
List<PatientIdentifier> pis = ps.getPatientIdentifiers(identifier, Collections.singletonList(pit), null,
null, null);
if (pis.size() != 1)
throw new IllegalArgumentException("Found " + pis.size() + " instances of identifier " + identifier
+ " of type " + pit);
Patient p = pis.get(0).getPatient();
Program program = programsByName.get(temp[2]);
if (program == null)
throw new RuntimeException("Couldn't find program \"" + temp[2] + "\" in " + programsByName);
Date enrollmentDate = temp.length < 4 ? null : parseDate(temp[3]);
Date completionDate = temp.length < 5 ? null : parseDate(temp[4]);
PatientProgram pp = new PatientProgram();
pp.setPatient(p);
pp.setProgram(program);
pp.setDateEnrolled(enrollmentDate);
pp.setDateCompleted(completionDate);
patientPrograms.add(pp);
knownPatientPrograms.put(temp[0] + "," + temp[1] + "," + temp[2], pp); // "HIVEMR-V1,9266,IMB HIV PROGRAM"
} else if (s.startsWith("STATUS:")) {
// STATUS:HIVEMR-V1,9266,IMB HIV PROGRAM,TREATMENT STATUS,ACTIVE,2005-08-25,,
s = s.substring(s.indexOf(":") + 1);
String[] temp = s.split(",");
/* We're using a cache of 'knownPatientPrograms' instead of the following commented code
PatientIdentifierType pit = ps.getPatientIdentifierType(temp[0]);
String identifier = temp[1];
List<PatientIdentifier> pis = ps.getPatientIdentifiers(identifier, pit);
if (pis.size() != 1)
throw new IllegalArgumentException("Found " + pis.size() + " instances of identifier " + identifier + " of type " + pit);
Patient p = pis.get(0).getPatient();
*/
Program program = programsByName.get(temp[2]);
if (program == null)
throw new RuntimeException("Couldn't find program \"" + temp[2] + "\" in " + programsByName);
//ProgramWorkflow wf = pws.getWorkflow(program, temp[3]);
ProgramWorkflow wf = program.getWorkflowByName(temp[3]);
if (wf == null)
throw new RuntimeException("Couldn't find workflow \"" + temp[3] + "\" for program " + program + " (in "
+ program.getAllWorkflows() + ")");
//ProgramWorkflowState st = pws.getState(wf, temp[4]);
ProgramWorkflowState st = wf.getStateByName(temp[4]);
if (st == null)
throw new RuntimeException("Couldn't find state \"" + temp[4] + "\" for workflow " + wf + " (in "
+ wf.getStates() + ")");
Date startDate = temp.length < 6 ? null : parseDate(temp[5]);
Date endDate = temp.length < 7 ? null : parseDate(temp[6]);
PatientState state = new PatientState();
PatientProgram pp = knownPatientPrograms.get(temp[0] + "," + temp[1] + "," + temp[2]);
state.setPatientProgram(pp);
state.setState(st);
state.setStartDate(startDate);
state.setEndDate(endDate);
pp.getStates().add(state);
}
}
int numAdded = 0;
for (PatientProgram pp : knownPatientPrograms.values()) {
pws.savePatientProgram(pp);
++numAdded;
}
return numAdded;
}
}