tanzania-postcodes 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tanzania_postcodes-0.1.0/PKG-INFO +110 -0
- tanzania_postcodes-0.1.0/README.md +89 -0
- tanzania_postcodes-0.1.0/pyproject.toml +34 -0
- tanzania_postcodes-0.1.0/setup.cfg +4 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes/__init__.py +86 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes/data/postcodes.json +1 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes.egg-info/PKG-INFO +110 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes.egg-info/SOURCES.txt +9 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes.egg-info/dependency_links.txt +1 -0
- tanzania_postcodes-0.1.0/tanzania_postcodes.egg-info/top_level.txt +1 -0
- tanzania_postcodes-0.1.0/tests/test_tanzania_postcodes.py +68 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tanzania-postcodes
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Real Tanzania postal codes, sourced directly from TCRA's official gazetted National Postcode System -- not a third-party compilation.
|
|
5
|
+
Author-email: Freddy Nyanda <freddynyanda@proton.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nyandajr/tanzania-postcodes
|
|
8
|
+
Project-URL: Repository, https://github.com/nyandajr/tanzania-postcodes
|
|
9
|
+
Project-URL: Source, https://www.tcra.go.tz/services/publication-of-postcode-list
|
|
10
|
+
Keywords: tanzania,postcode,postal-code,zip-code,tcra,address
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# tanzania-postcodes
|
|
23
|
+
|
|
24
|
+
Real Tanzania postal codes, sourced directly from **TCRA's official
|
|
25
|
+
gazetted National Postcode System** (Government Gazette Notice No. 240,
|
|
26
|
+
22 April 2016) — not a third-party site's compiled or scraped version.
|
|
27
|
+
|
|
28
|
+
No Python package for Tanzania postcodes existed before this one.
|
|
29
|
+
[`pgeocode`](https://pypi.org/project/pgeocode/), the most common
|
|
30
|
+
general-purpose postal code library, explicitly doesn't support Tanzania
|
|
31
|
+
(`ValueError: country=TZ is not a known country code`) — its data comes
|
|
32
|
+
from GeoNames, which has no real Tanzania coverage.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install tanzania-postcodes
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import tanzania_postcodes as tzpc
|
|
44
|
+
|
|
45
|
+
# Look up everything at a postcode
|
|
46
|
+
tzpc.lookup("16112")
|
|
47
|
+
# [Postcode(region='Dar Es Salaam', district='UBUNGO', ward='Goba',
|
|
48
|
+
# postcode='16112', locality='Goba', ...), ...]
|
|
49
|
+
|
|
50
|
+
# Search by name (ward, district, region, or locality)
|
|
51
|
+
tzpc.search("Goba")[0].postcode
|
|
52
|
+
# '16112'
|
|
53
|
+
|
|
54
|
+
# List all 26 regions
|
|
55
|
+
tzpc.regions()
|
|
56
|
+
|
|
57
|
+
# List all wards, optionally scoped to one region
|
|
58
|
+
tzpc.wards("Dar Es Salaam")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each result is a `Postcode`:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
Postcode(
|
|
65
|
+
region: str, # e.g. "Dar Es Salaam"
|
|
66
|
+
district: str | None, # e.g. "UBUNGO"
|
|
67
|
+
ward: str, # e.g. "Goba" -- the unit TCRA's postcode identifies
|
|
68
|
+
postcode: str, # e.g. "16112"
|
|
69
|
+
locality: str | None, # mtaa/village within the ward
|
|
70
|
+
sub_locality: str | None, # kitongoji, where the source PDF has one
|
|
71
|
+
old_postcode: str | None, # legacy code, where the source PDF has one
|
|
72
|
+
district_postcode: str | None,
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Coverage
|
|
77
|
+
|
|
78
|
+
All **26 regions**, **67,000+ locality-level records**, **3,900+ unique
|
|
79
|
+
ward postcodes**. A postcode identifies a *ward* — the same postcode
|
|
80
|
+
applies to every village/mtaa within it, which is why `lookup()` usually
|
|
81
|
+
returns multiple records for one code.
|
|
82
|
+
|
|
83
|
+
## Where this data comes from, and how it was built
|
|
84
|
+
|
|
85
|
+
TCRA publishes the postcode list as 26 separate regional PDFs (no
|
|
86
|
+
CSV/Excel version exists) at
|
|
87
|
+
[tcra.go.tz/services/publication-of-postcode-list](https://www.tcra.go.tz/services/publication-of-postcode-list).
|
|
88
|
+
Each PDF's table uses merged cells for repeated values, and — found
|
|
89
|
+
during development — **three different column layouts** across the 26
|
|
90
|
+
files (some have a separate District level, some don't; some have an Old
|
|
91
|
+
Postcode column, some don't; one region's table has no header row at
|
|
92
|
+
all). `scripts/parse_pdfs.py` detects each table's actual header and
|
|
93
|
+
maps columns by name rather than assuming one fixed schema, with a
|
|
94
|
+
documented fallback for the one region missing a header entirely.
|
|
95
|
+
|
|
96
|
+
`scripts/download_pdfs.py` and `scripts/parse_pdfs.py` are included in
|
|
97
|
+
this repo if you want to verify the extraction yourself or regenerate
|
|
98
|
+
the dataset from TCRA's source PDFs directly.
|
|
99
|
+
|
|
100
|
+
## Why this exists
|
|
101
|
+
|
|
102
|
+
Built after actually needing a Tanzania postcode (Goba ward, Dar es
|
|
103
|
+
Salaam — `16112`), confirming no Python package or well-covered general
|
|
104
|
+
library had it, and finding TCRA's own official data was real, current,
|
|
105
|
+
and genuinely extractable — just never turned into a package before.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT for this package's code. The underlying postcode data is Tanzania
|
|
110
|
+
government public record (Government Gazette Notice No. 240, 2016).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# tanzania-postcodes
|
|
2
|
+
|
|
3
|
+
Real Tanzania postal codes, sourced directly from **TCRA's official
|
|
4
|
+
gazetted National Postcode System** (Government Gazette Notice No. 240,
|
|
5
|
+
22 April 2016) — not a third-party site's compiled or scraped version.
|
|
6
|
+
|
|
7
|
+
No Python package for Tanzania postcodes existed before this one.
|
|
8
|
+
[`pgeocode`](https://pypi.org/project/pgeocode/), the most common
|
|
9
|
+
general-purpose postal code library, explicitly doesn't support Tanzania
|
|
10
|
+
(`ValueError: country=TZ is not a known country code`) — its data comes
|
|
11
|
+
from GeoNames, which has no real Tanzania coverage.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install tanzania-postcodes
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import tanzania_postcodes as tzpc
|
|
23
|
+
|
|
24
|
+
# Look up everything at a postcode
|
|
25
|
+
tzpc.lookup("16112")
|
|
26
|
+
# [Postcode(region='Dar Es Salaam', district='UBUNGO', ward='Goba',
|
|
27
|
+
# postcode='16112', locality='Goba', ...), ...]
|
|
28
|
+
|
|
29
|
+
# Search by name (ward, district, region, or locality)
|
|
30
|
+
tzpc.search("Goba")[0].postcode
|
|
31
|
+
# '16112'
|
|
32
|
+
|
|
33
|
+
# List all 26 regions
|
|
34
|
+
tzpc.regions()
|
|
35
|
+
|
|
36
|
+
# List all wards, optionally scoped to one region
|
|
37
|
+
tzpc.wards("Dar Es Salaam")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each result is a `Postcode`:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
Postcode(
|
|
44
|
+
region: str, # e.g. "Dar Es Salaam"
|
|
45
|
+
district: str | None, # e.g. "UBUNGO"
|
|
46
|
+
ward: str, # e.g. "Goba" -- the unit TCRA's postcode identifies
|
|
47
|
+
postcode: str, # e.g. "16112"
|
|
48
|
+
locality: str | None, # mtaa/village within the ward
|
|
49
|
+
sub_locality: str | None, # kitongoji, where the source PDF has one
|
|
50
|
+
old_postcode: str | None, # legacy code, where the source PDF has one
|
|
51
|
+
district_postcode: str | None,
|
|
52
|
+
)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Coverage
|
|
56
|
+
|
|
57
|
+
All **26 regions**, **67,000+ locality-level records**, **3,900+ unique
|
|
58
|
+
ward postcodes**. A postcode identifies a *ward* — the same postcode
|
|
59
|
+
applies to every village/mtaa within it, which is why `lookup()` usually
|
|
60
|
+
returns multiple records for one code.
|
|
61
|
+
|
|
62
|
+
## Where this data comes from, and how it was built
|
|
63
|
+
|
|
64
|
+
TCRA publishes the postcode list as 26 separate regional PDFs (no
|
|
65
|
+
CSV/Excel version exists) at
|
|
66
|
+
[tcra.go.tz/services/publication-of-postcode-list](https://www.tcra.go.tz/services/publication-of-postcode-list).
|
|
67
|
+
Each PDF's table uses merged cells for repeated values, and — found
|
|
68
|
+
during development — **three different column layouts** across the 26
|
|
69
|
+
files (some have a separate District level, some don't; some have an Old
|
|
70
|
+
Postcode column, some don't; one region's table has no header row at
|
|
71
|
+
all). `scripts/parse_pdfs.py` detects each table's actual header and
|
|
72
|
+
maps columns by name rather than assuming one fixed schema, with a
|
|
73
|
+
documented fallback for the one region missing a header entirely.
|
|
74
|
+
|
|
75
|
+
`scripts/download_pdfs.py` and `scripts/parse_pdfs.py` are included in
|
|
76
|
+
this repo if you want to verify the extraction yourself or regenerate
|
|
77
|
+
the dataset from TCRA's source PDFs directly.
|
|
78
|
+
|
|
79
|
+
## Why this exists
|
|
80
|
+
|
|
81
|
+
Built after actually needing a Tanzania postcode (Goba ward, Dar es
|
|
82
|
+
Salaam — `16112`), confirming no Python package or well-covered general
|
|
83
|
+
library had it, and finding TCRA's own official data was real, current,
|
|
84
|
+
and genuinely extractable — just never turned into a package before.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT for this package's code. The underlying postcode data is Tanzania
|
|
89
|
+
government public record (Government Gazette Notice No. 240, 2016).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tanzania-postcodes"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Real Tanzania postal codes, sourced directly from TCRA's official gazetted National Postcode System -- not a third-party compilation."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Freddy Nyanda", email = "freddynyanda@proton.me" }]
|
|
13
|
+
keywords = ["tanzania", "postcode", "postal-code", "zip-code", "tcra", "address"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.9",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/nyandajr/tanzania-postcodes"
|
|
27
|
+
Repository = "https://github.com/nyandajr/tanzania-postcodes"
|
|
28
|
+
Source = "https://www.tcra.go.tz/services/publication-of-postcode-list"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
include = ["tanzania_postcodes*"]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.package-data]
|
|
34
|
+
tanzania_postcodes = ["data/*.json"]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""tanzania-postcodes: real Tanzania postal codes, sourced directly from
|
|
2
|
+
TCRA's official gazetted National Postcode System (Government Gazette
|
|
3
|
+
Notice No. 240, 22 April 2016) -- not a third-party's compiled version.
|
|
4
|
+
|
|
5
|
+
>>> import tanzania_postcodes as tzpc
|
|
6
|
+
>>> tzpc.lookup("16112")[0].ward
|
|
7
|
+
'Goba'
|
|
8
|
+
>>> tzpc.search("Goba")[0].postcode
|
|
9
|
+
'16112'
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
_DATA_PATH = Path(__file__).parent / "data" / "postcodes.json"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class Postcode:
|
|
24
|
+
"""One locality-level record. Multiple localities share the same
|
|
25
|
+
ward-level `postcode` -- that's the actual unit TCRA's system
|
|
26
|
+
identifies, not the individual village/mtaa.
|
|
27
|
+
"""
|
|
28
|
+
region: str
|
|
29
|
+
district: Optional[str]
|
|
30
|
+
ward: str
|
|
31
|
+
postcode: str
|
|
32
|
+
locality: Optional[str]
|
|
33
|
+
sub_locality: Optional[str]
|
|
34
|
+
old_postcode: Optional[str] = None
|
|
35
|
+
district_postcode: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _load():
|
|
39
|
+
with open(_DATA_PATH, encoding="utf-8") as f:
|
|
40
|
+
raw = json.load(f)
|
|
41
|
+
return [Postcode(**r) for r in raw]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_RECORDS = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _records():
|
|
48
|
+
global _RECORDS
|
|
49
|
+
if _RECORDS is None:
|
|
50
|
+
_RECORDS = _load()
|
|
51
|
+
return _RECORDS
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def lookup(postcode: str) -> list[Postcode]:
|
|
55
|
+
"""All records sharing this exact postcode (usually several -- one
|
|
56
|
+
ward maps to multiple villages/mtaa)."""
|
|
57
|
+
postcode = postcode.strip()
|
|
58
|
+
return [r for r in _records() if r.postcode == postcode]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def search(query: str) -> list[Postcode]:
|
|
62
|
+
"""Case-insensitive substring search across ward, district, region,
|
|
63
|
+
locality, and sub_locality names."""
|
|
64
|
+
q = query.strip().lower()
|
|
65
|
+
if not q:
|
|
66
|
+
return []
|
|
67
|
+
results = []
|
|
68
|
+
for r in _records():
|
|
69
|
+
haystack = " ".join(filter(None, [r.region, r.district, r.ward, r.locality, r.sub_locality])).lower()
|
|
70
|
+
if q in haystack:
|
|
71
|
+
results.append(r)
|
|
72
|
+
return results
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def regions() -> list[str]:
|
|
76
|
+
"""All 26 regions with postcode data."""
|
|
77
|
+
return sorted({r.region for r in _records()})
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def wards(region: Optional[str] = None) -> list[str]:
|
|
81
|
+
"""All ward names, optionally filtered to one region."""
|
|
82
|
+
recs = _records() if region is None else [r for r in _records() if r.region.lower() == region.lower()]
|
|
83
|
+
return sorted({r.ward for r in recs})
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
__all__ = ["Postcode", "lookup", "search", "regions", "wards", "__version__"]
|