feat: add area name to telephone number (#90)

reference: https://www.prokerala.com/how-to/call/nepal/area-codes/?utm_source=chatgpt.com
potential issue: some districts use same code, eg: gandaki and gorkha, 
might not return the expected district
This commit is contained in:
Bipin Thapa authored and GitHub committed 2026-03-12 12:50:05 +05:45
1 parent 8148bbf851
commit e693606ab1
3 files changed
+165 -3

No files matched your search

+6 -1
View File
@@ -344,6 +344,7 @@ number_text = number.add_comma("1234567890") # 1,23,45,67,890
```
### nepalinumber
`nepalinumber` is a new data type, which can be used to represent Nepali (Devanagari) numbers. It allows us to perform arithmetic operations, just like with int and float. Additionally, it can be used to parse numbers and output them in Devanagari format.
```python
@@ -351,6 +352,7 @@ from nepali.number import nepalinumber
```
**Parsing**
```python
a = nepalinumber("१८.२७")
print(a) # 18.27
@@ -360,18 +362,21 @@ print(b) # 15
```
**Nepali (Devanagari) output**
```python
a = nepalinumber("18.27")
print(a.str_ne()) # १८.२७
```
**Arithmetic operations**
```python
a = nepalinumber("1")
b = nepalinumber("२")
c = a + b * 3
print(c) # 7
```
---
## Phone Number
@@ -398,7 +403,7 @@ phone_number.parse("9851377890")
# {'type': 'Mobile', 'number': '9851377890', 'operator': <Operator: Nepal Telecom>}
phone_number.parse("+977-142314819")
# {'type': 'Landline', 'number': '0142314819', 'area_code': '01'}
# {'type': 'Landline', 'number': '0142314819', 'area_code': '01', 'area': ['Bhaktapur', 'Kathmandu', 'Lalitpur']}
```
---
+124 -2
View File
@@ -21,6 +21,113 @@ class Operator(Enum):
return f"<Operator: {self.value}>"
class Area(Enum):
# Koshi Province
BHOJPUR = ("Bhojpur", "029")
DHANKUTA = ("Dhankuta", "026")
ILAM = ("Ilam", "027")
JHAPA = ("Jhapa", "023")
KHOTANG = ("Khotang", "036")
MORANG = ("Morang", "021")
OKHALDHUNGA = ("Okhaldhunga", "037")
PANCHTHAR = ("Panchthar", "024")
SANKHUWASABHA = ("Sankhuwasabha", "029")
SOLUKHUMBU = ("Solukhumbu", "038")
SUNSARI = ("Sunsari", "025")
TAPLEJUNG = ("Taplejung", "024")
TERHATHUM = ("Terhathum", "026")
UDAYAPUR = ("Udayapur", "035")
# Madhesh Province
BARA = ("Bara", "053")
DHANUSA = ("Dhanusa", "041")
MAHOTTARI = ("Mahottari", "044")
PARSA = ("Parsa", "051")
RAUTAHAT = ("Rautahat", "055")
SAPTARI = ("Saptari", "031")
SARLAHI = ("Sarlahi", "046")
SIRAHA = ("Siraha", "033")
# Bagmati Province
BHAKTAPUR = ("Bhaktapur", "01")
CHITWAN = ("Chitwan", "056")
DHADING = ("Dhading", "010")
DOLAKHA = ("Dolakha", "049")
KATHMANDU = ("Kathmandu", "01")
KAVREPALANCHOK = ("Kavrepalanchok", "011")
LALITPUR = ("Lalitpur", "01")
MAKWANPUR = ("Makwanpur", "057")
NUWAKOT = ("Nuwakot", "010")
RAMECHHAP = ("Ramechhap", "048")
RASUWA = ("Rasuwa", "010")
SINDHUPALCHOK = ("Sindhupalchok", "011")
SINDHULI = ("Sindhuli", "047")
# Gandaki Province
BAGLUNG = ("Baglung", "068")
GORKHA = ("Gorkha", "064")
KASKI = ("Kaski", "061")
LAMJUNG = ("Lamjung", "066")
MANANG = ("Manang", "066")
MUSTANG = ("Mustang", "069")
MYAGDI = ("Myagdi", "069")
NAWALPUR = ("Nawalpur", "078")
PARBAT = ("Parbat", "067")
SYANGJA = ("Syangja", "063")
TANAHU = ("Tanahu", "065")
# Lumbini Provinc
ARGHAKHANCHI = ("Arghakhanchi", "077")
BANKE = ("Banke", "081")
BARDIYA = ("Bardiya", "084")
DANG = ("Dang", "082")
GULMI = ("Gulmi", "079")
KAPILVASTU = ("Kapilvastu", "076")
PARASI = ("Parasi", "078")
PALPA = ("Palpa", "075")
PYUTHAN = ("Pyuthan", "086")
ROLPA = ("Rolpa", "086")
RUKUM_EAST = ("Rukum East", "088")
RUPANDEHI = ("Rupandehi", "071")
# Karnali Province
DAILEKH = ("Dailekh", "089")
DOLPA = ("Dolpa", "087")
HUMLA = ("Humla", "019")
JAJARKOT = ("Jajarkot", "089")
JUMLA = ("Jumla", "087")
KALIKOT = ("Kalikot", "087")
MUGU = ("Mugu", "019")
RUKUM_WEST = ("Rukum West", "088")
SALYAN = ("Salyan", "088")
SURKHET = ("Surkhet", "083")
# Sudurpashchim Province
ACHHAM = ("Achham", "097")
BAITADI = ("Baitadi", "095")
BAJHANG = ("Bajhang", "092")
BAJURA = ("Bajura", "097")
DADELDHURA = ("Dadeldhura", "096")
DARCHULA = ("Darchula", "093")
DOTI = ("Doti", "094")
KAILALI = ("Kailali", "091")
KANCHANPUR = ("Kanchanpur", "099")
def __init__(self, area: str, area_code: str):
self.area = area
self.area_code = area_code
def __str__(self):
return f"{self.area}: {self.area_code}"
@classmethod
def get_by_area_code(cls, area_code: str = None) -> list[str]:
if not area_code:
return []
results = [area.value[0] for area in cls if area.value[1] == area_code]
return results
def is_mobile_number(number: str) -> bool:
"""
Returns True is the input number is mobile number.
@@ -168,16 +275,31 @@ def _parse_landline_number(number) -> dict:
"type": "Landline",
"number": "98XXXXXXXX",
"area_code": <AreaCode>
"area": list of areas/districts
}
"""
number = get_exact_number(number)
# adding zero
if number[0] != "0":
number = f"0{number}"
number = get_exact_number(number)
area_code = _get_area_code(number)
area = _get_area(area_code)
if not area:
return None
return {
"type": "Landline",
"number": number,
"area_code": _get_area_code(number),
"area_code": area_code,
"area": area,
}
def _get_area(area_code: str) -> list[str] | None:
"""
Returns the details of the area code.
"""
return Area.get_by_area_code(area_code)
+35
View File
@@ -3,8 +3,10 @@ from unittest import mock
from nepali.phone_number import (
Operator,
Area,
_get_area_code,
_get_operator,
_get_area,
_parse_landline_number,
_parse_mobile_number,
get_exact_number,
@@ -33,6 +35,28 @@ class TestOperator(unittest.TestCase):
self.assertEqual(repr(operator), f"<Operator: {operator.value}>")
class TestArea(unittest.TestCase):
def test_area_with_invalid_data(self):
with self.assertRaises(ValueError):
Area(("Bhojpur", "026"))
Area(("India", "01"))
def test_area_with_valid_data(self):
a_1 = Area(("Udayapur", "035"))
a_2 = Area(("Rolpa", "086"))
self.assertEqual(a_1, Area.UDAYAPUR)
self.assertEqual(a_2, Area.ROLPA)
def test_get_by_area_code(self):
self.assertEqual(
set(Area.get_by_area_code("01")),
set(["Bhaktapur", "Kathmandu", "Lalitpur"]),
)
self.assertEqual(Area.get_by_area_code(""), [])
self.assertEqual(Area.get_by_area_code(), [])
class TestPhoneNumberValidation(unittest.TestCase):
def test_is_mobile_number(self):
# True case
@@ -202,19 +226,27 @@ class TestLandlineNumberParser(unittest.TestCase):
self.assertEqual(_get_area_code("063420211"), "063")
@mock.patch("nepali.phone_number._get_area_code", return_value="test")
@mock.patch("nepali.phone_number._get_area", return_value="area name")
def test__parse_landline_number_returns_valid_data(self, *_):
data = _parse_landline_number("0142314819")
self.assertEqual(data["type"], "Landline")
self.assertEqual(data["area_code"], "test")
self.assertEqual(data["area"], "area name")
self.assertEqual(data["number"], "0142314819")
@mock.patch("nepali.phone_number._get_area_code", return_value="test")
@mock.patch("nepali.phone_number._get_area", return_value="area name")
def test__parse_landline_number_returns_valid_data_for_977(self, *_):
data = _parse_landline_number("+977142314819")
self.assertEqual(data["type"], "Landline")
self.assertEqual(data["area_code"], "test")
self.assertEqual(data["area"], "area name")
self.assertEqual(data["number"], "0142314819")
@mock.patch("nepali.phone_number.Area.get_by_area_code", return_value="area")
def test__get_area(self, *_):
self.assertEqual(_get_area("code"), "area")
class TestParser(unittest.TestCase):
def test_none_number_returns_none(self):
@@ -252,3 +284,6 @@ class TestParseIntegration(unittest.TestCase):
self.assertEqual(data and data["type"], "Landline")
self.assertEqual(data and data["number"], "0142314819")
self.assertEqual(data and data["area_code"], "01")
self.assertEqual(
data and set(data["area"]), set(["Kathmandu", "Bhaktapur", "Lalitpur"])
)