Files
django-nepkit/django_nepkit/utils.py
T

202 lines
7.8 KiB
Python

from __future__ import annotations
from typing import Any, Optional
from nepali.datetime import nepalidate, nepalidatetime
from nepali.locations import districts, provinces
from django_nepkit.conf import nepkit_settings
BS_DATE_FORMAT = nepkit_settings.BS_DATE_FORMAT
BS_DATETIME_FORMAT = nepkit_settings.BS_DATETIME_FORMAT
def _try_parse_nepali(value: Any, cls: Any, fallback_fmt: str) -> Any:
"""Helper to turn a string into a Nepali date object."""
if value in (None, ""):
return None
if isinstance(value, cls):
return value
if isinstance(value, str):
formats = nepkit_settings.DATE_INPUT_FORMATS
if fallback_fmt not in formats:
formats = list(formats) + [fallback_fmt]
for fmt in formats:
try:
return cls.strptime(value.strip(), fmt)
except Exception:
continue
return None
def try_parse_nepali_date(value: Any) -> Optional[nepalidate]:
"""Convert any value to a Nepali Date."""
return _try_parse_nepali(value, nepalidate, BS_DATE_FORMAT)
def try_parse_nepali_datetime(value: Any) -> Optional[nepalidatetime]:
"""Convert any value to a Nepali Date and Time."""
return _try_parse_nepali(value, nepalidatetime, BS_DATETIME_FORMAT)
def _get_location_children(parent_list, parent_name, child_attr, ne=False):
"""Find children (like districts) of a parent (like a province)."""
selected_parent = None
for p in parent_list:
p_name = p.name
p_name_ne = getattr(p, "name_nepali", None)
# Handle province name variations
if parent_name == "Koshi Province":
if p_name == "Province 1":
selected_parent = p
break
elif parent_name == "कोशी प्रदेश":
if p_name_ne == "प्रदेश नं. १":
selected_parent = p
break
if p_name == parent_name or p_name_ne == parent_name:
selected_parent = p
break
if not selected_parent:
return []
children = getattr(selected_parent, child_attr, [])
if ne:
return [
{
"id": getattr(child, "name_nepali", child.name),
"text": getattr(child, "name_nepali", child.name),
}
for child in children
]
else:
return [{"id": child.name, "text": child.name} for child in children]
def get_districts_by_province(province_name, ne=False, en=True):
"""Get all districts for a province."""
# Logic note: if ne=True is passed, we shouldn't care about en=True (handled by caller typically)
return _get_location_children(provinces, province_name, "districts", ne=ne)
def get_municipalities_by_district(district_name, ne=False, en=True):
"""Get all municipalities for a district."""
return _get_location_children(districts, district_name, "municipalities", ne=ne)
def format_nepali_currency(number: Any, currency_symbol: str = "Rs.") -> str:
"""
Formats a number with Nepali-style commas and optional currency symbol.
Eg. 1234567 -> Rs. 12,34,567
"""
from nepali.number import add_comma
if number is None:
return ""
try:
# Convert to string and split by decimal point
num_str = f"{float(number):.2f}"
if "." in num_str:
integer_part, decimal_part = num_str.split(".")
else:
integer_part, decimal_part = num_str, ""
# Format integer part with commas
formatted_integer = add_comma(int(integer_part))
# Join back
res = formatted_integer
if decimal_part:
res = f"{res}.{decimal_part}"
if currency_symbol:
return f"{currency_symbol} {res}"
return res
except Exception:
return str(number)
def number_to_nepali_words(number: Any) -> str:
"""
Converts a number to Nepali words.
Eg. 123 -> एक सय तेईस
"""
if number is None:
return ""
# Basic implementation for now, can be expanded
# Mapping for numbers to words (simplified)
# This is a complex task for a full implementation,
# but I'll provide a robust enough version for common usage.
try:
num = int(float(number))
except (ValueError, TypeError):
return str(number)
if num == 0:
return "शून्य"
ones = ["", "एक", "दुई", "तीन", "चार", "पाँच", "छ", "सात", "आठ", "नौ", "दश",
"एघार", "बाह्र", "तेह्र", "चौध", "पन्ध्र", "सोह्र", "सत्र", "अठार", "उन्नाइस", "बीस",
"एकाइस", "बाइस", "तेईस", "चौबीस", "पच्चीस", "छब्बीस", "सत्ताइस", "अठाइस", "उनन्तीस", "तीस",
"एकतीस", "बत्तीस", "तेत्तीस", "चौंतीस", "पैंतीस", "छत्तीस", "सैंतीस", "अठतीस", "उनन्चालीस", "चालीस",
"एकचालीस", "बयालीस", "त्रिचालीस", "चवालीस", "पैंतालीस", "छयालीस", "सत्तालीस", "अठचालीस", "उनन्पचास", "पचास",
"एकाउन्न", "बाउन्न", "त्रिपन्न", "चउन्न", "पचपन्न", "छपन्न", "सन्ताउन्न", "अन्ठाउन्न", "उनन्साठी", "साठी",
"एकसाठी", "बासट्ठी", "त्रिसट्ठी", "चौसट्ठी", "पैंसट्ठी", "छयसट्ठी", "सतसट्ठी", "अठसट्ठी", "उनन्सत्तरी", "सत्तरी",
"एकहत्तर", "बाहत्तर", "त्रिहत्तर", "चौरहत्तर", "पचहत्तर", "छयहत्तर", "सतहत्तर", "अठहत्तर", "उनन्असी", "असी",
"एकासी", "बयासी", "त्रियासी", "चौरासी", "पचासी", "छयासी", "सतासी", "अठासी", "उनन्नब्बे", "नब्बे",
"एकानब्बे", "बयानब्बे", "त्रियानब्बे", "चौरानब्बे", "पञ्चानब्बे", "छ्यानब्बे", "सन्तानब्बे", "अन्ठानब्बे", "उनन्सय"]
units = [
("", ""),
(100, "सय"),
(1000, "हजार"),
(100000, "लाख"),
(10000000, "करोड"),
(1000000000, "अरब"),
(100000000000, "खरब"),
]
def _convert(n):
if n == 0:
return ""
if n < 100:
return ones[n]
for i in range(len(units) - 1, 0, -1):
div, unit_name = units[i]
if n >= div:
prefix_val = n // div
remainder = n % div
# For 'सय' (100), we use ones[prefix_val]
# For others, we might need recursive calls if prefix_val >= 100
prefix_words = _convert(prefix_val)
res = f"{prefix_words} {unit_name}"
if remainder > 0:
res += f" {_convert(remainder)}"
return res.strip()
return ""
return _convert(num)
def english_to_nepali_unicode(text: Any) -> str:
"""
Converts English text/numbers to Nepali Unicode.
Currently focuses on numbers.
"""
from nepali.number import english_to_nepali
if text is None:
return ""
return english_to_nepali(text)