Module Gnumed.business.gmVaccination

GNUmed vaccination related business objects.

Expand source code
"""GNUmed vaccination related business objects.
"""
#============================================================
__author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
__license__ = "GPL"

import sys
import locale
import logging
import functools


if __name__ == '__main__':
        sys.path.insert(0, '../../')
        _ = lambda x:x
from Gnumed.pycommon import gmBusinessDBObject
from Gnumed.pycommon import gmPG2

from Gnumed.pycommon import gmTools
from Gnumed.pycommon import gmDateTime
from Gnumed.business import gmMedication


_log = logging.getLogger('gm.vacc')

#============================================================
URL_vaccination_plan = 'http://www.rki.de/DE/Content/Infekt/EpidBull/Archiv/2017/Ausgaben/34_17.pdf?__blob=publicationFile'

# http://www.pei.de/cln_042/SharedDocs/Downloads/fachkreise/uaw/meldeboegen/b-ifsg-meldebogen,templateId=raw,property=publicationFile.pdf/b-ifsg-meldebogen.pdf
URL_vaccine_adr_german_default = 'https://nebenwirkungen.pei.de'

#============================================================
_SQL_create_substance4vaccine = """-- in case <%(substance_tag)s> already exists: add ATC
UPDATE ref.substance SET atc = '%(atc)s' WHERE lower(description) = lower('%(desc)s') AND atc IS NULL;

INSERT INTO ref.substance (description, atc)
        SELECT
                '%(desc)s',
                '%(atc)s'
        WHERE NOT EXISTS (
                SELECT 1 FROM ref.substance WHERE
                        atc = '%(atc)s'
                                AND
                        description = '%(desc)s'
        );

-- generic English
SELECT i18n.upd_tx('en', '%(orig)s', '%(trans)s');
-- user language, if any, fails if not set
SELECT i18n.upd_tx('%(orig)s', '%(trans)s');"""

_SQL_map_indication2substance = """-- old-style "%(v21_ind)s" => "%(desc)s"
INSERT INTO staging.lnk_vacc_ind2subst_dose (fk_indication, fk_dose, is_live)
        SELECT
                (SELECT id FROM ref.vacc_indication WHERE description = '%(v21_ind)s'),
                (SELECT pk_dose FROM ref.v_substance_doses WHERE
                        amount = 1
                                AND
                        unit = 'dose'
                                AND
                        dose_unit = 'shot'
                                AND
                        substance = '%(desc)s'
                ),
                %(is_live)s
        WHERE EXISTS (
                SELECT 1 FROM ref.vacc_indication WHERE description = '%(v21_ind)s'
        );"""

_SQL_create_vacc_product = """-- --------------------------------------------------------------
-- in case <%(prod_name)s> exists: add ATC
UPDATE ref.drug_product SET atc_code = '%(atc_prod)s' WHERE
        atc_code IS NULL
                AND
        description = '%(prod_name)s'
                AND
        preparation = '%(prep)s'
                AND
        is_fake IS TRUE;

INSERT INTO ref.drug_product (description, preparation, is_fake, atc_code)
        SELECT
                '%(prod_name)s',
                '%(prep)s',
                TRUE,
                '%(atc_prod)s'
        WHERE NOT EXISTS (
                SELECT 1 FROM ref.drug_product WHERE
                        description = '%(prod_name)s'
                                AND
                        preparation = '%(prep)s'
                                AND
                        is_fake = TRUE
                                AND
                        atc_code = '%(atc_prod)s'
        );"""

_SQL_create_vaccine = """-- add vaccine if necessary
INSERT INTO ref.vaccine (is_live, fk_drug_product)
        SELECT
                %(is_live)s,
                (SELECT pk FROM ref.drug_product WHERE
                        description = '%(prod_name)s'
                                AND
                        preparation = '%(prep)s'
                                AND
                        is_fake = TRUE
                                AND
                        atc_code = '%(atc_prod)s'
                )
        WHERE NOT EXISTS (
                SELECT 1 FROM ref.vaccine WHERE
                        is_live IS %(is_live)s
                                AND
                        fk_drug_product = (
                                SELECT pk FROM ref.drug_product WHERE
                                        description = '%(prod_name)s'
                                                AND
                                        preparation = '%(prep)s'
                                                AND
                                        is_fake = TRUE
                                                AND
                                        atc_code = '%(atc_prod)s'
                        )
        );"""

_SQL_create_vacc_subst_dose = """-- create dose, assumes substance exists
INSERT INTO ref.dose (fk_substance, amount, unit, dose_unit)
        SELECT
                (SELECT pk FROM ref.substance WHERE atc = '%(atc_subst)s' AND description = '%(name_subst)s' LIMIT 1),
                1,
                'dose',
                'shot'
        WHERE NOT EXISTS (
                SELECT 1 FROM ref.dose WHERE
                        fk_substance = (SELECT pk FROM ref.substance WHERE atc = '%(atc_subst)s' AND description = '%(name_subst)s' LIMIT 1)
                                AND
                        amount = 1
                                AND
                        unit = 'dose'
                                AND
                        dose_unit IS NOT DISTINCT FROM 'shot'
        );"""

_SQL_link_dose2vacc_prod = """-- link dose to product
INSERT INTO ref.lnk_dose2drug (fk_dose, fk_drug_product)
        SELECT
                (SELECT pk from ref.dose WHERE
                        fk_substance = (SELECT pk FROM ref.substance WHERE atc = '%(atc_subst)s' AND description = '%(name_subst)s' LIMIT 1)
                                AND
                        amount = 1
                                AND
                        unit = 'dose'
                                AND
                        dose_unit IS NOT DISTINCT FROM 'shot'
                ),
                (SELECT pk FROM ref.drug_product WHERE
                        description = '%(prod_name)s'
                                AND
                        preparation = '%(prep)s'
                                AND
                        is_fake = TRUE
                                AND
                        atc_code = '%(atc_prod)s'
                )
        WHERE NOT EXISTS (
                SELECT 1 FROM ref.lnk_dose2drug WHERE
                        fk_dose = (
                                SELECT PK from ref.dose WHERE
                                        fk_substance = (SELECT pk FROM ref.substance WHERE atc = '%(atc_subst)s' AND description = '%(name_subst)s' LIMIT 1)
                                                AND
                                        amount = 1
                                                AND
                                        unit = 'dose'
                                                AND
                                        dose_unit IS NOT DISTINCT FROM 'shot'
                        )
                                AND
                        fk_drug_product = (
                                SELECT pk FROM ref.drug_product WHERE
                                        description = '%(prod_name)s'
                                                AND
                                        preparation = '%(prep)s'
                                                AND
                                        is_fake = TRUE
                                                AND
                                        atc_code = '%(atc_prod)s'
                        )
        );"""

