Skip to content
Commits on Source (13)
......@@ -6,6 +6,29 @@
# rel-1-7-patches
------------------------------------------------
1.7.4
NEW: placeholders now nest with $2<>2$ $3<>3$ rather than $<<>>$ $<<<>>>$
NEW: placeholder $<patient_mcf>$
NEW: placeholder $<praxis_mcf>$
NEW: placehodler $<qrcode>$
NEW: placeholder $<if_debugging>$
NEW: LaTeX letter template example
NEW: Begleitbrief mit Diagnosen (LaTeX)
FIX: map None to '' in address parts placeholder
FIX: export area export-to-media
FIX: $<vaccination_history::%(l10n_indications)s::>$ field
FIX: vaccine creation
FIX: error in closing expired episodes
FIX: date formatting in document tree
IMPROVED: AppStream and desktop metadata
IMPROVED: add "preset" option to $<free_text>$ placeholder
IMPROVED: include MCF in export area metadata
IMPROVED: Begleitbrief template
1.7.3
FIX: failure to merge patients under some conditions [thanks Marc]
......@@ -1960,6 +1983,11 @@ FIX: missing cast to ::text in dem.date_trunc_utc() calls
# gnumed_v22
------------------------------------------------
22.4
FIX: LaTeX-Template for Begleitbrief
FIX: 2nd/3rd level placeholders in LaTeX templates
22.2
FIX: staff/v_staff plausibility check [thanks Marc]
......
......@@ -3,11 +3,11 @@
<!-- Thanks to Carsten Grohmann <mail@carstengrohmann.de> -->
<!-- Thanks to Richard Hughes (https://people.freedesktop.org/~hughsient/appdata/) -->
<component type="desktop">
<id>gnumed-client.desktop</id>
<id>gnumed_client.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+ and GFDL-1.3</project_license>
<name>GNUmed EMR</name>
<summary>An Electronic Medical Record for community care.</summary>
<summary>An Electronic Medical Record for community care</summary>
<description>
<p>
GNUmed enables doctors to keep a medically sound record
......@@ -32,11 +32,10 @@
<caption>http://wiki.gnumed.de</caption>
</screenshot>
<!--<screenshot type="default">
<image>http://www.hughsie.com/en_US/main.png</image>
<image>https://screenshots.debian.net/screenshots/000/002/117/large.png</image>
<caption>The main window showing the application in action</caption>
</screenshot>-->
</screenshots>
<url type="homepage">http://www.gnumed.de</url>
<updatecontact>gnumed-devel_at_gnu.org</updatecontact>
<!--<project_group>GNOME</project_group>-->
<update_contact>gnumed-devel_AT_gnu.org</update_contact>
</component>
# -*- coding: utf-8 -*-
"""Manage German AMTS BMP data."""
#============================================================
__author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
__license__ = "GPL v2"
# std lib
import sys
import os
import logging
import decimal
import datetime as pyDT
#from xml.etree import ElementTree as py_etree
import typing
# 3rd party
#lxml_etree = None
import lxml.etree as lxml_etree
# GNUmed
if __name__ == '__main__':
sys.path.insert(0, '../../')
from Gnumed.pycommon import gmLog2
from Gnumed.pycommon import gmI18N
gmI18N.activate_locale()
gmI18N.install_domain()
from Gnumed.pycommon import gmTools
from Gnumed.pycommon import gmDateTime
from Gnumed.business import gmPerson
AMTS_BMP_ENCODING = 'latin1'
AMTS_BMP_DOB_FORMAT = '%Y%m%d'
AMTS_BMP_DOB_FORMAT_NO_DAY = '%Y%m'
AMTS_BMP_DOB_FORMAT_YEAR_ONLY = '%Y'
AMTS2GMD_GENDER_MAP = {
'W': 'f',
'M': 'm',
'X': None
}
# the attribute
#
# <xs:attribute name="p" use="prohibited">
# <xs:annotation>
# <xs:documentation>Name: Patchnummer
#
# Beschreibung: Patchnummer des zugrunde liegenden BMP (zusätzlich zum Attribut Version)
# </xs:documentation>
# </xs:annotation>
# </xs:attribute>
#
# needs to be removed from the XSDs or else lxml will
# choke on the <use="prohibited"> part
AMTS_BMP_XSDs = {
'2.5': 'bmp_V2.5.xsd',
'2.4.1': 'bmp_V2.4.1.xsd',
'2.3': 'bmp023.xsd'
}
_log = logging.getLogger('AMTS_BMP')
#============================================================
class cDTO_AmtsBmp(gmPerson.cDTO_person):
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
#--------------------------------------------------------
# internal helpers
#--------------------------------------------------------
def _get_unambiguous_identity(self) -> typing.Union[None, gmPerson.cPerson]:
egk_tag = 'eGK'
egk_issuer = 'Krankenkasse'
egk_id = None
for ext_id in self.external_ids:
if ext_id['name'] == egk_tag and ext_id['issuer'] == egk_issuer:
egk_id = ext_id['value']
break
if egk_id is None:
_log.debug('cannot search for patient by eGK ID')
return None
candidates = gmPerson.get_persons_by_external_id (
external_id = egk_id,
external_id_type = egk_tag,
issuer = egk_issuer
)
if len(candidates) != 1:
_log.debug('cannot uniquely identify person by eGK ID [%s]', egk_id)
return None
return candidates[0]
#============================================================
class cAmtsBmpFile:
# take up to three filenames (or more, in non-strict mode)
def __init__(self, filename) -> None:
self.__filename:str = filename
self.__xml_schema:lxml_etree.XMLSchema = None
self.xml_doc = None
self.bmp_version = None
#--------------------------------------------------------
# version=..., strict=True/False
def valid(self):
for bmp_version in AMTS_BMP_XSDs:
xsd_filename = os.path.join(gmTools.gmPaths().system_app_data_dir, 'resources', 'amts', AMTS_BMP_XSDs[bmp_version])
try:
self.__xml_schema = lxml_etree.XMLSchema(file = xsd_filename)
except lxml_etree.XMLSchemaParseError:
_log.exception('cannot find [%s], trying local base dir', xsd_filename)
# retry, maybe in dev tree
xsd_filename = os.path.join(gmTools.gmPaths().local_base_dir, 'resources', 'amts', AMTS_BMP_XSDs[bmp_version])
self.__xml_schema = lxml_etree.XMLSchema(file = xsd_filename)
with open(self.__filename, encoding = 'iso-8859-1') as bmp_file:
try:
self.xml_doc = lxml_etree.parse(bmp_file)
except lxml_etree.XMLSyntaxError:
_log.exception('[%s] does not parse as XML', self.__filename)
break
validated = self.__xml_schema.validate(self.xml_doc)
if validated:
self.bmp_version = bmp_version
_log.debug('[%s] validates as AMTS BMP v%s', self.__filename, self.bmp_version)
# check for second/third file !
return True
_log.debug('[%s] does not validate against [%s]', self.__filename, xsd_filename)
_log.debug('[%s] does not validate as AMTS BMP', self.__filename)
return False
#--------------------------------------------------------
def format(self, eol:str=None, verbose:bool=False) -> typing.Union[str, typing.List[str]]:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
lines = []
lines.append(_('AMTS BMP file: %s') % self.__filename)
lines.append(_('Created: %s') % self.created_when)
lines.append(_('Version: %s') % self.bmp_version)
lines.append(_('UID: %s') % self.uid)
lines.append(_('Creator:'))
lines.append(' ' + str(self.originator))
lines.append(_('Patient:'))
lines.append(' ' + str(self.patient_as_dto))
lines.append(_('Clinical data:'))
lines.append(' ' + str(self.clinical_data))
if verbose:
lines.append(_('Raw data:'))
lines.extend(lxml_etree.tostring(self.xml_doc, encoding = 'unicode', pretty_print = True, method = 'xml').split('\n'))
if eol is None:
return lines
return eol.join(lines)
#--------------------------------------------------------
# properties
#--------------------------------------------------------
def _patient_as_dto(self) -> cDTO_AmtsBmp:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
pat = self.xml_doc.find('P').attrib
dto = cDTO_AmtsBmp()
# lastname parts
dto.lastnames = pat['f']
try:
dto.lastnames = u'%s %s' % (pat['v'], dto.lastnames)
except KeyError:
pass
try:
dto.lastnames = u'%s %s' % (pat['z'], dto.lastnames)
except KeyError:
pass
# firstnames
dto.firstnames = pat['g']
# title
try:
dto.title = pat['t']
except KeyError:
pass
# gender
try:
dto.gender = pat['s']
except KeyError:
pass
# DOB
dob_str = pat['b']
while dob_str.endswith('00'):
dto.dob_is_estimated = True
dob_str = dob_str[:-2]
dto.dob = dob_str
source = 'AMTS BMP v%s [uid=%s]' % (self.bmp_version, self.uid)
# eGK
try:
dto.remember_external_id(name = 'eGK', value = pat['egk'], issuer = 'Krankenkasse', comment = source)
except KeyError:
pass
dto.source = source
return dto
patient_as_dto = property(_patient_as_dto)
#--------------------------------------------------------
def _get_clinical_data(self) -> dict:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
O_tag = self.xml_doc.find('O')
if O_tag is None:
return {}
clinical_data = {'valid_when': self.created_when}
O = O_tag.attrib
try:
clinical_data['allergies'] = O['ai'].split(',')
except KeyError:
pass
try:
clinical_data['pregnant'] = (O['p'] == '1')
except KeyError:
pass
try:
clinical_data['breast_feeding'] = (O['b'] == '1')
except KeyError:
pass
try:
clinical_data['weight'] = (decimal.Decimal(O['w']), 'kg')
except KeyError:
pass
try:
clinical_data['height'] = (int(O['h']), 'cm')
except KeyError:
pass
try:
clinical_data['creatinine'] = (decimal.Decimal(O['c']), 'mg/dl')
except KeyError:
pass
try:
clinical_data['comment'] = O['x'].strip()
except KeyError:
pass
return clinical_data
clinical_data = property(_get_clinical_data)
#--------------------------------------------------------
def _get_created_when(self) -> pyDT.datetime:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
return pyDT.datetime.strptime (
self.xml_doc.find('A').attrib['t'],
'%Y-%m-%dT%H:%M:%S'
)
created_when = property(_get_created_when)
#--------------------------------------------------------
def _get_uid(self) -> str:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
return self.xml_doc.getroot().attrib['U']
uid = property(_get_uid)
#--------------------------------------------------------
#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)
def _get_originator(self) -> dict:
assert (self.xml_doc is not None), 'self.xml_doc is None, forgot to call valid() before'
A_tag = self.xml_doc.find('A')
if A_tag is None:
return {}
originator = {}
A = A_tag.attrib
try:
originator['id'] = (A['lanr'].strip(), 'lanr')
except KeyError:
pass
try:
originator['id'] = (A['idf'].strip(), 'idf')
except KeyError:
pass
try:
originator['id'] = (A['kik'].strip(), 'kik')
except KeyError:
pass
try:
originator['name'] = A['n'].strip()
except KeyError:
pass
try:
originator['street'] = A['s'].strip()
except KeyError:
pass
try:
originator['zip'] = A['z'].strip()
except KeyError:
pass
try:
originator['city'] = A['c'].strip()
except KeyError:
pass
try:
originator['phone'] = A['p'].strip()
except KeyError:
pass
try:
originator['email'] = A['e'].strip()
except KeyError:
pass
return originator
originator = property(_get_originator)
#============================================================
#============================================================
#============================================================
#============================================================
# main/testing
#============================================================
if __name__ == '__main__':
if len(sys.argv) == 1:
sys.exit()
if sys.argv[1] != 'test':
sys.exit()
print('log file:', gmLog2._logfile_name)
gmDateTime.init()
#-------------------------------------------------------
def test_validate_files():
for fname in sys.argv[2:]:
print(fname)
bmp = cAmtsBmpFile(fname)
if not bmp.valid():
print('-> failed')
continue
print('-> conformant with version', bmp.bmp_version)
#-------------------------------------------------------
def test():
print(sys.argv[2])
print()
bmp = cAmtsBmpFile(sys.argv[2])
if not bmp.valid():
print('failed')
return
print('validated as version', bmp.bmp_version)
print(lxml_etree.tostring(bmp.xml_doc, encoding = 'unicode', pretty_print = True, method = 'xml'))
dto = bmp.patient_as_dto
print(dto)
print(dto.external_ids)
print(bmp.created_when)
print('clinical data:', bmp.clinical_data)
print('originator:', bmp.originator)
# print('exists:', dto.exists)
# cands = dto.get_candidate_identities(can_create=False)
# print('candidates:')
# for cand in cands:
# print(cand)
#-------------------------------------------------------
test_validate_files()
#test()
......@@ -174,11 +174,11 @@ def delete_billable(pk_billable=None):
#============================================================
# bill items
#------------------------------------------------------------
_SQL_fetch_bill_item_fields = u"SELECT * FROM bill.v_bill_items WHERE %s"
_SQL_get_bill_item_fields = u"SELECT * FROM bill.v_bill_items WHERE %s"
class cBillItem(gmBusinessDBObject.cBusinessDBObject):
_cmd_fetch_payload = _SQL_fetch_bill_item_fields % u"pk_bill_item = %s"
_cmd_fetch_payload = _SQL_get_bill_item_fields % u"pk_bill_item = %s"
_cmds_store_payload = [
u"""UPDATE bill.bill_item SET
fk_provider = %(pk_provider)s,
......@@ -287,9 +287,9 @@ class cBillItem(gmBusinessDBObject.cBusinessDBObject):
#------------------------------------------------------------
def get_bill_items(pk_patient=None, non_invoiced_only=False):
if non_invoiced_only:
cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s AND pk_bill IS NULL"
cmd = _SQL_get_bill_item_fields % u"pk_patient = %(pat)s AND pk_bill IS NULL"
else:
cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s"
cmd = _SQL_get_bill_item_fields % u"pk_patient = %(pat)s"
args = {'pat': pk_patient}
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
return [ cBillItem(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill_item'}) for r in rows ]
......
......@@ -1962,7 +1962,7 @@ WHERE
args['encs'] = tuple(encounters)
cmd = u'%s %s' % (
gmVaccination.sql_fetch_vaccination % u'\nAND '.join(where_parts),
gmVaccination._SQL_get_vaccination_fields % u'\nAND '.join(where_parts),
order_by
)
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
......
......@@ -166,7 +166,7 @@ class cDocumentFolder:
#--------------------------------------------------------
def get_unsigned_documents(self):
args = {'pat': self.pk_patient}
cmd = _sql_fetch_document_fields % u"""
cmd = _SQL_get_document_fields % u"""
pk_doc IN (
SELECT DISTINCT ON (b_vo.pk_doc) b_vo.pk_doc
FROM blobs.v_obj4doc_no_data b_vo
......@@ -214,7 +214,7 @@ class cDocumentFolder:
if order_by is None:
order_by = u'ORDER BY clin_when'
cmd = u"%s\n%s" % (_sql_fetch_document_fields % u' AND '.join(where_parts), order_by)
cmd = u"%s\n%s" % (_SQL_get_document_fields % u' AND '.join(where_parts), order_by)
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
return [ cDocument(row = {'pk_field': 'pk_doc', 'idx': idx, 'data': r}) for r in rows ]
......@@ -248,12 +248,12 @@ class cDocumentFolder:
all_document_org_units = property(_get_all_document_org_units, lambda x:x)
#============================================================
_sql_fetch_document_part_fields = u"select * from blobs.v_obj4doc_no_data where %s"
_SQL_get_document_part_fields = u"select * from blobs.v_obj4doc_no_data where %s"
class cDocumentPart(gmBusinessDBObject.cBusinessDBObject):
"""Represents one part of a medical document."""
_cmd_fetch_payload = _sql_fetch_document_part_fields % u"pk_obj = %s"
_cmd_fetch_payload = _SQL_get_document_part_fields % u"pk_obj = %s"
_cmds_store_payload = [
u"""UPDATE blobs.doc_obj SET
seq_idx = %(seq_idx)s,
......@@ -621,12 +621,12 @@ def delete_document_part(part_pk=None, encounter_pk=None):
return
#============================================================
_sql_fetch_document_fields = u"SELECT * FROM blobs.v_doc_med b_vdm WHERE %s"
_SQL_get_document_fields = u"SELECT * FROM blobs.v_doc_med b_vdm WHERE %s"
class cDocument(gmBusinessDBObject.cBusinessDBObject):
"""Represents one medical document."""
_cmd_fetch_payload = _sql_fetch_document_fields % u"pk_doc = %s"
_cmd_fetch_payload = _SQL_get_document_fields % u"pk_doc = %s"
_cmds_store_payload = [
u"""UPDATE blobs.doc_med SET
fk_type = %(pk_type)s,
......@@ -698,7 +698,7 @@ class cDocument(gmBusinessDBObject.cBusinessDBObject):
#--------------------------------------------------------
def _get_parts(self):
cmd = _sql_fetch_document_part_fields % u"pk_doc = %s ORDER BY seq_idx"
cmd = _SQL_get_document_part_fields % u"pk_doc = %s ORDER BY seq_idx"
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
return [ cDocumentPart(row = {'pk_field': 'pk_obj', 'idx': idx, 'data': r}) for r in rows ]
......@@ -991,7 +991,7 @@ def search_for_documents(patient_id=None, type_id=None, external_reference=None,
where_parts.append(u'pk_type IN %(pk_types)s')
args['pk_types'] = tuple(pk_types)
cmd = _sql_fetch_document_fields % u' AND '.join(where_parts)
cmd = _SQL_get_document_fields % u' AND '.join(where_parts)
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
return [ cDocument(row = {'data': r, 'idx': idx, 'pk_field': 'pk_doc'}) for r in rows ]
......
......@@ -181,7 +181,8 @@ class cHealthIssue(gmBusinessDBObject.cBusinessDBObject):
open_episode = self.open_episode
if open_episode is None:
return True
clinical_end = open_episode.best_guess_clinical_end_date
#clinical_end = open_episode.best_guess_clinical_end_date
clinical_end = open_episode.latest_access_date # :-/
ttl = datetime.timedelta(ttl)
now = datetime.datetime.now(tz = clinical_end.tzinfo)
if (clinical_end + ttl) > now:
......
......@@ -726,13 +726,15 @@ class cExportArea(object):
))
readme_file.close()
# patient demographics as GDT/XML/VCF
# patient demographics as GDT/XML/VCF/MCF
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'))
pat.export_as_mecard(filename = os.path.join(media_base_dir, u'patient.mcf'))
# praxis VCF
# praxis VCF/MCF
shutil.move(prax.vcf, os.path.join(media_base_dir, u'praxis.vcf'))
prax.export_as_mecard(filename = os.path.join(media_base_dir, u'praxis.mcf'))
return media_base_dir
......
......@@ -1073,6 +1073,9 @@ class cLaTeXForm(cFormEngine):
#--------------------------------------------------------
def substitute_placeholders(self, data_source=None):
# debugging
#data_source.debug = True
if self.template is not None:
# inject placeholder values
data_source.set_placeholder(u'form_name_long', self.template['name_long'])
......@@ -1096,9 +1099,9 @@ class cLaTeXForm(cFormEngine):
]
regexen = [
'dummy',
data_source.first_pass_placeholder_regex,
data_source.second_pass_placeholder_regex,
data_source.third_pass_placeholder_regex
r'\$1{0,1}<[^<].+?>1{0,1}\$',
r'\$2<[^<].+?>2\$',
r'\$3<[^<].+?>3\$'
]
current_pass = 1
......@@ -1148,17 +1151,30 @@ class cLaTeXForm(cFormEngine):
continue
# 2) replace them
_log.debug('%s placeholders found in this line', len(placeholders_in_line))
_log.debug('replacing in non-empty, non-comment line: >>>%s<<<', line.rstrip(u'\n'))
_log.debug('%s placeholder(s) detected', len(placeholders_in_line))
for placeholder in placeholders_in_line:
original_ph_def = placeholder
_log.debug('placeholder: >>>%s<<<', original_ph_def)
# normalize start/end
if placeholder.startswith('$<'):
placeholder = '$1<' + placeholder[2:]
if placeholder.endswith('>$'):
placeholder = placeholder[:-2] + '>1$'
_log.debug('normalized : >>>%s<<<', placeholder)
# remove start/end
placeholder = placeholder[3:-3]
_log.debug('stripped : >>>%s<<<', placeholder)
try:
val = data_source[placeholder]
except:
_log.exception('error with placeholder [%s]', placeholder)
val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
_log.exception('error with placeholder [%s]', original_ph_def)
val = gmTools.tex_escape_string(_('error with placeholder [%s]') % original_ph_def)
if val is None:
_log.debug('error with placeholder [%s]', placeholder)
val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
line = line.replace(placeholder, val)
_log.debug('error with placeholder [%s]', original_ph_def)
val = gmTools.tex_escape_string(_('error with placeholder [%s]') % original_ph_def)
_log.debug('value : >>>%s<<<', val)
line = line.replace(original_ph_def, val)
instance_file.write(line)
instance_file.close()
......
......@@ -2662,7 +2662,7 @@ def format_substance_intake_as_amts_latex(intake=None, strict=True):
_esc = gmTools.tex_escape_string
# %(contains)s & %(product)s & %(amount)s%(unit)s & %(preparation)s & \multicolumn{4}{l|}{%(schedule)s} & Einheit & %(notes)s & %(aim)s \tabularnewline \hline
# %(contains)s & %(product)s & %(amount)s%(unit)s & %(preparation)s & \multicolumn{4}{l|}{%(schedule)s} & Einheit & %(notes)s & %(aim)s \tabularnewline{}\hline
cells = []
# components
components = intake.containing_drug['components']
......@@ -2744,7 +2744,7 @@ def format_substance_intake_as_amts_latex(intake=None, strict=True):
cells.append(u'\\fontsize{10pt}{12pt}\selectfont %s ' % _esc(intake['aim']))
table_row = u' & '.join(cells)
table_row += u'\\tabularnewline\n\\hline'
table_row += u'\\tabularnewline{}\n\\hline'
return table_row
......@@ -3040,15 +3040,14 @@ def __generate_enhanced_amts_data_template_definition_file_v2_0(work_dir=None):
#------------------------------------------------------------
def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-product'):
tex = u'\\noindent %s\n' % _('Additional notes for healthcare professionals')
tex = u'%s\n' % _('Additional notes for healthcare professionals')
tex += u'%%%% requires "\\usepackage{longtable}"\n'
tex += u'%%%% requires "\\usepackage{tabu}"\n'
tex += u'\\noindent \\begin{longtabu} to \\textwidth {|X[,L]|r|X[,L]|}\n'
tex += u'\\begin{longtabu} to \\textwidth {|X[,L]|r|X[,L]|}\n'
tex += u'\\hline\n'
tex += u'%s {\\scriptsize (%s)} & %s & %s \\tabularnewline \n' % (_('Substance'), _('Drug Product'), _('Strength'), _('Aim'))
tex += u'%s {\\scriptsize (%s)} & %s & %s\\\\\n' % (_('Substance'), _('Drug Product'), _('Strength'), _('Aim'))
tex += u'\\hline\n'
tex += u'\\hline\n'
tex += u'%s\n'
tex += u'%s\n' # this is where the lines end up
tex += u'\\end{longtabu}\n'
current_meds = emr.get_current_medications (
......@@ -3056,23 +3055,22 @@ def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u
include_unapproved = False,
order_by = u'product, substance'
)
# create lines
lines = []
for med in current_meds:
product = u'{\\small :} {\\tiny %s}' % gmTools.tex_escape_string(med['product'])
if med['aim'] is None:
aim = u''
else:
aim = u'{\\scriptsize %s}' % gmTools.tex_escape_string(med['aim'])
lines.append(u'%s ({\\small %s}%s) & %s%s & %s \\tabularnewline\n \\hline' % (
lines.append(u'%s {\\small (%s: {\\tiny %s})} & %s%s & %s\\\\' % (
gmTools.tex_escape_string(med['substance']),
gmTools.tex_escape_string(med['l10n_preparation']),
product,
gmTools.tex_escape_string(med['product']),
med['amount'],
gmTools.tex_escape_string(med.formatted_units),
aim
))
lines.append(u'\\hline')
return tex % u'\n'.join(lines)
......@@ -3081,7 +3079,7 @@ def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-pr
# FIXME: add intake_instructions
tex = u'\\noindent %s {\\tiny (%s)}\n' % (
tex = u'%s {\\tiny (%s)}\n' % (
gmTools.tex_escape_string(_('Medication list')),
gmTools.tex_escape_string(_('ordered by brand'))
)
......@@ -3089,7 +3087,7 @@ def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-pr
tex += u'%% requires "\\usepackage{tabu}"\n'
tex += u'\\begin{longtabu} to \\textwidth {|X[-1,L]|X[2.5,L]|}\n'
tex += u'\\hline\n'
tex += u'%s & %s \\tabularnewline \n' % (
tex += u'%s & %s\\\\\n' % (
gmTools.tex_escape_string(_('Drug')),
gmTools.tex_escape_string(_('Regimen / Advice'))
)
......@@ -3097,13 +3095,12 @@ def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-pr
tex += u'%s\n'
tex += u'\\end{longtabu}\n'
# aggregate medication data
current_meds = emr.get_current_medications (
include_inactive = False,
include_unapproved = False,
order_by = u'product, substance'
)
# aggregate data
line_data = {}
for med in current_meds:
identifier = med['product']
......@@ -3126,28 +3123,23 @@ def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-pr
if med['notes'] is not None:
if med['notes'] not in line_data[identifier]['notes']:
line_data[identifier]['notes'].append(med['notes'])
# create lines
# format aggregated data
already_seen = []
lines = []
line1_template = u'\\rule{0pt}{3ex}{\\Large %s} %s & %s \\tabularnewline'
line2_template = u'{\\tiny %s} & {\\scriptsize %s} \\tabularnewline'
line3_template = u' & {\\scriptsize %s} \\tabularnewline'
#line1_template = u'\\rule{0pt}{3ex}{\\Large %s} %s & %s \\\\'
line1_template = u'{\\Large %s} %s & %s\\\\'
line2_template = u'{\\tiny %s} & {\\scriptsize %s}\\\\'
line3_template = u' & {\\scriptsize %s}\\\\'
for med in current_meds:
identifier = med['product']
if identifier in already_seen:
continue
already_seen.append(identifier)
lines.append (line1_template % (
gmTools.tex_escape_string(line_data[identifier]['product']),
gmTools.tex_escape_string(line_data[identifier]['l10n_preparation']),
gmTools.tex_escape_string(line_data[identifier]['schedule'])
))
strengths = gmTools.tex_escape_string(u' / '.join(line_data[identifier]['strengths']))
if len(line_data[identifier]['notes']) == 0:
first_note = u''
......@@ -3157,7 +3149,6 @@ def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-pr
if len(line_data[identifier]['notes']) > 1:
for note in line_data[identifier]['notes'][1:]:
lines.append(line3_template % gmTools.tex_escape_string(note))
lines.append(u'\\hline')
return tex % u'\n'.join(lines)
......
# -*- coding: utf-8 -*-
from __future__ import print_function
"""GNUmed patient objects.
This is a patient object intended to let a useful client-side
......@@ -14,7 +17,6 @@ import os.path
import time
import re as regex
import datetime as pyDT
import io
import thread
import threading
import logging
......@@ -154,7 +156,7 @@ class cDTO_person(object):
def delete_from_source(self):
pass
#--------------------------------------------------------
def is_unique(self):
def _is_unique(self):
where_snippets = [
u'firstnames = %(first)s',
u'lastnames = %(last)s'
......@@ -174,9 +176,10 @@ class cDTO_person(object):
return rows[0][0] == 1
is_unique = property(is_unique, lambda x:x)
is_unique = property(_is_unique)
#--------------------------------------------------------
def exists(self):
def _exists(self):
where_snippets = [
u'firstnames = %(first)s',
u'lastnames = %(last)s'
......@@ -196,7 +199,8 @@ class cDTO_person(object):
return rows[0][0] > 0
exists = property(exists, lambda x:x)
exists = property(_exists)
#--------------------------------------------------------
def get_candidate_identities(self, can_create=False):
"""Generate generic queries.
......@@ -1409,6 +1413,60 @@ class cPerson(gmBusinessDBObject.cBusinessDBObject):
vcf.close()
return filename
#--------------------------------------------------------
def export_as_mecard(self, filename=None):
if filename is None:
filename = gmTools.get_unique_filename (
prefix = 'gm-patient-',
suffix = '.mcf'
)
with io.open(filename, mode = 'wt', encoding = 'utf8') as mecard_file:
mecard_file.write(self.MECARD)
return filename
#--------------------------------------------------------
def _get_mecard(self):
"""
http://blog.thenetimpact.com/2011/07/decoding-qr-codes-how-to-format-data-for-qr-code-generators/
https://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html
MECARD:N:NAME;ADR:pobox,subunit,unit,street,ort,region,zip,country;TEL:111111111;FAX:22222222;EMAIL:mail@praxis.org;
MECARD:N:lastname,firstname;BDAY:YYYYMMDD;ADR:pobox,subunit,number,street,location,region,zip,country;;
MECARD:N:$<lastname::::>$,$<firstname::::>$;BDAY:$<date_of_birth::%Y%m%d::>$;ADR:,$<adr_subunit::home::>$,$<adr_number::home::>$,$<adr_street::home::>$,$<adr_location::home::>$,,$<adr_postcode::home::>$,$<adr_country::home::>$;;
"""
MECARD = 'MECARD:N:%s,%s;' % (
self._payload[self._idx['lastnames']],
self._payload[self._idx['firstnames']]
)
if self._payload[self._idx['dob']] is not None:
MECARD += 'BDAY:%s;' % gmDateTime.pydt_strftime (
self._payload[self._idx['dob']],
'%Y%m%d',
accuracy = gmDateTime.acc_days,
none_str = ''
)
adrs = self.get_addresses(address_type = 'home')
if len(adrs) > 0:
MECARD += 'ADR:,%(subunit)s,%(number)s,%(street)s,%(urb)s,,%(postcode)s,%(l10n_country)s;' % adrs[0]
comms = self.get_comm_channels(comm_medium = 'homephone')
if len(comms) > 0:
if not comms[0]['is_confidential']:
MECARD += 'TEL:%s;' % comms[0]['url']
comms = self.get_comm_channels(comm_medium = 'fax')
if len(comms) > 0:
if not comms[0]['is_confidential']:
MECARD += 'FAX:%s;' % comms[0]['url']
comms = self.get_comm_channels(comm_medium = 'email')
if len(comms) > 0:
if not comms[0]['is_confidential']:
MECARD += 'EMAIL:%s;' % comms[0]['url']
return MECARD
MECARD = property(_get_mecard)
#--------------------------------------------------------
# occupations API
#--------------------------------------------------------
......@@ -2217,9 +2275,9 @@ class gmCurrentPatient(gmBorg.cBorg):
successful = call_back()
except:
_log.exception('callback [%s] failed', call_back)
print "*** pre-change callback failed ***"
print type(call_back)
print call_back
print("*** pre-change callback failed ***")
print(type(call_back))
print(call_back)
return False
if not successful:
......@@ -2568,22 +2626,22 @@ if __name__ == '__main__':
def test_set_active_pat():
ident = cPerson(1)
print "setting active patient with", ident
print("setting active patient with", ident)
set_active_patient(patient=ident)
patient = cPatient(12)
print "setting active patient with", patient
print("setting active patient with", patient)
set_active_patient(patient=patient)
pat = gmCurrentPatient()
print pat['dob']
print(pat['dob'])
#pat['dob'] = 'test'
# staff = cStaff()
# print "setting active patient with", staff
# print("setting active patient with", staff)
# set_active_patient(patient=staff)
print "setting active patient with -1"
print("setting active patient with -1")
set_active_patient(patient=-1)
#--------------------------------------------------------
def test_dto_person():
......@@ -2592,46 +2650,46 @@ if __name__ == '__main__':
dto.lastnames = 'Herberger'
dto.gender = 'male'
dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
print dto
print(dto)
print dto['firstnames']
print dto['lastnames']
print dto['gender']
print dto['dob']
print(dto['firstnames'])
print(dto['lastnames'])
print(dto['gender'])
print(dto['dob'])
for key in dto.keys():
print key
print(key)
#--------------------------------------------------------
def test_identity():
# create patient
print '\n\nCreating identity...'
print('\n\nCreating identity...')
new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
print 'Identity created: %s' % new_identity
print('Identity created: %s' % new_identity)
print '\nSetting title and gender...'
print('\nSetting title and gender...')
new_identity['title'] = 'test title';
new_identity['gender'] = 'f';
new_identity.save_payload()
print 'Refetching identity from db: %s' % cPerson(aPK_obj=new_identity['pk_identity'])
print('Refetching identity from db: %s' % cPerson(aPK_obj=new_identity['pk_identity']))
print '\nGetting all names...'
print('\nGetting all names...')
for a_name in new_identity.get_names():
print a_name
print 'Active name: %s' % (new_identity.get_active_name())
print 'Setting nickname...'
print(a_name)
print('Active name: %s' % (new_identity.get_active_name()))
print('Setting nickname...')
new_identity.set_nickname(nickname='test nickname')
print 'Refetching all names...'
print('Refetching all names...')
for a_name in new_identity.get_names():
print a_name
print 'Active name: %s' % (new_identity.get_active_name())
print(a_name)
print('Active name: %s' % (new_identity.get_active_name()))
print '\nIdentity occupations: %s' % new_identity['occupations']
print 'Creating identity occupation...'
print('\nIdentity occupations: %s' % new_identity['occupations'])
print('Creating identity occupation...')
new_identity.link_occupation('test occupation')
print 'Identity occupations: %s' % new_identity['occupations']
print('Identity occupations: %s' % new_identity['occupations'])
print '\nIdentity addresses: %s' % new_identity.get_addresses()
print 'Creating identity address...'
print('\nIdentity addresses: %s' % new_identity.get_addresses())
print('Creating identity address...')
# make sure the state exists in the backend
new_identity.link_address (
number = 'test 1234',
......@@ -2641,56 +2699,65 @@ if __name__ == '__main__':
region_code = u'SN',
country_code = u'DE'
)
print 'Identity addresses: %s' % new_identity.get_addresses()
print('Identity addresses: %s' % new_identity.get_addresses())
print '\nIdentity communications: %s' % new_identity.get_comm_channels()
print 'Creating identity communication...'
print('\nIdentity communications: %s' % new_identity.get_comm_channels())
print('Creating identity communication...')
new_identity.link_comm_channel('homephone', '1234566')
print 'Identity communications: %s' % new_identity.get_comm_channels()
print('Identity communications: %s' % new_identity.get_comm_channels())
#--------------------------------------------------------
def test_name():
for pk in range(1,16):
name = cPersonName(aPK_obj=pk)
print name.description
print ' ', name
print(name.description)
print(' ', name)
#--------------------------------------------------------
def test_gender_list():
genders, idx = get_gender_list()
print "\n\nRetrieving gender enum (tag, label, weight):"
print("\n\nRetrieving gender enum (tag, label, weight):")
for gender in genders:
print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']])
print("%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']]))
#--------------------------------------------------------
def test_export_area():
person = cPerson(aPK_obj = 12)
print person
print person.export_area
print person.export_area.items
print(person)
print(person.export_area)
print(person.export_area.items)
#--------------------------------------------------------
def test_ext_id():
person = cPerson(aPK_obj = 9)
print person.get_external_ids(id_type=u'Fachgebiet', issuer=u'Ärztekammer')
#print person.get_external_ids()
print(person.get_external_ids(id_type=u'Fachgebiet', issuer=u'Ärztekammer'))
#print(person.get_external_ids()
#--------------------------------------------------------
def test_vcf():
person = cPerson(aPK_obj = 12)
print person.export_as_vcard()
print(person.export_as_vcard())
#--------------------------------------------------------
def test_mecard():
person = cPerson(aPK_obj = 12)
print(person.MECARD)
mcf = person.export_as_mecard()
print(mcf)
#print(gmTools.create_qrcode(filename = mcf, qr_filename = None, verbose = True)
print(gmTools.create_qrcode(text = person.MECARD, qr_filename = None, verbose = True))
#--------------------------------------------------------
def test_current_patient():
pat = gmCurrentPatient()
print "pat.emr", pat.emr
print("pat.emr", pat.emr)
#--------------------------------------------------------
def test_ext_id():
person = cPerson(aPK_obj = 12)
print person.suggest_external_id(target = u'Orthanc')
print(person.suggest_external_id(target = u'Orthanc'))
#--------------------------------------------------------
def test_assimilate_identity():
patient = cPatient(12)
set_active_patient(patient = patient)
curr_pat = gmCurrentPatient()
other_pat = cIdentity(1111111)
other_pat = cPerson(1111111)
curr_pat.assimilate_identity(other_identity=None)
#--------------------------------------------------------
......@@ -2705,12 +2772,13 @@ if __name__ == '__main__':
# module functions
#comms = get_comm_list()
#print "\n\nRetrieving communication media enum (id, description): %s" % comms
#print("\n\nRetrieving communication media enum (id, description): %s" % comms)
#test_export_area()
#test_ext_id()
#test_vcf()
test_mecard()
#test_ext_id()
#test_current_patient()
test_assimilate_identity()
#test_assimilate_identity()
#============================================================
......@@ -177,6 +177,44 @@ class cPraxisBranch(gmBusinessDBObject.cBusinessDBObject):
vcf = property(_get_vcf, lambda x:x)
#--------------------------------------------------------
def export_as_mecard(self, filename=None):
if filename is None:
filename = gmTools.get_unique_filename (
prefix = 'gm-praxis-',
suffix = '.mcf'
)
with io.open(filename, mode = 'wt', encoding = 'utf8') as mecard_file:
mecard_file.write(self.MECARD)
return filename
#--------------------------------------------------------
def _get_mecard(self):
"""
http://blog.thenetimpact.com/2011/07/decoding-qr-codes-how-to-format-data-for-qr-code-generators/
https://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html
MECARD:N:NAME;ADR:pobox,subunit,unit,street,ort,region,zip,country;TEL:111111111;FAX:22222222;EMAIL:mail@praxis.org;
MECARD:N:$<praxis::%(praxis)s, %(branch)s::>$;ADR:$<praxis_address::,%(subunit)s,%(number)s,%(street)s,%(urb)s,,%(postcode)s,%(l10n_country)s::>$;TEL:$<praxis_comm::workphone::>$;FAX:$<praxis_comm::fax::>$;EMAIL:$<praxis_comm::email::60>$;
"""
MECARD = 'MECARD:N:%(praxis)s,%(branch)s;' % self
adr = self.address
if adr is not None:
MECARD += 'ADR:,%(subunit)s,%(number)s,%(street)s,%(urb)s,,%(postcode)s,%(l10n_country)s;' % adr
comms = self.get_comm_channels(comm_medium = 'workphone')
if len(comms) > 0:
MECARD += 'TEL:%(url)s;' % comms[0]
comms = self.get_comm_channels(comm_medium = 'fax')
if len(comms) > 0:
MECARD += 'FAX:%(url)s;' % comms[0]
comms = self.get_comm_channels(comm_medium = 'email')
if len(comms) > 0:
MECARD += 'EMAIL:%(url)s;' % comms[0]
return MECARD
MECARD = property(_get_mecard)
#------------------------------------------------------------
def lock_praxis_branch(pk_praxis_branch=None, exclusive=False):
return gmPG2.lock_row(table = u'dem.praxis_branch', pk = pk_praxis_branch, exclusive = exclusive)
......
......@@ -27,10 +27,10 @@ _map_gm_role2pg_group = {
}
#============================================================
_SQL_fetch_staff_fields = u'SELECT *, _(role) AS l10n_role FROM dem.v_staff WHERE %s'
_SQL_get_staff_fields = u'SELECT *, _(role) AS l10n_role FROM dem.v_staff WHERE %s'
class cStaff(gmBusinessDBObject.cBusinessDBObject):
_cmd_fetch_payload = _SQL_fetch_staff_fields % u"pk_staff = %s"
_cmd_fetch_payload = _SQL_get_staff_fields % u"pk_staff = %s"
_cmds_store_payload = [
u"""UPDATE dem.staff SET
short_alias = %(short_alias)s,
......@@ -50,7 +50,7 @@ class cStaff(gmBusinessDBObject.cBusinessDBObject):
def __init__(self, aPK_obj=None, row=None):
# by default get staff corresponding to CURRENT_USER
if (aPK_obj is None) and (row is None):
cmd = _SQL_fetch_staff_fields % u"db_user = CURRENT_USER"
cmd = _SQL_get_staff_fields % u"db_user = CURRENT_USER"
try:
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
except:
......@@ -146,9 +146,9 @@ class cStaff(gmBusinessDBObject.cBusinessDBObject):
#============================================================
def get_staff_list(active_only=False):
if active_only:
cmd = _SQL_fetch_staff_fields % u'is_active ORDER BY can_login DESC, short_alias ASC'
cmd = _SQL_get_staff_fields % u'is_active ORDER BY can_login DESC, short_alias ASC'
else:
cmd = _SQL_fetch_staff_fields % u'TRUE ORDER BY can_login DESC, is_active DESC, short_alias ASC'
cmd = _SQL_get_staff_fields % u'TRUE ORDER BY can_login DESC, is_active DESC, short_alias ASC'
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
staff_list = []
for row in rows:
......
......@@ -404,12 +404,12 @@ def create_generic_vaccine_sql(version, include_indications_mapping=False):
#============================================================
# vaccine related code
#------------------------------------------------------------
_sql_fetch_vaccine = u"""SELECT * FROM ref.v_vaccines WHERE %s"""
_SQL_get_vaccine_fields = u"""SELECT * FROM ref.v_vaccines WHERE %s"""
class cVaccine(gmBusinessDBObject.cBusinessDBObject):
"""Represents one vaccine."""
_cmd_fetch_payload = _sql_fetch_vaccine % u"pk_vaccine = %s"
_cmd_fetch_payload = _SQL_get_vaccine_fields % u"pk_vaccine = %s"
_cmds_store_payload = [
u"""UPDATE ref.vaccine SET
......@@ -487,10 +487,11 @@ class cVaccine(gmBusinessDBObject.cBusinessDBObject):
is_in_use = property(_get_is_in_use, lambda x:x)
#------------------------------------------------------------
def create_vaccine(pk_drug_product=None, product_name=None, indications=None):
def create_vaccine(pk_drug_product=None, product_name=None, indications=None, is_live=None):
conn = gmPG2.get_connection(readonly = False)
assert (is_live is not None), '<is_live> must not be <None>'
conn = gmPG2.get_connection(readonly = False)
if pk_drug_product is None:
#prep = _('vaccine')
prep = u'vaccine'
......@@ -507,9 +508,8 @@ def create_vaccine(pk_drug_product=None, product_name=None, indications=None):
vacc_prod['atc'] = u'J07'
vacc_prod.save(conn = conn)
pk_drug_product = vacc_prod['pk_drug_product']
cmd = u'INSERT INTO ref.vaccine (fk_drug_product) values (%(pk_drug_product)s) RETURNING pk'
queries = [{'cmd': cmd, 'args': {'pk_drug_product': pk_drug_product}}]
cmd = u'INSERT INTO ref.vaccine (fk_drug_product, is_live) values (%(pk_drug_product)s, %(live)s) RETURNING pk'
queries = [{'cmd': cmd, 'args': {'pk_drug_product': pk_drug_product, 'live': is_live}}]
rows, idx = gmPG2.run_rw_queries(link_obj = conn, queries = queries, get_col_idx = False, return_data = True, end_tx = True)
conn.close()
return cVaccine(aPK_obj = rows[0]['pk'])
......@@ -532,9 +532,9 @@ def delete_vaccine(vaccine=None):
def get_vaccines(order_by=None):
if order_by is None:
cmd = _sql_fetch_vaccine % u'TRUE'
cmd = _SQL_get_vaccine_fields % u'TRUE'
else:
cmd = _sql_fetch_vaccine % (u'TRUE\nORDER BY %s' % order_by)
cmd = _SQL_get_vaccine_fields % (u'TRUE\nORDER BY %s' % order_by)
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
......@@ -543,11 +543,11 @@ def get_vaccines(order_by=None):
#============================================================
# vaccination related classes
#============================================================
sql_fetch_vaccination = u"""SELECT * FROM clin.v_vaccinations WHERE %s"""
_SQL_get_vaccination_fields = u"""SELECT * FROM clin.v_vaccinations WHERE %s"""
class cVaccination(gmBusinessDBObject.cBusinessDBObject):
_cmd_fetch_payload = sql_fetch_vaccination % u"pk_vaccination = %s"
_cmd_fetch_payload = _SQL_get_vaccination_fields % u"pk_vaccination = %s"
_cmds_store_payload = [
u"""UPDATE clin.vaccination SET
......@@ -623,6 +623,13 @@ class cVaccination(gmBusinessDBObject.cBusinessDBObject):
vaccine = property(_get_vaccine, lambda x:x)
#------------------------------------------------------------
def get_vaccinations():
cmd = _SQL_get_vaccination_fields % 'True'
args = {}
rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
return [ cVaccination(row = {'data': r, 'idx': idx, 'pk_field': 'pk_vaccine'}) for r in rows ]
#------------------------------------------------------------
def create_vaccination(encounter=None, episode=None, vaccine=None, batch_no=None):
......@@ -677,31 +684,30 @@ def format_latest_vaccinations(output_format=u'latex', emr=None):
def __format_latest_vaccinations_latex(vaccinations=None):
if len(vaccinations) == 0:
return u'\\noindent %s' % _('No vaccinations to format.')
return u'\\noindent %s' % _('No vaccinations recorded.')
tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Latest vaccinations'), _('per target condition'))
tex += u'\n'
tex += u'\\noindent \\begin{tabular}{|l|l|l|l|l|l|}\n'
tex += u'\\hline\n'
tex += u'%s & %s & {\\footnotesize %s} & {\\footnotesize %s} & {\\footnotesize %s\\footnotemark} & {\\footnotesize %s\\footnotemark} \\\\ \n' % (
tex += u'%s & %s & {\\footnotesize %s} & {\\footnotesize %s} & {\\footnotesize %s\\footnotemark} & {\\footnotesize $\\Sigma$\\footnotemark}\\\\\n' % (
_('Target'),
_('Last given'),
_('Vaccine'),
_('Lot \#'),
_('SoaP'),
gmTools.u_sum
_('SoaP')
)
tex += u'\\hline\n'
tex += u'\n'
tex += u'\\hline\n'
tex += u'%s'
tex += u'%s' # this is where the actual vaccination rows end up
tex += u'\n'
tex += u'\\end{tabular}\n'
tex += u'\n'
tex += u'\\addtocounter{footnote}{-1}\n'
tex += u'\\footnotetext{%s}\n' % _('SoaP -- "S"ubjective: vaccination was remembered by patient. "P"lan: vaccination was administered in the practice or copied from trustworthy records.')
tex += u'\\addtocounter{footnote}{1}\n'
tex += u'\\footnotetext{%s -- %s} \n' % (gmTools.u_sum, _('Total number of vaccinations recorded for the corresponding target condition.'))
tex += u'\\footnotetext{$\\Sigma$ -- %s}\n' % _('Total number of vaccinations recorded for the corresponding target condition.')
tex += u'\n'
row_template = u'%s & %s & {\\scriptsize %s} & {\\scriptsize %s} & {\\scriptsize %s} & {\\scriptsize %s}\\\\\n'
......@@ -846,6 +852,6 @@ if __name__ == '__main__':
#test_due_booster()
#test_get_vaccines()
#test_get_vaccinations()
test_get_vaccinations()
#test_create_generic_vaccine_sql()
test_write_generic_vaccine_sql(sys.argv[2], sys.argv[3])
#test_write_generic_vaccine_sql(sys.argv[2], sys.argv[3])
......@@ -106,7 +106,7 @@
<li> <strong class="uidlink"><a href="Gnumed.business.gmMedication-module.html">Gnumed.business.gmMedication</a></strong>: <em class="summary">Medication handling code.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmOrganization-module.html">Gnumed.business.gmOrganization</a></strong>: <em class="summary">Organization classes</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPathLab-module.html">Gnumed.business.gmPathLab</a></strong>: <em class="summary">GNUmed measurements related business objects.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPerson-module.html">Gnumed.business.gmPerson</a></strong>: <em class="summary">GNUmed patient objects.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPerson-module.html">Gnumed.business.gmPerson</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPersonSearch-module.html">Gnumed.business.gmPersonSearch</a></strong>: <em class="summary">GNUmed person searching code.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPracSoftAU-module.html">Gnumed.business.gmPracSoftAU</a></strong>: <em class="summary">GNUmed to PracSoft(tm) AU connector classes.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.business.gmPraxis-module.html">Gnumed.business.gmPraxis</a></strong>: <em class="summary">GNUmed Praxis related middleware.</em> </li>
......@@ -135,7 +135,7 @@
<ul>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmClinicaExporter-module.html">Gnumed.importers.gmClinicaExporter</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmDrugsFromCSV-module.html">Gnumed.importers.gmDrugsFromCSV</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmImportIncoming-module.html">Gnumed.importers.gmImportIncoming</a></strong>: <em class="summary">Incoming data importer.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmImportIncoming-module.html">Gnumed.importers.gmImportIncoming</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmLDTimporter-module.html">Gnumed.importers.gmLDTimporter</a></strong>: <em class="summary">GNUmed LDT importer.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.importers.gmSMSImporter-module.html">Gnumed.importers.gmSMSImporter</a></strong> </li>
</ul>
......@@ -144,18 +144,19 @@
<ul>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmBackendListener-module.html">Gnumed.pycommon.gmBackendListener</a></strong>: <em class="summary">GNUmed database backend listener.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmBorg-module.html">Gnumed.pycommon.gmBorg</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmBusinessDBObject-module.html">Gnumed.pycommon.gmBusinessDBObject</a></strong>: <em class="summary">GNUmed database object business class.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmBusinessDBObject-module.html">Gnumed.pycommon.gmBusinessDBObject</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmCfg-module.html">Gnumed.pycommon.gmCfg</a></strong>: <em class="summary">GNUmed configuration handling.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmCfg2-module.html">Gnumed.pycommon.gmCfg2</a></strong>: <em class="summary">GNUmed configuration handling.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmCrypto-module.html">Gnumed.pycommon.gmCrypto</a></strong>: <em class="summary">GNUmed crypto tools.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmDateTime-module.html">Gnumed.pycommon.gmDateTime</a></strong>: <em class="summary">GNUmed date/time handling.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmDispatcher-module.html">Gnumed.pycommon.gmDispatcher</a></strong>: <em class="summary">GNUmed client internal signal handling.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmExceptions-module.html">Gnumed.pycommon.gmExceptions</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmGuiBroker-module.html">Gnumed.pycommon.gmGuiBroker</a></strong>: <em class="summary">GNUmed GUI element brokerage</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmHooks-module.html">Gnumed.pycommon.gmHooks</a></strong>: <em class="summary">GNUmed hooks framework.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmI18N-module.html">Gnumed.pycommon.gmI18N</a></strong>: <em class="summary">GNUmed client internationalization/localization.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmI18N-module.html">Gnumed.pycommon.gmI18N</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmLog2-module.html">Gnumed.pycommon.gmLog2</a></strong>: <em class="summary">GNUmed logging framework setup.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmLoginInfo-module.html">Gnumed.pycommon.gmLoginInfo</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmMatchProvider-module.html">Gnumed.pycommon.gmMatchProvider</a></strong>: <em class="summary">Base classes for match providers.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmMatchProvider-module.html">Gnumed.pycommon.gmMatchProvider</a></strong> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmMimeLib-module.html">Gnumed.pycommon.gmMimeLib</a></strong>: <em class="summary">This module encapsulates mime operations.</em> </li>
<li> <strong class="uidlink"><a href="Gnumed.pycommon.gmMimeMagic-module.html">Gnumed.pycommon.gmMimeMagic</a></strong>: <em class="summary">magic.py
determines a file type by its magic number</em> </li>
......@@ -1013,7 +1014,7 @@
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......
......@@ -98,7 +98,7 @@ expandto(location.href);
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......
......@@ -123,7 +123,7 @@
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......
......@@ -99,7 +99,7 @@ expandto(location.href);
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......
......@@ -154,7 +154,7 @@
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......
......@@ -125,7 +125,7 @@ expandto(location.href);
<tr>
<td align="left" class="footer">
Generated by Epydoc 3.0.1
on Sun Aug 19 01:55:20 2018
on Wed Dec 19 02:55:28 2018
</td>
<td align="right" class="footer">
<a target="mainFrame" href="http://epydoc.sourceforge.net"
......