def __refresh_results(self, patient=None): list_items = [] list_data = [] emr = patient.get_emr() most_recent = emr.get_most_recent_results(no_of_results = 1) if most_recent is None: self._LCTRL_results.set_string_items(items = []) self._LCTRL_results.set_data(data = []) return now = gmDateTime.pydt_now_here() list_items.append(_('Latest: %s ago (%s %s%s%s%s)') % ( gmDateTime.format_interval_medically(now - most_recent['clin_when']), most_recent['unified_abbrev'], most_recent['unified_val'], gmTools.coalesce(most_recent['val_unit'], u'', u' %s'), gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' %s'), gmTools.bool2subst(most_recent['reviewed'], u'', (u' %s' % gmTools.u_writing_hand)) )) list_data.append(most_recent) most_recent_needs_red = False if most_recent.is_considered_abnormal is True: if most_recent['is_clinically_relevant']: most_recent_needs_red = True unsigned = emr.get_unsigned_results(order_by = u"(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev") no_of_reds = 0 for result in unsigned: if result['pk_test_result'] == most_recent['pk_test_result']: continue if result['abnormality_indicator'] is not None: if result['abnormality_indicator'].strip() != u'': no_of_reds += 1 list_items.append(_('%s %s%s%s (%s ago, %s)') % ( result['unified_abbrev'], result['unified_val'], gmTools.coalesce(result['val_unit'], u'', u' %s'), gmTools.coalesce(result['abnormality_indicator'], u'', u' %s'), gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']), gmTools.u_writing_hand )) list_data.append(result) self._LCTRL_results.set_string_items(items = list_items) self._LCTRL_results.set_data(data = list_data) if most_recent_needs_red: self._LCTRL_results.SetItemTextColour(0, wx.NamedColour('RED')) if no_of_reds > 0: for idx in range(1, no_of_reds + 1): self._LCTRL_results.SetItemTextColour(idx, wx.NamedColour('RED'))
def __refresh_results(self, patient=None): list_items = [] list_data = [] emr = patient.emr most_recent = emr.get_most_recent_results(no_of_results = 1) if most_recent is None: self._LCTRL_results.set_string_items(items = []) self._LCTRL_results.set_data(data = []) return now = gmDateTime.pydt_now_here() list_items.append(_('Latest: %s ago (%s %s%s%s%s)') % ( gmDateTime.format_interval_medically(now - most_recent['clin_when']), most_recent['unified_abbrev'], most_recent['unified_val'], gmTools.coalesce(most_recent['val_unit'], '', ' %s'), gmTools.coalesce(most_recent['abnormality_indicator'], '', ' %s'), gmTools.bool2subst(most_recent['reviewed'], '', (' %s' % gmTools.u_writing_hand)) )) list_data.append(most_recent) most_recent_needs_red = False if most_recent.is_considered_abnormal is True: if most_recent['is_clinically_relevant']: most_recent_needs_red = True unsigned = emr.get_unsigned_results(order_by = "(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev") no_of_reds = 0 for result in unsigned: if result['pk_test_result'] == most_recent['pk_test_result']: continue if result['abnormality_indicator'] is not None: if result['abnormality_indicator'].strip() != '': no_of_reds += 1 list_items.append(_('%s %s%s%s (%s ago, %s)') % ( result['unified_abbrev'], result['unified_val'], gmTools.coalesce(result['val_unit'], '', ' %s'), gmTools.coalesce(result['abnormality_indicator'], '', ' %s'), gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']), gmTools.u_writing_hand )) list_data.append(result) self._LCTRL_results.set_string_items(items = list_items) self._LCTRL_results.set_data(data = list_data) if most_recent_needs_red: self._LCTRL_results.SetItemTextColour(0, wx.Colour('RED')) if no_of_reds > 0: for idx in range(1, no_of_reds + 1): self._LCTRL_results.SetItemTextColour(idx, wx.Colour('RED'))
def _get_data_tooltip(self): if len(self._data) == 0: return '' date = self.GetData() # if match provider only provided completions # but not a full date with it if date is None: return '' ts = date.timestamp now = gmDateTime.pydt_now_here() if ts > now: intv = ts - now template = _('%s\n %s\n in %s') else: intv = now - ts template = _('%s\n%s\n%s ago') txt = template % ( date.format_accurately(self.display_accuracy), gmDateTime.pydt_strftime ( ts, format = '%A, %B-%d %Y (%c)', ), gmDateTime.format_interval ( interval = intv, accuracy_wanted = gmDateTime.acc_days, verbose = True ) ) return txt
def _get_data_tooltip(self): if len(self._data) == 0: return '' date = self.GetData() # if match provider only provided completions # but not a full date with it if date is None: return '' ts = date.timestamp now = gmDateTime.pydt_now_here() if ts > now: intv = ts - now template = _('%s\n %s\n in %s') else: intv = now - ts template = _('%s\n%s\n%s ago') txt = template % (date.format_accurately(self.display_accuracy), gmDateTime.pydt_strftime( ts, format='%A, %B-%d %Y (%c)', ), gmDateTime.format_interval( interval=intv, accuracy_wanted=gmDateTime.acc_days, verbose=True)) return txt
def get_EDC(self, lmp=None, nullipara=True): result = cClinicalResult(_('unknown EDC')) result.formula_name = u'EDC (Mittendorf 1990)' result.formula_source = u'Mittendorf, R. et al., "The length of uncomplicated human gestation," OB/GYN, Vol. 75, No., 6 June, 1990, pp. 907-932.' if lmp is None: result.message = _('EDC: unknown LMP') return result result.variables['LMP'] = lmp result.variables['nullipara'] = nullipara if nullipara: result.variables['parity_offset'] = 15 # days else: result.variables['parity_offset'] = 10 # days now = gmDateTime.pydt_now_here() if lmp > now: result.warnings.append(_(u'LMP in the future')) if self.__patient is None: result.warnings.append(_(u'cannot run sanity checks, no patient')) else: if self.__patient['dob'] is None: result.warnings.append(_(u'cannot run sanity checks, no DOB')) else: years, months, days, hours, minutes, seconds = gmDateTime.calculate_apparent_age( start=self.__patient['dob']) # 5 years -- Myth ? # http://www.mirror.co.uk/news/uk-news/top-10-crazy-amazing-and-world-789842 if years < 10: result.warnings.append( _(u'patient less than 10 years old')) if self.__patient['gender'] in [None, u'm']: result.warnings.append( _(u'atypical gender for pregnancy: %s') % self.__patient.gender_string) if self.__patient['deceased'] is not None: result.warnings.append(_(u'patient already passed away')) if lmp.month > 3: edc_month = lmp.month - 3 edc_year = lmp.year + 1 else: edc_month = lmp.month + 9 edc_year = lmp.year result.numeric_value = gmDateTime.pydt_replace( dt=lmp, year=edc_year, month=edc_month, strict=False) + pydt.timedelta( days=result.variables['parity_offset']) result.message = _('EDC: %s') % gmDateTime.pydt_strftime( result.numeric_value, format='%Y %b %d') result.date_valid = now _log.debug(u'%s' % result) return result
def _check_birthday(patient=None): if patient['dob'] is None: return dbcfg = gmCfg.cCfgSQL() dob_distance = dbcfg.get2 ( option = 'patient_search.dob_warn_interval', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = 'user', default = '1 week' ) if not patient.dob_in_range(dob_distance, dob_distance): return now = gmDateTime.pydt_now_here() enc = gmI18N.get_encoding() msg = _('%(pat)s turns %(age)s on %(month)s %(day)s ! (today is %(month_now)s %(day_now)s)') % { 'pat': patient.get_description_gender(), 'age': patient.get_medical_age().strip('y'), 'month': patient.get_formatted_dob(format = '%B'), 'day': patient.get_formatted_dob(format = '%d'), 'month_now': gmDateTime.pydt_strftime(now, '%B', gmDateTime.acc_months), 'day_now': gmDateTime.pydt_strftime(now, '%d', gmDateTime.acc_days) } gmDispatcher.send(signal = 'statustext', msg = msg)
def _refresh_as_new(self): self._PRW_hospital.SetText(value = '', data = None) self._PRW_episode.SetText(value = '') self._PRW_admission.SetText(data = gmDateTime.pydt_now_here()) self._PRW_discharge.SetText() self._TCTRL_comment.SetValue('') self._PRW_hospital.SetFocus()
def _get_data_tooltip(self): if len(self._data) == 0: return '' date = self.GetData() # if match provider only provided completions # but not a full date with it if date is None: return '' now = gmDateTime.pydt_now_here() if date > now: intv = date - now template = _('%s\n (a %s in %s)') else: intv = now - date template = _('%s\n (a %s %s ago)') return template % ( gmDateTime.pydt_strftime( date, format='%B %d %Y -- %c', accuracy=gmDateTime.acc_days), gmDateTime.pydt_strftime( date, format='%A', accuracy=gmDateTime.acc_days), gmDateTime.format_interval(interval=intv, accuracy_wanted=gmDateTime.acc_days, verbose=True))
def archive_forms(episode_name=None, comment=None): if episode_name is None: epi = None # will ask for episode further down else: pat = gmPerson.gmCurrentPatient() emr = pat.emr epi = emr.add_episode(episode_name = episode_name, is_open = False) for form in forms: files2import = [] files2import.extend(form.final_output_filenames) files2import.extend(form.re_editable_filenames) if len(files2import) == 0: continue save_files_as_new_document ( parent = parent, filenames = files2import, document_type = form.template['instance_type'], unlock_patient = False, episode = epi, review_as_normal = review_copy_as_normal, reference = None, pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit'], comment = comment, date_generated = gmDateTime.pydt_now_here() ) return True
def parse_xml_linuxmednews(xml_text=None, filename=None): dob_format = '%Y-%m-%d' try: if xml_text is None: _log.debug('parsing XML in [%s]', filename) pat = etree.parse(filename) else: pat = etree.fromstring(xml_text) except etree.ParseError: _log.exception('Cannot parse, is this really XML ?') return None dto = gmPerson.cDTO_person() dto.firstnames = pat.find('firstname').text dto.lastnames = pat.find('lastname').text dto.title = pat.find('name_prefix').text dto.gender = pat.find('gender').text dob = pyDT.datetime.strptime(pat.find('DOB').text, dob_format) dto.dob = dob.replace(tzinfo = gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False dto.source = 'LinuxMedNews XML' #dto.remember_comm_channel(channel=None, url=None): #dto.remember_address(number=None, street=None, urb=None, region_code=None, zip=None, country_code=None, adr_type=None, subunit=None) return dto
def _refresh_as_new(self): self._PRW_hospital.SetText(value=u'', data=None) self._PRW_episode.SetText(value=u'') self._PRW_admission.SetText(data=gmDateTime.pydt_now_here()) self._PRW_discharge.SetText() self._TCTRL_comment.SetValue(u'') self._PRW_hospital.SetFocus()
def _check_birthday(patient=None): if patient['dob'] is None: return dbcfg = gmCfg.cCfgSQL() dob_distance = dbcfg.get2 ( option = 'patient_search.dob_warn_interval', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = 'user', default = '1 week' ) if not patient.dob_in_range(dob_distance, dob_distance): return now = gmDateTime.pydt_now_here() msg = _('%(pat)s turns %(age)s on %(month)s %(day)s ! (today is %(month_now)s %(day_now)s)') % { 'pat': patient.get_description_gender(), 'age': patient.get_medical_age(at_date = patient.birthday_this_year).strip('y'), 'month': patient.get_formatted_dob(format = '%B'), 'day': patient.get_formatted_dob(format = '%d'), 'month_now': gmDateTime.pydt_strftime(now, '%B', gmDateTime.acc_months), 'day_now': gmDateTime.pydt_strftime(now, '%d', gmDateTime.acc_days) } gmDispatcher.send(signal = 'statustext', msg = msg)
def parse_xml_linuxmednews(xml_text=None, filename=None): dob_format = '%Y-%m-%d' try: if xml_text is None: _log.debug('parsing XML in [%s]', filename) pat = etree.parse(filename) else: pat = etree.fromstring(xml_text) except etree.ParseError: _log.exception('Cannot parse, is this really XML ?') return None dto = gmPerson.cDTO_person() dto.firstnames = pat.find('firstname').text dto.lastnames = pat.find('lastname').text dto.title = pat.find('name_prefix').text dto.gender = pat.find('gender').text dob = pyDT.datetime.strptime(pat.find('DOB').text, dob_format) dto.dob = dob.replace(tzinfo=gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False dto.source = 'LinuxMedNews XML' #dto.remember_comm_channel(channel=None, url=None): #dto.remember_address(number=None, street=None, urb=None, region_code=None, zip=None, country_code=None, adr_type=None, subunit=None) return dto
def _valid_for_save(self): has_errors = False if not self._DPRW_date.is_valid_timestamp(): self._DPRW_date.display_as_valid(False) has_errors = True else: self._DPRW_date.display_as_valid(True) start = self._DPRW_date.GetData() end = self._DPRW_end.GetData() self._DPRW_end.display_as_valid(True) if end is not None: end = end.get_pydt() if start is not None: start = start.get_pydt() if end < start: has_errors = True self._DPRW_end.display_as_valid(False) if self._CHBOX_ongoing.IsChecked(): now = gmDateTime.pydt_now_here() if end < now: has_errors = True self._DPRW_end.display_as_valid(False) if self._PRW_hospital_stay.GetData() is None: if self._PRW_episode.GetValue().strip() == u'': self._PRW_episode.display_as_valid(False) has_errors = True else: self._PRW_episode.display_as_valid(True) else: self._PRW_episode.display_as_valid(True) if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''): self._PRW_procedure.display_as_valid(False) has_errors = True else: self._PRW_procedure.display_as_valid(True) invalid_location = ((self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetData() is None) or (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetData() is not None)) if invalid_location: self._PRW_hospital_stay.display_as_valid(False) self._PRW_location.display_as_valid(False) has_errors = True else: self._PRW_hospital_stay.display_as_valid(True) self._PRW_location.display_as_valid(True) gmDispatcher.send(signal='statustext', msg=_('Cannot save procedure.'), beep=True) return (has_errors is False)
def get_invoice_id(pk_patient=None): return 'GM%s / %s' % ( pk_patient, gmDateTime.pydt_strftime ( gmDateTime.pydt_now_here(), '%Y-%m-%d / %H%M%S' ) )
def __init__(self): super().__init__() self.dob_formats = [ AMTS_BMP_DOB_FORMAT, AMTS_BMP_DOB_FORMAT_NO_DAY, AMTS_BMP_DOB_FORMAT_YEAR_ONLY ] self.dob_tz = gmDateTime.pydt_now_here().tzinfo self.gender_map = AMTS2GMD_GENDER_MAP
def _valid_for_save(self): has_errors = False if not self._DPRW_date.is_valid_timestamp(): self._DPRW_date.display_as_valid(False) has_errors = True else: self._DPRW_date.display_as_valid(True) start = self._DPRW_date.GetData() end = self._DPRW_end.GetData() self._DPRW_end.display_as_valid(True) if end is not None: end = end.get_pydt() if start is not None: start = start.get_pydt() if end < start: has_errors = True self._DPRW_end.display_as_valid(False) if self._CHBOX_ongoing.IsChecked(): now = gmDateTime.pydt_now_here() if end < now: has_errors = True self._DPRW_end.display_as_valid(False) if self._PRW_hospital_stay.GetData() is None: if self._PRW_episode.GetValue().strip() == '': self._PRW_episode.display_as_valid(False) has_errors = True else: self._PRW_episode.display_as_valid(True) else: self._PRW_episode.display_as_valid(True) if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == ''): self._PRW_procedure.display_as_valid(False) has_errors = True else: self._PRW_procedure.display_as_valid(True) invalid_location = ( (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetData() is None) or (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetData() is not None) ) if invalid_location: self._PRW_hospital_stay.display_as_valid(False) self._PRW_location.display_as_valid(False) has_errors = True else: self._PRW_hospital_stay.display_as_valid(True) self._PRW_location.display_as_valid(True) gmDispatcher.send(signal = 'statustext', msg = _('Cannot save procedure.'), beep = True) return (has_errors is False)
def fit_last_year(self): end = gmDateTime.pydt_now_here() g_end = GregorianDateTime(end.year, end.month, end.day, end.hour, end.minute, end.second).to_time() g_start = GregorianDateTime(end.year - 1, end.month, end.day, end.hour, end.minute, end.second).to_time() last_year = TimePeriod(g_start, g_end) self.Navigate( lambda tp: tp.update(last_year.start_time, last_year.end_time))
def get_EDC(self, lmp=None, nullipara=True): result = cClinicalResult(_('unknown EDC')) result.formula_name = 'EDC (Mittendorf 1990)' result.formula_source = 'Mittendorf, R. et al., "The length of uncomplicated human gestation," OB/GYN, Vol. 75, No., 6 June, 1990, pp. 907-932.' if lmp is None: result.message = _('EDC: unknown LMP') return result result.variables['LMP'] = lmp result.variables['nullipara'] = nullipara if nullipara: result.variables['parity_offset'] = 15 # days else: result.variables['parity_offset'] = 10 # days now = gmDateTime.pydt_now_here() if lmp > now: result.warnings.append(_('LMP in the future')) if self.__patient is None: result.warnings.append(_('cannot run sanity checks, no patient')) else: if self.__patient['dob'] is None: result.warnings.append(_('cannot run sanity checks, no DOB')) else: years, months, days, hours, minutes, seconds = gmDateTime.calculate_apparent_age(start = self.__patient['dob']) # 5 years -- Myth ? # http://www.mirror.co.uk/news/uk-news/top-10-crazy-amazing-and-world-789842 if years < 10: result.warnings.append(_('patient less than 10 years old')) if self.__patient['gender'] in [None, 'm']: result.warnings.append(_('atypical gender for pregnancy: %s') % self.__patient.gender_string) if self.__patient['deceased'] is not None: result.warnings.append(_('patient already passed away')) if lmp.month > 3: edc_month = lmp.month - 3 edc_year = lmp.year + 1 else: edc_month = lmp.month + 9 edc_year = lmp.year result.numeric_value = gmDateTime.pydt_replace(dt = lmp, year = edc_year, month = edc_month, strict = False) + pydt.timedelta(days = result.variables['parity_offset']) result.message = _('EDC: %s') % gmDateTime.pydt_strftime ( result.numeric_value, format = '%Y %b %d' ) result.date_valid = now _log.debug('%s' % result) return result
def __refresh_results(self, patient=None): emr = patient.emr most_recent = emr.get_most_recent_results_for_patient() if len(most_recent) == 0: self._LCTRL_results.set_string_items(items = []) self._LCTRL_results.set_data(data = []) return most_recent = most_recent[0] list_items = [] list_data = [] now = gmDateTime.pydt_now_here() list_items.append(_('Most recent lab work: %s ago (%s)') % ( gmDateTime.format_interval_medically(now - most_recent['clin_when']), gmDateTime.pydt_strftime(most_recent['clin_when'], format = '%Y %b %d') )) list_data.append(most_recent) unsigned = emr.get_unsigned_results(order_by = "(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev") no_of_reds = 0 for result in unsigned: if result['abnormality_indicator'] is not None: if result['abnormality_indicator'].strip() != '': no_of_reds += 1 list_items.append(_('%s %s%s%s (%s ago, %s)') % ( result['unified_abbrev'], result['unified_val'], gmTools.coalesce(result['val_unit'], '', ' %s'), gmTools.coalesce(result['abnormality_indicator'], '', ' %s'), gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']), gmTools.u_writing_hand )) list_data.append(result) self._LCTRL_results.set_string_items(items = list_items) self._LCTRL_results.set_data(data = list_data) if no_of_reds > 0: for idx in range(1, no_of_reds + 1): self._LCTRL_results.SetItemTextColour(idx, wx.Colour('RED'))
def get_scan2pay_data(branch, bill, provider=None, comment=None): """Create scan2pay data for generating a QR code. https://www.scan2pay.info -------------------------------- BCD # (3) fixed, barcode tag 002 # (3) fixed, version 1 # (1) charset, 1 = utf8 SCT # (3) fixed $<praxis_id::BIC//Bank//%(value)s::11>$ # (11) <BIC> $2<range_of::$<current_provider_name::%(lastnames)s::>$,$<praxis::%(praxis)s::>$::70>2$ # (70) <Name of beneficiary> "Empfänger" - Praxis $<praxis_id::IBAN//Bank//%(value)s::34>$ # (34) <IBAN> EUR$<bill::%(total_amount_with_vat)s::12>$ # (12) <Amount in EURO> "EUR12.5" # (4) <purpose of transfer> - leer # (35) <remittance info - struct> - only this XOR the next field - GNUmed: leer $2<range_of::InvID=$<bill::%(invoice_id)s::>$/Date=$<today::%d.%B %Y::>$::140$>2$ # (140) <remittance info - text> "Client:Marie Louise La Lune" - "Rg Nr, date" <beneficiary-to-payor info> # (70) "pay soon :-)" - optional - GNUmed nur wenn bytes verfügbar -------------------------------- total: 331 bytes (not chars ! - cave UTF8) EOL: LF or CRLF last *used* element not followed by anything, IOW can omit pending non-used elements """ assert (branch is not None), '<branch> must not be <None>' assert (bill is not None), '<bill> must not be <None>' data = {} IBANs = branch.get_external_ids(id_type = 'IBAN', issuer = 'Bank') if len(IBANs) == 0: _log.debug('no IBAN found, cannot create scan2pay data') return None data['IBAN'] = IBANs[0]['value'][:34] data['beneficiary'] = gmTools.coalesce ( initial = provider, instead = branch['praxis'][:70], template_initial = '%%(lastnames)s, %s' % branch['praxis'] )[:70] BICs = branch.get_external_ids(id_type = 'BIC', issuer = 'Bank') if len(BICs) == 0: data['BIC'] = '' else: data['BIC'] = BICs[0]['value'][:11] data['amount'] = bill['total_amount_with_vat'][:9] data['ref'] = (_('Inv: %s, %s') % ( bill['invoice_id'], gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), '%d.%B %Y') ))[:140] data['cmt'] = gmTools.coalesce(comment, '', '\n%s')[:70] data_str = 'BCD\n002\n1\nSCT\n%(BIC)s\n%(beneficiary)s\n%(IBAN)s\nEUR%(amount)s\n\n\n%(ref)s%(cmt)s' % data data_str_bytes = bytes(data_str, 'utf8')[:331] return str(data_str_bytes, 'utf8')
def get_scan2pay_data(branch, bill, provider=None, comment=None): """Create scan2pay data for generating a QR code. https://www.scan2pay.info -------------------------------- BCD # (3) fixed, barcode tag 002 # (3) fixed, version 1 # (1) charset, 1 = utf8 SCT # (3) fixed $<praxis_id::BIC//Bank//%(value)s::11>$ # (11) <BIC> $2<range_of::$<current_provider_name::%(lastnames)s::>$,$<praxis::%(praxis)s::>$::70>2$ # (70) <Name of beneficiary> "Empfänger" - Praxis $<praxis_id::IBAN//Bank//%(value)s::34>$ # (34) <IBAN> EUR$<bill::%(total_amount_with_vat)s::12>$ # (12) <Amount in EURO> "EUR12.5" # (4) <purpose of transfer> - leer # (35) <remittance info - struct> - only this XOR the next field - GNUmed: leer $2<range_of::InvID=$<bill::%(invoice_id)s::>$/Date=$<today::%d.%B %Y::>$::140$>2$ # (140) <remittance info - text> "Client:Marie Louise La Lune" - "Rg Nr, date" <beneficiary-to-payor info> # (70) "pay soon :-)" - optional - GNUmed nur wenn bytes verfügbar -------------------------------- total: 331 bytes (not chars ! - cave UTF8) EOL: LF or CRLF last *used* element not followed by anything, IOW can omit pending non-used elements """ assert (branch is not None), '<branch> must not be <None>' assert (bill is not None), '<bill> must not be <None>' data = {} IBANs = branch.get_external_ids(id_type = 'IBAN', issuer = 'Bank') if len(IBANs) == 0: _log.debug('no IBAN found, cannot create scan2pay data') return None data['IBAN'] = IBANs[0]['value'][:34] data['beneficiary'] = gmTools.coalesce ( value2test = provider, return_instead = branch['praxis'][:70], template4value = '%%(lastnames)s, %s' % branch['praxis'] )[:70] BICs = branch.get_external_ids(id_type = 'BIC', issuer = 'Bank') if len(BICs) == 0: data['BIC'] = '' else: data['BIC'] = BICs[0]['value'][:11] data['amount'] = bill['total_amount_with_vat'][:9] data['ref'] = (_('Inv: %s, %s') % ( bill['invoice_id'], gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), '%d.%B %Y') ))[:140] data['cmt'] = gmTools.coalesce(comment, '', '\n%s')[:70] data_str = 'BCD\n002\n1\nSCT\n%(BIC)s\n%(beneficiary)s\n%(IBAN)s\nEUR%(amount)s\n\n\n%(ref)s%(cmt)s' % data data_str_bytes = bytes(data_str, 'utf8')[:331] return str(data_str_bytes, 'utf8')
def _refresh_from_existing(self): self._DPRW_date.SetData(data=self.data['clin_when']) if self.data['clin_end'] is None: self._DPRW_end.SetText() self._CHBOX_ongoing.Enable(True) self._CHBOX_ongoing.SetValue(self.data['is_ongoing']) else: self._DPRW_end.SetData(data=self.data['clin_end']) self._CHBOX_ongoing.Enable(False) now = gmDateTime.pydt_now_here() if self.data['clin_end'] > now: self._CHBOX_ongoing.SetValue(True) else: self._CHBOX_ongoing.SetValue(False) self._PRW_episode.SetText(value=self.data['episode'], data=self.data['pk_episode']) self._PRW_procedure.SetText(value=self.data['performed_procedure'], data=self.data['performed_procedure']) self._PRW_document.SetData(self.data['pk_doc']) self._TCTRL_comment.SetValue( gmTools.coalesce(self.data['comment'], u'')) if self.data['pk_hospital_stay'] is None: self._PRW_hospital_stay.SetText() self._PRW_hospital_stay.Enable(False) self._LBL_hospital_details.SetLabel(u'') self._PRW_location.SetText( value=u'%s @ %s' % (self.data['unit'], self.data['organization']), data=self.data['pk_org_unit']) self._PRW_location.Enable(True) self._PRW_episode.Enable(True) else: self._PRW_hospital_stay.SetText( value=u'%s @ %s' % (self.data['unit'], self.data['organization']), data=self.data['pk_hospital_stay']) self._PRW_hospital_stay.Enable(True) self._LBL_hospital_details.SetLabel( gmEMRStructItems.cHospitalStay( aPK_obj=self.data['pk_hospital_stay']).format()) self._PRW_location.SetText() self._PRW_location.Enable(False) self._PRW_episode.Enable(False) val, data = self._PRW_codes.generic_linked_codes2item_dict( self.data.generic_codes) self._PRW_codes.SetText(val, data) self._PRW_procedure.SetFocus()
def _on_ongoing_checkbox_checked(self, event): if self._CHBOX_ongoing.IsChecked(): end = self._DPRW_end.GetData() if end is None: self._DPRW_end.display_as_valid(True) else: end = end.get_pydt() now = gmDateTime.pydt_now_here() if end > now: self._DPRW_end.display_as_valid(True) else: self._DPRW_end.display_as_valid(False) else: self._DPRW_end.is_valid_timestamp() event.Skip()
def _refresh_as_new(self): self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here()) self._CHBOX_anamnestic.SetValue(False) self._PRW_vaccine.SetText(value = '', data = None, suppress_smarts = True) self._PRW_batch.unset_context(context = 'pk_vaccine') self._PRW_batch.SetValue('') self._PRW_episode.SetText(value = '', data = None, suppress_smarts = True) self._PRW_site.SetValue('') self._PRW_provider.SetData(data = None) self._PRW_reaction.SetText(value = '', data = None, suppress_smarts = True) self._BTN_report.Enable(False) self._TCTRL_comment.SetValue('') self.__refresh_indications() self._PRW_date_given.SetFocus()
def __recalculate(self): lmp = self._PRW_lmp.date if lmp is None: self._PRW_edc.SetData(None) self._TCTRL_details.SetValue('') return edc = self.__calc.get_EDC(lmp = lmp, nullipara = self._CHBOX_first_pregnancy.GetValue()) self._PRW_edc.SetData(edc.numeric_value) details = '' now = gmDateTime.pydt_now_here() # Beulah Hunter, 375 days (http://www.reference.com/motif/health/longest-human-pregnancy-on-record) if (lmp < now) and (edc.numeric_value > (now + pydt.timedelta(days = 380))): age = now - lmp weeks, days = divmod(age.days, 7) week = weeks if days > 0: week = weeks + 1 month, tmp = divmod(age.days, 28) if days > 0: month += 1 details += _( 'Current age of pregnancy (%s):\n' ' day %s = %s weeks %s days = week %s = month %s\n\n' ) % ( gmDateTime.pydt_strftime(now, '%Y %b %d'), age.days, int(weeks), int(days), week, month ) details += edc.format ( left_margin = 1, width = 50, with_formula = False, with_warnings = True, with_variables = True, with_sub_results = True, return_list = False ) self._TCTRL_details.SetValue(details)
def __refresh_indications(self): self._TCTRL_indications.SetValue('') vaccine = self._PRW_vaccine.GetData(as_instance = True) if vaccine is None: return lines = [] emr = gmPerson.gmCurrentPatient().emr latest_vaccs = emr.get_latest_vaccinations ( atc_indications = [ i['atc_indication'] for i in vaccine['indications'] ] ) for l10n_ind in [ i['l10n_indication'] for i in vaccine['indications'] ]: try: no_of_shots4ind, latest_vacc4ind = latest_vaccs[l10n_ind] ago = gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given']) lines.append(_('%s (most recent shot of %s: %s ago)') % (l10n_ind, no_of_shots4ind, ago)) except KeyError: lines.append(_('%s (no previous vaccination recorded)') % l10n_ind) self._TCTRL_indications.SetValue(_('Protects against:\n ') + '\n '.join(lines))
def __recalculate(self): lmp = self._PRW_lmp.date if lmp is None: self._PRW_edc.SetData(None) self._TCTRL_details.SetValue(u"") return edc = self.__calc.get_EDC(lmp=lmp, nullipara=self._CHBOX_first_pregnancy.GetValue()) self._PRW_edc.SetData(edc.numeric_value) details = u"" now = gmDateTime.pydt_now_here() # Beulah Hunter, 375 days (http://www.reference.com/motif/health/longest-human-pregnancy-on-record) if (lmp < now) and (edc.numeric_value > (now + pydt.timedelta(days=380))): age = now - lmp weeks, days = divmod(age.days, 7) week = weeks if days > 0: week = weeks + 1 month, tmp = divmod(age.days, 28) if days > 0: month += 1 details += _(u"Current age of pregnancy (%s):\n" u" day %s = %s weeks %s days = week %s = month %s\n\n") % ( gmDateTime.pydt_strftime(now, "%Y %b %d"), age.days, int(weeks), int(days), week, month, ) details += edc.format( left_margin=1, width=50, with_formula=False, with_warnings=True, with_variables=True, with_sub_results=True, return_list=False, ) self._TCTRL_details.SetValue(details)
def _get_data_tooltip(self): if len(self._data) == 0: return '' date = self.GetData() # if match provider only provided completions # but not a full date with it if date is None: return '' now = gmDateTime.pydt_now_here() if date > now: intv = date - now template = _('%s\n (a %s in %s)') else: intv = now - date template = _('%s\n (a %s %s ago)') return template % ( gmDateTime.pydt_strftime(date, format = '%B %d %Y -- %c', accuracy = gmDateTime.acc_days), gmDateTime.pydt_strftime(date, format = '%A', accuracy = gmDateTime.acc_days), gmDateTime.format_interval(interval = intv, accuracy_wanted = gmDateTime.acc_days, verbose = True) )
def _on_end_lost_focus(self): end = self._DPRW_end.GetData() if end is None: self._CHBOX_ongoing.Enable(True) self._DPRW_end.display_as_valid(True) else: self._CHBOX_ongoing.Enable(False) end = end.get_pydt() now = gmDateTime.pydt_now_here() if end > now: self._CHBOX_ongoing.SetValue(True) else: self._CHBOX_ongoing.SetValue(False) start = self._DPRW_date.GetData() if start is None: self._DPRW_end.display_as_valid(True) else: start = start.get_pydt() if end > start: self._DPRW_end.display_as_valid(True) else: self._DPRW_end.display_as_valid(False)
def _refresh_from_existing(self): self._DPRW_date.SetData(data = self.data['clin_when']) if self.data['clin_end'] is None: self._DPRW_end.SetText() self._CHBOX_ongoing.Enable(True) self._CHBOX_ongoing.SetValue(self.data['is_ongoing']) else: self._DPRW_end.SetData(data = self.data['clin_end']) self._CHBOX_ongoing.Enable(False) now = gmDateTime.pydt_now_here() if self.data['clin_end'] > now: self._CHBOX_ongoing.SetValue(True) else: self._CHBOX_ongoing.SetValue(False) self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode']) self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure']) self._PRW_document.SetData(self.data['pk_doc']) self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], '')) if self.data['pk_hospital_stay'] is None: self._PRW_hospital_stay.SetText() self._PRW_hospital_stay.Enable(False) self._LBL_hospital_details.SetLabel('') self._PRW_location.SetText(value = '%s @ %s' % (self.data['unit'], self.data['organization']), data = self.data['pk_org_unit']) self._PRW_location.Enable(True) self._PRW_episode.Enable(True) else: self._PRW_hospital_stay.SetText(value = '%s @ %s' % (self.data['unit'], self.data['organization']), data = self.data['pk_hospital_stay']) self._PRW_hospital_stay.Enable(True) self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = self.data['pk_hospital_stay']).format()) self._PRW_location.SetText() self._PRW_location.Enable(False) self._PRW_episode.Enable(False) val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) self._PRW_codes.SetText(val, data) self._PRW_procedure.SetFocus()
def __update_age_label(self): # no patient if not self.curr_pat.connected: self._LBL_age.SetLabel(_('<Age>')) self._LBL_age.SetToolTipString(_('no patient selected')) return # gender is always known tt = _(u'Gender: %s (%s) - %s\n') % ( self.curr_pat.gender_symbol, gmTools.coalesce(self.curr_pat[u'gender'], u'?'), self.curr_pat.gender_string ) # dob is not known if self.curr_pat['dob'] is None: age = u'%s %s' % ( self.curr_pat.gender_symbol, self.curr_pat.get_formatted_dob() ) self._LBL_age.SetLabel(age) self._LBL_age.SetToolTipString(tt) return tt += _('Born: %s\n') % self.curr_pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()) # patient is dead if self.curr_pat['deceased'] is not None: tt += _('Died: %s\n') % gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y') tt += _('At age: %s\n') % self.curr_pat['medical_age'] age = u'%s %s - %s (%s)' % ( self.curr_pat.gender_symbol, self.curr_pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()), gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y'), self.curr_pat['medical_age'] ) if self.curr_pat['dob_is_estimated']: tt += _(' (date of birth and age are estimated)\n') self._LBL_age.SetLabel(age) self._LBL_age.SetToolTipString(tt) return # patient alive now = gmDateTime.pydt_now_here() # patient birthday ? if self.curr_pat.get_formatted_dob(format = '%m-%d') == now.strftime('%m-%d'): template = _('%(sex)s %(dob)s (%(age)s today !)') tt += _("\nToday is the patient's birthday !\n\n") else: if self.curr_pat.current_birthday_passed(): template = u'%(sex)s %(dob)s%(l_arr)s (%(age)s)' tt += _(u'Birthday: %s ago\n') % gmDateTime.format_apparent_age_medically ( age = gmDateTime.calculate_apparent_age(start = self.curr_pat.birthday_this_year, end = now) ) else: template = u'%(sex)s %(r_arr)s%(dob)s (%(age)s)' tt += _(u'Birthday: in %s\n') % gmDateTime.format_apparent_age_medically ( age = gmDateTime.calculate_apparent_age(start = now, end = self.curr_pat.birthday_this_year) ) tt += _('Age: %s\n') % self.curr_pat['medical_age'] # FIXME: if the age is below, say, 2 hours we should fire # a timer here that updates the age in increments of 1 minute ... :-) age = template % { u'sex': self.curr_pat.gender_symbol, u'dob': self.curr_pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()), u'age': self.curr_pat['medical_age'], u'r_arr': gmTools.u_right_arrow, u'l_arr': gmTools.u_left_arrow } # Easter Egg ;-) if self.curr_pat['lastnames'] == u'Leibner': if self.curr_pat['firstnames'] == u'Steffi': if self.curr_pat['preferred'] == u'Wildfang': age = u'%s %s' % (gmTools.u_black_heart, age) if self.curr_pat['dob_is_estimated']: tt += _(' (date of birth and age are estimated)\n') self._LBL_age.SetLabel(age) self._LBL_age.SetToolTipString(tt)
def create_fake_timeline_file(patient=None, filename=None): """Used to create an 'empty' timeline file for display. - needed because .clear_timeline() doesn't really work """ emr = patient.emr global now now = gmDateTime.pydt_now_here() if filename is None: timeline_fname = gmTools.get_unique_filename(prefix = 'gm-', suffix = '.timeline') else: timeline_fname = filename _log.debug('creating dummy timeline in [%s]', timeline_fname) timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace') timeline.write(__fake_timeline_start) # birth if patient['dob'] is None: start = now.replace(year = now.year - 100) timeline.write(__xml_encounter_template % ( format_pydt(start), format_pydt(start), _('Birth') + ': ?', _('Life events'), _('Date of birth unknown'), 'False' )) else: start = patient['dob'] timeline.write(__xml_encounter_template % ( format_pydt(patient['dob']), format_pydt(patient['dob']), _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], ' (%s)' % gmTools.u_almost_equal_to, ''), _('Life events'), '', 'False' )) # death if patient['deceased'] is None: end = now else: end = patient['deceased'] timeline.write(__xml_encounter_template % ( format_pydt(end), format_pydt(end), #u'', _('Death'), _('Life events'), '', 'False' )) # fake issue timeline.write(__fake_timeline_body_template % ( format_pydt(start), format_pydt(end), _('Cannot display timeline.'), _('Cannot display timeline.') )) # display range if end.month == 2: if end.day == 29: # leap years aren't consecutive end = end.replace(day = 28) target_year = end.year + 1 end = end.replace(year = target_year) timeline.write(xml_end % ( format_pydt(start), format_pydt(end) )) timeline.close() return timeline_fname
def create_timeline_file(patient=None, filename=None, include_documents=False, include_vaccinations=False, include_encounters=False): emr = patient.emr global now now = gmDateTime.pydt_now_here() if filename is None: timeline_fname = gmTools.get_unique_filename(prefix = 'gm-', suffix = '.timeline') # .timeline required ... else: timeline_fname = filename _log.debug('exporting EMR as timeline into [%s]', timeline_fname) timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace') if patient['dob'] is None: lifespan_start = format_pydt(now.replace(year = now.year - 100)) else: lifespan_start = format_pydt(patient['dob']) if patient['deceased'] is None: life_ends2day = 'True' lifespan_end = format_pydt(now) else: life_ends2day = 'False' lifespan_end = format_pydt(patient['deceased']) earliest_care_date = emr.earliest_care_date most_recent_care_date = emr.most_recent_care_date if most_recent_care_date is None: most_recent_care_date = lifespan_end care_ends2day = life_ends2day else: most_recent_care_date = format_pydt(most_recent_care_date) care_ends2day = 'False' timeline.write(xml_start % ( # era: life span of patient _('Lifespan'), lifespan_start, lifespan_end, life_ends2day, ERA_NAME_CARE_PERIOD, format_pydt(earliest_care_date), most_recent_care_date, care_ends2day, # categories _('Health issues'), _('Episodes'), _('Encounters'), _('Hospital stays'), _('Procedures'), _('Documents'), _('Vaccinations'), _('Substances'), _('Life events') )) # birth if patient['dob'] is None: start = now.replace(year = now.year - 100) timeline.write(__xml_encounter_template % ( format_pydt(start), format_pydt(start), '?', _('Life events'), _('Date of birth unknown'), 'True' )) else: start = patient['dob'] timeline.write(__xml_encounter_template % ( format_pydt(patient['dob']), format_pydt(patient['dob']), '*', _('Life events'), '%s: %s (%s)' % ( _('Birth'), patient.get_formatted_dob(format = '%Y %b %d %H:%M', honor_estimation = True), patient.get_medical_age() ), 'True' )) # start of care timeline.write(__xml_encounter_template % ( format_pydt(earliest_care_date), format_pydt(earliest_care_date), gmTools.u_heavy_greek_cross, _('Life events'), _('Start of Care: %s\n(the earliest recorded event of care in this praxis)') % format_pydt(earliest_care_date, format = '%Y %b %d'), 'True' )) # containers must be defined before their # subevents, so put health issues first timeline.write('\n <!-- ========================================\n Health issues\n======================================== -->') for issue in emr.health_issues: timeline.write(__format_health_issue_as_timeline_xml(issue, patient, emr)) timeline.write('\n <!-- ========================================\n Episodes\n======================================== -->') for epi in emr.get_episodes(order_by = 'pk_health_issue'): timeline.write(__format_episode_as_timeline_xml(epi, patient)) if include_encounters: timeline.write(u'\n<!--\n========================================\n Encounters\n======================================== -->') for enc in emr.get_encounters(skip_empty = True): timeline.write(__format_encounter_as_timeline_xml(enc, patient)) timeline.write('\n<!--\n========================================\n Hospital stays\n======================================== -->') for stay in emr.hospital_stays: timeline.write(__format_hospital_stay_as_timeline_xml(stay)) timeline.write('\n<!--\n========================================\n Procedures\n======================================== -->') for proc in emr.performed_procedures: timeline.write(__format_procedure_as_timeline_xml(proc)) if include_vaccinations: timeline.write(u'\n<!--\n========================================\n Vaccinations\n======================================== -->') for vacc in emr.vaccinations: timeline.write(__format_vaccination_as_timeline_xml(vacc)) timeline.write('\n<!--\n========================================\n Substance intakes\n======================================== -->') for intake in emr.get_current_medications(include_inactive = True, include_unapproved = False): timeline.write(__format_intake_as_timeline_xml(intake)) if include_documents: timeline.write(u'\n<!--\n========================================\n Documents\n======================================== -->') for doc in patient.document_folder.documents: timeline.write(__format_document_as_timeline_xml(doc)) # allergies ? # - unclear where and how to place # test results ? # - too many events, at most "day sample drawn" # death if patient['deceased'] is None: end = now else: end = patient['deceased'] timeline.write(__xml_encounter_template % ( format_pydt(end), format_pydt(end), gmTools.u_dagger, _('Life events'), _('Death: %s') % format_pydt(end, format = '%Y %b %d %H:%M') )) # display range if end.month == 2: if end.day == 29: # leap years aren't consecutive end = end.replace(day = 28) target_year = end.year + 1 end = end.replace(year = target_year) timeline.write(xml_end % ( format_pydt(start), format_pydt(end) )) timeline.close() return timeline_fname
def process_staged_single_PID_hl7_file(staged_item): log_name = gmTools.get_unique_filename ( prefix = 'gm-staged_hl7_import-', suffix = '.log' ) import_logger = logging.FileHandler(log_name) import_logger.setLevel(logging.DEBUG) root_logger = logging.getLogger('') root_logger.addHandler(import_logger) _log.debug('log file: %s', log_name) if not staged_item.lock(): _log.error('cannot lock staged data for HL7 import') root_logger.removeHandler(import_logger) return False, log_name _log.debug('reference ID of staged HL7 data: %s', staged_item['external_data_id']) filename = staged_item.save_to_file() _log.debug('unstaged HL7 data into: %s', filename) if staged_item['pk_identity_disambiguated'] is None: emr = None else: emr = gmPerson.cPatient(staged_item['pk_identity_disambiguated']).emr success = False try: success = __import_single_PID_hl7_file(filename, emr = emr) if success: gmIncomingData.delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched']) staged_item.unlock() root_logger.removeHandler(import_logger) return True, log_name _log.error('error when importing single-PID/single-MSH file') except Exception: _log.exception('error when importing single-PID/single-MSH file') if not success: staged_item['comment'] = _('failed import: %s\n') % gmDateTime.pydt_strftime(gmDateTime.pydt_now_here()) staged_item['comment'] += '\n' staged_item['comment'] += ('-' * 80) staged_item['comment'] += '\n\n' log = io.open(log_name, mode = 'rt', encoding = 'utf8') staged_item['comment'] += log.read() log.close() staged_item['comment'] += '\n' staged_item['comment'] += ('-' * 80) staged_item['comment'] += '\n\n' staged_item['comment'] += format_hl7_file ( filename, skip_empty_fields = True, eol = '\n ', return_filename = False ) staged_item.save() staged_item.unlock() root_logger.removeHandler(import_logger) return success, log_name
def create_gnumed_import_sql(filename): # CREATE TABLE patients (gender TEXT, doctor INTEGER, surname TEXT, ID INTEGER PRIMARY KEY, identification_code TEXT, phone TEXT, given_name TEXT, birth_date TEXT, residence_address TEXT); print u'' print u'set default_transaction_read_only to off;' print u'' print u"begin;" print u'' now = gmDateTime.pydt_now_here().isoformat() clinica_db = sqlite.connect(database = filename) curs = clinica_db.cursor() cmd = u'select * from patients' curs.execute(cmd) keys = [ r[0] for r in curs.description ] row = curs.fetchone() if row is None: print "-- no patients in database" return row = sanitize_patient_row(dict(zip(keys, row))) print u'-- import-related encounter type' print u"INSERT INTO clin.encounter_type (description) SELECT '%s' WHERE NOT EXISTS (SELECT 1 FROM clin.encounter_type WHERE description = '%s' LIMIT 1);" % ( Clinica_encounter_type, Clinica_encounter_type ) while row is not None: print u'' print u'-- next patient' print u"INSERT INTO dem.identity (gender, dob, comment) VALUES ('%s', NULL, 'Clinica import @ %s');" % ( row['gender'], now ) if row['birth_date'] is not None: if row['birth_date'].strip() != u'': print u"""UPDATE dem.identity SET dob = '%s'::timestamp with time zone WHERE pk = currval('dem.identity_pk_seq');""" % row['birth_date'] print u"""SELECT dem.add_name(currval('dem.identity_pk_seq')::integer, '%s'::text, '%s'::text, True);""" % ( row['given_name'], row['surname'] ) print u"""INSERT INTO dem.lnk_identity2ext_id (id_identity, external_id, fk_origin) VALUES (currval('dem.identity_pk_seq'), '%s', dem.add_external_id_type('Clinica primary key', 'Clinica EMR'));""" % row['ID'] if row['identification_code'] is not None: print u"""INSERT INTO dem.lnk_identity2ext_id (id_identity, external_id, fk_origin) VALUES (currval('dem.identity_pk_seq'), '%s', dem.add_external_id_type('Clinica-external ID', 'Clinica EMR'));""" % row['identification_code'] if row['phone'] is not None: print u"""INSERT INTO dem.lnk_identity2comm (fk_identity, url, fk_type) VALUES (currval('dem.identity_pk_seq'), '%s', dem.create_comm_type('homephone'));""" % row['phone'] if row['residence_address'] is not None: print u"""INSERT INTO dem.lnk_identity2comm (fk_identity, url, fk_type) VALUES (currval('dem.identity_pk_seq'), '%s', dem.create_comm_type('Clinica address'));""" % row['residence_address'] create_visit_sql(row['ID'], clinica_db) row = curs.fetchone() if row is not None: row = sanitize_patient_row(dict(zip(keys, row))) print u'' print u'-- comment this out when you are ready to *really* run the data import:' print u'rollback;' print u'' print u'commit;'
def __import_single_PID_hl7_file(filename, emr=None): """Assumes single-PID/single-MSH HL7 file.""" _log.debug('importing single-PID single-MSH HL7 data from [%s]', filename) # read the file MSH_file = io.open(filename, mode = 'rt', encoding = 'utf8', newline = '') HL7 = pyhl7.parse(MSH_file.read(1024 * 1024 * 5)) # 5 MB max MSH_file.close() # sanity checks if len(HL7.segments('MSH')) != 1: _log.error('more than one MSH segment') return False if len(HL7.segments('PID')) != 1: _log.error('more than one PID segment') return False # ensure lab is in database hl7_lab = HL7.extract_field('MSH', field_num = MSH_field__sending_lab) gm_lab = __find_or_create_lab(hl7_lab) # ensure test types exist conn = gmPG2.get_connection(readonly = False) __ensure_hl7_test_types_exist_in_gnumed(link_obj = conn, hl7_data = HL7, pk_test_org = gm_lab['pk_test_org']) # find patient if emr is None: #PID = HL7.segment('PID') pats = __PID2dto(HL7 = HL7) if len(pats) == 0: conn.rollback() return False if len(pats) > 1: conn.rollback() return False emr = pats[0].emr # import values: loop over segments when_list = {} current_result = None previous_segment = None had_errors = False msh_seen = False pid_seen = False last_obr = None obr = {} for seg_idx in range(len(HL7)): seg = HL7[seg_idx] seg_type = seg[0][0] _log.debug('processing line #%s = segment of type <%s>', seg_idx, seg_type) if seg_type == 'MSH': msh_seen = True if seg_type == 'PID': if not msh_seen: conn.rollback() _log.error('PID segment before MSH segment') return False pid_seen = True if seg_type in ['MSH', 'PID']: _log.info('segment already handled') previous_segment = seg_type obr = {} current_result = None continue if seg_type in ['ORC']: _log.info('currently ignoring %s segments', seg_type) previous_segment = seg_type obr = {} current_result = None continue if seg_type == 'OBR': previous_segment = seg_type last_obr = seg current_result = None obr['abbrev'] = ('%s' % seg[OBR_field__service_name][0]).strip() try: obr['name'] = ('%s' % seg[OBR_field__service_name][1]).strip() except IndexError: obr['name'] = obr['abbrev'] for field_name in [OBR_field__ts_ended, OBR_field__ts_started, OBR_field__ts_specimen_received, OBR_field__ts_requested]: obr['clin_when'] = seg[field_name][0].strip() if obr['clin_when'] != '': break continue if seg_type == 'OBX': current_result = None # determine value val_alpha = seg[OBX_field__value][0].strip() is_num, val_num = gmTools.input2decimal(initial = val_alpha) if is_num: val_alpha = None else: val_num = None val_alpha = val_alpha.replace('\.br\\', '\n') # determine test type unit = seg[OBX_field__unit][0].strip() if unit == '': if is_num: unit = '1/1' else: unit = None test_type = __find_or_create_test_type ( loinc = '%s' % seg[OBX_field__type][0][OBX_component__loinc-1], name = '%s' % seg[OBX_field__type][0][OBX_component__name-1], pk_lab = gm_lab['pk_test_org'], unit = unit, link_obj = conn ) # eventually, episode should be read from lab_request epi = emr.add_episode ( link_obj = conn, episode_name = 'administrative', is_open = False, allow_dupes = False ) current_result = emr.add_test_result ( link_obj = conn, episode = epi['pk_episode'], type = test_type['pk_test_type'], intended_reviewer = gmStaff.gmCurrentProvider()['pk_staff'], val_num = val_num, val_alpha = val_alpha, unit = unit ) # handle range information et al ref_range = seg[OBX_field__range][0].strip() if ref_range != '': current_result.reference_range = ref_range flag = seg[OBX_field__abnormal_flag][0].strip() if flag != '': current_result['abnormality_indicator'] = flag current_result['status'] = seg[OBX_field__status][0].strip() current_result['val_grouping'] = seg[OBX_field__subid][0].strip() current_result['source_data'] = '' if last_obr is not None: current_result['source_data'] += str(last_obr) current_result['source_data'] += '\n' current_result['source_data'] += str(seg) clin_when = seg[OBX_field__timestamp][0].strip() if clin_when == '': _log.warning('no <Observation timestamp> in OBX, trying OBR timestamp') clin_when = obr['clin_when'] try: clin_when = __hl7dt2pydt(clin_when) except ValueError: _log.exception('clin_when from OBX or OBR not useable, assuming <today>') if clin_when is not None: current_result['clin_when'] = clin_when current_result.save(conn = conn) when_list[gmDateTime.pydt_strftime(current_result['clin_when'], '%Y %b %d')] = 1 previous_segment = seg_type continue if seg_type == 'NTE': note = seg[NET_field__note][0].strip().replace('\.br\\', '\n') if note == '': _log.debug('empty NTE segment') previous_segment = seg_type # maybe not ? (HL7 providers happen to use empty NTE segments to "structure" raw HL7 |-) continue # if this is an NTE following an OBR (IOW an order-related # comment): make this a test result all of its own :-) if previous_segment == 'OBR': _log.debug('NTE following OBR: general note, using OBR timestamp [%s]', obr['clin_when']) current_result = None name = obr['name'] if name == '': name = _('Comment') # FIXME: please suggest a LOINC for "order comment" test_type = __find_or_create_test_type(name = name, pk_lab = gm_lab['pk_test_org'], abbrev = obr['abbrev'], link_obj = conn) # eventually, episode should be read from lab_request epi = emr.add_episode ( link_obj = conn, episode_name = 'administrative', is_open = False, allow_dupes = False ) nte_result = emr.add_test_result ( link_obj = conn, episode = epi['pk_episode'], type = test_type['pk_test_type'], intended_reviewer = gmStaff.gmCurrentProvider()['pk_staff'], val_alpha = note ) #nte_result['val_grouping'] = seg[OBX_field__subid][0].strip() nte_result['source_data'] = str(seg) try: nte_result['clin_when'] = __hl7dt2pydt(obr['clin_when']) except ValueError: _log.exception('no .clin_when from OBR for NTE pseudo-OBX available') nte_result.save(conn = conn) continue if (previous_segment == 'OBX') and (current_result is not None): current_result['source_data'] += '\n' current_result['source_data'] += str(seg) current_result['note_test_org'] = gmTools.coalesce ( current_result['note_test_org'], note, '%%s\n%s' % note ) current_result.save(conn = conn) previous_segment = seg_type continue _log.error('unexpected NTE segment') had_errors = True break _log.error('unknown segment, aborting') _log.debug('line: %s', seg) had_errors = True break if had_errors: conn.rollback() return False conn.commit() # record import in chart try: no_results = len(HL7.segments('OBX')) except KeyError: no_results = '?' soap = _( 'Imported HL7 file [%s]:\n' ' lab "%s" (%s@%s), %s results (%s)' ) % ( filename, hl7_lab, gm_lab['unit'], gm_lab['organization'], no_results, ' / '.join(list(when_list)) ) epi = emr.add_episode ( episode_name = 'administrative', is_open = False, allow_dupes = False ) emr.add_clin_narrative ( note = soap, soap_cat = None, episode = epi ) # keep copy of HL7 data in document archive folder = gmPerson.cPatient(emr.pk_patient).document_folder hl7_docs = folder.get_documents ( doc_type = 'HL7 data', pk_episodes = [epi['pk_episode']], order_by = 'ORDER BY clin_when DESC' ) if len(hl7_docs) > 0: # there should only ever be one unless the user manually creates more, # also, it should always be the latest since "ORDER BY clin_when DESC" hl7_doc = hl7_docs[0] else: hl7_doc = folder.add_document ( document_type = 'HL7 data', encounter = emr.active_encounter['pk_encounter'], episode = epi['pk_episode'] ) hl7_doc['comment'] = _('list of imported HL7 data files') hl7_doc['pk_org_unit'] = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit'] hl7_doc['clin_when'] = gmDateTime.pydt_now_here() hl7_doc.save() part = hl7_doc.add_part(file = filename) part['obj_comment'] = _('Result dates: %s') % ' / '.join(list(when_list)) part.save() hl7_doc.set_reviewed(technically_abnormal = False, clinically_relevant = False) return True
def center_at_today(self): now = gmDateTime.pydt_now_here() g_now = GregorianDateTime(now.year, now.month, now.day, now.hour, now.minute, now.second).to_time() self.Navigate(lambda tp: tp.center(g_now))
def __import_single_PID_hl7_file(filename, emr=None): """Assumes single-PID/single-MSH HL7 file.""" _log.debug('importing single-PID single-MSH HL7 data from [%s]', filename) # read the file MSH_file = io.open(filename, mode = 'rt', encoding = 'utf8') HL7 = pyhl7.parse(MSH_file.read(1024 * 1024 * 5)) # 5 MB max MSH_file.close() # sanity checks if len(HL7.segments('MSH')) != 1: _log.error('more than one MSH segment') return False if len(HL7.segments('PID')) != 1: _log.error('more than one PID segment') return False # ensure lab is in database hl7_lab = HL7.extract_field('MSH', field_num = MSH_field__sending_lab) gm_lab = __find_or_create_lab(hl7_lab) # ensure test types exist conn = gmPG2.get_connection(readonly = False) __ensure_hl7_test_types_exist_in_gnumed(link_obj = conn, hl7_data = HL7, pk_test_org = gm_lab['pk_test_org']) # find patient if emr is None: #PID = HL7.segment('PID') pats = __PID2dto(HL7 = HL7) if len(pats) == 0: conn.rollback() return False if len(pats) > 1: conn.rollback() return False emr = pats[0].emr # import values: loop over segments when_list = {} current_result = None previous_segment = None had_errors = False msh_seen = False pid_seen = False last_obr = None obr = {} for seg_idx in range(len(HL7)): seg = HL7[seg_idx] seg_type = seg[0][0] _log.debug('processing line #%s = segment of type <%s>', seg_idx, seg_type) if seg_type == u'MSH': msh_seen = True if seg_type == u'PID': if not msh_seen: conn.rollback() _log.error('PID segment before MSH segment') return False pid_seen = True if seg_type in [u'MSH', u'PID']: _log.info('segment already handled') previous_segment = seg_type obr = {} current_result = None continue if seg_type in [u'ORC']: _log.info('currently ignoring %s segments', seg_type) previous_segment = seg_type obr = {} current_result = None continue if seg_type == u'OBR': previous_segment = seg_type last_obr = seg current_result = None obr['abbrev'] = (u'%s' % seg[OBR_field__service_name][0]).strip() try: obr['name'] = (u'%s' % seg[OBR_field__service_name][1]).strip() except IndexError: obr['name'] = obr['abbrev'] for field_name in [OBR_field__ts_ended, OBR_field__ts_started, OBR_field__ts_specimen_received, OBR_field__ts_requested]: obr['clin_when'] = seg[field_name][0].strip() if obr['clin_when'] != u'': break continue if seg_type == u'OBX': current_result = None # determine value val_alpha = seg[OBX_field__value][0].strip() is_num, val_num = gmTools.input2decimal(initial = val_alpha) if is_num: val_alpha = None else: val_num = None val_alpha = val_alpha.replace('\.br\\', u'\n') # determine test type unit = seg[OBX_field__unit][0].strip() if unit == u'': if is_num: unit = u'1/1' else: unit = None test_type = __find_or_create_test_type ( loinc = u'%s' % seg[OBX_field__type][0][OBX_component__loinc-1], name = u'%s' % seg[OBX_field__type][0][OBX_component__name-1], pk_lab = gm_lab['pk_test_org'], unit = unit ) # eventually, episode should be read from lab_request epi = emr.add_episode ( link_obj = conn, episode_name = u'administrative', is_open = False, allow_dupes = False ) current_result = emr.add_test_result ( link_obj = conn, episode = epi['pk_episode'], type = test_type['pk_test_type'], intended_reviewer = gmStaff.gmCurrentProvider()['pk_staff'], val_num = val_num, val_alpha = val_alpha, unit = unit ) # handle range information et al ref_range = seg[OBX_field__range][0].strip() if ref_range != u'': current_result.reference_range = ref_range flag = seg[OBX_field__abnormal_flag][0].strip() if flag != u'': current_result['abnormality_indicator'] = flag current_result['status'] = seg[OBX_field__status][0].strip() current_result['val_grouping'] = seg[OBX_field__subid][0].strip() current_result['source_data'] = u'' if last_obr is not None: current_result['source_data'] += unicode(last_obr) current_result['source_data'] += u'\n' current_result['source_data'] += unicode(seg) clin_when = seg[OBX_field__timestamp][0].strip() if clin_when == u'': _log.warning('no <Observation timestamp> in OBX, trying OBR timestamp') clin_when = obr['clin_when'] try: clin_when = __hl7dt2pydt(clin_when) except ValueError: _log.exception('clin_when from OBX or OBR not useable, assuming <today>') if clin_when is not None: current_result['clin_when'] = clin_when current_result.save(conn = conn) when_list[gmDateTime.pydt_strftime(current_result['clin_when'], '%Y %b %d')] = 1 previous_segment = seg_type continue if seg_type == u'NTE': note = seg[NET_field__note][0].strip().replace('\.br\\', u'\n') if note == u'': _log.debug('empty NTE segment') previous_segment = seg_type # maybe not ? (HL7 providers happen to use empty NTE segments to "structure" raw HL7 |-) continue # if this is an NTE following an OBR (IOW an order-related # comment): make this a test result all of its own :-) if previous_segment == u'OBR': _log.debug('NTE following OBR: general note, using OBR timestamp [%s]', obr['clin_when']) current_result = None name = obr['name'] if name == u'': name = _('Comment') # FIXME: please suggest a LOINC for "order comment" test_type = __find_or_create_test_type(name = name, pk_lab = gm_lab['pk_test_org'], abbrev = obr['abbrev']) # eventually, episode should be read from lab_request epi = emr.add_episode ( link_obj = conn, episode_name = u'administrative', is_open = False, allow_dupes = False ) nte_result = emr.add_test_result ( link_obj = conn, episode = epi['pk_episode'], type = test_type['pk_test_type'], intended_reviewer = gmStaff.gmCurrentProvider()['pk_staff'], val_alpha = note ) #nte_result['val_grouping'] = seg[OBX_field__subid][0].strip() nte_result['source_data'] = unicode(seg) try: nte_result['clin_when'] = __hl7dt2pydt(obr['clin_when']) except ValueError: _log.exception('no .clin_when from OBR for NTE pseudo-OBX available') nte_result.save(conn = conn) continue if (previous_segment == u'OBX') and (current_result is not None): current_result['source_data'] += u'\n' current_result['source_data'] += unicode(seg) current_result['note_test_org'] = gmTools.coalesce ( current_result['note_test_org'], note, u'%%s\n%s' % note ) current_result.save(conn = conn) previous_segment = seg_type continue _log.error(u'unexpected NTE segment') had_errors = True break _log.error('unknown segment, aborting') _log.debug('line: %s', seg) had_errors = True break if had_errors: conn.rollback() return False conn.commit() # record import in chart try: no_results = len(HL7.segments('OBX')) except KeyError: no_results = u'?' soap = _( 'Imported HL7 file [%s]:\n' ' lab "%s" (%s@%s), %s results (%s)' ) % ( filename, hl7_lab, gm_lab['unit'], gm_lab['organization'], no_results, u' / '.join(when_list.keys()) ) epi = emr.add_episode ( episode_name = u'administrative', is_open = False, allow_dupes = False ) emr.add_clin_narrative ( note = soap, soap_cat = None, episode = epi ) # keep copy of HL7 data in document archive folder = gmPerson.cPatient(emr.pk_patient).document_folder hl7_docs = folder.get_documents ( doc_type = u'HL7 data', episodes = [epi['pk_episode']], order_by = u'ORDER BY clin_when DESC' ) if len(hl7_docs) > 0: # there should only ever be one unless the user manually creates more, # also, it should always be the latest since "ORDER BY clin_when DESC" hl7_doc = hl7_docs[0] else: hl7_doc = folder.add_document ( document_type = u'HL7 data', encounter = emr.active_encounter['pk_encounter'], episode = epi['pk_episode'] ) hl7_doc['comment'] = _('list of imported HL7 data files') hl7_doc['clin_when'] = gmDateTime.pydt_now_here() hl7_doc.save() part = hl7_doc.add_part(file = filename) part['obj_comment'] = _('Result dates: %s') % u' / '.join(when_list.keys()) part.save() hl7_doc.set_reviewed(technically_abnormal = False, clinically_relevant = False) return True
class cExportArea(object): def __init__(self, pk_identity): self.__pk_identity = pk_identity #-------------------------------------------------------- def add_form(self, form=None, designation=None): if len(form.final_output_filenames) == 0: return True items = [] for fname in form.final_output_filenames: item = self.add_file(filename=fname) if item is None: for prev_item in items: delete_export_item( pk_export_item=prev_item['pk_export_item']) return False items.append(item) item['description'] = _(u'form: %s %s (%s)') % ( form.template['name_long'], form.template['external_version'], fname) item['designation'] = designation item.save() return True #-------------------------------------------------------- def add_forms(self, forms=None, designation=None): all_ok = True for form in forms: all_ok = all_ok and self.add_form(form=form, designation=designation) return all_ok #-------------------------------------------------------- def add_file(self, filename=None, hint=None): try: open(filename).close() except Exception: _log.exception('cannot open file <%s>', filename) return None file_md5 = gmTools.file2md5(filename=filename, return_hex=True) existing_item = self.md5_exists(md5=file_md5, include_document_parts=False) if existing_item is not None: _log.debug('md5 match (%s): %s already in export area', file_md5, filename) return existing_item path, basename = os.path.split(filename) item = create_export_item( description=u'%s: %s (%s/)' % (gmTools.coalesce(hint, _(u'file'), u'%s'), basename, path), pk_identity=self.__pk_identity, filename=filename) if item.update_data_from_file(filename=filename): return item delete_export_item(pk_export_item=item['pk_export_item']) return None #-------------------------------------------------------- def add_files(self, filenames=None, hint=None): all_ok = True for fname in filenames: all_ok = all_ok and (self.add_file(filename=fname, hint=hint) is not None) return all_ok #-------------------------------------------------------- def add_documents(self, documents=None): for doc in documents: doc_tag = _(u'%s (%s)%s') % ( doc['l10n_type'], gmDateTime.pydt_strftime(doc['clin_when'], '%Y %b %d'), gmTools.coalesce(doc['comment'], u'', u' "%s"')) for obj in doc.parts: if self.document_part_item_exists(pk_part=obj['pk_obj']): continue f_ext = u'' if obj['filename'] is not None: f_ext = os.path.splitext( obj['filename'])[1].strip('.').strip() if f_ext != u'': f_ext = u' .' + f_ext.upper() obj_tag = _(u'part %s (%s%s)%s') % ( obj['seq_idx'], gmTools.size2str(obj['size']), f_ext, gmTools.coalesce(obj['obj_comment'], u'', u' "%s"')) create_export_item(description=u'%s - %s' % (doc_tag, obj_tag), pk_doc_obj=obj['pk_obj']) #-------------------------------------------------------- def document_part_item_exists(self, pk_part=None): cmd = u"SELECT EXISTS (SELECT 1 FROM clin.export_item WHERE fk_doc_obj = %(pk_obj)s)" args = {'pk_obj': pk_part} rows, idx = gmPG2.run_ro_queries(queries=[{ 'cmd': cmd, 'args': args }], get_col_idx=False) return rows[0][0] #-------------------------------------------------------- def md5_exists(self, md5=None, include_document_parts=False): where_parts = [u'pk_identity = %(pat)s', u'md5_sum = %(md5)s'] args = {'pat': self.__pk_identity, 'md5': md5} if not include_document_parts: where_parts.append(u'pk_doc_obj IS NULL') cmd = _SQL_get_export_items % u' AND '.join(where_parts) rows, idx = gmPG2.run_ro_queries(queries=[{ 'cmd': cmd, 'args': args }], get_col_idx=True) if len(rows) == 0: return None r = rows[0] return cExportItem(row={ 'data': r, 'idx': idx, 'pk_field': 'pk_export_item' }) #-------------------------------------------------------- def dump_items_to_disk(self, base_dir=None, items=None): if items is None: items = self.items if len(items) == 0: return None if base_dir is None: from Gnumed.business.gmPerson import cPatient pat = cPatient(aPK_obj=self.__pk_identity) base_dir = gmTools.mk_sandbox_dir(prefix=u'exp-%s-' % pat.dirname) _log.debug('dumping export items to: %s', base_dir) gmTools.mkdir(base_dir) for item in items: item.save_to_file(directory=base_dir) return base_dir #-------------------------------------------------------- def export(self, base_dir=None, items=None, expand_compressed=False): if items is None: items = self.items if len(items) == 0: return None media_base_dir = base_dir from Gnumed.business.gmPerson import cPatient pat = cPatient(aPK_obj=self.__pk_identity) if media_base_dir is None: media_base_dir = gmTools.mk_sandbox_dir(prefix=u'exp-%s-' % pat.dirname) _log.debug('patient media base dir: %s', media_base_dir) doc_dir = os.path.join(media_base_dir, r'documents') if os.path.isdir(doc_dir): index_existing_docs = True else: index_existing_docs = False gmTools.mkdir(doc_dir) _html_start_data = { u'html_title_header': _('Patient data for'), u'html_title_patient': gmTools.html_escape_string( pat.get_description_gender(with_nickname=False) + u', ' + _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')), u'title': _('Patient data export'), u'pat_name': gmTools.html_escape_string( pat.get_description_gender(with_nickname=False)), u'pat_dob': gmTools.html_escape_string( _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')), u'mugshot_url': u'documents/no-such-file.png', u'mugshot_alt': _('no patient photograph available'), u'mugshot_title': u'', u'docs_title': _(u'Documents'), u'browse_root': _(u'browse storage medium'), u'browse_docs': _(u'browse documents area'), u'browse_dicomdir': u'', u'run_dicom_viewer': u'' } mugshot = pat.document_folder.latest_mugshot if mugshot is not None: _html_start_data['mugshot_url'] = mugshot.save_to_file( directory=doc_dir, adjust_extension=True) _html_start_data['mugshot_alt'] = _( 'patient photograph from %s') % gmDateTime.pydt_strftime( mugshot['date_generated'], '%B %Y') _html_start_data['mugshot_title'] = gmDateTime.pydt_strftime( mugshot['date_generated'], '%B %Y') if u'DICOMDIR' in os.listdir(media_base_dir): _html_start_data[ u'browse_dicomdir'] = u'<li><a href="./DICOMDIR">%s</a></li>' % _( u'show DICOMDIR file') # copy DWV into target dir dwv_target_dir = os.path.join(media_base_dir, u'dwv') gmTools.rmdir(dwv_target_dir) dwv_src_dir = os.path.join(gmTools.gmPaths().local_base_dir, u'dwv4export') if not os.path.isdir(dwv_src_dir): dwv_src_dir = os.path.join( gmTools.gmPaths().system_app_data_dir, u'dwv4export') try: shutil.copytree(dwv_src_dir, dwv_target_dir) _html_start_data[ u'run_dicom_viewer'] = u'<li><a href="./dwv/viewers/mobile-local/index.html">%s</a></li>' % _( u'run Radiology Images (DICOM) Viewer') except shutil.Error, OSError: _log.exception('cannot include DWV, skipping') # index.html # - header idx_fname = os.path.join(media_base_dir, u'index.html') idx_file = io.open(idx_fname, mode=u'wt', encoding=u'utf8') idx_file.write(_html_start % _html_start_data) # - middle (side effect ! -> exports items into files ...) existing_docs = os.listdir( doc_dir ) # get them now, or else we will include the to-be-exported items # - export items for item in items: item_path = item.save_to_file(directory=doc_dir) item_fname = os.path.split(item_path)[1] idx_file.write( _html_list_item % (item_fname, gmTools.html_escape_string(item['description']))) # - preexisting documents for doc_fname in existing_docs: idx_file.write( _html_list_item % (doc_fname, gmTools.html_escape_string(_(u'other: %s') % doc_fname))) # - footer _cfg = gmCfg2.gmCfgData() from Gnumed.business.gmPraxis import gmCurrentPraxisBranch prax = gmCurrentPraxisBranch() lines = [] adr = prax.branch.org_unit.address if adr is not None: lines.extend(adr.format()) for comm in prax.branch.org_unit.comm_channels: if comm['is_confidential'] is True: continue lines.append(u'%s: %s' % (comm['l10n_comm_type'], comm['url'])) adr = u'' if len(lines) > 0: adr = gmTools.html_escape_string(u'\n'.join(lines), replace_eol=True, keep_visual_eol=True) _html_end_data = { 'branch': gmTools.html_escape_string(prax['branch']), 'praxis': gmTools.html_escape_string(prax['praxis']), 'date': gmTools.html_escape_string( gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), format='%Y %B %d', encoding=u'utf8')), 'gm_ver': gmTools.html_escape_string(_cfg.get(option=u'client_version')), #'gm_ver': 'git HEAD', # for testing 'adr': adr } idx_file.write(_html_end % _html_end_data) idx_file.close() # start.html (just a copy of index.html, really ;-) start_fname = os.path.join(media_base_dir, u'start.html') try: shutil.copy2(idx_fname, start_fname) except Exception: _log.exception('cannot copy %s to %s', idx_fname, start_fname) # autorun.inf # - compute label autorun_dict = {} name = pat.active_name last = name['lastnames'][:14] first = name['firstnames'][:min(14, 18 - len(last))] autorun_dict['label'] = ( (u'%s%s%s' % (u'%s,%s' % (last, first), gmTools.coalesce(pat['gender'], u'', u' (%s)'), pat.get_formatted_dob(format=' %Y%m%d', none_string=u'', honor_estimation=False))).strip() )[: 32] # max 32 chars, supposedly ASCII, but CP1252 likely works pretty well # - compute icon media_icon_kwd = u'$$gnumed_patient_media_export_icon' media_icon_kwd_exp = gmKeywordExpansion.get_expansion( keyword=media_icon_kwd, textual_only=False, binary_only=True) icon_tmp_file = media_icon_kwd_exp.save_to_file( target_mime=u'image/x-icon', target_extension=u'.ico', ignore_conversion_problems=True) autorun_dict['icon'] = u'' if icon_tmp_file is None: _log.debug(u'cannot retrieve <%s>', media_icon_kwd) else: media_icon_fname = os.path.join(media_base_dir, u'gnumed.ico') try: shutil.move(icon_tmp_file, media_icon_fname) autorun_dict['icon'] = u'icon=gnumed.ico' except Exception: _log.exception('cannot move %s to %s', icon_tmp_file, media_icon_fname) # - compute action autorun_dict['action'] = _('Browse patient data') # - create file autorun_fname = os.path.join(media_base_dir, u'autorun.inf') autorun_file = io.open(autorun_fname, mode='wt', encoding='cp1252', errors='replace') autorun_file.write(_autorun_inf % autorun_dict) autorun_file.close() # cd.inf cd_inf_fname = os.path.join(media_base_dir, u'cd.inf') cd_inf_file = io.open(cd_inf_fname, mode=u'wt', encoding=u'utf8') cd_inf_file.write( _cd_inf % (pat['lastnames'], pat['firstnames'], gmTools.coalesce(pat['gender'], u'?'), pat.get_formatted_dob('%Y-%m-%d'), gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), format='%Y-%m-%d', encoding=u'utf8'), pat.ID, _cfg.get(option=u'client_version'), u' / '.join([ u'%s = %s (%s)' % (g['tag'], g['label'], g['l10n_label']) for g in pat.gender_list ]))) cd_inf_file.close() # README readme_fname = os.path.join(media_base_dir, u'README') readme_file = io.open(readme_fname, mode=u'wt', encoding=u'utf8') readme_file.write( _README % (pat.get_description_gender(with_nickname=False) + u', ' + _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d'))) readme_file.close() # patient demographics as GDT/XML/VCF pat.export_as_gdt( filename=os.path.join(media_base_dir, u'patient.gdt')) pat.export_as_xml_linuxmednews( filename=os.path.join(media_base_dir, u'patient.xml')) pat.export_as_vcard( filename=os.path.join(media_base_dir, u'patient.vcf')) # praxis VCF shutil.move(prax.vcf, os.path.join(media_base_dir, u'praxis.vcf')) return media_base_dir
def fit_last_year(self): end = gmDateTime.pydt_now_here() g_end = GregorianDateTime(end.year, end.month, end.day, end.hour, end.minute, end.second).to_time() g_start = GregorianDateTime(end.year - 1, end.month, end.day, end.hour, end.minute, end.second).to_time() last_year = TimePeriod(g_start, g_end) self.Navigate(lambda tp: tp.update(last_year.start_time, last_year.end_time))
def _get_body_mass_index(self): try: return self.__cache['body_mass_index'] except KeyError: pass result = cClinicalResult(_('unknown BMI')) result.formula_name = 'BMI/Quetelet Index' result.formula_source = '08/2014: https://en.wikipedia.org/wiki/Body_mass_index' if self.__patient is None: result.message = _('BMI: no patient') return result result.variables['height'] = self.__patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_height, no_of_results = 1) if result.variables['height'] is None: result.message = _('BMI: height not found') return result if result.variables['height']['val_num'] is None: result.message = _('BMI: height not numeric') return result if result.variables['height']['val_unit'] == 'cm': result.variables['height_m'] = self.d(result.variables['height']['val_num'] / self.d(100)) elif result.variables['height']['val_unit'] == 'mm': result.variables['height_m'] = self.d(result.variables['height']['val_num'] / self.d(1000)) elif result.variables['height']['val_unit'] == 'm': result.variables['height_m'] = self.d(result.variables['height']['val_num']) else: result.message = _('BMI: height not in m, cm, or mm') return result result.variables['weight'] = self.__patient.emr.get_result_at_timestamp ( timestamp = result.variables['height']['clin_when'], loinc = gmLOINC.LOINC_weight, tolerance_interval = '10 days' ) if result.variables['weight'] is None: result.message = _('BMI: weight not found') return result if result.variables['weight']['val_num'] is None: result.message = _('BMI: weight not numeric') return result if result.variables['weight']['val_unit'] == 'kg': result.variables['weight_kg'] = self.d(result.variables['weight']['val_num']) elif result.variables['weight']['val_unit'] == 'g': result.variables['weight_kg'] = self.d(result.variables['weight']['val_num'] / self.d(1000)) else: result.message = _('BMI: weight not in kg or g') return result result.variables['dob'] = self.__patient['dob'] start = result.variables['dob'] end = result.variables['height']['clin_when'] multiplier = 1 if end < start: start = result.variables['height']['clin_when'] end = result.variables['dob'] multiplier = -1 result.variables['age@height'] = multiplier * self.d(gmDateTime.calculate_apparent_age(start, end)[0]) if (result.variables['age@height'] < 18): result.message = _('BMI (Quetelet): formula does not apply at age [%s] (0 < age < 18)') % result.variables['age@height'] return result # bmi = mass kg / height m2 result.numeric_value = result.variables['weight_kg'] / (result.variables['height_m'] * result.variables['height_m']) result.unit = 'kg/m²' result.message = _('BMI (Quetelet): %.2f %s') % ( result.numeric_value, result.unit ) result.date_valid = gmDateTime.pydt_now_here() self.__cache['body_mass_index'] = result _log.debug('%s' % result) return result
def __refresh_history(self, patient=None): emr = patient.emr sort_key_list = [] date_format4sorting = '%Y %m %d %H %M %S' now = gmDateTime.pydt_now_here() data = {} # undated entries # pregnancy edc = emr.EDC if edc is not None: sort_key = '99999 edc' if emr.EDC_is_fishy: label = _('EDC (!?!): %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d') tt = _( 'The Expected Date of Confinement is rather questionable.\n' '\n' 'Please check patient age, patient gender, time until/since EDC.' ) else: label = _('EDC: %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d') tt = '' sort_key_list.append(sort_key) data[sort_key] = [label, tt] # family history fhxs = emr.get_family_history() for fhx in fhxs: sort_key = '99998 %s::%s' % (fhx['l10n_relation'], fhx['pk_family_history']) sort_key_list.append(sort_key) #gmDateTime.pydt_strftime(fhx['when_known_to_patient'], format = '%Y %m %d %H %M %S') label = '%s%s: %s' % (fhx['l10n_relation'], gmTools.coalesce(fhx['age_noted'], '', ' (@ %s)'), fhx['condition']) data[sort_key] = [label, fhx] del fhxs # dated entries issues = [ i for i in emr.get_health_issues() if ((i['clinically_relevant'] is False) or (i['is_active'] is False)) ] for issue in issues: last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue']) linked_encounter = gmEMRStructItems.cEncounter(issue['pk_encounter']) when_candidates = [issue['modified_when'], linked_encounter['last_affirmed']] if last_encounter is not None: when_candidates.append(last_encounter['last_affirmed']) if (patient['dob'] is not None) and (issue['age_noted'] is not None): when_candidates.append(patient['dob'] + issue['age_noted']) if issue['is_active']: # sort active issues by time of most recent clinical access, which # means the most recent of: # issue.modified_when # last_encounter.last_affirmed # linked_encounter.last_affirmed # dob + age relevant_date = max(when_candidates) else: # sort IN-active issues by best guess of real clinical start # means either: # - dob + age # or the earliest of: # - issue.modified_when # - last_encounter.last_affirmed # - linked_encounter.last_affirmed if (patient['dob'] is not None) and (issue['age_noted'] is not None): relevant_date = patient['dob'] + issue['age_noted'] else: relevant_date = min(when_candidates) sort_key = '%s::%s' % (gmDateTime.pydt_strftime(relevant_date, format = date_format4sorting), issue['pk_health_issue']) relevant_date_str = gmDateTime.pydt_strftime(relevant_date, format = '%Y %b') if issue['age_noted'] is None: encounter = gmEMRStructItems.cEncounter(issue['pk_encounter']) age = _(' (entered %s ago)') % gmDateTime.format_interval_medically(now - encounter['started']) else: age = ' (@ %s)' % gmDateTime.format_interval_medically(issue['age_noted']) sort_key_list.append(sort_key) data[sort_key] = ['%s %s%s' % (relevant_date_str, issue['description'], age), issue] del issues stays = emr.get_hospital_stays() for stay in stays: sort_key = '%s::%s' % (gmDateTime.pydt_strftime(stay['admission'], format = date_format4sorting), stay['pk_hospital_stay']) label = '%s %s: %s (%s)' % ( gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b'), stay['hospital'], stay['episode'], _('%s ago') % gmDateTime.format_interval_medically(now - stay['admission']) ) sort_key_list.append(sort_key) data[sort_key] = [label, stay] del stays procs = emr.get_performed_procedures() for proc in procs: sort_key = '%s::%s' % (gmDateTime.pydt_strftime(proc['clin_when'], format = date_format4sorting), proc['pk_procedure']) label = '%s%s %s (%s @ %s)' % ( gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b'), gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, '', ''), proc['performed_procedure'], _('%s ago') % gmDateTime.format_interval_medically(now - proc['clin_when']), gmDateTime.format_interval_medically(proc['clin_when'] - patient['dob']) ) sort_key_list.append(sort_key) data[sort_key] = [label, proc] del procs vaccs = emr.get_latest_vaccinations() for ind, tmp in vaccs.items(): no_of_shots, vacc = tmp sort_key = '%s::%s::%s' % (gmDateTime.pydt_strftime(vacc['date_given'], format = date_format4sorting), vacc['pk_vaccination'], ind) label = _('%s Vacc: %s (latest of %s: %s ago)') % ( gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b'), ind, no_of_shots, gmDateTime.format_interval_medically(now - vacc['date_given']) ) sort_key_list.append(sort_key) data[sort_key] = [label, vacc] del vaccs for abuse in [ a for a in emr.abused_substances if a['harmful_use_type'] == 3 ]: sort_key = '%s::%s' % (gmDateTime.pydt_strftime(abuse['last_checked_when'], format = date_format4sorting), abuse['substance']) label = _('Hx of addiction: %s') % abuse['substance'] sort_key_list.append(sort_key) data[sort_key] = [label, abuse] sort_key_list.sort() sort_key_list.reverse() list_items = [] list_data = [] for key in sort_key_list: label, item = data[key] list_items.append(label) list_data.append(item) self._LCTRL_history.set_string_items(items = list_items) self._LCTRL_history.set_data(data = list_data)
def parse_vcard2dto(vc_text=None, filename=None): import vobject if vc_text is None: _log.info('trying to parse vCard from [%s]', filename) for encoding in ['utf8', 'Windows-1252']: try: vcf = io.open(filename, mode='rt', encoding=encoding) vc_text = vcf.read() vcf.close() break except UnicodeDecodeError: _log.exception('vCard not encoded as [%s]', encoding) if vc_text is None: return None vcf_lines = [] found_first = False for line in vc_text.split('\n'): if not found_first: if line.strip() == 'BEGIN:VCARD': found_first = True vcf_lines.append(line) continue vcf_lines.append(line) if line.strip() == 'END:VCARD': break vc_text = '\n'.join(vcf_lines) dob_format = '%Y%m%d' try: vc = vobject.readOne(vc_text) except vobject.base.ParseError: _log.exception('cannot parse, really a vcf ?') return None try: if vc.kind.value.strip() != 'individual': _log.warning('not a vCard for a single person (vCard.KIND=%s)', vc.kind.value) return None except AttributeError: _log.debug('vCard.KIND attribute not available') dto = gmPerson.cDTO_person() dto.firstnames = vc.n.value.given.strip() dto.lastnames = vc.n.value.family.strip() try: dto.title = vc.title.value.strip() except AttributeError: _log.debug('vCard.TITLE attribute not available') try: gender = vc.gender.value.strip().lower() if gender != '': dto.gender = gender except AttributeError: _log.debug('vCard.GENDER attribute not available') try: dob = pyDT.datetime.strptime(vc.bday.value.strip(), dob_format) dto.dob = dob.replace(tzinfo=gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False except AttributeError: _log.debug('vCard.BDAY attribute not available') dto.source = 'vCard %s' % vc.version.value.strip() adr = None try: adr = vc.adr.value except AttributeError: _log.debug('vCard.ADR attribute not available') if adr is not None: region_code = None region = adr.region.strip() if region == '': region = None # deduce country country_code = None country = adr.country.strip() if country == '': country = None if country is None: country_row = gmDemographicRecord.map_urb_zip_region2country( urb=adr.city, zip=adr.code, region=region) if country_row is not None: country = country_row['country'] country_code = country_row['code_country'] else: country_code = gmDemographicRecord.map_country2code( country=country) if None in [country, country_code]: _log.error('unknown vCard.ADR.country (%s), skipping address', adr.country) else: # deduce region if region is None: region_row = gmDemographicRecord.map_urb_zip_country2region( urb=adr.city, zip=adr.code, country_code=country_code) if region_row is not None: region = region_row['region'] region_code = region_row['code_region'] else: region_code = gmDemographicRecord.map_region2code( region=region, country_code=country_code) if region_code is None: _log.warning( 'unknown vCard.ADR.region (%s), using default region', adr.region) dto.remember_address(number='?', street=adr.street, urb=adr.city, region_code=region_code, zip=adr.code, country_code=country_code, adr_type='home', subunit=None) tel = None try: tel = vc.tel.value except AttributeError: _log.debug('vCard.TEL attribute not available') if tel is not None: if 'TYPE' in vc.tel.params: channel = (vc.tel.params['TYPE'][0]).lower() if not channel.endswith('phone'): channel += 'phone' else: channel = 'homephone' dto.remember_comm_channel(channel=channel, url=tel) email = None try: email = vc.email.value except AttributeError: _log.debug('vCard.EMAIL attribute not available') if email is not None: dto.remember_comm_channel(channel='email', url=email) url = None try: url = vc.url.value except AttributeError: _log.debug('vCard.URL attribute not available') if url is not None: dto.remember_comm_channel(channel='web', url=url) return dto
def _get_body_surface_area(self): try: return self.__cache['body_surface_area'] except KeyError: pass result = cClinicalResult(_('unknown body surface area')) result.formula_name = 'Du Bois Body Surface Area' result.formula_source = '12/2012: http://en.wikipedia.org/wiki/Body_surface_area' if self.__patient is None: result.message = _('Body Surface Area: no patient') return result result.variables['height'] = self.__patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_height, no_of_results = 1) if result.variables['height'] is None: result.message = _('Body Surface Area: height not found') return result if result.variables['height']['val_num'] is None: result.message = _('Body Surface Area: height not numeric') return result if result.variables['height']['val_unit'] == 'cm': result.variables['height_cm'] = self.d(result.variables['height']['val_num']) elif result.variables['height']['val_unit'] == 'mm': result.variables['height_cm'] = self.d(result.variables['height']['val_num'] / self.d(10)) elif result.variables['height']['val_unit'] == 'm': result.variables['height_cm'] = self.d(result.variables['height']['val_num'] * 100) else: result.message = _('Body Surface Area: height not in m, cm, or mm') return result result.variables['weight'] = self.__patient.emr.get_result_at_timestamp ( timestamp = result.variables['height']['clin_when'], loinc = gmLOINC.LOINC_weight, tolerance_interval = '10 days' ) if result.variables['weight'] is None: result.message = _('Body Surface Area: weight not found') return result if result.variables['weight']['val_num'] is None: result.message = _('Body Surface Area: weight not numeric') return result if result.variables['weight']['val_unit'] == 'kg': result.variables['weight_kg'] = self.d(result.variables['weight']['val_num']) elif result.variables['weight']['val_unit'] == 'g': result.variables['weight_kg'] = self.d(result.variables['weight']['val_num'] / self.d(1000)) else: result.message = _('Body Surface Area: weight not in kg or g') return result result.numeric_value = self.d('0.007184') * \ pow(result.variables['weight_kg'], self.d('0.425')) * \ pow(result.variables['height_cm'], self.d('0.725')) result.unit = 'm²' result.message = _('BSA (DuBois): %.2f %s') % ( result.numeric_value, result.unit ) result.date_valid = gmDateTime.pydt_now_here() self.__cache['body_surface_area'] = result _log.debug('%s' % result) return result
def parse_vcard2dto(vc_text=None, filename=None): import vobject if vc_text is None: _log.info('trying to parse vCard from [%s]', filename) for encoding in ['utf8', 'Windows-1252']: try: vcf = io.open(filename, mode = u'rt', encoding = encoding) vc_text = vcf.read() vcf.close() break except UnicodeDecodeError: _log.exception('vCard not encoded as [%s]', encoding) if vc_text is None: return None dob_format = '%Y%m%d' try: vc = vobject.readOne(vc_text) except vobject.base.ParseError: _log.exception('cannot parse, really a vcf ?') return None try: if vc.kind.value.strip() != u'individual': _log.warning('not a vCard for a single person (vCard.KIND=%s)', vc.kind.value) return None except AttributeError: _log.debug('vCard.KIND attribute not available') dto = gmPerson.cDTO_person() dto.firstnames = vc.n.value.given.strip() dto.lastnames = vc.n.value.family.strip() if vc.title: dto.title = vc.title.value.strip() try: gender = vc.gender.value.strip().lower() if gender != u'': dto.gender = gender except AttributeError: _log.debug('vCard.GENDER attribute not available') try: dob = pyDT.datetime.strptime(vc.bday.value.strip(), dob_format) dto.dob = dob.replace(tzinfo = gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False except AttributeError: _log.debug('vCard.BDAY attribute not available') dto.source = u'vCard %s' % vc.version.value.strip() adr = None try: adr = vc.adr.value except AttributeError: _log.debug('vCard.ADR attribute not available') if adr is not None: region_code = None region = adr.region.strip() if region == u'': region = None # deduce country country_code = None country = adr.country.strip() if country == u'': country = None if country is None: country_row = gmDemographicRecord.map_urb_zip_region2country(urb = adr.city, zip = adr.code, region = region) if country_row is not None: country = country_row['country'] country_code = country_row['code_country'] else: country_code = gmDemographicRecord.map_country2code(country = country) if None in [country, country_code]: _log.error('unknown vCard.ADR.country (%s), skipping address', adr.country) else: # deduce region if region is None: region_row = gmDemographicRecord.map_urb_zip_country2region(urb = adr.city, zip = adr.code, country_code = country_code) if region_row is not None: region = region_row['state'] region_code = region_row['code_state'] else: region_code = gmDemographicRecord.map_region2code(region = region, country_code = country_code) if region_code is None: _log.warning('unknown vCard.ADR.region (%s), using default region', adr.region) dto.remember_address ( number = u'?', street = adr.street, urb = adr.city, region_code = region_code, zip = adr.code, country_code = country_code, adr_type = 'home', subunit = None ) tel = None try: tel = vc.tel.value except AttributeError: _log.debug('vCard.TEL attribute not available') if tel is not None: if u'TYPE' in vc.tel.params: channel = (vc.tel.params[u'TYPE'][0]).lower() if not channel.endswith('phone'): channel += 'phone' else: channel = u'homephone' dto.remember_comm_channel(channel = channel, url = tel) email = None try: email = vc.email.value except AttributeError: _log.debug('vCard.EMAIL attribute not available') if email is not None: dto.remember_comm_channel(channel = 'email', url = email) url = None try: url = vc.url.value except AttributeError: _log.debug('vCard.URL attribute not available') if url is not None: dto.remember_comm_channel(channel = 'web', url = url) return dto
def _get_body_mass_index(self): try: return self.__cache['body_mass_index'] except KeyError: pass result = cClinicalResult(_('unknown BMI')) result.formula_name = u'BMI/Quetelet Index' result.formula_source = u'08/2014: https://en.wikipedia.org/wiki/Body_mass_index' if self.__patient is None: result.message = _('BMI: no patient') return result result.variables[ 'height'] = self.__patient.emr.get_most_recent_results( loinc=gmLOINC.LOINC_height, no_of_results=1) if result.variables['height'] is None: result.message = _('BMI: height not found') return result if result.variables['height']['val_num'] is None: result.message = _('BMI: height not numeric') return result if result.variables['height']['val_unit'] == u'cm': result.variables['height_m'] = self.d( result.variables['height']['val_num'] / self.d(100)) elif result.variables['height']['val_unit'] == u'mm': result.variables['height_m'] = self.d( result.variables['height']['val_num'] / self.d(1000)) elif result.variables['height']['val_unit'] == u'm': result.variables['height_m'] = self.d( result.variables['height']['val_num']) else: result.message = _('BMI: height not in m, cm, or mm') return result result.variables[ 'weight'] = self.__patient.emr.get_result_at_timestamp( timestamp=result.variables['height']['clin_when'], loinc=gmLOINC.LOINC_weight, tolerance_interval='10 days') if result.variables['weight'] is None: result.message = _('BMI: weight not found') return result if result.variables['weight']['val_num'] is None: result.message = _('BMI: weight not numeric') return result if result.variables['weight']['val_unit'] == u'kg': result.variables['weight_kg'] = self.d( result.variables['weight']['val_num']) elif result.variables['weight']['val_unit'] == u'g': result.variables['weight_kg'] = self.d( result.variables['weight']['val_num'] / self.d(1000)) else: result.message = _('BMI: weight not in kg or g') return result result.variables['dob'] = self.__patient['dob'] start = result.variables['dob'] end = result.variables['height']['clin_when'] multiplier = 1 if end < start: start = result.variables['height']['clin_when'] end = result.variables['dob'] multiplier = -1 result.variables['age@height'] = multiplier * self.d( gmDateTime.calculate_apparent_age(start, end)[0]) if (result.variables['age@height'] < 18): result.message = _( 'BMI (Quetelet): formula does not apply at age [%s] (0 < age < 18)' ) % result.variables['age@height'] return result # bmi = mass kg / height m2 result.numeric_value = result.variables['weight_kg'] / ( result.variables['height_m'] * result.variables['height_m']) result.unit = u'kg/m²' result.message = _('BMI (Quetelet): %.2f %s') % (result.numeric_value, result.unit) result.date_valid = gmDateTime.pydt_now_here() self.__cache['body_mass_index'] = result _log.debug(u'%s' % result) return result
def print_generic_document(parent=None, jobtype:str=None, episode=None): """Call LibreOffice Writer with a generated (fake) ODT file. Once Writer is closed, the ODT is imported if different from its initial content. Note: The file passed to LO _must_ reside in a directory the parents of which are all R-X by the user or else LO will throw up its arms in despair and fall back to the user's home dir upon saving. So, /tmp/user/$UID/some-file.odt will *not* work (because .../user/... is "drwx--x--x root.root") while /home/$USER/some/dir/some-file.odt does. """ sandbox = os.path.join(gmTools.gmPaths().user_tmp_dir, 'libreoffice') gmTools.mkdir(sandbox) fpath = gmTools.get_unique_filename(suffix = '.txt', tmp_dir = sandbox) doc_file = open(fpath, mode = 'wt') doc_file.write(__ODT_FILE_PREAMBLE) doc_file.write(_('Today: %s') % gmDateTime.pydt_now_here().strftime('%c')) doc_file.write('\n\n') prax = gmPraxis.gmCurrentPraxisBranch() doc_file.write('Praxis:\n') doc_file.write(prax.format()) doc_file.write('\n') doc_file.write('Praxis branch:\n') doc_file.write('\n'.join(prax.org_unit.format ( with_address = True, with_org = True, with_comms = True ))) doc_file.write('\n\n') pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(12)) if pat.connected: doc_file.write('Patient:\n') doc_file.write(pat.get_description_gender()) doc_file.write('\n\n') for adr in pat.get_addresses(): doc_file.write(adr.format(single_line = False, verbose = True, show_type = True)) doc_file.write('\n\n') for chan in pat.get_comm_channels(): doc_file.werite(chan.format()) doc_file.write('\n\n') doc_file.write('Provider:\n') doc_file.write('\n'.join(gmStaff.gmCurrentProvider().get_staff().format())) doc_file.write('\n\n-----------------------------------------------------------------------------\n\n') doc_file.close() # convert txt -> odt success, ret_code, stdout = gmShellAPI.run_process ( cmd_line = [ 'lowriter', '--convert-to', 'odt', '--outdir', os.path.split(fpath)[0], fpath ], verbose = True ) if success: fpath = gmTools.fname_stem_with_path(fpath) + '.odt' else: _log.warning('cannot convert .txt to .odt') md5_before = gmTools.file2md5(fpath) gmShellAPI.run_process(cmd_line = ['lowriter', fpath], verbose = True) md5_after = gmTools.file2md5(fpath) if md5_before == md5_after: gmDispatcher.send(signal = 'statustext', msg = _('Document not modified. Discarding.'), beep = False) return if not pat.connected: shutil.move(fpath, gmTools.gmPaths().user_work_dir) gmDispatcher.send(signal = 'statustext', msg = _('No patient. Moved file into %s') % gmTools.gmPaths().user_work_dir, beep = False) return pat.export_area.add_file ( filename = fpath, hint = _('Generic letter, written at %s') % gmDateTime.pydt_now_here().strftime('%Y %b %d %H:%M') ) gmDispatcher.send(signal = 'display_widget', name = 'gmExportAreaPlugin')
def refresh(lctrl): items = [] data = [] if latest_only: latest_vaccs = emr.get_latest_vaccinations() for indication in sorted(latest_vaccs.keys()): no_of_shots4ind, latest_vacc4ind = latest_vaccs[indication] items.append ([ indication, _('%s (latest of %s: %s ago)') % ( gmDateTime.pydt_strftime(latest_vacc4ind['date_given'], format = '%Y %b'), no_of_shots4ind, gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given']) ), latest_vacc4ind['vaccine'], latest_vacc4ind['batch_no'], gmTools.coalesce(latest_vacc4ind['site'], ''), gmTools.coalesce(latest_vacc4ind['reaction'], ''), gmTools.coalesce(latest_vacc4ind['comment'], '') ]) data.append(latest_vacc4ind) else: shots = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination') if expand_indications: shots_by_ind = {} for shot in shots: for ind in shot['indications']: try: shots_by_ind[ind['l10n_indication']].append(shot) except KeyError: shots_by_ind[ind['l10n_indication']] = [shot] for ind in sorted(shots_by_ind.keys()): idx = len(shots_by_ind[ind]) for shot in shots_by_ind[ind]: items.append ([ '%s (#%s)' % (ind, idx), _('%s (%s ago)') % ( gmDateTime.pydt_strftime(shot['date_given'], '%Y %b %d'), gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - shot['date_given']) ), shot['vaccine'], shot['batch_no'], gmTools.coalesce(shot['site'], ''), gmTools.coalesce(shot['reaction'], ''), gmTools.coalesce(shot['comment'], '') ]) idx -= 1 data.append(shot) else: items = [ [ gmDateTime.pydt_strftime(s['date_given'], '%Y %b %d'), s['vaccine'], ', '.join([ i['l10n_indication'] for i in s['indications'] ]), s['batch_no'], gmTools.coalesce(s['site'], ''), gmTools.coalesce(s['reaction'], ''), gmTools.coalesce(s['comment'], '') ] for s in shots ] data = shots lctrl.set_string_items(items) lctrl.set_data(data)
def _get_body_surface_area(self): try: return self.__cache['body_surface_area'] except KeyError: pass result = cClinicalResult(_('unknown body surface area')) result.formula_name = u'Du Bois Body Surface Area' result.formula_source = u'12/2012: http://en.wikipedia.org/wiki/Body_surface_area' if self.__patient is None: result.message = _('Body Surface Area: no patient') return result result.variables[ 'height'] = self.__patient.emr.get_most_recent_results( loinc=gmLOINC.LOINC_height, no_of_results=1) if result.variables['height'] is None: result.message = _('Body Surface Area: height not found') return result if result.variables['height']['val_num'] is None: result.message = _('Body Surface Area: height not numeric') return result if result.variables['height']['val_unit'] == u'cm': result.variables['height_cm'] = self.d( result.variables['height']['val_num']) elif result.variables['height']['val_unit'] == u'mm': result.variables['height_cm'] = self.d( result.variables['height']['val_num'] / self.d(10)) elif result.variables['height']['val_unit'] == u'm': result.variables['height_cm'] = self.d( result.variables['height']['val_num'] * 100) else: result.message = _('Body Surface Area: height not in m, cm, or mm') return result result.variables[ 'weight'] = self.__patient.emr.get_result_at_timestamp( timestamp=result.variables['height']['clin_when'], loinc=gmLOINC.LOINC_weight, tolerance_interval='10 days') if result.variables['weight'] is None: result.message = _('Body Surface Area: weight not found') return result if result.variables['weight']['val_num'] is None: result.message = _('Body Surface Area: weight not numeric') return result if result.variables['weight']['val_unit'] == u'kg': result.variables['weight_kg'] = self.d( result.variables['weight']['val_num']) elif result.variables['weight']['val_unit'] == u'g': result.variables['weight_kg'] = self.d( result.variables['weight']['val_num'] / self.d(1000)) else: result.message = _('Body Surface Area: weight not in kg or g') return result result.numeric_value = self.d('0.007184') * \ pow(result.variables['weight_kg'], self.d('0.425')) * \ pow(result.variables['height_cm'], self.d('0.725')) result.unit = u'm²' result.message = _('BSA (DuBois): %.2f %s') % (result.numeric_value, result.unit) result.date_valid = gmDateTime.pydt_now_here() self.__cache['body_surface_area'] = result _log.debug(u'%s' % result) return result
def create_timeline_file(patient=None, filename=None): emr = patient.emr global now now = gmDateTime.pydt_now_here() if filename is None: timeline_fname = gmTools.get_unique_filename(prefix = u'gm-', suffix = u'.timeline') else: timeline_fname = filename _log.debug('exporting EMR as timeline into [%s]', timeline_fname) timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace') timeline.write(xml_start % ( _('Health issues'), _('Episodes'), _('Encounters'), _('Hospital stays'), _('Procedures'), _('Documents'), _('Vaccinations'), _('Substances'), _('Life events') )) # birth if patient['dob'] is None: start = now.replace(year = now.year - 100) timeline.write(__xml_encounter_template % ( format_pydt(start), format_pydt(start), _('Birth') + u': ?', _('Life events'), _('Date of birth unknown') )) else: start = patient['dob'] timeline.write(__xml_encounter_template % ( format_pydt(patient['dob']), format_pydt(patient['dob']), _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], u' (%s)' % gmTools.u_almost_equal_to, u''), _('Life events'), u'' )) # start of care timeline.write(__xml_encounter_template % ( format_pydt(emr.earliest_care_date), format_pydt(emr.earliest_care_date), _('Start of Care'), _('Life events'), _('The earliest recorded event of care in this praxis.') )) timeline.write(u'\n<!--\n========================================\n Health issues\n======================================== -->') for issue in emr.health_issues: timeline.write(__format_health_issue_as_timeline_xml(issue, patient, emr)) timeline.write(u'\n<!--\n========================================\n Episodes\n======================================== -->') for epi in emr.get_episodes(order_by = u'pk_health_issue'): timeline.write(__format_episode_as_timeline_xml(epi, patient)) timeline.write(u'\n<!--\n========================================\n Encounters\n======================================== -->') for enc in emr.get_encounters(skip_empty = True): timeline.write(__format_encounter_as_timeline_xml(enc, patient)) timeline.write(u'\n<!--\n========================================\n Hospital stays\n======================================== -->') for stay in emr.hospital_stays: timeline.write(__format_hospital_stay_as_timeline_xml(stay)) timeline.write(u'\n<!--\n========================================\n Procedures\n======================================== -->') for proc in emr.performed_procedures: timeline.write(__format_procedure_as_timeline_xml(proc)) timeline.write(u'\n<!--\n========================================\n Vaccinations\n======================================== -->') for vacc in emr.vaccinations: timeline.write(__format_vaccination_as_timeline_xml(vacc)) timeline.write(u'\n<!--\n========================================\n Substance intakes\n======================================== -->') for intake in emr.get_current_substance_intakes(include_inactive = True, include_unapproved = False): timeline.write(__format_intake_as_timeline_xml(intake)) timeline.write(u'\n<!--\n========================================\n Documents\n======================================== -->') for doc in patient.document_folder.documents: timeline.write(__format_document_as_timeline_xml(doc)) # allergies ? # test results ? # death if patient['deceased'] is None: end = now else: end = patient['deceased'] timeline.write(__xml_encounter_template % ( format_pydt(end), format_pydt(end), #u'', _('Death'), _('Life events'), u'' )) # display range if end.month == 2: if end.day == 29: # leap years aren't consecutive end = end.replace(day = 28) target_year = end.year + 1 end = end.replace(year = target_year) timeline.write(xml_end % ( format_pydt(start), format_pydt(end) )) timeline.close() return timeline_fname
def generate_invoice_id(template=None, pk_patient=None, person=None, date_format='%Y-%m-%d', time_format='%H%M%S'): """Generate invoice ID string, based on template. No template given -> generate old style fixed format invoice ID. Placeholders: %(pk_pat)s %(date)s %(time)s if included, $counter$ is not *needed* (but still possible) %(firstname)s %(lastname)s %(dob)s #counter# will be replaced by a counter, counting up from 1 until the invoice id is unique, max 999999 """ assert (None in [ pk_patient, person ]), u'either of <pk_patient> or <person> can be defined, but not both' if (template is None) or (template.strip() == u''): template = DEFAULT_INVOICE_ID_TEMPLATE date_format = '%Y-%m-%d' time_format = '%H%M%S' template = template.strip() _log.debug('invoice ID template: %s', template) if pk_patient is None: if person is not None: pk_patient = person.ID now = gmDateTime.pydt_now_here() data = {} data['pk_pat'] = gmTools.coalesce(pk_patient, '?') data['date'] = gmDateTime.pydt_strftime(now, date_format).strip() data['time'] = gmDateTime.pydt_strftime(now, time_format).strip() if person is None: data['firstname'] = u'?' data['lastname'] = u'?' data['dob'] = u'?' else: data['firstname'] = person['firstnames'].replace( ' ', gmTools.u_space_as_open_box).strip() data['lastname'] = person['lastnames'].replace( ' ', gmTools.u_space_as_open_box).strip() data['dob'] = person.get_formatted_dob(format=date_format, none_string=u'?', honor_estimation=False).strip() candidate_invoice_id = template % data if u'#counter#' not in candidate_invoice_id: if u'%(time)s' in template: return candidate_invoice_id candidate_invoice_id = candidate_invoice_id + u' [##counter#]' _log.debug('invoice id candidate: %s', candidate_invoice_id) # get existing invoice IDs consistent with candidate search_term = u'^\s*%s\s*$' % gmPG2.sanitize_pg_regex( expression=candidate_invoice_id).replace(u'#counter#', '\d+') cmd = u'SELECT invoice_id FROM bill.bill WHERE invoice_id ~* %(search_term)s UNION ALL SELECT invoice_id FROM audit.log_bill WHERE invoice_id ~* %(search_term)s' args = {'search_term': search_term} rows, idx = gmPG2.run_ro_queries(queries=[{'cmd': cmd, 'args': args}]) if len(rows) == 0: return candidate_invoice_id.replace(u'#counter#', u'1') existing_invoice_ids = [r['invoice_id'].strip() for r in rows] counter = None counter_max = 999999 for idx in range(1, counter_max): candidate = candidate_invoice_id.replace(u'#counter#', '%s' % idx) if candidate not in existing_invoice_ids: counter = idx break if counter is None: # exhausted the range, unlikely (1 million bills are possible # even w/o any other invoice ID data) but technically possible _log.debug( 'exhausted uniqueness space of [%s] invoice IDs per template', counter_max) counter = '>%s[%s]' % (counter_max, data['time']) return candidate_invoice_id.replace(u'#counter#', '%s' % counter)
def create_fake_timeline_file(patient=None, filename=None): """Used to create an 'empty' timeline file for display. - needed because .clear_timeline() doesn't really work """ emr = patient.emr global now now = gmDateTime.pydt_now_here() if filename is None: timeline_fname = gmTools.get_unique_filename(prefix = u'gm-', suffix = u'.timeline') else: timeline_fname = filename _log.debug('creating dummy timeline in [%s]', timeline_fname) timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace') timeline.write(__fake_timeline_start) # birth if patient['dob'] is None: start = now.replace(year = now.year - 100) timeline.write(__xml_encounter_template % ( format_pydt(start), format_pydt(start), _('Birth') + u': ?', _('Life events'), _('Date of birth unknown') )) else: start = patient['dob'] timeline.write(__xml_encounter_template % ( format_pydt(patient['dob']), format_pydt(patient['dob']), _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], u' (%s)' % gmTools.u_almost_equal_to, u''), _('Life events'), u'' )) # death if patient['deceased'] is None: end = now else: end = patient['deceased'] timeline.write(__xml_encounter_template % ( format_pydt(end), format_pydt(end), #u'', _('Death'), _('Life events'), u'' )) # fake issue timeline.write(__fake_timeline_body_template % ( format_pydt(start), format_pydt(end), _('Cannot display timeline.'), _('Cannot display timeline.') )) # display range if end.month == 2: if end.day == 29: # leap years aren't consecutive end = end.replace(day = 28) target_year = end.year + 1 end = end.replace(year = target_year) timeline.write(xml_end % ( format_pydt(start), format_pydt(end) )) timeline.close() return timeline_fname