_SQL_create_indications_mapping_table = """-- set up helper table for conversion of vaccines from using
-- linked indications to using linked substances,
-- to be dropped after converting vaccines
DROP TABLE IF EXISTS staging.lnk_vacc_ind2subst_dose CASCADE;

CREATE UNLOGGED TABLE staging.lnk_vacc_ind2subst_dose (
        fk_indication INTEGER
                NOT NULL
                REFERENCES ref.vacc_indication(id)
                        ON UPDATE CASCADE
                        ON DELETE RESTRICT,
        fk_dose INTEGER
                NOT NULL
                REFERENCES ref.dose(pk)
                        ON UPDATE CASCADE
                        ON DELETE RESTRICT,
        is_live
                BOOLEAN
                NOT NULL
                DEFAULT false,
        UNIQUE(fk_indication, fk_dose),
        UNIQUE(fk_indication, is_live)
);


DROP VIEW IF EXISTS staging.v_lnk_vacc_ind2subst_dose CASCADE;

CREATE VIEW staging.v_lnk_vacc_ind2subst_dose AS
SELECT
        s_lvi2sd.is_live
                as mapping_is_for_live_vaccines,
        r_vi.id
                as pk_indication,
        r_vi.description
                as indication,
        r_vi.atcs_single_indication,
        r_vi.atcs_combi_indication,
        r_d.pk
                as pk_dose,
        r_d.amount,
        r_d.unit,
        r_d.dose_unit,
        r_s.pk
                as pk_substance,
        r_s.description
                as substance,
        r_s.atc
                as atc_substance
FROM
        staging.lnk_vacc_ind2subst_dose s_lvi2sd
                inner join ref.vacc_indication r_vi on (r_vi.id = s_lvi2sd.fk_indication)
                inner join ref.dose r_d on (r_d.pk = s_lvi2sd.fk_dose)
                        inner join ref.substance r_s on (r_s.pk = r_d.fk_substance)
;"""

_SQL_create_generic_vaccines_script = """-- ==============================================================
-- GNUmed database schema change script
--
-- License: GPL v2 or later
-- Author: karsten.hilbert@gmx.net
--
-- THIS IS A GENERATED FILE. DO NOT EDIT.
--
-- ==============================================================
\set ON_ERROR_STOP 1
--set default_transaction_read_only to off;

-- --------------------------------------------------------------
-- indications mapping helper table
-- --------------------------------------------------------------
%s

-- --------------------------------------------------------------
-- generic vaccine "substances" (= indications)
-- --------------------------------------------------------------
%s

-- --------------------------------------------------------------
-- generic vaccines
-- --------------------------------------------------------------
-- new-style vaccines are not linked to indications, so drop
-- trigger asserting that condition,
DROP FUNCTION IF EXISTS clin.trf_sanity_check_vaccine_has_indications() CASCADE;


-- need to disable trigger before running
ALTER TABLE ref.drug_product
        DISABLE TRIGGER tr_assert_product_has_components
;

%s

-- want to re-enable trigger as now all inserted
-- vaccines satisfy the conditions
ALTER TABLE ref.drug_product
        ENABLE TRIGGER tr_assert_product_has_components
;

-- --------------------------------------------------------------
-- indications mapping data
-- --------------------------------------------------------------
-- map old style
--              (clin|ref).vacc_indication.description
-- to new style
--              ref.v_substance_doses.substance

%s

-- --------------------------------------------------------------
select gm.log_script_insertion('v%s-ref-create_generic_vaccines.sql', '%s');
"""

#============================================================
def write_generic_vaccine_sql(version, include_indications_mapping=False, filename=None):
        if filename is None:
                filename = gmTools.get_unique_filename(suffix = '.sql')
        _log.debug('writing SQL for creating generic vaccines to: %s', filename)
        sql_file = open(filename, mode = 'wt', encoding = 'utf8')
        sql_file.write(create_generic_vaccine_sql (
                version,
                include_indications_mapping = include_indications_mapping
        ))
        sql_file.close()
        return filename

#------------------------------------------------------------
def create_generic_vaccine_sql(version, include_indications_mapping=False):

        _log.debug('including indications mapping table with generic vaccines creation SQL: %s', include_indications_mapping)

        from Gnumed.business import gmVaccDefs

        sql_create_substances = []
        sql_populate_ind2subst_map = []
        sql_create_vaccines = []

        for substance_tag in gmVaccDefs._VACCINE_SUBSTANCES:
                subst = gmVaccDefs._VACCINE_SUBSTANCES[substance_tag]
                args = {
                        'substance_tag': substance_tag,
                        'atc': subst['atc4target'],
                        'desc': subst['name'],
                        'orig': subst['target'].split('::')[0],
                        'trans': subst['target'].split('::')[-1]
                }
                sql_create_substances.append(_SQL_create_substance4vaccine % args)
                try:
                        for v21_ind in subst['v21_indications']:
                                args['v21_ind'] = v21_ind
                                args['is_live'] = 'false'
                                sql_populate_ind2subst_map.append(_SQL_map_indication2substance % args)
                except KeyError:
                        pass
                try:
                        for v21_ind in subst['v21_indications_live']:
                                args['v21_ind'] = v21_ind
                                args['is_live'] = 'true'
                                sql_populate_ind2subst_map.append(_SQL_map_indication2substance % args)
                except KeyError:
                        pass
                args = {}

        for key in gmVaccDefs._GENERIC_VACCINES:
                vaccine_def = gmVaccDefs._GENERIC_VACCINES[key]
                # create product
                args = {
                        'atc_prod': vaccine_def['atc'],
                        'prod_name': vaccine_def['name'],
                        # generic vaccines always have the English preparation
                        'prep': 'vaccine',
                        'is_live': vaccine_def['live']
                }
                sql_create_vaccines.append(_SQL_create_vacc_product % args)
                # create doses
                for ingredient_tag in vaccine_def['ingredients']:
                        vacc_subst_def = gmVaccDefs._VACCINE_SUBSTANCES[ingredient_tag]
                        args['atc_subst'] = vacc_subst_def['atc4target']
                        args['name_subst'] = vacc_subst_def['name']
                        # substance already created, only need to create dose
                        sql_create_vaccines.append(_SQL_create_vacc_subst_dose % args)
                        # link dose to product
                        sql_create_vaccines.append(_SQL_link_dose2vacc_prod % args)
                        # the following does not work because there are mixed vaccines
                        # any live ingredients included ?
#                       if vacc_subst_def.has_key('v21_indications_live'):
#                               if vaccine_def['live'] is False:
#                                       print vaccine_def
#                                       raise Exception('vaccine def says "NOT live" but ingredients DO map to <v21_indications_LIVE>')
#                       if vacc_subst_def.has_key('v21_indications'):
#                               if vaccine_def['live'] is True:
#                                       print vaccine_def
#                                       raise Exception('vaccine def says "live" but ingredients do NOT map to v21_indications_LIVE')

                # create vaccine
                sql_create_vaccines.append(_SQL_create_vaccine % args)

        # join
        sql = _SQL_create_generic_vaccines_script % (
                gmTools.bool2subst (
                        include_indications_mapping,
                        _SQL_create_indications_mapping_table,
                        '-- indications mapping table not included'
                ),
                '\n\n'.join(sql_create_substances),
                '\n\n'.join(sql_create_vaccines),
                gmTools.bool2subst (
                        include_indications_mapping,
                        '\n\n'.join(sql_populate_ind2subst_map),
                        '-- indications mapping table not populated'
                ),
                version,
                version
        )
        return sql

