Spaces:
Running
Running
File size: 4,498 Bytes
dd6303a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | """
Text Utilities
--------------
Helpers for number-to-words, date formatting, amount formatting etc.
Used by generators to fill human-readable fields.
"""
import re
from datetime import datetime
# ββ Number to Bangladeshi words ββββββββββββββββββββββββββββββββββββββββββββββ
_ONES = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
"Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
"Sixteen", "Seventeen", "Eighteen", "Nineteen"]
_TENS = ["", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"]
def _below_hundred(n: int) -> str:
if n < 20:
return _ONES[n]
return (_TENS[n // 10] + (" " + _ONES[n % 10] if n % 10 else "")).strip()
def _below_thousand(n: int) -> str:
if n < 100:
return _below_hundred(n)
return (_ONES[n // 100] + " Hundred" + (" " + _below_hundred(n % 100) if n % 100 else "")).strip()
def amount_to_words_bd(amount: float) -> str:
"""
Convert amount to Bangladeshi words using Crore/Lac/Thousand system.
e.g. 7500000 -> "Seven Crore Fifty Lac"
"""
n = int(round(amount))
if n == 0:
return "Zero"
parts = []
crore = n // 10_000_000
n %= 10_000_000
lac = n // 100_000
n %= 100_000
thousand = n // 1000
n %= 1000
if crore:
parts.append(_below_thousand(crore) + " Crore")
if lac:
parts.append(_below_thousand(lac) + " Lac")
if thousand:
parts.append(_below_thousand(thousand) + " Thousand")
if n:
parts.append(_below_thousand(n))
return " ".join(parts)
def format_bdt(amount: float) -> str:
"""Format as 'Tk. 75,00,000.00' (Bangladeshi comma grouping)."""
n = int(round(amount))
s = str(n)
# BD style: last 3 digits, then groups of 2
if len(s) > 3:
result = s[-3:]
s = s[:-3]
while s:
result = s[-2:] + "," + result
s = s[:-2]
return f"Tk. {result}.00"
return f"Tk. {s}.00"
# ββ Date helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
_MONTH_MAP = {
"jan": "January", "feb": "February", "mar": "March", "apr": "April",
"may": "May", "jun": "June", "jul": "July", "aug": "August",
"sep": "September", "oct": "October", "nov": "November", "dec": "December",
}
def parse_date_flexible(date_str: str) -> datetime:
"""Try multiple date formats and return a datetime object."""
formats = [
"%d-%b-%Y", "%d-%B-%Y", "%d/%m/%Y", "%Y-%m-%d",
"%d-%m-%Y", "%d.%m.%Y",
]
for fmt in formats:
try:
return datetime.strptime(date_str.strip(), fmt)
except ValueError:
continue
raise ValueError(f"Cannot parse date: {date_str!r}")
def date_to_long(date_str: str) -> str:
"""Convert '30-Jun-2022' to '30th June 2022'."""
try:
dt = parse_date_flexible(date_str)
day = dt.day
suffix = "th" if 11 <= day <= 13 else {1: "st", 2: "nd", 3: "rd"}.get(day % 10, "th")
return f"{day}{suffix} {dt.strftime('%B %Y')}"
except ValueError:
return date_str
def date_to_document(date_str: str) -> str:
"""Convert '15-Apr-2021' to '15-04-2021'."""
try:
dt = parse_date_flexible(date_str)
return dt.strftime("%d-%m-%Y")
except ValueError:
return date_str
def bg_validity_date(completion_date_str: str, extra_days: int = 150) -> str:
"""
Bank Guarantee validity = completion date + ~150 days (28 days after tender validity).
Returns formatted date like '24-August-2021'.
"""
try:
dt = parse_date_flexible(completion_date_str)
from datetime import timedelta
dt2 = dt + timedelta(days=extra_days)
return f"{dt2.day}-{dt2.strftime('%B')}-{dt2.year}"
except ValueError:
return ""
# ββ String helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def work_period_label(start: str, end: str) -> str:
"""e.g. '15-Apr-2021 to 30-Jun-2022'"""
return f"{start}\nto \n{end}\n"
def tender_id_label(tender_id: str) -> str:
return f"Tender ID: {tender_id}"
|