def __init__(self, poutDirectory, processingOptions, debugMessages=None): #print "%s Class Initialized" % self.__name__ if settings.DEBUG: print "XML File to be dumped to: %s" % poutDirectory self.log = logger.Logger(configFile=settings.LOGGING_INI, loglevel=40) # JCS 10/3/11 self.outDirectory = poutDirectory self.pickList = Interpretpicklist() # SBB20070626 Adding the declaration for outcomes list self.options = processingOptions # SBB20070628 adding a buffer for errors to be displayed at the end of the process. self.errorMsgs = [] self.db = dbobjects.DB() # JCS 10/05/11 self.db.Base.metadata.create_all()
class SvcPointXML5Writer(): # Writer Interface implements(Writer) hmis_namespace = "http://www.hmis.info/schema/2_8/HUD_HMIS_2_8.xsd" airs_namespace = "http://www.hmis.info/schema/2_8/AIRS_3_0_draft5_mod.xsd" nsmap = {"hmis" : hmis_namespace, "airs" : airs_namespace} svcpt_version = '5.00' def __init__(self, poutDirectory, processingOptions, debugMessages=None): #print "%s Class Initialized" % self.__name__ if settings.DEBUG: print "XML File to be dumped to: %s" % poutDirectory self.log = logger.Logger(configFile=settings.LOGGING_INI, loglevel=40) # JCS 10/3/11 self.outDirectory = poutDirectory self.pickList = Interpretpicklist() # SBB20070626 Adding the declaration for outcomes list self.options = processingOptions # SBB20070628 adding a buffer for errors to be displayed at the end of the process. self.errorMsgs = [] self.db = dbobjects.DB() # JCS 10/05/11 self.db.Base.metadata.create_all() def write(self): self.startTransaction() self.processXML() self.prettify() print '==== Self:', self xmlutilities.writeOutXML(self, xml_declaration=True, encoding="UTF-8") # JCS, 1 Sep 2012 #self.commitTransaction() return True def updateReported(self, currentObject): # update the reported field of the currentObject being passed in. These should all exist. try: if settings.DEBUG: print 'Updating reporting for object: %s' % currentObject.__class__ currentObject.reported = True #currentObject.update() self.commitTransaction() except: print "Exception occurred during update the 'reported' flag" pass def prettify(self): xmlutilities.indent(self.root_element) def dumpErrors(self): print "Error Reporting" print "-" * 80 for row in range(len(self.errorMsgs)): print "%s %s" % (row, self.errorMsgs[row]) def setSysID(self, pSysID): self.sysID = pSysID def commitTransaction(self): self.session.commit() def startTransaction(self): self.session = self.db.Session() def pullConfiguration(self, pExportID): # need to use both ExportID and Processing Mode (Test or Prod) export = self.session.query(dbobjects.Export).filter(dbobjects.Export.export_id == pExportID).one() if settings.DEBUG: print "trying to do pullConfiguration" #print "export is:", export, "pExportID is", pExportID #print "export.export_id is: ", export.export_id #print "dbobjects.SystemConfiguration.source_id is ", dbobjects.SystemConfiguration.source_id selink = self.session.query(dbobjects.SourceExportLink).filter(dbobjects.SourceExportLink.export_index_id == export.id).one() #print '==== Selink.id:', selink.id source = self.session.query(dbobjects.Source).filter(dbobjects.Source.id == selink.source_index_id).one() #print '==== Source.id:', source.id self.configurationRec = self.session.query(dbobjects.SystemConfiguration).filter(and_(dbobjects.SystemConfiguration.source_id == source.source_id, dbobjects.SystemConfiguration.processing_mode == settings.MODE)).one() #print '==== sys config.id', self.configurationRec.id def processXML(self): # records represents whatever element you're tacking more onto, like entry_exits or clients if settings.DEBUG: print "processXML: Appending XML to Base Record" self.root_element = self.createDoc() #makes root element with XML header attributes #print '==== root created' clients = self.createClients(self.root_element) # JCS - tag is <clientRecords> Only node under clients is <Client> print '==== clientRecords created' if self.options.reported == True: Persons = self.session.query(dbobjects.Person).filter(dbobjects.Person.reported == True) elif self.options.unreported == True: Persons = self.session.query(dbobjects.Person).filter(or_(dbobjects.Person.reported == False, dbobjects.Person.reported == None)) elif self.options.reported == None: Persons = self.session.query(dbobjects.Person) # Now apply the dates to the result set. if self.options.alldates == None: Persons = Persons.filter(between(dbobjects.Person.person_id_date_collected, self.options.startDate, self.options.endDate)) pulledConfigID = 0 # JCS Only pull it if it has changed for self.person in Persons: #print "person is: ", self.person export = self.person.fk_person_to_export # this is a single record because: # person has: export_index_id = Column(Integer, ForeignKey('export.id')) # export has: fk_export_to_person = relationship('Person', backref='fk_person_to_export') # Therefore there are multiple persons to one export - but only one export to a person #print "==== export before pullconfig:", export.id, export # JCS if pulledConfigID != export.id: self.pullConfiguration(export.export_id) pulledConfigID = export.id self.ph = self.person.fk_person_to_person_historical # JCS This is a list of records self.race = self.person.fk_person_to_races self.site_service_part = self.person.site_service_participations # JCS #information_releases = self.person.fk_person_to_release_of_information # JCS a set #self.service_event = self.person.fk_person_to_service_event # Instead of generating a number (above), use the client number that is already provided in the legacy system # or # self.iDG.initializeSystemID(self.person.id) self.sysID = self.person.id # JCS beware set self.sysID #if settings.DEBUG: #print "self.person is:", self.person if self.person: # and not self.person.person_legal_first_name_unhashed+self.person.person_legal_last_name_unhashed == None: self.client = self.createClient(clients) # JCS - no clients in svc5? yes as clientRecords # Sub can be: active, anonymous, firstName, suffix, unnamedClient, alias, middleName, childEntryExit, # childReleaseOfInfo, childGoal self.customizeClient(self.client) self.customizeClientPersonalIdentifiers(self.client, self.person) self.assessment_data = self.createAssessmentData(self.client) # JCS New - self? self.customizeAssessmentData(self.assessment_data) if self.site_service_part: # JCS 21 Dec 2012 self.child_entry_exit = self.createChildEntryExit(self.client) for ssp in self.site_service_part: self.createEntryExit(self.child_entry_exit, ssp) # update the reported flag for person (This needs to be applied to all objects that we are getting data from) self.updateReported(self.person) # Query Mechanism for Site Service Participation (Entry Exits) same as for Person? # This is only if we want to create an EE summary at the end for all Clients # if self.options.reported == True: # site_service_part = self.session.query(dbobjects.SiteServiceParticipation).filter(dbobjects.SiteServiceParticipation.reported == True) # elif self.options.unreported == True: # site_service_part = self.session.query(dbobjects.SiteServiceParticipation).filter(or_(dbobjects.SiteServiceParticipation.reported == False, dbobjects.SiteServiceParticipation.reported == None)) # elif self.options.reported == None: # site_service_part = self.session.query(dbobjects.SiteServiceParticipation) # else: # pass # # # setup the date filter also # site_service_part = site_service_part.filter(between(dbobjects.SiteServiceParticipation.site_service_participation_idid_num_date_collected, self.options.startDate, self.options.endDate)) # # entry_exits = self.createEntryExits(self.root_element) # for EE in site_service_part: # # SBB20100405 do this to pull the configuration record # person = EE.fk_participation_to_person # export = person.fk_person_to_export # self.pullConfiguration(export.export_id) # self.updateReported(EE) # Reporting Update # self.sysID = EE.id # JCS beware set self.sysID # self.createEntryExit(entry_exits, EE) # End of ProcessXML() def createDoc(self): # From hl7 #self.mymap = { None : "urn:hl7-org:v3", # "voc" : "urn:hl7-org:v3/voc", # "xsi" : "http://www.w3.org/2001/XMLSchema-instance"} #root_element = ET.Element("ClinicalDocument", nsmap=self.mymap) #root_element.attrib["{"+self.mymap["xsi"]+"}schemaLocation"] = "urn:hl7-org:v3 infrastructure/cda/CDA.xsd" # From hl7 end #sp5_instance looks like this # <records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" # xsi:noNamespaceSchemaLocation="file:/home/eric/workspace/servicepoint_schema/sp5/sp5.xsd" # odb_identifier="qwo7Wsoi" # import_identifier="v7:1bl.e"> self.mymap = {"xsi" : "http://www.w3.org/2001/XMLSchema-instance"} # Yes lxml #root_element = ET.Element("records") # Non-lxml root_element = ET.Element("records", nsmap=self.mymap) # Yes lxml #root_element.attrib["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance" # Non-lxml #root_element.attrib["xsi:noNamespaceSchemaLocation"] = "sp5.xsd" # Non-lxml root_element.attrib["{"+self.mymap["xsi"]+"}noNamespaceSchemaLocation"] = "sp5.xsd" # Yes lxml # Added by JCS 1 Sep 2012 root_element.attrib["odb_identifier"] = "qwo7Wsoi" # Yes lxml root_element.attrib["import_identifier"] = "v7:1bl.e" # Yes lxml #root_element.attrib["schema_revision"] = "300_108" # JCS Not in Schema #root_element.text = "\n" return root_element def createClients(self, root_element): clients = ET.SubElement(root_element, "clientRecords") return clients def createClient(self, clients): client = ET.SubElement(clients, "Client") # Cap 'C' in svc5 return client def createChildEntryExit(self,client): child_entry_exit = ET.SubElement(client, "childEntryExit") # JCS new - sub-client return child_entry_exit def createEntryExits(self,root_element): entry_exits = ET.SubElement(root_element, "entryExitRecords") # JCS - not in SVP5? return entry_exits def customizeClient(self, client): #print "==== Customize Client:", self.configurationRec.odbid, self.person.person_id_id_num client.attrib["record_id"] = "CL-" + str(self.person.id) #client.attrib["external_id"] = self.person.person_id_id_num # JCS -this item is optional client.attrib["system_id"] = self.person.person_id_id_num # JCS just a guess ???? client.attrib["date_added"] = dateutils.fixDate(datetime.now()) client.attrib["date_updated"] = dateutils.fixDate(datetime.now()) # SBB20070702 check if self.intakes has none, this is a daily census that is alone def customizeClientPersonalIdentifiers(self,client,recordset): # params are: self.client, self.person if recordset.person_legal_first_name_unhashed <> "" and recordset.person_legal_first_name_unhashed <> None: first_name = ET.SubElement(client, "firstName") first_name.text = recordset.person_legal_first_name_unhashed if recordset.person_legal_last_name_unhashed <> "" and recordset.person_legal_last_name_unhashed <> None: last_name = ET.SubElement(client, "lastName") last_name.text = recordset.person_legal_last_name_unhashed #we don't have the following elements for daily_census only clients, but SvcPt requires them: # I simulated this w/my datasets. Column names are as in the program if recordset.person_legal_middle_name_unhashed <> "" and recordset.person_legal_middle_name_unhashed <> None: mi_initial = ET.SubElement(client, "mi_initial") mi_initial.text = self.fixMiddleInitial(recordset.person_legal_middle_name_unhashed) # SBB20070831 incoming SSN's are 123456789 and need to be 123-45-6789 fixedSSN = self.fixSSN(recordset.person_social_security_number_unhashed) # JCS .person_SSN_unhashed) if fixedSSN <> "" and fixedSSN <> None: #ECJ20071111 Omit SSN if it's blank soc_sec_no = ET.SubElement(client, "socSecNoDashed") soc_sec_no.text = fixedSSN ssn_data_quality = ET.SubElement(client, "ssnDataQualityValue") ssn_data_quality.text = "full ssn reported (hud)" def createEntryExit(self, entry_exits, EE): # Outer Node, one EntryExit(ssp) entry_exit = ET.SubElement(entry_exits, "EntryExit") entry_exit.attrib["record_id"] = "EE-"+str(EE.id) # ssp-idid-num looks like it ought to be unique, but isn't in sample input data, so append client id???? entry_exit.attrib["system_id"] = EE.site_service_participation_idid_num+"-"+EE.person.person_id_id_num # person.site_service_participations = relationship("SiteServiceParticipation", backref="person") entry_exit.attrib["date_added"] = dateutils.fixDate(datetime.now()) entry_exit.attrib["date_updated"] = dateutils.fixDate(datetime.now()) self.customizeEntryExit(entry_exit, EE) return entry_exit def customizeEntryExit(self, entry_exit, EE): # Schema expects one of ( active, typeEntryExit, client, exitDate, reasonLeavingValue, reasonLeavingOther, # destinationValue, destinationOther, notes, group ) # There is no type in our input XML, nor a field in ssp. Schema needs {'basic', 'basic center program entry/exit', # 'hprp', 'hud', 'path', 'quick call', 'standard', 'transitional living program entry/exit'} type1 = ET.SubElement(entry_exit, "typeEntryExit") # JCS this is a fudge to pass validation type1.text = "basic" # "hud-40118" provider_id = ET.SubElement(entry_exit, "provider") provider_id.text = '%s' % self.configurationRec.providerid if EE.participation_dates_start_date <> "" and EE.participation_dates_start_date <> None: entry_date = ET.SubElement(entry_exit, "entryDate") entry_date.text = dateutils.fixDate(EE.participation_dates_start_date) if EE.participation_dates_end_date <> "" and EE.participation_dates_end_date <> None: exit_date = ET.SubElement(entry_exit, "exitDate") exit_date.text = dateutils.fixDate(EE.participation_dates_end_date) return def createAssessmentData(self, client): # dynamic content type assessment_data = ET.SubElement(client, "assessmentData") return assessment_data def customizeAssessmentData(self, assessment_data): if self.person.person_gender_unhashed <> "" and self.person.person_gender_unhashed <> None: persGender = ET.SubElement(assessment_data, "svpprofgender" ) #"gender") persGender.attrib["date_added"] = dateutils.fixDate(self.person.person_gender_unhashed_date_collected) persGender.attrib["date_effective"] = dateutils.fixDate(self.person.person_gender_unhashed_date_effective) persGender.text = str(self.person.person_gender_unhashed) # dob (Date of Birth) lots of:SVPPROFDOB a few:DATEOFBIRTH if self.person.person_date_of_birth_unhashed <> "" and self.person.person_date_of_birth_unhashed <> None: dob = ET.SubElement(assessment_data, "svpprofdob") dob.attrib["date_added"] = dateutils.fixDate(self.person.person_date_of_birth_unhashed_date_collected) dob.attrib["date_effective"] = dateutils.fixDate(datetime.now()) # No date effect. in Person dob.text = dateutils.fixDate(self.person.person_date_of_birth_unhashed) # Ethnicity lots of:SVPPROFETH a few:Ethnicity uses:ETHNICITYPickOption if self.person.person_ethnicity_unhashed <> "" and self.person.person_ethnicity_unhashed <> None: # Our Interpretpicklist basically has 2 options. The schema has 23 ethText = self.pickList.getValue("EthnicityPick",str(self.person.person_ethnicity_unhashed)) eth = ET.SubElement(assessment_data, "svpprofeth") eth.attrib["date_added"] = dateutils.fixDate(self.person.person_ethnicity_unhashed_date_collected) eth.attrib["date_effective"] = dateutils.fixDate(datetime.now()) # No date effect. in Person eth.text = ethText # str(self.person.person_ethnicity_unhashed) # Race more than one?? JCS for race in self.race: # JCS schema has 'RACEPickOption' - using existing RacePick for now raceText = self.pickList.getValue("RacePick",str(race.race_unhashed)) # print '==== race:', race.race_unhashed, raceText if raceText <> None: raceNode = ET.SubElement(assessment_data, "svpprofrace") # JCS "primaryrace" or "svpprofrace"? raceNode.attrib["date_added"] = dateutils.fixDate(race.race_date_collected) raceNode.attrib["date_effective"] = dateutils.fixDate(race.race_date_effective) raceNode.text = raceText for ph in self.ph: #print '==== ph person id:', ph.person_index_id #, ph.__dict__ # JCS - Fails if none - seen in going from tbc to here - but don't know if that ever happens hs = self.session.query(dbobjects.HousingStatus).filter(dbobjects.HousingStatus.person_historical_index_id == ph.id).one() hsText = self.pickList.getValue("HOUSINGSTATUSPickOption",hs.housing_status) #print '==== hs:', hsText if hsText <> None: housingStatus = ET.SubElement(assessment_data, "svp_hud_housingstatus") # JCS housingStatus.attrib["date_added"] = dateutils.fixDate(hs.housing_status_date_collected) housingStatus.attrib["date_effective"] = dateutils.fixDate(hs.housing_status_date_effective) housingStatus.text = hsText foster = self.session.query(dbobjects.FosterChildEver).filter(dbobjects.FosterChildEver.person_historical_index_id == ph.id).one() fosterText = self.pickList.getValue("ENHANCEDYESNOPickOption",str(foster.foster_child_ever)) if fosterText <> None: fosterEver = ET.SubElement(assessment_data, "x20wereyoueverafoster") # JCS fosterEver.attrib["date_added"] = dateutils.fixDate(foster.foster_child_ever_date_collected) fosterEver.attrib["date_effective"] = dateutils.fixDate(foster.foster_child_ever_date_effective) fosterEver.text = fosterText # length of stay at prior residence losapr = self.session.query(dbobjects.LengthOfStayAtPriorResidence).filter(dbobjects.LengthOfStayAtPriorResidence.person_historical_index_id == ph.id).one() losaprText = self.pickList.getValue("LENGTHOFTHESTAYPickOption",losapr.length_of_stay_at_prior_residence) #print '==== losapr:', losaprText if losaprText <> None: lengthOfStay = ET.SubElement(assessment_data, "hud_lengthofstay") # JCS lengthOfStay.attrib["date_added"] = dateutils.fixDate(losapr.length_of_stay_at_prior_residence_date_collected) lengthOfStay.attrib["date_effective"] = dateutils.fixDate(losapr.length_of_stay_at_prior_residence_date_effective) lengthOfStay.text = losaprText # "Prior Residence" becomes "typeoflivingsituation" tols = self.session.query(dbobjects.PriorResidence).filter(dbobjects.PriorResidence.person_historical_index_id == ph.id).one() tolsText = self.pickList.getValue("LIVINGSITTYPESPickOption",tols.prior_residence_code) #print '==== (prior) tols:', tolsText if tolsText <> None: priorLiving = ET.SubElement(assessment_data, "typeoflivingsituation") # JCS priorLiving.attrib["date_added"] = dateutils.fixDate(tols.prior_residence_code_date_collected) priorLiving.attrib["date_effective"] = dateutils.fixDate(tols.prior_residence_code_date_effective) priorLiving.text = tolsText # There's also a prior_residence_id_id_num populated with a 13 digit number as string JCS # Physical Disability - Boolean pdyn = self.session.query(dbobjects.PhysicalDisability).filter(dbobjects.PhysicalDisability.person_historical_index_id == ph.id).one() pdynText = pdyn.has_physical_disability #print '==== pdyn:', pdynText if pdynText <> None: physDisabYN = ET.SubElement(assessment_data, "svpphysicaldisabilit") # JCS physDisabYN.attrib["date_added"] = dateutils.fixDate(pdyn.has_physical_disability_date_collected) # This is required, but input is usually blank - something plugs in now() physDisabYN.attrib["date_effective"] = dateutils.fixDate(pdyn.has_physical_disability_date_effective) physDisabYN.text = pdynText # There is also a complex type "disabilities_1" # Veteran Status - Uses "ENHANCEDYESNOPickOption" which is a union, and allows anything vvs = self.session.query(dbobjects.VeteranVeteranStatus).filter(dbobjects.VeteranVeteranStatus.person_historical_index_id == ph.id).one() vvsText = vvs.veteran_status #print '==== vvs:', vvsText if vvsText <> None: vetStat = ET.SubElement(assessment_data, "veteran") # JCS vetStat.attrib["date_added"] = dateutils.fixDate(vvs.veteran_status_date_collected) vetStat.attrib["date_effective"] = dateutils.fixDate(vvs.veteran_status_date_effective) vetStat.text = vvsText # def customizeDisabilities_1(self, disabilities_1, ph): # #if self.intakes['DisabilityDiscription'] <> "": # noteondisability = ET.SubElement(disabilities_1,'noteondisability') # noteondisability.attrib["date_added"] = dateutils.fixDate(datetime.now()) # noteondisability.attrib["date_effective"] = dateutils.fixDate(ph.physical_disability_date_collected) # noteondisability.text = ph.physical_disability def current_picture(self, node): ''' Internal function. Debugging aid for the export module.''' if settings.DEBUG: print "Current XML Picture is" print "======================\n" * 2 ET.dump(node) print "======================\n" * 2 def calcHourlyWage(self, monthlyWage): if monthlyWage <> "": if monthlyWage.strip().isdigit(): if float(monthlyWage) > 5000.00: hourlyWage = float(monthlyWage) / 160.00#IGNORE:@UnusedVariable else: hourlyWage = float(monthlyWage)#IGNORE:@UnusedVariable else: hourlyWage = 0.00 return str(round(hourlyWage,2)) def fixMiddleInitial(self, middle_initial): fixed_middle_initial = middle_initial[0].upper().lstrip() return fixed_middle_initial def fixSSN(self, incomingSSN): originalSSN = incomingSSN if incomingSSN == "" or incomingSSN == None: return incomingSSN dashCount = incomingSSN.count('-') if dashCount > 0: if dashCount == 2: # already has the dashes, return the string if settings.DEBUG: self.debugMessages.log("incoming SSN is correctly formatted: %s\n" % (incomingSSN)) return incomingSSN else: # incoming SSN has 1 dash but not 2. This is an error # fix this data incomingSSN = incomingSSN.replace( '-', '') if len(incomingSSN) < 9: # reformat the string and return theError = (1020, 'Data format error discovered in trying to cleanup incoming SSN: %s, original SSN: %s' % (incomingSSN, originalSSN)) if settings.DEBUG: self.debugMessages.log(">>>> Incoming SSN is INcorrectly formatted. Original SSN from input file is: %s and Attempted cleaned up SSN is: %s\n" % (originalSSN, incomingSSN)) raise DataFormatError, theError # If we are here, we can simply reformat the string into dashes if settings.DEBUG: pass # JCS # self.debugMessages.log("incoming SSN is INcorrectly formatted: %s. Reformatting to: %s\n" % (incomingSSN, '%s-%s-%s' % (incomingSSN[0:3], incomingSSN[3:5], incomingSSN[5:10]))) return '%s-%s-%s' % (incomingSSN[0:3], incomingSSN[3:5], incomingSSN[5:10])