#============================================================
# vaccine related code
#------------------------------------------------------------
_SQL_get_vaccine_fields = """SELECT * FROM ref.v_vaccines WHERE %s"""

class cVaccine(gmBusinessDBObject.cBusinessDBObject):
        """Represents one vaccine."""

        _cmd_fetch_payload = _SQL_get_vaccine_fields % "pk_vaccine = %s"

        _cmds_store_payload = [
                """UPDATE ref.vaccine SET
                                is_live = %(is_live)s,
                                min_age = %(min_age)s,
                                max_age = %(max_age)s,
                                comment = gm.nullify_empty_string(%(comment)s),
                                fk_drug_product = %(pk_drug_product)s
                        WHERE
                                pk = %(pk_vaccine)s
                                        AND
                                xmin = %(xmin_vaccine)s
                        RETURNING
                                xmin as xmin_vaccine
                """
        ]

        _updatable_fields = [
                'is_live',
                'min_age',
                'max_age',
                'comment',
                'pk_drug_product'
        ]

        #--------------------------------------------------------
        def format(self, *args, **kwargs):
                lines = []
                lines.append(_('%s with %s %s     #%s') % (
                        gmTools.bool2subst(self._payload['is_live'], _('Vaccine (live)'), _('Vaccine'), '<liveness error in DB>'),
                        len(self._payload['indications']),
                        gmTools.bool2subst(len(self._payload['indications']) == 1, _('indication'), _('indications'), _('indication(s)')),
                        self._payload['pk_vaccine']
                ))
                lines.append(_(' Product: "%s"     #%s') % (
                        self._payload['vaccine'],
                        self._payload['pk_drug_product']
                ))
                lines.append(_('  %s%s%s%s') % (
                        self._payload['l10n_preparation'],
                        gmTools.coalesce(gmTools.bool2subst(self._payload['is_fake_vaccine'], _('fake product'), None, None), '', ', %s'),
                        gmTools.coalesce(self._payload['atc_code'], '', ' [ATC:%s]'),
                        gmTools.coalesce(self._payload['external_code'], '', ' [%s:%%s]' % self._payload['external_code_type'])
                ))
                #lines.append(_(u' %sage %s - %s') % (
                #       gmTools.coalesce(self._payload['route_description'], u'', u'%s, '),             #route_abbreviation
                lines.append(_(' Age %s - %s') % (
                        gmTools.coalesce(self._payload['min_age'], '?'),
                        gmTools.coalesce(self._payload['max_age'], '?')
                ))
                if self._payload['comment'] is not None:
                        lines.extend([ ' %s' % l for l in self._payload['comment'].split('\n')] )
                lines.append(_(' Indications'))
                lines.extend( [ '  %s [ATC:%s]' % (i['l10n_indication'], i['atc_indication']) for i in self._payload['indications'] ])

                return lines

        #--------------------------------------------------------
        # properties
        #--------------------------------------------------------
        def _get_product(self):
                return gmMedication.cDrugProduct(aPK_obj = self._payload['pk_drug_product'])

        product = property(_get_product)

        #--------------------------------------------------------
        def _get_is_in_use(self):
                cmd = 'SELECT EXISTS(SELECT 1 FROM clin.vaccination WHERE fk_vaccine = %(pk)s)'
                args = {'pk': self._payload['pk_vaccine']}
                rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
                return rows[0][0]

        is_in_use = property(_get_is_in_use)

#------------------------------------------------------------
def create_vaccine(pk_drug_product=None, product_name=None, indications=None, is_live=None):

        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 = 'vaccine'
                _log.debug('creating vaccine drug product [%s %s]', product_name, prep)
                vacc_prod = gmMedication.create_drug_product (
                        product_name = product_name,
                        preparation = prep,
                        return_existing = True,
                        # indications are ref.dose rows
                        doses = indications,
                        link_obj = conn
                )
                #conn.commit()
                vacc_prod['atc'] = 'J07'
                vacc_prod.save(conn = conn)
                pk_drug_product = vacc_prod['pk_drug_product']
        cmd = '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 = gmPG2.run_rw_queries(link_obj = conn, queries = queries, return_data = True, end_tx = True)
        conn.close()
        return cVaccine(aPK_obj = rows[0]['pk'])

#------------------------------------------------------------
def delete_vaccine(vaccine=None):

        cmd = 'DELETE FROM ref.vaccine WHERE pk = %(pk)s'
        args = {'pk': vaccine}

        try:
                gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
        except gmPG2.dbapi.IntegrityError:
                _log.exception('cannot delete vaccine [%s]', vaccine)
                return False

        return True

#------------------------------------------------------------
def get_vaccines(order_by=None, return_pks=False):

        if order_by is None:
                cmd = _SQL_get_vaccine_fields % 'TRUE'
        else:
                cmd = _SQL_get_vaccine_fields % ('TRUE\nORDER BY %s' % order_by)

        rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
        if return_pks:
                return [ r['pk_vaccine'] for r in rows ]
        return [ cVaccine(row = {'data': r, 'pk_field': 'pk_vaccine'}) for r in rows ]

#============================================================
# vaccination related classes
#============================================================
_SQL_get_vaccination_fields = """SELECT * FROM clin.v_vaccinations WHERE %s"""

class cVaccination(gmBusinessDBObject.cBusinessDBObject):

        _cmd_fetch_payload = _SQL_get_vaccination_fields % "pk_vaccination = %s"

        _cmds_store_payload = [
                """UPDATE clin.vaccination SET
                                soap_cat = %(soap_cat)s,
                                clin_when = %(date_given)s,
                                site = gm.nullify_empty_string(%(site)s),
                                batch_no = gm.nullify_empty_string(%(batch_no)s),
                                reaction = gm.nullify_empty_string(%(reaction)s),
                                narrative = gm.nullify_empty_string(%(comment)s),
                                fk_vaccine = %(pk_vaccine)s,
                                fk_provider = %(pk_provider)s,
                                fk_encounter = %(pk_encounter)s,
                                fk_episode = %(pk_episode)s
                        WHERE
                                pk = %(pk_vaccination)s
                                        AND
                                xmin = %(xmin_vaccination)s
                        RETURNING
                                xmin as xmin_vaccination
                """
        ]

        _updatable_fields = [
                'soap_cat',
                'date_given',
                'site',
                'batch_no',
                'reaction',
                'comment',
                'pk_vaccine',
                'pk_provider',
                'pk_encounter',
                'pk_episode'
        ]

        #--------------------------------------------------------
        def format_maximum_information(self, patient=None):
                return self.format (
                        with_indications = True,
                        with_comment = True,
                        with_reaction = True,
                        date_format = '%Y %b %d'
                )

        #--------------------------------------------------------
        def format(self, with_indications=False, with_comment=False, with_reaction=False, date_format='%Y-%m-%d'):

                lines = []

                lines.append (' %s: %s [%s]%s' % (
                        self._payload['date_given'].strftime(date_format),
                        self._payload['vaccine'],
                        self._payload['batch_no'],
                        gmTools.coalesce(self._payload['site'], '', ' (%s)')
                ))

                if with_comment:
                        if self._payload['comment'] is not None:
                                lines.append('   %s' % self._payload['comment'])

                if with_reaction:
                        if self._payload['reaction'] is not None:
                                lines.append('   %s' % self._payload['reaction'])

                if with_indications:
                        lines.append('   %s' % ' / '.join([ i['l10n_indication'] for i in self._payload['indications'] ]))

                return lines

        #--------------------------------------------------------
        def format_for_failsafe_output(self, max_width:int=80) -> list[str]:
                lines = []
                lines.append(gmTools.shorten_text(
                        '%s: %s [%s]%s' % (
                                self._payload['date_given'].strftime('%Y %b %d'),
                                self._payload['vaccine'],
                                self._payload['batch_no'],
                                gmTools.coalesce(self._payload['site'], '', ' (%s)')
                        ),
                        max_width
                ))
                lines.append('  ' + _('Indications: %s') % ' / '.join([ i['l10n_indication'] for i in self._payload['indications'] ]))
                if self._payload['comment']:
                        lines.append(gmTools.shorten_text('  ' + self._payload['comment'], max_width))
                if self._payload['reaction']:
                        lines.append(gmTools.shorten_text('  ' + self._payload['reaction'], max_width))
                return lines

        #--------------------------------------------------------
        def _get_vaccine(self):
                return cVaccine(aPK_obj = self._payload['pk_vaccine'])

        vaccine = property(_get_vaccine)

