Zoonosis counts
Carry last week's counts of zoonoses from animal health analytics into the public health surveillance data set.
This page follows Zoonotic notifications in the One Health series. Once a week, the service below reads last week's counts of zoonoses per veterinary unit from animal health, adds them up per health district and posts them to the public health surveillance data set.
The data elements
The data elements differ between the instances, and data_elements.ini maps them the way org_units.ini maps the org units. The level of the veterinary units in the animal health hierarchy goes to one_health.ini:
# config/user-conf/data_elements.ini
[ANIMAL_HEALTH]
Fw3kR9pLx2M = RABIES_ANIMAL
Gt8sV1cNz5Q = ANTHRAX_ANIMAL
[PUBLIC_HEALTH]
Jd6yH2bWq7K = RABIES_ANIMAL
Lp4mT9xEr3C = ANTHRAX_ANIMAL
Read the counts
Analytics returns one row per data element and veterinary unit, as data element, period, org unit and value:
# -*- coding: utf-8 -*-
# stdlib
from collections import defaultdict
# Zato
from zato.server.service import Service
class CarryCounts(Service):
name = 'dhis2.one-health.carry-counts'
def read_counts(self) -> 'list':
# Every data element the file maps ..
animal_elements = self.config.data_elements.ANIMAL_HEALTH
element_ids = animal_elements.keys()
dx = ';'.join(element_ids)
# .. for last week and each veterinary unit.
level = self.config.one_health.counts.unit_level
dimensions = [f'dx:{dx}', 'pe:LAST_WEEK', f'ou:LEVEL-{level}']
params = {'dimension': dimensions}
conn = self.rest['Animal Health Analytics']
response = conn.get(params=params)
out = response.data['rows']
return out
Sum per district
Several veterinary units can belong to one district, so the counts are added up per district before they are sent. Each one sent separately would overwrite the one before it:
def sum_by_district(self, rows:'list') -> 'dict':
data_elements = self.config.data_elements
org_units = self.config.org_units
out = defaultdict(int)
for animal_element, period, uid, value in rows:
element = data_elements.translate(
source='ANIMAL_HEALTH', code=animal_element, target='PUBLIC_HEALTH')
org_unit = org_units.translate(
source='ANIMAL_HEALTH', code=uid, target='PUBLIC_HEALTH')
# A unit no district is mapped to is logged and left out ..
if org_unit is None:
self.logger.warning('No district for unit %s', uid)
continue
# .. and the rest add up per data element, week and district.
key = element, period, org_unit
count = int(value)
out[key] += count
return out
Post the totals
The totals are posted as one data value set, each value as a string:
def handle(self) -> 'None':
rows = self.read_counts()
totals = self.sum_by_district(rows)
data_values = []
for (data_element, period, org_unit), total in totals.items():
value = str(total)
data_value = {
'dataElement': data_element,
'period': period,
'orgUnit': org_unit,
'value': value,
}
data_values.append(data_value)
payload = {'dataValues': data_values}
conn = self.rest['Public Health Data Values']
response = conn.post(payload)
import_summary = response.data['response']
for conflict in import_summary['conflicts']:
self.logger.warning('DHIS2 conflict: %s', conflict['value'])
The period identifier comes from the analytics rows, so a week posted to public health is the week animal health reported it under. Analytics leaves out veterinary units with no cases, so a district with none has no value posted for the week. Data elements with a category combination other than the default also need a categoryOptionCombo in each data value.
The schedule
The service runs from a weekly scheduler job, timed after the reporting deadline of the week and after the animal health instance has updated its analytics tables:

Next
Antimicrobial resistance loads the WHONET exports of each laboratory.
See also
| Feature | What it does |
|---|---|
| Weekly data | Analytics requests and DHIS2 periods |
| Config tables | Translate the codes of one party into another's |