NHP malaria cases and deaths¶
The Central Bureau of Health Intelligence (CBHI) publishes the National Health Profile each year. Table 3.1.1 of NHP 2023, on page 120 of the PDF, gives malaria cases and deaths by State/UT for 2018-2022, from NCVBDC (whose monthly counts are in the MMIS report). The 2022 header reads 2022 (till Dec-Prov.).
The files are in pdfexorcist/recipes/nhp_malaria/: recipe.toml and nhp_malaria.py. The fixture is page 120 of the report, in tests/fixtures/nhp/.
The recipe¶
name = "NHP malaria cases and deaths"
[pages]
# skips the contents page, which repeats the title
match = 'Deaths due to Malaria in India.{0,300}Cases\s+Deaths'
[engines]
ocr = false
[parser]
type = "custom"
function = "nhp_malaria.py:parse"
key = ["page", "state", "col"]
# the States and UTs add up to India, in each year and measure
[[checks]]
column = "state"
rule = "India = rest"
by = ["page", "col"]
name = "States/UTs add up to India"
[output]
layout = "table"
rows = ["state"]
columns = "col"
format = "csv"
match also asks for the Cases Deaths header row, since the contents page repeats the title. On the whole report it keeps page 120 only.
A cell is keyed by State/UT and column, 2018 Cases to 2022 (Prov.) Deaths. The parser reads the years from the header. The printed serial number goes in sno; India and Daman & Diu have none.
Run it¶
pdfexorcist extract https://cbhidghs.mohfw.gov.in/sites/default/files/2024-09/Final-Central-Bureau-of-Health-Intelligene-July-2024.pdf --recipe pdfexorcist/recipes/nhp_malaria/recipe.toml -o nhp.csv
The whole report gives the same table as the fixture:
pdfexorcist extract tests/fixtures/nhp/nhp_2023_p120.pdf --recipe pdfexorcist/recipes/nhp_malaria/recipe.toml -o nhp.csv
✓ pdftotext 374 readings 0.0s
✓ pdfplumber 374 readings 0.1s
✓ pymupdf 374 readings 0.0s
✓ camelot 374 readings 1.5s
✓ pdfium 374 readings 0.0s
✓ Agreed 374 100.0% of cells
Unresolved 0
✓ Checks 1 rule all passed
Wrote nhp.csv 38 rows, table layout
state,sno,2018 Cases,2018 Deaths,2019 Cases,2019 Deaths,2020 Cases,2020 Deaths,2021 Cases,2021 Deaths,2022 (Prov.) Cases,2022 (Prov.) Deaths
India,,429928,96,338494,77,186532,93,161753,90,173975,64
Andhra Pradesh,1,6040,0,3104,0,2027,0,1315,0,2019,0
...
Chattisgarh,5,78717,39,60458,31,36667,34,29733,38,30028,11
...
Andaman & Nicobar Islands,29,259,0,202,0,85,2,27,0,46,0
Chandigarh,30,44,0,22,0,7,0,6,0,1,0
Dadra & Nagar Haveli,31,221,1,73,0,40,0,54,0,39,0
Daman & Diu,,24,0,8,0,,,,,,
...
Puducherry,36,54,1,21,0,15,0,5,0,0,0
The parser¶
parse() in nhp_malaria.py, in outline (pseudocode):
def parse(pages):
for page_no, lines in pages:
the first line with "State" and three or more years -> the header
each year, "2022 (Prov.)" when "(till" follows it
each later line: label, then its values
"29 Andaman & Nicobar" followed by "Islands" (no values) -> one name
"<serial> <name>" -> sno, state
values pair up with (year, Cases), (year, Deaths) from the left
-> yield {"page", "state", "col": "2018 Cases", ..., "sno", "year", "measure", "value"}
Four engines read Andaman & Nicobar and Islands as two lines and camelot as one cell; joining the wrapped line gives every engine the same name. Daman & Diu has values for 2018 and 2019 only, before it merged with Dadra & Nagar Haveli, so its four values fill the first four columns.
The check¶
States/UTs = India, in each of the ten columns. Every column adds up exactly, so the run exits with code 0.
The check passes when two rows swap values. In 2021 Uttar Pradesh has 13 cases and Uttarakhand 10792, the reverse of their 2020 and 2022 sizes. The output keeps them as printed.
tests/test_nhp_malaria.py compares every value with tests/fixtures/nhp/expected.csv, built from pdftotext -layout with a regular expression, and checks that changing India’s 2021 cases by one fails that column alone.