#------------------------------------------------------------
def get_vaccinations(pk_identity=None, pk_episodes=None, pk_health_issues=None, pk_encounters=None, order_by=None, return_pks=False):

        args = {}
        where_parts = []

        if pk_identity is not None:
                args = {'pk_identity': pk_identity}
                where_parts.append('pk_patient = %(pk_identity)s')

        if (pk_episodes is not None) and (len(pk_episodes) > 0):
                where_parts.append('pk_episode = ANY(%(pk_epis)s)')
                args['pk_epis'] = pk_episodes

        if (pk_health_issues is not None) and (len(pk_health_issues) > 0):
                where_parts.append('pk_episode = ANY(SELECT pk FROM clin.episode WHERE fk_health_issue = ANY(%(pk_issues)s))')
                args['pk_issues'] = pk_health_issues

        if (pk_encounters is not None) and (len(pk_encounters) > 0):
                where_parts.append('pk_encounter = ANY(%(pk_encs)s)')
                args['pk_encs'] = pk_encounters

        ORDER_BY = gmTools.coalesce (
                value2test = order_by,
                return_instead = '',
                value2return = 'ORDER BY %s' % order_by
        )
        if len(where_parts) == 0:
                WHERE = 'True'
        else:
                WHERE = '\nAND '.join(where_parts)

        SQL = '%s %s' % (
                _SQL_get_vaccination_fields % WHERE,
                ORDER_BY
        )
        rows = gmPG2.run_ro_queries(queries = [{'cmd': SQL, 'args': args}])
        if return_pks:
                return [ r['pk_vaccination'] for r in rows ]

        vaccs = [ cVaccination(row = {'data': r, 'pk_field': 'pk_vaccination'}) for r in rows ]
        return vaccs

#------------------------------------------------------------
def format_vaccinations_by_indication_for_failsafe_output(pk_patient:int, max_width:int=80) -> list[str]:
        shots = get_vaccinations(pk_identity = pk_patient)
        if not shots:
                return []

        shots_by_ind:dict[str, list[cVaccination]] = {}
        for shot in shots:
                inds = shot['indications']
                for ind in inds:
                        try:
                                shots_by_ind[ind['l10n_indication']]
                        except KeyError:
                                shots_by_ind[ind['l10n_indication']] = []
                        shots_by_ind[ind['l10n_indication']].append(shot)
        lines = []
        l10n_inds_sorted = sorted(shots_by_ind.keys(), key = functools.cmp_to_key(locale.strcoll))
        for ind in l10n_inds_sorted:
                lines.append('')
                lines.append(_('Indication: %s') % ind)
                shots4ind = sorted(shots_by_ind[ind], key = lambda d: d['date_given'], reverse = True)
                for shot in shots4ind:
                        lines.append(gmTools.shorten_text (
                                '  %s: %s [%s]' % (
                                        shot['date_given'].strftime('%Y %b %d'),
                                        shot['vaccine'],
                                        shot['batch_no']
                                ),
                                max_width
                        ))
                        if shot['comment']:
                                lines.append(gmTools.shorten_text('   ' + shot['comment'], max_width))
                        if shot._payload['reaction']:
                                lines.append(gmTools.shorten_text('   ' + shot['reaction'], max_width))
        return lines

#------------------------------------------------------------
def create_vaccination(encounter=None, episode=None, vaccine=None, batch_no=None):

        cmd = """
                INSERT INTO clin.vaccination (
                        fk_encounter,
                        fk_episode,
                        fk_vaccine,
                        batch_no
                ) VALUES (
                        %(enc)s,
                        %(epi)s,
                        %(vacc)s,
                        %(batch)s
                ) RETURNING pk;
        """
        args = {
                'enc': encounter,
                'epi': episode,
                'vacc': vaccine,
                'batch': batch_no
        }
        rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
        return cVaccination(aPK_obj = rows[0][0])

#------------------------------------------------------------
def delete_vaccination(vaccination=None):
        cmd = """DELETE FROM clin.vaccination WHERE pk = %(pk)s"""
        args = {'pk': vaccination}

        gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])

#------------------------------------------------------------

def format_latest_vaccinations(output_format='latex', emr=None):

        _log.debug('formatting latest vaccinations into [%s]', output_format)

        vaccs = emr.get_latest_vaccinations()

        if output_format == 'latex':
                return __format_latest_vaccinations_latex(vaccinations = vaccs)

        msg = _('unknown vaccinations output format [%s]') % output_format
        _log.error(msg)
        return msg

#------------------------------------------------------------

def __format_latest_vaccinations_latex(vaccinations=None):

        if len(vaccinations) == 0:
                return '\\noindent %s' % _('No vaccinations recorded.')

        tex =  '\\noindent %s {\\tiny (%s)\\par}\n' % (_('Latest vaccinations'), _('per target condition'))
        tex += '\n'
        tex += '\\noindent \\begin{tabular}{|l|l|l|l|l|l|}\n'
        tex += '\\hline\n'
        tex += '%s & %s & {\\footnotesize %s} & {\\footnotesize %s} & {\\footnotesize %s\\footnotemark} & {\\footnotesize $\\Sigma$\\footnotemark}\\\\\n' % (
                _('Target'),
                _('Last given'),
                _('Vaccine'),
                _('Lot \#'),
                _('SoaP')
        )
        tex += '\\hline\n'
        tex += '\n'
        tex += '\\hline\n'
        tex += '%s'                     # this is where the actual vaccination rows end up
        tex += '\n'
        tex += '\\end{tabular}\n'
        tex += '\n'
        tex += '\\addtocounter{footnote}{-1}\n'
        tex += '\\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 += '\\addtocounter{footnote}{1}\n'
        tex += '\\footnotetext{$\\Sigma$ -- %s}\n' % _('Total number of vaccinations recorded for the corresponding target condition.')
        tex += '\n'

        row_template = '%s & %s & {\\scriptsize %s} & {\\scriptsize %s} & {\\scriptsize %s} & {\\scriptsize %s}\\\\\n'
        lines = ''
        targets = sorted(vaccinations)
        for target in targets:
                target_count, vacc = vaccinations[target]
                lines += row_template % (
                        target,
                        gmDateTime.pydt_strftime(vacc['date_given'], '%Y %b %d'),
                        vacc['vaccine'],
                        gmTools.tex_escape_string(vacc['batch_no'].strip()),
                        vacc['soap_cat'].upper(),
                        target_count
                )
                if vacc['site'] is not None:
                        lines += ' & \\multicolumn{5}{l|}{\\scriptsize %s: %s\\par}\\\\\n' % (_('Injection site'), vacc['site'].strip())
                if vacc['reaction'] is not None:
                        lines += ' & \\multicolumn{5}{l|}{\\scriptsize %s: %s\\par}\\\\\n' % (_('Reaction'), vacc['reaction'].strip())
                if vacc['comment'] is not None:
                        lines += ' & \\multicolumn{5}{l|}{\\scriptsize %s: %s\\par}\\\\\n' % (_('Comment'), vacc['comment'].strip())
                lines += '\\hline\n'

        return tex % lines

