|
|
|
Burke Mamlin
|
Tammy/Vibha,
Do you have any documentation or description of how program/workflow was adapted for CHICA's needs? Any chance either of you are going to be going to AMIA? -Burke _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
|
Anand, Vibha
|
Burke
The program/workflow was not used for CHICA's needs because the major flaw I saw at the time was it being non-deterministic. We ended up writing our own DFA state machine which is linked to atomic actions at each state (also configurable by use of custom java classes). This has been on the backburner but we have discussed with Paul a couple of times that it needs to go back in the core. We are using the program as a high level construct to drive the machine (not based on concept id as is the case now) and more recently we have added support to configure same or different programs for different locations / sub-locations. Tammy can perhaps fill in the gaps here, she is not going to be at AMIA but I will be and can answer more if you like. Also, if there is any discussion about SMS services in OpenMRS at AMIA, I would like to participate. Please let me know. Thanks! Vibha -----Original Message----- From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin Sent: Thursday, November 05, 2009 11:34 AM To: [hidden email] Subject: [OPENMRS-DEV] Program Workflow refactoring Tammy/Vibha, Do you have any documentation or description of how program/workflow was adapted for CHICA's needs? Any chance either of you are going to be going to AMIA? -Burke _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
||||||||||||||||
|
Tammy Dugan
|
It is pretty complicated so I am going to take some time this afternoon
and pull the state stuff out of atd and put it in a module. Once that is done I will try to send a brief overview to the list and reference the module for specifics. Thanks, Tammy Anand, Vibha wrote: > Burke > > The program/workflow was not used for CHICA's needs because the major flaw I saw at the time was it being non-deterministic. We ended up writing our own DFA state machine which is linked to atomic actions at each state (also configurable by use of custom java classes). This has been on the backburner but we have discussed with Paul a couple of times that it needs to go back in the core. > > We are using the program as a high level construct to drive the machine (not based on concept id as is the case now) and more recently we have added support to configure same or different programs for different locations / sub-locations. Tammy can perhaps fill in the gaps here, she is not going to be at AMIA but I will be and can answer more if you like. Also, if there is any discussion about SMS services in OpenMRS at AMIA, I would like to participate. Please let me know. Thanks! > > Vibha > > > -----Original Message----- > From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin > Sent: Thursday, November 05, 2009 11:34 AM > To: [hidden email] > Subject: [OPENMRS-DEV] Program Workflow refactoring > > Tammy/Vibha, > > Do you have any documentation or description of how program/workflow > was adapted for CHICA's needs? Any chance either of you are going to > be going to AMIA? > > -Burke > > _________________________________________ > > To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. > > [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] > > _________________________________________ > > To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. > > [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] > _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
||||||||||||||||
|
Tammy Dugan
|
In reply to this post
by Burke Mamlin
I created a module called stateprocessing? Is it OK to commit it?
Tammy Dugan Burke Mamlin wrote: > Tammy/Vibha, > > Do you have any documentation or description of how program/workflow > was adapted for CHICA's needs? Any chance either of you are going to > be going to AMIA? > > -Burke > > _________________________________________ > > To unsubscribe from OpenMRS Developers' mailing list, send an e-mail > to [hidden email] with "SIGNOFF openmrs-devel-l" in the > body (not the subject) of your e-mail. > > [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
||||||||||||||||
|
Tammy Dugan
|
I just committed the stateprocessing module. After my meeting this
morning, I will send some documentation to the list about the stateprocessing module along with the randomization module (since Darius mentioned supporting studies, which the randomization module does). Tammy Dugan Tammy Dugan wrote: > I created a module called stateprocessing? Is it OK to commit it? > > > Tammy Dugan > > Burke Mamlin wrote: >> Tammy/Vibha, >> >> Do you have any documentation or description of how program/workflow >> was adapted for CHICA's needs? Any chance either of you are going to >> be going to AMIA? >> >> -Burke >> >> _________________________________________ >> >> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail >> to [hidden email] with "SIGNOFF openmrs-devel-l" in the >> body (not the subject) of your e-mail. >> >> [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] > > _________________________________________ > > To unsubscribe from OpenMRS Developers' mailing list, send an e-mail > to [hidden email] with "SIGNOFF openmrs-devel-l" in the > body (not the subject) of your e-mail. > > [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
||||||||||||||||
|
Tammy Dugan
|
The "randomization" module supports study definition and randomization.
It should be fairly self explanatory. Here is a brief overview of the state processing in the "stateprocessing" module: StateManager class ------------------- This class takes care of making state transitions (based on the state mapping table), recording the current patient state in the patient_state table, and processing the state action. StateActionHandler class -------------------------- This is an interface that is implemented to tell how to process actions. I don't think this really needs to exist anymore now that we have the action_class column in the state_action table. Attached is the ChicaStateActionHandler class that we use to implement this interface. The interface method defined in this class can probably be moved into StateActionHandler and remove the need for an interface. ProcessStateAction class -------------------------- This is an interface implemented by state actions to tell what code should be executed for the action. There are two methods: processAction and changeState. The processAction method is the method that gets called to initiate a method transition. It can optionally end the patient state. The changeState method is used when the code has to wait for some external event trigger (the presence of a file created by Teleform, for example). The changeState method executes some code and ends the patient state. state table ----------- The state table keeps track of state metadata like name, description, etc. It also links to a state action from the state_action table to describe what java code should run when entering the given state. The form_name column is currently used to keep track of the name of the form that we want to produce from a given state. Since we can have different forms for different clinic locations, we look up the exact form configuration in a location_tag_attribute_value table that we created. The form_name column probably needs to be replaced by some other way of configuring things. state_action table ----------------- This table keeps track of state action metadata like name, description, etc. The action_class column defines the class that executes the action. This class must implement the ProcessStateAction interface. state_mapping table -------------------- This table defines the transitions between states for a given program. The program id points to our version of the program table. program table -------------- This table defines metadata about the program including start and end states. program_tag_map ----------------- This table defines links between location and location_tag and a given program so we can look up program by location information. patient_state ------------- The patient state table keeps track of what state the patient is currently in. We keep track of patien_id,state_id,start_time, and end_time. We also keep track of location_id and location_tag_id so we can associate the states with specific clinic locations. We have retirement columns so the states can be retired. We also keep track of a session_id that keeps track of the given instance of state transitions from the state_mapping table for the given patient. We also keep track of form based information like form_id and form_instance_id to know which instance of the form we are talking about. session ------- This table auto generates the session id for each patient's instance of a program's state mapping process. It also links a session with an encounter. Multiple sessions are allowed for a single encounter. form_instance -------------- This table generates a unique id for each form NAME per location. It is form name and not form_id because we don't want to start the numbering over again if we just update to a new form version. These are some configuration tables that we created to extend the openmrs data model that we would like to see in core as well: atd_form_attribute atd_form_attribute_value location_attribute location_attribute_value location_tag_attribute location_tag_attribute_value The data model we are using could use some design tweaking but it is functional and running live in two clinics right now. Please let me know if you have any questions. I would love to get as much of this code into core as possible so we don't have to maintain it. Thanks, Tammy Dugan Tammy Dugan wrote: > I just committed the stateprocessing module. After my meeting this > morning, I will send some documentation to the list about the > stateprocessing module along with the randomization module (since > Darius mentioned supporting studies, which the randomization module > does). > > Tammy Dugan > > Tammy Dugan wrote: >> I created a module called stateprocessing? Is it OK to commit it? >> >> >> Tammy Dugan >> >> Burke Mamlin wrote: >>> Tammy/Vibha, >>> >>> Do you have any documentation or description of how program/workflow >>> was adapted for CHICA's needs? Any chance either of you are going >>> to be going to AMIA? >>> >>> -Burke >>> >>> _________________________________________ >>> >>> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail >>> to [hidden email] with "SIGNOFF openmrs-devel-l" in >>> the body (not the subject) of your e-mail. >>> >>> [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] >> >> _________________________________________ >> >> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail >> to [hidden email] with "SIGNOFF openmrs-devel-l" in the >> body (not the subject) of your e-mail. >> >> [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] > > _________________________________________ > > To unsubscribe from OpenMRS Developers' mailing list, send an e-mail > to [hidden email] with "SIGNOFF openmrs-devel-l" in the > body (not the subject) of your e-mail. > > [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] /** * */ package org.openmrs.module.chica; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.Concept; import org.openmrs.FormField; import org.openmrs.Location; import org.openmrs.LocationTag; import org.openmrs.Obs; import org.openmrs.Patient; import org.openmrs.api.AdministrationService; import org.openmrs.api.ConceptService; import org.openmrs.api.LocationService; import org.openmrs.api.ObsService; import org.openmrs.api.PatientService; import org.openmrs.api.context.Context; import org.openmrs.logic.LogicService; import org.openmrs.logic.result.EmptyResult; import org.openmrs.logic.result.Result; import org.openmrs.module.atd.StateActionHandler; import org.openmrs.module.atd.StateManager; import org.openmrs.module.atd.TeleformFileMonitor; import org.openmrs.module.atd.TeleformFileState; import org.openmrs.module.atd.action.ProcessStateAction; import org.openmrs.module.atd.datasource.TeleformExportXMLDatasource; import org.openmrs.module.atd.hibernateBeans.ATDError; import org.openmrs.module.atd.hibernateBeans.FormInstance; import org.openmrs.module.atd.hibernateBeans.PatientState; import org.openmrs.module.atd.hibernateBeans.Program; import org.openmrs.module.atd.hibernateBeans.State; import org.openmrs.module.atd.hibernateBeans.StateAction; import org.openmrs.module.atd.service.ATDService; import org.openmrs.module.chica.advice.ThreadManager; import org.openmrs.module.chica.hibernateBeans.Encounter; import org.openmrs.module.chica.service.ChicaService; import org.openmrs.module.chica.service.EncounterService; import org.openmrs.module.chica.util.Util; /** * @author tmdugan * */ public class ChicaStateActionHandler implements StateActionHandler { private static Log log = LogFactory.getLog(ChicaStateActionHandler.class); private static ChicaStateActionHandler stateActionHandler = null; public void fillUnfinishedStates() { AdministrationService adminService = Context.getAdministrationService(); Context.authenticate(adminService .getGlobalProperty("scheduler.username"), adminService .getGlobalProperty("scheduler.password")); ATDService atdService = Context.getService(ATDService.class); Calendar todaysDate = Calendar.getInstance(); todaysDate.set(Calendar.HOUR_OF_DAY, 0); todaysDate.set(Calendar.MINUTE, 0); todaysDate.set(Calendar.SECOND, 0); LocationService locationService = Context.getLocationService(); List<Location> locations = locationService.getAllLocations(); for(Location location:locations){ Set<LocationTag> tags = location.getTags(); if(tags != null){ for(LocationTag tag:tags){ Integer locationId = location.getLocationId(); Integer locationTagId = tag.getLocationTagId(); List<PatientState> unfinishedStatesToday = atdService. getUnfinishedPatientStatesAllPatients(todaysDate.getTime(),locationTagId,locationId); int numUnfinishedStates = unfinishedStatesToday.size(); double processedStates = 0; log.info("fillUnfinishedStates(): Starting Today's state initialization...."); for(PatientState currPatientState:unfinishedStatesToday) { State state = currPatientState.getState(); if (state != null) { StateAction stateAction = state.getAction(); try { if (stateAction!=null&&stateAction.getActionName().equalsIgnoreCase( "CONSUME FORM INSTANCE")) { TeleformFileState teleformFileState = TeleformFileMonitor .addToPendingStatesWithoutFilename( currPatientState.getFormInstance()); teleformFileState.addParameter("patientState", currPatientState); } HashMap<String,Object> parameters = new HashMap<String,Object>(); parameters.put("formInstance", currPatientState.getFormInstance()); processAction(stateAction, currPatientState.getPatient(), currPatientState,parameters); } catch (Exception e) { log.error(e.getMessage()); log .error(org.openmrs.module.dss.util.Util .getStackTrace(e)); } } if(processedStates%100==0){ log.info("State initialization is: "+(int)((processedStates/numUnfinishedStates)*100)+"% complete. "+ processedStates+" out of "+numUnfinishedStates+" processed."); } processedStates++; } log.info("Today's state initialization is: "+(int)((processedStates/numUnfinishedStates)*100)+"% complete."); }}} Thread thread = new Thread(new InitializeOldStates()); ThreadManager.startThread(thread); } public static ChicaStateActionHandler getInstance() { if(stateActionHandler == null){ stateActionHandler = new ChicaStateActionHandler(); } return stateActionHandler; } public ChicaStateActionHandler(){ } private ProcessStateAction loadProcessStateAction(StateAction stateAction){ ProcessStateAction processStateAction = null; try { String stateActionClass = stateAction.getActionClass(); Class classInstatiation = Class.forName(stateActionClass); if(stateActionClass == null) { return null; } //class the initialization method is in processStateAction = (ProcessStateAction) classInstatiation.newInstance(); } catch (Exception e) { log.error(e.getMessage()); log.error(org.openmrs.module.dss.util.Util.getStackTrace(e)); } return processStateAction; } public synchronized void changeState(PatientState patientState, HashMap<String,Object> parameters) throws Exception{ StateAction stateAction = patientState.getState().getAction(); if (stateAction == null) { return; } ProcessStateAction processStateAction = loadProcessStateAction(stateAction); if (processStateAction != null) { processStateAction.changeState(patientState, parameters); } } public synchronized void processAction(StateAction stateAction, Patient patient, PatientState patientState,HashMap<String,Object> parameters) throws Exception { if (stateAction == null) { return; } // lookup the patient again to avoid lazy initialization errors PatientService patientService = Context.getPatientService(); Integer patientId = patient.getPatientId(); patient = patientService.getPatient(patientId); ProcessStateAction processStateAction = loadProcessStateAction(stateAction); if (processStateAction != null) { processStateAction.processAction(stateAction, patient, patientState, parameters); } } public static synchronized void consume(Integer sessionId,FormInstance formInstance,Patient patient, HashMap<String,Object> parameters,List<FormField> fieldsToConsume, Integer locationTagId) { AdministrationService adminService = Context.getAdministrationService(); ATDService atdService = Context.getService(ATDService.class); ChicaService chicaService = Context.getService(ChicaService.class); Integer encounterId = atdService.getSession(sessionId).getEncounterId(); String exportFilename = null; if(parameters != null){ exportFilename = (String) parameters.get("filename"); } try { InputStream input = new FileInputStream(exportFilename); chicaService.consume(input,patient,encounterId, formInstance,sessionId,fieldsToConsume,locationTagId); input.close(); } catch (Exception e) { log.error("Error consuming chica file: " + exportFilename); log.error(e.getMessage()); log.error(org.openmrs.module.dss.util.Util.getStackTrace(e)); } // save specific observations saveObs(encounterId, patient,locationTagId); // remove the parsed xml from the xml datasource try { Integer purgeXMLDatasourceProperty = null; try { purgeXMLDatasourceProperty = Integer.parseInt(adminService .getGlobalProperty("atd.purgeXMLDatasource")); } catch (Exception e) { } LogicService logicService = Context.getLogicService(); TeleformExportXMLDatasource xmlDatasource = (TeleformExportXMLDatasource) logicService .getLogicDataSource("xml"); if (purgeXMLDatasourceProperty != null && purgeXMLDatasourceProperty == 1) { xmlDatasource.deleteParsedFile(formInstance); } } catch (Exception e) { log.error(e.getMessage()); log.error(org.openmrs.module.dss.util.Util.getStackTrace(e)); } } private static synchronized void saveObs(Integer encounterId,Patient patient, Integer locationTagId){ EncounterService encounterService = Context.getService(EncounterService.class); Encounter encounter = (Encounter) encounterService.getEncounter(encounterId); ObsService obsService = Context.getObsService(); ATDService atdService = Context.getService(ATDService.class); List<org.openmrs.Encounter> encounters = new ArrayList<org.openmrs.Encounter>(); encounters.add(encounter); List<Concept> questions = new ArrayList<Concept>(); HashMap<String,Object> parameters = new HashMap<String,Object>(); Calculator calculator = new Calculator(); parameters.put("encounterId", encounterId); ConceptService conceptService = Context.getConceptService(); Concept concept = conceptService.getConcept("BMICentile"); questions.add(concept); List<Obs> obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if (obs == null || obs.size() == 0) { Result result = atdService.evaluateRule("bmi", patient, parameters, null); if (!(result instanceof EmptyResult)) { Double percentile = calculator.calculatePercentile(result .toNumber(), patient.getGender(), patient .getBirthdate(), "bmi", null); if (percentile != null) { percentile = org.openmrs.module.dss.util.Util.round( percentile, 2); // round percentile to two places Util.saveObs(patient, concept, encounterId, percentile .toString(), null,null,locationTagId); } } } questions = new ArrayList<Concept>(); concept = conceptService.getConcept("HCCentile"); questions.add(concept); obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if(obs == null || obs.size()==0){ parameters.put("concept", "HC"); Result result = atdService.evaluateRule("conceptRule", patient, parameters, null); if (!(result instanceof EmptyResult)) { Double percentile = calculator.calculatePercentile(result .toNumber(), patient.getGender(), patient .getBirthdate(), "hc", null); if (percentile != null) { percentile = org.openmrs.module.dss.util.Util.round( percentile, 2); // round percentile to two places Util.saveObs(patient, concept, encounterId, percentile .toString(), null,null,locationTagId); } } } questions = new ArrayList<Concept>(); concept = conceptService.getConcept("HtCentile"); questions.add(concept); obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if(obs == null || obs.size()==0){ parameters.put("concept", "HEIGHT"); Result result = atdService.evaluateRule("conceptRule", patient, parameters, null); if (!(result instanceof EmptyResult)) { Double percentile = calculator.calculatePercentile(result .toNumber(), patient.getGender(), patient .getBirthdate(), "length", org.openmrs.module.dss.util.Util.MEASUREMENT_IN); if (percentile != null) { percentile = org.openmrs.module.dss.util.Util.round( percentile, 2); // round percentile to two places Util.saveObs(patient, concept, encounterId, percentile .toString() , null,null,locationTagId); } } } questions = new ArrayList<Concept>(); concept = conceptService.getConcept("WtCentile"); questions.add(concept); obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if(obs == null || obs.size()==0){ parameters.put("concept", "WEIGHT"); Result result = atdService.evaluateRule("conceptRule", patient, parameters, null); if (!(result instanceof EmptyResult)) { Double percentile = calculator.calculatePercentile(result .toNumber(), patient.getGender(), patient .getBirthdate(), "weight", org.openmrs.module.dss.util.Util.MEASUREMENT_LB); if (percentile != null) { percentile = org.openmrs.module.dss.util.Util.round( percentile, 2); // round percentile to two places Util.saveObs(patient, concept, encounterId, percentile .toString(), null,null,locationTagId); } } } //save BP questions = new ArrayList<Concept>(); concept = conceptService.getConcept("BP"); questions.add(concept); obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if(obs == null || obs.size()==0){ Result result = atdService.evaluateRule("bp", patient, parameters, null); if (!(result instanceof EmptyResult)) { Util.saveObs(patient, concept, encounterId, result .toString(), null, null,locationTagId); } } //save BMI questions = new ArrayList<Concept>(); concept = conceptService.getConcept("BMI CHICA"); questions.add(concept); obs = obsService.getObservations(null, encounters, questions, null, null, null, null, null, null, null, null, false); if(obs == null || obs.size()==0){ Result result = atdService.evaluateRule("bmi", patient, parameters, null); if (!(result instanceof EmptyResult)) { Util.saveObs(patient, concept, encounterId, result .toString(), null, null,locationTagId); } } } public static synchronized void changeState(Patient patient, Integer sessionId, State currState,StateAction action, HashMap<String,Object> parameters, Integer locationTagId,Integer locationId) throws Exception { ATDService atdService = Context.getService(ATDService.class); List<ATDError> errors = null; // change to error state if fatal error exists for session //only look up errors for consume state, for now if (action!=null&&action.getActionName().equalsIgnoreCase("CONSUME FORM INSTANCE")) { errors = atdService.getATDErrorsByLevel( "Fatal", sessionId); } if (errors != null && errors.size() > 0) { //open an error state currState = atdService.getStateByName("ErrorState"); atdService.addPatientState(patient, currState, sessionId,locationTagId,locationId); } else { Program program = atdService.getProgram(locationTagId,locationId); StateManager.changeState(patient, sessionId, currState,program, parameters,locationTagId,locationId,ChicaStateActionHandler.getInstance()); } } } _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [hidden email] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[hidden email]?body=SIGNOFF%20openmrs-devel-l] |
||||||||||||||||
| Free Embeddable Forum Powered by Nabble | Help |