| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- from __future__ import annotations
- import json
- import os
- from functools import lru_cache
- from typing import List, Tuple, Dict, Optional
- from django.conf import settings
- def _json_path(filename: str) -> str:
- return os.path.join(settings.BASE_DIR, 'static', 'json', filename)
- @lru_cache(maxsize=1)
- def load_countries() -> List[dict]:
- with open(_json_path('countries.json'), 'r', encoding='utf-8') as f:
- return json.load(f)
- @lru_cache(maxsize=1)
- def load_states() -> List[dict]:
- with open(_json_path('states.json'), 'r', encoding='utf-8') as f:
- return json.load(f)
- @lru_cache(maxsize=1)
- def load_cities() -> List[dict]:
- with open(_json_path('cities.json'), 'r', encoding='utf-8') as f:
- return json.load(f)
- @lru_cache(maxsize=1)
- def _index_states_by_country() -> Dict[str, List[Tuple[str, str]]]:
- idx: Dict[str, List[Tuple[str, str]]] = {}
- for s in load_states():
- cc = s.get('country_code')
- code = s.get('iso2') or s.get('state_code')
- name = s.get('name')
- if not (cc and code and name):
- continue
- idx.setdefault(cc, []).append((str(code), name))
- return idx
- @lru_cache(maxsize=1)
- def _index_cities_by_country_state() -> Dict[Tuple[str, str], List[Tuple[str, str]]]:
- idx: Dict[Tuple[str, str], List[Tuple[str, str]]] = {}
- for c in load_cities():
- cc = c.get('country_code')
- sc = (c.get('state_code') or '').upper()
- name = c.get('name')
- if not (cc and sc and name):
- continue
- key = (cc, sc)
- idx.setdefault(key, []).append((name, name))
- return idx
- def country_label(code: str) -> Optional[str]:
- code = (code or '').upper()
- for c in load_countries():
- if c.get('iso2') == code:
- return c.get('name')
- return None
- def state_label(country_code: str, state_code: str) -> Optional[str]:
- cc = (country_code or '').upper()
- sc = (state_code or '').upper()
- for s in load_states():
- if s.get('country_code') == cc and s.get('iso2', '').upper() == sc:
- return s.get('name')
- if s.get('country_code') == cc and s.get('state_code', '').upper() == sc:
- return s.get('name')
- return None
- def list_countries(q: str | None = None) -> List[Tuple[str, str]]:
- items: List[Tuple[str, str]] = []
- ql = (q or '').strip().lower()
- for c in load_countries():
- code = c.get('iso2')
- name = c.get('name')
- if not code or not name:
- continue
- if ql and ql not in name.lower() and ql not in code.lower():
- continue
- items.append((code, name))
- return items
- def list_states(country_code: str | None, q: str | None = None) -> List[Tuple[str, str]]:
- items: List[Tuple[str, str]] = []
- if not country_code:
- return items
- cc = country_code.upper()
- ql = (q or '').strip().lower()
- pool = _index_states_by_country().get(cc, [])
- if not ql:
- return list(pool)
- for code, name in pool:
- if ql and ql not in name.lower() and ql not in (code or '').lower():
- continue
- items.append((code, name))
- return items
- def list_cities(country_code: str | None, state_code: str | None, q: str | None = None) -> List[Tuple[str, str]]:
- items: List[Tuple[str, str]] = []
- if not (country_code and state_code):
- return items
- cc = country_code.upper()
- sc = state_code.upper()
- ql = (q or '').strip().lower()
- pool = _index_cities_by_country_state().get((cc, sc), [])
- if not ql:
- # Return first 50 to avoid huge payloads without query
- return pool[:50]
- for code, name in pool:
- if ql in name.lower():
- items.append((code, name))
- return items
|