#============================================================
# main - unit testing
#------------------------------------------------------------
if __name__ == '__main__':

        if len(sys.argv) < 2:
                sys.exit()

        if sys.argv[1] != 'test':
                sys.exit()

        del _
        from Gnumed.pycommon import gmI18N
        gmI18N.activate_locale()
        gmI18N.install_domain()

#       from Gnumed.pycommon import gmPG
        #--------------------------------------------------------
        def test_vacc():
                vacc = cVaccination(aPK_obj=1)
                print(vacc)
                fields = vacc.get_fields()
                for field in fields:
                        print(field, ':', vacc[field])
                print("updatable:", vacc.get_updatable_fields())

#       #--------------------------------------------------------
#       def test_due_vacc():
#               # Test for a due vaccination
#               pk_args = {
#                       'pat_id': 12,
#                       'indication': 'meningococcus C',
#                       'seq_no': 1
#               }
#               missing_vacc = cMissingVaccination(aPK_obj=pk_args)
#               fields = missing_vacc.get_fields()
#               print("\nDue vaccination:")
#               print(missing_vacc)
#               for field in fields:
#                       print(field, ':', missing_vacc[field])
#               # Test for an overdue vaccination
#               pk_args = {
#                       'pat_id': 12,
#                       'indication': 'haemophilus influenzae b',
#                       'seq_no': 2
#               }
#               missing_vacc = cMissingVaccination(aPK_obj=pk_args)
#               fields = missing_vacc.get_fields()
#               print("\nOverdue vaccination (?):")
#               print(missing_vacc)
#               for field in fields:
#                       print(field, ':', missing_vacc[field])

#       #--------------------------------------------------------
#       def test_due_booster():
#               pk_args = {
#                       'pat_id': 12,
#                       'indication': 'tetanus'
#               }
#               missing_booster = cMissingBooster(aPK_obj=pk_args)
#               fields = missing_booster.get_fields()
#               print("\nDue booster:")
#               print(missing_booster)
#               for field in fields:
#                       print(field, ':', missing_booster[field])

        #--------------------------------------------------------
#       def test_scheduled_vacc():
#               scheduled_vacc = cScheduledVaccination(aPK_obj=20)
#               print("\nScheduled vaccination:")
#               print(scheduled_vacc)
#               fields = scheduled_vacc.get_fields()
#               for field in fields:
#                       print(field, ':', scheduled_vacc[field])
#               print("updatable:", scheduled_vacc.get_updatable_fields())

#       #--------------------------------------------------------
#       def test_vaccination_course():
#               vaccination_course = cVaccinationCourse(aPK_obj=7)
#               print("\nVaccination course:")
#               print(vaccination_course)
#               fields = vaccination_course.get_fields()
#               for field in fields:
#                       print(field, ':', vaccination_course[field])
#               print("updatable:", vaccination_course.get_updatable_fields())

        #--------------------------------------------------------
#       def test_put_patient_on_schedule():
#               result, msg = put_patient_on_schedule(patient_id=12, course_id=1)
#               print('\nPutting patient id 12 on schedule id 1... %s (%s)' % (result, msg))

        #--------------------------------------------------------
        def test_get_vaccines():
                for vaccine in get_vaccines():
                        print('--------------------------------')
                        #print u'%s' % vaccine
                        print('\n'.join(vaccine.format()))

        #--------------------------------------------------------
        def test_get_vaccinations():
                #v1 = get_vaccinations(return_pks = True, order_by = 'date_given')
                #print(v1)
                for v in get_vaccinations(order_by = 'date_given, vaccine'):
                        print('\n'.join(v.format_for_failsafe_output()))

        #--------------------------------------------------------
        def test_create_generic_vaccine_sql():
                print(create_generic_vaccine_sql('22.0'))

        #--------------------------------------------------------
        def test_write_generic_vaccine_sql(version, filename):
                print(write_generic_vaccine_sql (
                        version,
                        include_indications_mapping = True,
                        filename = filename
                ))

        #--------------------------------------------------------
        def test_format_vaccs_failsafe():
                print('\n'.join(format_vaccinations_by_indication_for_failsafe_output(pk_patient = 12)))

        #--------------------------------------------------------
        gmPG2.request_login_params(setup_pool = True)
        #test_vaccination_course()
        #test_put_patient_on_schedule()
        #test_scheduled_vacc()
        #test_vacc()
        #test_due_vacc()
        #test_due_booster()

        test_get_vaccines()
        #test_get_vaccinations()
        #test_format_vaccs_failsafe()
        #test_create_generic_vaccine_sql()
        #test_write_generic_vaccine_sql(sys.argv[2], sys.argv[3])

Functions

def create_generic_vaccine_sql(version, include_indications_mapping=False)
Expand source code
def create_generic_vaccine_sql(version, include_indications_mapping=False):

        _log.debug('including indications mapping table with generic vaccines creation SQL: %s', include_indications_mapping)

        from Gnumed.business import gmVaccDefs

        sql_create_substances = []
        sql_populate_ind2subst_map = []
        sql_create_vaccines = []

        for substance_tag in gmVaccDefs._VACCINE_SUBSTANCES:
                subst = gmVaccDefs._VACCINE_SUBSTANCES[substance_tag]
                args = {
                        'substance_tag': substance_tag,
                        'atc': subst['atc4target'],
                        'desc': subst['name'],
                        'orig': subst['target'].split('::')[0],
                        'trans': subst['target'].split('::')[-1]
                }
                sql_create_substances.append(_SQL_create_substance4vaccine % args)
                try:
                        for v21_ind in subst['v21_indications']:
                                args['v21_ind'] = v21_ind
                                args['is_live'] = 'false'
                                sql_populate_ind2subst_map.append(_SQL_map_indication2substance % args)
                except KeyError:
                        pass
                try:
                        for v21_ind in subst['v21_indications_live']:
                                args['v21_ind'] = v21_ind
                                args['is_live'] = 'true'
                                sql_populate_ind2subst_map.append(_SQL_map_indication2substance % args)
                except KeyError:
                        pass
                args = {}

        for key in gmVaccDefs._GENERIC_VACCINES:
                vaccine_def = gmVaccDefs._GENERIC_VACCINES[key]
                # create product
                args = {
                        'atc_prod': vaccine_def['atc'],
                        'prod_name': vaccine_def['name'],
                        # generic vaccines always have the English preparation
                        'prep': 'vaccine',
                        'is_live': vaccine_def['live']
                }
                sql_create_vaccines.append(_SQL_create_vacc_product % args)
                # create doses
                for ingredient_tag in vaccine_def['ingredients']:
                        vacc_subst_def = gmVaccDefs._VACCINE_SUBSTANCES[ingredient_tag]
                        args['atc_subst'] = vacc_subst_def['atc4target']
                        args['name_subst'] = vacc_subst_def['name']
                        # substance already created, only need to create dose
                        sql_create_vaccines.append(_SQL_create_vacc_subst_dose % args)
                        # link dose to product
                        sql_create_vaccines.append(_SQL_link_dose2vacc_prod % args)
                        # the following does not work because there are mixed vaccines
                        # any live ingredients included ?
