tanzania-postcodes 0.1.0__py3-none-any.whl
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.
|
@@ -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__"]
|