# 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](https://zato.io/docs/dev/healthcare/dhis2/one-health/notifications.html) in the [One Health](https://zato.io/docs/dev/healthcare/dhis2/one-health/index.html) 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}

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`:

```ini
# config/user-conf/data_elements.ini

[ANIMAL_HEALTH]
Fw3kR9pLx2M = RABIES_ANIMAL
Gt8sV1cNz5Q = ANTHRAX_ANIMAL

[PUBLIC_HEALTH]
Jd6yH2bWq7K = RABIES_ANIMAL
Lp4mT9xEr3C = ANTHRAX_ANIMAL
```

```ini
# config/user-conf/one_health.ini

[counts]
unit_level = 3
```

## Read the counts {#read-the-counts}

Analytics returns one row per data element and veterinary unit, as data element, period, org unit and value:

```python
# -*- 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 {#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:

```python
    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 {#post-the-totals}

The totals are posted as one data value set, each value as a string:

```python
    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-schedule}

The service runs from a weekly [scheduler](https://zato.io/docs/dev/examples/scheduler.html) job, timed after the reporting deadline of the week and after the animal health instance has updated its analytics tables:

![Weekly zoonosis counts job](https://zatosource-production.b-cdn.net/docs/gfx/dhis2/one-health-counts-job-create.webp?v=1791212105)

Dashboard menu: Scheduler > Config

## Next {#next}

[Antimicrobial resistance](https://zato.io/docs/dev/healthcare/dhis2/one-health/amr.html) loads the WHONET exports of each laboratory.

## See also {#see-also}

- [Weekly data](https://zato.io/docs/dev/healthcare/dhis2/outbreak-alerts/weekly-data.html) - Analytics requests and DHIS2 periods
- [Config tables](https://zato.io/docs/dev/examples/config-tables.html) - Translate the codes of one party into another's

## Learn more {#learn-more}

- [Healthcare interface engine](https://zato.io/docs/dev/healthcare/) - Clinical messages, FHIR, conversion and operations
- [HL7 v2 parsing](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/) - Messages as typed objects, with field access and validation
- [MLLP channels](https://zato.io/docs/dev/healthcare/hl7v2/mllp/) - Receiving and sending HL7 v2 over MLLP sockets
- [FHIR integrations](https://zato.io/docs/dev/healthcare/hl7/fhir/) - Read and write FHIR resources, with paths, bundles and extensions
- [HL7 v2 to FHIR](https://zato.io/docs/dev/healthcare/hl7/to-fhir/) - Converting v2 messages into FHIR bundles, codes and references included
- [EDIFACT](https://zato.io/docs/dev/healthcare/edifact/) - Parsing interchanges, dialects and the transports they arrive on
- [Transformation](https://zato.io/docs/dev/healthcare/transformation/) - One message in, another standard out, in ordinary Python
- [Audit log](https://zato.io/docs/dev/healthcare/audit-log.html) - Every message with its acknowledgment, searchable by patient identifier
- [AI in clinical interfaces](https://zato.io/docs/dev/healthcare/ai/) - Services calling LLMs and AI agents calling clinical services as tools