#                       if vacc_subst_def.has_key('v21_indications_live'):
#                               if vaccine_def['live'] is False:
#                                       print vaccine_def
#                                       raise Exception('vaccine def says "NOT live" but ingredients DO map to <v21_indications_LIVE>')
#                       if vacc_subst_def.has_key('v21_indications'):
#                               if vaccine_def['live'] is True:
#                                       print vaccine_def
#                                       raise Exception('vaccine def says "live" but ingredients do NOT map to v21_indications_LIVE')

                # create vaccine
                sql_create_vaccines.append(_SQL_create_vaccine % args)

        # join
        sql = _SQL_create_generic_vaccines_script % (
                gmTools.bool2subst (
                        include_indications_mapping,
                        _SQL_create_indications_mapping_table,
                        '-- indications mapping table not included'
                ),
                '\n\n'.join(sql_create_substances),
                '\n\n'.join(sql_create_vaccines),
                gmTools.bool2subst (
                        include_indications_mapping,
                        '\n\n'.join(sql_populate_ind2subst_map),
                        '-- indications mapping table not populated'
                ),
                version,
                version
        )
        return sql
def create_vaccination(encounter=None, episode=None, vaccine=None, batch_no=None)
Expand source code
def create_vaccination(encounter=None, episode=None, vaccine=None, batch_no=None):

        cmd = """
                INSERT INTO clin.vaccination (
                        fk_encounter,
                        fk_episode,
                        fk_vaccine,
                        batch_no
                ) VALUES (
                        %(enc)s,
                        %(epi)s,
                        %(vacc)s,
                        %(batch)s
                ) RETURNING pk;
        """
        args = {
                'enc': encounter,
                'epi': episode,
                'vacc': vaccine,
                'batch': batch_no
        }
        rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
        return cVaccination(aPK_obj = rows[0][0])
def create_vaccine(pk_drug_product=None, product_name=None, indications=None, is_live=None)
Expand source code
def create_vaccine(pk_drug_product=None, product_name=None, indications=None, is_live=None):

        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 = 'vaccine'
                _log.debug('creating vaccine drug product [%s %s]', product_name, prep)
                vacc_prod = gmMedication.create_drug_product (
                        product_name = product_name,
                        preparation = prep,
                        return_existing = True,
                        # indications are ref.dose rows
                        doses = indications,
                        link_obj = conn
                )
                #conn.commit()
                vacc_prod['atc'] = 'J07'
                vacc_prod.save(conn = conn)
                pk_drug_product = vacc_prod['pk_drug_product']
        cmd = '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 = gmPG2.run_rw_queries(link_obj = conn, queries = queries, return_data = True, end_tx = True)
        conn.close()
        return cVaccine(aPK_obj = rows[0]['pk'])
def delete_vaccination(vaccination=None)
Expand source code
def delete_vaccination(vaccination=None):
        cmd = """DELETE FROM clin.vaccination WHERE pk = %(pk)s"""
        args = {'pk': vaccination}

        gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
def delete_vaccine(vaccine=None)
Expand source code
def delete_vaccine(vaccine=None):

        cmd = 'DELETE FROM ref.vaccine WHERE pk = %(pk)s'
        args = {'pk': vaccine}

        try:
                gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
        except gmPG2.dbapi.IntegrityError:
                _log.exception('cannot delete vaccine [%s]', vaccine)
                return False

        return True
def format_latest_vaccinations(output_format='latex', emr=None)
Expand source code
def format_latest_vaccinations(output_format='latex', emr=None):

        _log.debug('formatting latest vaccinations into [%s]', output_format)

        vaccs = emr.get_latest_vaccinations()

        if output_format == 'latex':
                return __format_latest_vaccinations_latex(vaccinations = vaccs)

        msg = _('unknown vaccinations output format [%s]') % output_format
        _log.error(msg)
        return msg
def format_vaccinations_by_indication_for_failsafe_output(pk_patient: int, max_width: int = 80) ‑> list[str]
Expand source code
def format_vaccinations_by_indication_for_failsafe_output(pk_patient:int, max_width:int=80) -> list[str]:
        shots = get_vaccinations(pk_identity = pk_patient)
        if not shots:
                return []

        shots_by_ind:dict[str, list[cVaccination]] = {}
        for shot in shots:
                inds = shot['indications']
                for ind in inds:
                        try:
                                shots_by_ind[ind['l10n_indication']]
                        except KeyError:
                                shots_by_ind[ind['l10n_indication']] = []
                        shots_by_ind[ind['l10n_indication']].append(shot)
        lines = []
        l10n_inds_sorted = sorted(shots_by_ind.keys(), key = functools.cmp_to_key(locale.strcoll))
        for ind in l10n_inds_sorted:
                lines.append('')
                lines.append(_('Indication: %s') % ind)
                shots4ind = sorted(shots_by_ind[ind], key = lambda d: d['date_given'], reverse = True)
                for shot in shots4ind:
                        lines.append(gmTools.shorten_text (
                                '  %s: %s [%s]' % (
                                        shot['date_given'].strftime('%Y %b %d'),
                                        shot['vaccine'],
                                        shot['batch_no']
                                ),
                                max_width
                        ))
                        if shot['comment']:
                                lines.append(gmTools.shorten_text('   ' + shot['comment'], max_width))
                        if shot._payload['reaction']:
                                lines.append(gmTools.shorten_text('   ' + shot['reaction'], max_width))
        return lines
def get_vaccinations(pk_identity=None, pk_episodes=None, pk_health_issues=None, pk_encounters=None, order_by=None, return_pks=False)
Expand source code
def get_vaccinations(pk_identity=None, pk_episodes=None, pk_health_issues=None, pk_encounters=None, order_by=None, return_pks=False):

        args = {}
        where_parts = []

        if pk_identity is not None:
                args = {'pk_identity': pk_identity}
                where_parts.append('pk_patient = %(pk_identity)s')

        if (pk_episodes is not None) and (len(pk_episodes) > 0):
                where_parts.append('pk_episode = ANY(%(pk_epis)s)')
                args['pk_epis'] = pk_episodes

        if (pk_health_issues is not None) and (len(pk_health_issues) > 0):
                where_parts.append('pk_episode = ANY(SELECT pk FROM clin.episode WHERE fk_health_issue = ANY(%(pk_issues)s))')
                args['pk_issues'] = pk_health_issues

        if (pk_encounters is not None) and (len(pk_encounters) > 0):
                where_parts.append('pk_encounter = ANY(%(pk_encs)s)')
                args['pk_encs'] = pk_encounters

        ORDER_BY = gmTools.coalesce (
                value2test = order_by,
                return_instead = '',
                value2return = 'ORDER BY %s' % order_by
        )
        if len(where_parts) == 0:
                WHERE = 'True'
        else:
                WHERE = '\nAND '.join(where_parts)

        SQL = '%s %s' % (
                _SQL_get_vaccination_fields % WHERE,
                ORDER_BY
        )
        rows = gmPG2.run_ro_queries(queries = [{'cmd': SQL, 'args': args}])
        if return_pks:
                return [ r['pk_vaccination'] for r in rows ]

        vaccs = [ cVaccination(row = {'data': r, 'pk_field': 'pk_vaccination'}) for r in rows ]
        return vaccs
def get_vaccines(order_by=None, return_pks=False)
Expand source code
def get_vaccines(order_by=None, return_pks=False):

        if order_by is None:
                cmd = _SQL_get_vaccine_fields % 'TRUE'
        else:
                cmd = _SQL_get_vaccine_fields % ('TRUE\nORDER BY %s' % order_by)

        rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
        if return_pks:
                return [ r['pk_vaccine'] for r in rows ]
        return [ cVaccine(row = {'data': r, 'pk_field': 'pk_vaccine'}) for r in rows ]
def write_generic_vaccine_sql(version, include_indications_mapping=False, filename=None)
Expand source code
def write_generic_vaccine_sql(version, include_indications_mapping=False, filename=None):
        if filename is None:
                filename = gmTools.get_unique_filename(suffix = '.sql')
        _log.debug('writing SQL for creating generic vaccines to: %s', filename)
        sql_file = open(filename, mode = 'wt', encoding = 'utf8')
        sql_file.write(create_generic_vaccine_sql (
                version,
                include_indications_mapping = include_indications_mapping
        ))
        sql_file.close()
        return filename

Classes

class cVaccination (aPK_obj: int | dict = None, row: dict = None, link_obj=None)

Represents business objects in the database.

Rules

  • instances ARE ASSUMED TO EXIST in the database
  • PK construction (aPK_obj): DOES verify its existence on instantiation (fetching data fails)
  • Row construction (row): allowed by using a dict of pairs of field name: field value (PERFORMANCE improvement)
  • does NOT verify FK target existence
  • does NOT create new entries in the database
  • does NOT lazy-fetch fields on access

Class scope SQL commands and variables:

_cmd_fetch_payload:

  • must return exactly one row
  • WHERE clause argument values are expected in self.pk_obj (taken from init(aPK_obj))
  • must return xmin of all rows that _cmds_store_payload will be updating, so views must support the xmin columns of their underlying tables

_cmds_store_payload:

  • one or multiple "update … set … where xmin_ = … and pk = …" statements which actually update the database from the data in self._payload,
  • the last query must refetch at least the XMIN values needed to detect concurrent updates, their field names had better be the same as in _cmd_fetch_payload,
  • the last query CAN return other fields which is particularly useful when those other fields are computed in the backend and may thus change upon save but will not have been set by the client code explicitly - this is only really of concern if the saved subclass is to be reused after saving rather than re-instantiated
  • when subclasses tend to live a while after save_payload() was called and they support computed fields (say, _(some_column) you need to return all columns (see cEncounter)

_updatable_fields:

  • a list of fields available for update via object['field']

Call init from child classes like so:

    super().__init__(aPK_obj = aPK_obj, row = row, link_obj = link_obj)

Args

aPK_obj
retrieve data from backend
  • an scalar value the ._cmd_fetch_payload WHERE condition must be a simple column: "… WHERE pk_col = %s"
  • a dictionary of values the ._cmd_fetch_payload WHERE condition must consume the dictionary and produce a unique row
row
must hold the fields
  • data: list of column values for the row selected by ._cmd_fetch_payload (as returned by cursor.fetchone() in the DB-API)
  • pk_field: the name of the primary key column OR
  • pk_obj: a dictionary suitable for being passed to cursor.execute and holding the primary key values, used for composite PKs
  • for example:
    row = {
            'data': rows[0],
            'pk_field': 'pk_XXX (the PK column name)',
            'pk_obj': {'pk_col1': pk_col1_val, 'pk_col2': pk_col2_val}
    }
    
    rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
    objects = [ cChildClass(row = {'data': r, 'pk_field': 'the PK column name'}) for r in rows ]
    
Expand source code
class cVaccination(gmBusinessDBObject.cBusinessDBObject):

        _cmd_fetch_payload = _SQL_get_vaccination_fields % "pk_vaccination = %s"

        _cmds_store_payload = [
                """UPDATE clin.vaccination SET
                                soap_cat = %(soap_cat)s,
                                clin_when = %(date_given)s,
                                site = gm.nullify_empty_string(%(site)s),
                                batch_no = gm.nullify_empty_string(%(batch_no)s),
                                reaction = gm.nullify_empty_string(%(reaction)s),
                                narrative = gm.nullify_empty_string(%(comment)s),
                                fk_vaccine = %(pk_vaccine)s,
                                fk_provider = %(pk_provider)s,
                                fk_encounter = %(pk_encounter)s,
                                fk_episode = %(pk_episode)s
                        WHERE
                                pk = %(pk_vaccination)s
                                        AND
                                xmin = %(xmin_vaccination)s
                        RETURNING
                                xmin as xmin_vaccination
                """
        ]

        _updatable_fields = [
                'soap_cat',
                'date_given',
                'site',
                'batch_no',
                'reaction',
                'comment',
                'pk_vaccine',
                'pk_provider',
                'pk_encounter',
                'pk_episode'
        ]

        #--------------------------------------------------------
        def format_maximum_information(self, patient=None):
                return self.format (
                        with_indications = True,
                        with_comment = True,
                        with_reaction = True,
                        date_format = '%Y %b %d'
                )

        #--------------------------------------------------------
        def format(self, with_indications=False, with_comment=False, with_reaction=False, date_format='%Y-%m-%d'):

                lines = []

                lines.append (' %s: %s [%s]%s' % (
                        self._payload['date_given'].strftime(date_format),
                        self._payload['vaccine'],
                        self._payload['batch_no'],
                        gmTools.coalesce(self._payload['site'], '', ' (%s)')
                ))

                if with_comment:
                        if self._payload['comment'] is not None:
                                lines.append('   %s' % self._payload['comment'])

                if with_reaction:
                        if self._payload['reaction'] is not None:
                                lines.append('   %s' % self._payload['reaction'])

                if with_indications:
                        lines.append('   %s' % ' / '.join([ i['l10n_indication'] for i in self._payload['indications'] ]))

                return lines

        #--------------------------------------------------------
        def format_for_failsafe_output(self, max_width:int=80) -> list[str]:
                lines = []
                lines.append(gmTools.shorten_text(
                        '%s: %s [%s]%s' % (
                                self._payload['date_given'].strftime('%Y %b %d'),
                                self._payload['vaccine'],
                                self._payload['batch_no'],
                                gmTools.coalesce(self._payload['site'], '', ' (%s)')
                        ),
                        max_width
                ))
                lines.append('  ' + _('Indications: %s') % ' / '.join([ i['l10n_indication'] for i in self._payload['indications'] ]))
                if self._payload['comment']:
                        lines.append(gmTools.shorten_text('  ' + self._payload['comment'], max_width))
                if self._payload['reaction']:
                        lines.append(gmTools.shorten_text('  ' + self._payload['reaction'], max_width))
                return lines

        #--------------------------------------------------------
        def _get_vaccine(self):
                return cVaccine(aPK_obj = self._payload['pk_vaccine'])

        vaccine = property(_get_vaccine)

Ancestors

Instance variables

var vaccine
Expand source code
def _get_vaccine(self):
        return cVaccine(aPK_obj = self._payload['pk_vaccine'])

Methods

def format_for_failsafe_output(self, max_width: int = 80) ‑> list[str]
Expand source code
def format_for_failsafe_output(self, max_width:int=80) -> list[str]:
        lines = []
        lines.append(gmTools.shorten_text(
                '%s: %s [%s]%s' % (
                        self._payload['date_given'].strftime('%Y %b %d'),
                        self._payload['vaccine'],
                        self._payload['batch_no'],
                        gmTools.coalesce(self._payload['site'], '', ' (%s)')
                ),
                max_width
        ))
        lines.append('  ' + _('Indications: %s') % ' / '.join([ i['l10n_indication'] for i in self._payload['indications'] ]))
        if self._payload['comment']:
                lines.append(gmTools.shorten_text('  ' + self._payload['comment'], max_width))
        if self._payload['reaction']:
                lines.append(gmTools.shorten_text('  ' + self._payload['reaction'], max_width))
        return lines
def format_maximum_information(self, patient=None)
Expand source code
def format_maximum_information(self, patient=None):
        return self.format (
                with_indications = True,
                with_comment = True,
                with_reaction = True,
                date_format = '%Y %b %d'
        )

Inherited members

class cVaccine (aPK_obj: int | dict = None, row: dict = None, link_obj=None)

Represents one vaccine.

Call init from child classes like so:

    super().__init__(aPK_obj = aPK_obj, row = row, link_obj = link_obj)

Args

aPK_obj
retrieve data from backend
  • an scalar value the ._cmd_fetch_payload WHERE condition must be a simple column: "… WHERE pk_col = %s"
  • a dictionary of values the ._cmd_fetch_payload WHERE condition must consume the dictionary and produce a unique row
row
must hold the fields
  • data: list of column values for the row selected by ._cmd_fetch_payload (as returned by cursor.fetchone() in the DB-API)
  • pk_field: the name of the primary key column OR
  • pk_obj: a dictionary suitable for being passed to cursor.execute and holding the primary key values, used for composite PKs
  • for example:
    row = {
            'data': rows[0],
            'pk_field': 'pk_XXX (the PK column name)',
            'pk_obj': {'pk_col1': pk_col1_val, 'pk_col2': pk_col2_val}
    }
    
    rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
    objects = [ cChildClass(row = {'data': r, 'pk_field': 'the PK column name'}) for r in rows ]
    
Expand source code
class cVaccine(gmBusinessDBObject.cBusinessDBObject):
        """Represents one vaccine."""

        _cmd_fetch_payload = _SQL_get_vaccine_fields % "pk_vaccine = %s"

        _cmds_store_payload = [
                """UPDATE ref.vaccine SET
                                is_live = %(is_live)s,
                                min_age = %(min_age)s,
                                max_age = %(max_age)s,
                                comment = gm.nullify_empty_string(%(comment)s),
                                fk_drug_product = %(pk_drug_product)s
                        WHERE
                                pk = %(pk_vaccine)s
                                        AND
                                xmin = %(xmin_vaccine)s
                        RETURNING
                                xmin as xmin_vaccine
                """
        ]

        _updatable_fields = [
                'is_live',
                'min_age',
                'max_age',
                'comment',
                'pk_drug_product'
        ]

        #--------------------------------------------------------
        def format(self, *args, **kwargs):
                lines = []
                lines.append(_('%s with %s %s     #%s') % (
                        gmTools.bool2subst(self._payload['is_live'], _('Vaccine (live)'), _('Vaccine'), '<liveness error in DB>'),
                        len(self._payload['indications']),
                        gmTools.bool2subst(len(self._payload['indications']) == 1, _('indication'), _('indications'), _('indication(s)')),
                        self._payload['pk_vaccine']
                ))
                lines.append(_(' Product: "%s"     #%s') % (
                        self._payload['vaccine'],
                        self._payload['pk_drug_product']
                ))
                lines.append(_('  %s%s%s%s') % (
                        self._payload['l10n_preparation'],
                        gmTools.coalesce(gmTools.bool2subst(self._payload['is_fake_vaccine'], _('fake product'), None, None), '', ', %s'),
                        gmTools.coalesce(self._payload['atc_code'], '', ' [ATC:%s]'),
                        gmTools.coalesce(self._payload['external_code'], '', ' [%s:%%s]' % self._payload['external_code_type'])
                ))
                #lines.append(_(u' %sage %s - %s') % (
                #       gmTools.coalesce(self._payload['route_description'], u'', u'%s, '),             #route_abbreviation
                lines.append(_(' Age %s - %s') % (
                        gmTools.coalesce(self._payload['min_age'], '?'),
                        gmTools.coalesce(self._payload['max_age'], '?')
                ))
                if self._payload['comment'] is not None:
                        lines.extend([ ' %s' % l for l in self._payload['comment'].split('\n')] )
                lines.append(_(' Indications'))
                lines.extend( [ '  %s [ATC:%s]' % (i['l10n_indication'], i['atc_indication']) for i in self._payload['indications'] ])

                return lines

        #--------------------------------------------------------
        # properties
        #--------------------------------------------------------
        def _get_product(self):
                return gmMedication.cDrugProduct(aPK_obj = self._payload['pk_drug_product'])

        product = property(_get_product)

        #--------------------------------------------------------
        def _get_is_in_use(self):
                cmd = 'SELECT EXISTS(SELECT 1 FROM clin.vaccination WHERE fk_vaccine = %(pk)s)'
                args = {'pk': self._payload['pk_vaccine']}
                rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
                return rows[0][0]

        is_in_use = property(_get_is_in_use)

Ancestors

Instance variables

var is_in_use
Expand source code
def _get_is_in_use(self):
        cmd = 'SELECT EXISTS(SELECT 1 FROM clin.vaccination WHERE fk_vaccine = %(pk)s)'
        args = {'pk': self._payload['pk_vaccine']}
        rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
        return rows[0][0]
var product
Expand source code
def _get_product(self):
        return gmMedication.cDrugProduct(aPK_obj = self._payload['pk_drug_product'])

Inherited members