django-nepkit 0.1.0__py3-none-any.whl → 0.2.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.
Files changed (37) hide show
  1. django_nepkit/__init__.py +20 -0
  2. django_nepkit/admin.py +243 -93
  3. django_nepkit/conf.py +34 -0
  4. django_nepkit/filters.py +113 -0
  5. django_nepkit/forms.py +9 -9
  6. django_nepkit/models.py +143 -162
  7. django_nepkit/serializers.py +127 -28
  8. django_nepkit/static/django_nepkit/js/address-chaining.js +105 -18
  9. django_nepkit/static/django_nepkit/js/nepal-data.js +1 -0
  10. django_nepkit/static/django_nepkit/js/nepali-datepicker-init.js +55 -46
  11. django_nepkit/utils.py +396 -46
  12. django_nepkit/validators.py +1 -1
  13. django_nepkit/views.py +66 -3
  14. django_nepkit/widgets.py +100 -31
  15. django_nepkit-0.2.0.dist-info/METADATA +308 -0
  16. django_nepkit-0.2.0.dist-info/RECORD +35 -0
  17. example/demo/admin.py +45 -21
  18. example/demo/models.py +41 -4
  19. example/demo/serializers.py +39 -0
  20. example/demo/urls.py +13 -2
  21. example/demo/views.py +125 -3
  22. example/example_project/settings.py +10 -0
  23. example/example_project/urls.py +1 -1
  24. example/manage.py +2 -2
  25. django_nepkit/templatetags/__init__.py +0 -0
  26. django_nepkit/templatetags/nepali.py +0 -74
  27. django_nepkit-0.1.0.dist-info/METADATA +0 -377
  28. django_nepkit-0.1.0.dist-info/RECORD +0 -39
  29. example/demo/migrations/0001_initial.py +0 -2113
  30. example/demo/migrations/0002_alter_person_phone_number.py +0 -18
  31. example/demo/migrations/0003_person_created_at_person_updated_at.py +0 -27
  32. example/demo/migrations/0004_alter_person_created_at_alter_person_updated_at.py +0 -23
  33. example/demo/migrations/0005_alter_person_created_at_alter_person_updated_at.py +0 -27
  34. example/demo/migrations/__init__.py +0 -0
  35. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.0.dist-info}/WHEEL +0 -0
  36. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.0.dist-info}/licenses/LICENSE +0 -0
  37. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.0.dist-info}/top_level.txt +0 -0
@@ -45,64 +45,73 @@
45
45
  if (!inputs || !inputs.length) return;
46
46
 
47
47
  inputs.forEach(function (el) {
48
+ var format = el.dataset.format || 'YYYY-MM-DD';
49
+ // Convert strftime format to datepicker format
50
+ format = format.replace(/%Y/g, 'YYYY').replace(/%m/g, 'MM').replace(/%d/g, 'DD');
51
+
52
+ var options = {
53
+ dateFormat: format,
54
+ closeOnDateSelect: true
55
+ };
56
+
57
+ // Determine language based on data attributes
58
+ var useNepali = el.dataset.ne === 'true';
59
+ var useEnglish = el.dataset.en === 'true';
60
+
61
+ if (useNepali) {
62
+ // Devanagari digits and Nepali month/day names
63
+ options.unicodeDate = true;
64
+ } else if (useEnglish || (!useNepali && !el.dataset.ne)) {
65
+ // English month/day names and digits
66
+ options.language = 'english';
67
+ }
68
+
48
69
  // Nepali Datepicker v5 exposes `element.NepaliDatePicker(options)`
49
70
  if (typeof el.NepaliDatePicker === 'function') {
50
- el.NepaliDatePicker({
51
- dateFormat: 'YYYY-MM-DD'
52
- });
71
+ el.NepaliDatePicker(options);
72
+ el.classList.add('nepali-datepicker-initialized');
73
+ }
74
+ // Fallback for jQuery plugin (v2.0.2)
75
+ else if (typeof window.jQuery !== 'undefined' && typeof window.jQuery(el).nepaliDatePicker === 'function') {
76
+ window.jQuery(el).nepaliDatePicker(options);
53
77
  el.classList.add('nepali-datepicker-initialized');
78
+ }
54
79
 
55
- // Ensure theme is applied when the picker is actually shown.
56
- // (The plugin often creates/inserts DOM on focus.)
57
- var applySoon = function () {
58
- // Run twice: once immediately, once after paint.
59
- applyThemeToDatepickerContainers();
60
- window.setTimeout(applyThemeToDatepickerContainers, 0);
61
- };
80
+ // Ensure theme is applied when the picker is actually shown.
81
+ // (The plugin often creates/inserts DOM on focus.)
82
+ var applySoon = function () {
83
+ // Run twice: once immediately, once after paint.
84
+ applyThemeToDatepickerContainers();
85
+ window.setTimeout(applyThemeToDatepickerContainers, 0);
86
+ };
62
87
 
63
- el.addEventListener('focus', applySoon);
64
- el.addEventListener('click', applySoon);
65
- }
88
+ el.addEventListener('focus', applySoon);
89
+ el.addEventListener('click', applySoon);
66
90
  });
67
91
  }
68
92
 
69
- // Initialize on page load
70
- if (document.readyState === 'loading') {
71
- document.addEventListener('DOMContentLoaded', initNepaliDatePickers);
72
- } else {
73
- initNepaliDatePickers();
74
- }
75
-
76
- // Keep in sync if system theme changes (fallback path)
77
- if (window.matchMedia) {
78
- try {
79
- var mql = window.matchMedia('(prefers-color-scheme: dark)');
80
- if (mql && typeof mql.addEventListener === 'function') {
81
- mql.addEventListener('change', applyThemeToDatepickerContainers);
82
- } else if (mql && typeof mql.addListener === 'function') {
83
- mql.addListener(applyThemeToDatepickerContainers);
84
- }
85
- } catch (e) {
86
- // ignore
93
+ // Also listen for DOM changes (admin popups/other dynamic content)
94
+ function initObserver() {
95
+ if (typeof MutationObserver !== 'undefined' && document.body) {
96
+ var observer = new MutationObserver(function () {
97
+ initNepaliDatePickers();
98
+ applyThemeToDatepickerContainers();
99
+ });
100
+ observer.observe(document.body, {
101
+ childList: true,
102
+ subtree: true
103
+ });
87
104
  }
88
105
  }
89
106
 
90
- // Re-initialize when Django admin adds inlines dynamically
91
- if (typeof django !== 'undefined' && django.jQuery) {
92
- django.jQuery(document).on('formset:added', function () {
93
- initNepaliDatePickers();
94
- });
95
- }
96
-
97
- // Also listen for DOM changes (admin popups/other dynamic content)
98
- if (typeof MutationObserver !== 'undefined') {
99
- var observer = new MutationObserver(function () {
107
+ // Initialize on page load
108
+ if (document.readyState === 'loading') {
109
+ document.addEventListener('DOMContentLoaded', function() {
100
110
  initNepaliDatePickers();
101
- applyThemeToDatepickerContainers();
102
- });
103
- observer.observe(document.body, {
104
- childList: true,
105
- subtree: true
111
+ initObserver();
106
112
  });
113
+ } else {
114
+ initNepaliDatePickers();
115
+ initObserver();
107
116
  }
108
117
  })();
django_nepkit/utils.py CHANGED
@@ -3,75 +3,425 @@ from __future__ import annotations
3
3
  from typing import Any, Optional
4
4
 
5
5
  from nepali.datetime import nepalidate, nepalidatetime
6
+ from nepali.locations import districts, municipalities, provinces
6
7
 
8
+ from django_nepkit.conf import nepkit_settings
7
9
 
8
- BS_DATE_FORMAT = "%Y-%m-%d"
9
- BS_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
10
+ BS_DATE_FORMAT = nepkit_settings.BS_DATE_FORMAT
11
+ BS_DATETIME_FORMAT = nepkit_settings.BS_DATETIME_FORMAT
10
12
 
11
13
 
12
- def try_parse_nepali_date(value: Any) -> Optional[nepalidate]:
13
- """
14
- Best-effort conversion to `nepalidate`.
15
-
16
- - Returns `None` for empty values.
17
- - Returns `nepalidate` for valid inputs.
18
- - Returns `None` if the value cannot be parsed.
19
-
20
- Callers decide whether to raise, fallback, etc.
21
- """
14
+ def _try_parse_nepali(value: Any, cls: Any, fallback_fmt: str) -> Any:
15
+ """Helper to turn a string into a Nepali date object."""
22
16
  if value in (None, ""):
23
17
  return None
24
- if isinstance(value, nepalidate):
18
+ if isinstance(value, cls):
25
19
  return value
26
20
  if isinstance(value, str):
27
- try:
28
- return nepalidate.strptime(value.strip(), BS_DATE_FORMAT)
29
- except Exception:
30
- return None
21
+ formats = nepkit_settings.DATE_INPUT_FORMATS
22
+ if fallback_fmt not in formats:
23
+ formats = list(formats) + [fallback_fmt]
24
+
25
+ for fmt in formats:
26
+ try:
27
+ return cls.strptime(value.strip(), fmt)
28
+ except Exception:
29
+ continue
31
30
  return None
32
31
 
33
32
 
33
+ def try_parse_nepali_date(value: Any) -> Optional[nepalidate]:
34
+ """Convert any value to a Nepali Date."""
35
+ return _try_parse_nepali(value, nepalidate, BS_DATE_FORMAT)
36
+
37
+
34
38
  def try_parse_nepali_datetime(value: Any) -> Optional[nepalidatetime]:
39
+ """Convert any value to a Nepali Date and Time."""
40
+ return _try_parse_nepali(value, nepalidatetime, BS_DATETIME_FORMAT)
41
+
42
+
43
+ def _get_location_children(parent_list, parent_name, child_attr, ne=False):
44
+ """Find children (like districts) of a parent (like a province)."""
45
+ selected_parent = None
46
+ for p in parent_list:
47
+ p_name = p.name
48
+ p_name_ne = getattr(p, "name_nepali", None)
49
+
50
+ # Handle province name variations
51
+ if parent_name == "Koshi Province":
52
+ if p_name == "Province 1":
53
+ selected_parent = p
54
+ break
55
+ elif parent_name == "कोशी प्रदेश":
56
+ if p_name_ne == "प्रदेश नं. १":
57
+ selected_parent = p
58
+ break
59
+
60
+ if p_name == parent_name or p_name_ne == parent_name:
61
+ selected_parent = p
62
+ break
63
+
64
+ if not selected_parent:
65
+ return []
66
+
67
+ children = getattr(selected_parent, child_attr, [])
68
+
69
+ if ne:
70
+ return [
71
+ {
72
+ "id": getattr(child, "name_nepali", child.name),
73
+ "text": getattr(child, "name_nepali", child.name),
74
+ }
75
+ for child in children
76
+ ]
77
+ else:
78
+ return [{"id": child.name, "text": child.name} for child in children]
79
+
80
+
81
+ def get_districts_by_province(province_name, ne=False, en=True):
82
+ """Get all districts for a province."""
83
+ # Logic note: if ne=True is passed, we shouldn't care about en=True (handled by caller typically)
84
+ return _get_location_children(provinces, province_name, "districts", ne=ne)
85
+
86
+
87
+ def get_municipalities_by_district(district_name, ne=False, en=True):
88
+ """Get all municipalities for a district."""
89
+ return _get_location_children(districts, district_name, "municipalities", ne=ne)
90
+
91
+
92
+ def format_nepali_currency(
93
+ number: Any, currency_symbol: str = "Rs.", ne: bool = False
94
+ ) -> str:
95
+ """
96
+ Formats a number with Nepali-style commas and optional currency symbol.
97
+ Eg. 1234567 -> Rs. 12,34,567
35
98
  """
36
- Best-effort conversion to `nepalidatetime`.
99
+ from nepali.number import add_comma, english_to_nepali
100
+
101
+ if number is None:
102
+ return ""
103
+
104
+ try:
105
+ # Convert to string and split by decimal point
106
+ num_str = f"{float(number):.2f}"
107
+ if "." in num_str:
108
+ integer_part, decimal_part = num_str.split(".")
109
+ else:
110
+ integer_part, decimal_part = num_str, ""
37
111
 
38
- - Returns `None` for empty values.
39
- - Returns `nepalidatetime` for valid inputs.
40
- - Returns `None` if the value cannot be parsed.
112
+ # Format integer part with commas
113
+ formatted_integer = add_comma(int(integer_part))
41
114
 
42
- Callers decide whether to raise, fallback, etc.
115
+ # Join back
116
+ res = formatted_integer
117
+ if decimal_part:
118
+ res = f"{res}.{decimal_part}"
119
+
120
+ if ne:
121
+ res = english_to_nepali(res)
122
+
123
+ if currency_symbol:
124
+ return f"{currency_symbol} {res}"
125
+ return res
126
+ except Exception:
127
+ return str(number)
128
+
129
+
130
+ def number_to_nepali_words(number: Any) -> str:
43
131
  """
44
- if value in (None, ""):
45
- return None
46
- if isinstance(value, nepalidatetime):
47
- return value
48
- if isinstance(value, str):
49
- try:
50
- return nepalidatetime.strptime(value.strip(), BS_DATETIME_FORMAT)
51
- except Exception:
52
- return None
53
- return None
132
+ Converts a number to Nepali words.
133
+ Eg. 123 -> एक सय तेईस
134
+ """
135
+ if number is None:
136
+ return ""
137
+
138
+ # Basic implementation for now, can be expanded
139
+ # Mapping for numbers to words (simplified)
140
+ # This is a complex task for a full implementation,
141
+ # but I'll provide a robust enough version for common usage.
142
+
143
+ try:
144
+ num = int(float(number))
145
+ except (ValueError, TypeError):
146
+ return str(number)
147
+
148
+ if num == 0:
149
+ return "शून्य"
150
+
151
+ ones = [
152
+ "",
153
+ "एक",
154
+ "दुई",
155
+ "तीन",
156
+ "चार",
157
+ "पाँच",
158
+ "छ",
159
+ "सात",
160
+ "आठ",
161
+ "नौ",
162
+ "दश",
163
+ "एघार",
164
+ "बाह्र",
165
+ "तेह्र",
166
+ "चौध",
167
+ "पन्ध्र",
168
+ "सोह्र",
169
+ "सत्र",
170
+ "अठार",
171
+ "उन्नाइस",
172
+ "बीस",
173
+ "एकाइस",
174
+ "बाइस",
175
+ "तेईस",
176
+ "चौबीस",
177
+ "पच्चीस",
178
+ "छब्बीस",
179
+ "सत्ताइस",
180
+ "अठाइस",
181
+ "उनन्तीस",
182
+ "तीस",
183
+ "एकतीस",
184
+ "बत्तीस",
185
+ "तेत्तीस",
186
+ "चौंतीस",
187
+ "पैंतीस",
188
+ "छत्तीस",
189
+ "सैंतीस",
190
+ "अठतीस",
191
+ "उनन्चालीस",
192
+ "चालीस",
193
+ "एकचालीस",
194
+ "बयालीस",
195
+ "त्रिचालीस",
196
+ "चवालीस",
197
+ "पैंतालीस",
198
+ "छयालीस",
199
+ "सत्तालीस",
200
+ "अठचालीस",
201
+ "उनन्पचास",
202
+ "पचास",
203
+ "एकाउन्न",
204
+ "बाउन्न",
205
+ "त्रिपन्न",
206
+ "चउन्न",
207
+ "पचपन्न",
208
+ "छपन्न",
209
+ "सन्ताउन्न",
210
+ "अन्ठाउन्न",
211
+ "उनन्साठी",
212
+ "साठी",
213
+ "एकसाठी",
214
+ "बासट्ठी",
215
+ "त्रिसट्ठी",
216
+ "चौसट्ठी",
217
+ "पैंसट्ठी",
218
+ "छयसट्ठी",
219
+ "सतसट्ठी",
220
+ "अठसट्ठी",
221
+ "उनन्सत्तरी",
222
+ "सत्तरी",
223
+ "एकहत्तर",
224
+ "बाहत्तर",
225
+ "त्रिहत्तर",
226
+ "चौरहत्तर",
227
+ "पचहत्तर",
228
+ "छयहत्तर",
229
+ "सतहत्तर",
230
+ "अठहत्तर",
231
+ "उनन्असी",
232
+ "असी",
233
+ "एकासी",
234
+ "बयासी",
235
+ "त्रियासी",
236
+ "चौरासी",
237
+ "पचासी",
238
+ "छयासी",
239
+ "सतासी",
240
+ "अठासी",
241
+ "उनन्नब्बे",
242
+ "नब्बे",
243
+ "एकानब्बे",
244
+ "बयानब्बे",
245
+ "त्रियानब्बे",
246
+ "चौरानब्बे",
247
+ "पञ्चानब्बे",
248
+ "छ्यानब्बे",
249
+ "सन्तानब्बे",
250
+ "अन्ठानब्बे",
251
+ "उनन्सय",
252
+ ]
253
+
254
+ units = [
255
+ ("", ""),
256
+ (100, "सय"),
257
+ (1000, "हजार"),
258
+ (100000, "लाख"),
259
+ (10000000, "करोड"),
260
+ (1000000000, "अरब"),
261
+ (100000000000, "खरब"),
262
+ ]
263
+
264
+ def _convert(n):
265
+ if n == 0:
266
+ return ""
267
+ if n < 100:
268
+ return ones[n]
269
+
270
+ for i in range(len(units) - 1, 0, -1):
271
+ div, unit_name = units[i]
272
+ if n >= div:
273
+ prefix_val = n // div
274
+ remainder = n % div
54
275
 
276
+ # For 'सय' (100), we use ones[prefix_val]
277
+ # For others, we might need recursive calls if prefix_val >= 100
278
+ prefix_words = _convert(prefix_val)
279
+ res = f"{prefix_words} {unit_name}"
280
+ if remainder > 0:
281
+ res += f" {_convert(remainder)}"
282
+ return res.strip()
283
+ return ""
55
284
 
56
- def get_districts_by_province(province_name):
285
+ return _convert(num)
286
+
287
+
288
+ def english_to_nepali_unicode(text: Any) -> str:
57
289
  """
58
- Returns a list of districts in the given province.
290
+ Converts English text/numbers to Nepali Unicode.
291
+ Currently focuses on numbers.
59
292
  """
60
- from nepali.locations import provinces
293
+ from nepali.number import english_to_nepali
61
294
 
62
- selected_province = next((p for p in provinces if p.name == province_name), None)
63
- if not selected_province:
64
- return []
65
- return [{"id": d.name, "text": d.name} for d in selected_province.districts]
295
+ if text is None:
296
+ return ""
297
+
298
+ return english_to_nepali(text)
66
299
 
67
300
 
68
- def get_municipalities_by_district(district_name):
301
+ def normalize_address(address_string: str) -> dict[str, Optional[str]]:
69
302
  """
70
- Returns a list of municipalities in the given district.
303
+ Attempts to normalize a Nepali address string into Province, District, and Municipality.
304
+ Returns a dictionary with 'province', 'district', and 'municipality'.
71
305
  """
72
- from nepali.locations import districts
306
+ if not address_string:
307
+ return {"province": None, "district": None, "municipality": None}
73
308
 
74
- selected_district = next((d for d in districts if d.name == district_name), None)
75
- if not selected_district:
76
- return []
77
- return [{"id": m.name, "text": m.name} for m in selected_district.municipalities]
309
+ result = {"province": None, "district": None, "municipality": None}
310
+
311
+ import re
312
+
313
+ content = address_string.replace(",", " ").replace("-", " ")
314
+
315
+ def normalize_nepali(text):
316
+ if not text:
317
+ return text
318
+ # Replace Chandrabindu with Anusvara for easier matching
319
+ return text.replace("ँ", "ं").replace("ाँ", "ां")
320
+
321
+ tokens = [t.strip() for t in content.split() if t.strip()]
322
+ normalized_tokens = [normalize_nepali(t) for t in tokens]
323
+
324
+ # We try to match from most specific to least specific
325
+ found_municipality = None
326
+ found_district = None
327
+ found_province = None
328
+
329
+ # Helper for name matching
330
+ def matches(name_eng, name_nep, token, normalized_token):
331
+ token_lower = token.lower()
332
+ name_nep_norm = normalize_nepali(name_nep)
333
+
334
+ # Exact matches
335
+ if (
336
+ token == name_nep
337
+ or normalized_token == name_nep_norm
338
+ or token_lower == name_eng.lower()
339
+ ):
340
+ return True
341
+
342
+ # Handle "Province 1" -> "Koshi" mapping
343
+ if (name_eng == "Province 1" and "koshi" in token_lower) or (
344
+ name_nep_norm == normalize_nepali("प्रदेश नं. १")
345
+ and "कोशी" in normalized_token
346
+ ):
347
+ return True
348
+
349
+ # Partial matches for English (e.g., "Pokhara" in "Pokhara Metropolitan City")
350
+ # Only if token is at least 4 characters to avoid too many false positives
351
+ if len(token) >= 4:
352
+ if token_lower in name_eng.lower():
353
+ return True
354
+
355
+ # Partial matches for Nepali
356
+ if len(normalized_token) >= 2:
357
+ if normalized_token in name_nep_norm:
358
+ return True
359
+
360
+ return False
361
+
362
+ # Check for municipality first
363
+ for i, token in enumerate(tokens):
364
+ nt = normalized_tokens[i]
365
+ for m in municipalities:
366
+ if matches(m.name, m.name_nepali, token, nt):
367
+ found_municipality = m
368
+ break
369
+ if found_municipality:
370
+ break
371
+
372
+ # Check for district
373
+ for i, token in enumerate(tokens):
374
+ nt = normalized_tokens[i]
375
+ for d in districts:
376
+ if matches(d.name, d.name_nepali, token, nt):
377
+ found_district = d
378
+ break
379
+ if found_district:
380
+ break
381
+
382
+ # Check for province
383
+ for i, token in enumerate(tokens):
384
+ nt = normalized_tokens[i]
385
+ for p in provinces:
386
+ if matches(p.name, p.name_nepali, token, nt):
387
+ found_province = p
388
+ break
389
+ if found_province:
390
+ break
391
+
392
+ # Fill in the gaps using hierarchy
393
+ if found_municipality:
394
+ result["municipality"] = found_municipality.name
395
+ if not found_district:
396
+ found_district = found_municipality.district
397
+ if not found_province:
398
+ found_province = found_municipality.province
399
+
400
+ if found_district:
401
+ result["district"] = found_district.name
402
+ if not found_province:
403
+ found_province = found_district.province
404
+
405
+ if found_province:
406
+ # Handle "Province 1" -> "Koshi Province" consistency
407
+ name = found_province.name
408
+ if name == "Province 1":
409
+ name = "Koshi Province"
410
+ result["province"] = name
411
+
412
+ # Check for Nepali context
413
+ is_nepali = any(
414
+ re.search(r"[\u0900-\u097F]", t) for t in tokens
415
+ ) # Basic check for Devanagari characters
416
+ if is_nepali:
417
+ if found_municipality:
418
+ result["municipality"] = found_municipality.name_nepali
419
+ if found_district:
420
+ result["district"] = found_district.name_nepali
421
+ if found_province:
422
+ name = found_province.name_nepali
423
+ if name == "प्रदेश नं. १":
424
+ name = "कोशी प्रदेश"
425
+ result["province"] = name
426
+
427
+ return result
@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
3
3
  from nepali import phone_number
4
4
 
5
5
 
6
- # validates the given value is a valid nepali phone number
6
+ # Check if a phone number is valid in Nepal
7
7
  def validate_nepali_phone_number(value):
8
8
  if not phone_number.is_valid(value):
9
9
  raise ValidationError(
django_nepkit/views.py CHANGED
@@ -1,4 +1,4 @@
1
- from django.http import JsonResponse
1
+ from django.http import JsonResponse, HttpResponse
2
2
 
3
3
  from django_nepkit.utils import (
4
4
  get_districts_by_province,
@@ -6,17 +6,80 @@ from django_nepkit.utils import (
6
6
  )
7
7
 
8
8
 
9
+ from django_nepkit.conf import nepkit_settings
10
+
11
+
12
+ def _render_options(data, placeholder):
13
+ """Internal helper to render list of options as HTML."""
14
+ options = [f'<option value="">{placeholder}</option>']
15
+ for item in data:
16
+ options.append(f'<option value="{item["id"]}">{item["text"]}</option>')
17
+ return HttpResponse("\n".join(options))
18
+
19
+
9
20
  def district_list_view(request):
10
21
  province = request.GET.get("province")
22
+
23
+ # Fallback: take the first non-internal parameter as province
24
+ if not province:
25
+ for key, value in request.GET.items():
26
+ if key not in ["ne", "en", "html"] and value:
27
+ province = value
28
+ break
29
+
11
30
  if not province:
12
31
  return JsonResponse([], safe=False)
13
- data = get_districts_by_province(province)
32
+
33
+ default_lang = nepkit_settings.DEFAULT_LANGUAGE
34
+ ne_param = request.GET.get("ne")
35
+ ne = ne_param.lower() == "true" if ne_param else default_lang == "ne"
36
+
37
+ en_param = request.GET.get("en")
38
+ en = en_param.lower() == "true" if en_param else not ne
39
+
40
+ as_html = (
41
+ request.GET.get("html", "false").lower() == "true"
42
+ or request.headers.get("HX-Request") == "true"
43
+ )
44
+
45
+ data = get_districts_by_province(province, ne=ne, en=en)
46
+
47
+ if as_html:
48
+ placeholder = "जिल्ला छान्नुहोस्" if ne else "Select District"
49
+ return _render_options(data, placeholder)
50
+
14
51
  return JsonResponse(data, safe=False)
15
52
 
16
53
 
17
54
  def municipality_list_view(request):
18
55
  district = request.GET.get("district")
56
+
57
+ # Fallback: take the first non-internal parameter as district
58
+ if not district:
59
+ for key, value in request.GET.items():
60
+ if key not in ["ne", "en", "html"] and value:
61
+ district = value
62
+ break
63
+
19
64
  if not district:
20
65
  return JsonResponse([], safe=False)
21
- data = get_municipalities_by_district(district)
66
+
67
+ default_lang = nepkit_settings.DEFAULT_LANGUAGE
68
+ ne_param = request.GET.get("ne")
69
+ ne = ne_param.lower() == "true" if ne_param else default_lang == "ne"
70
+
71
+ en_param = request.GET.get("en")
72
+ en = en_param.lower() == "true" if en_param else not ne
73
+
74
+ as_html = (
75
+ request.GET.get("html", "false").lower() == "true"
76
+ or request.headers.get("HX-Request") == "true"
77
+ )
78
+
79
+ data = get_municipalities_by_district(district, ne=ne, en=en)
80
+
81
+ if as_html:
82
+ placeholder = "नगरपालिका छान्नुहोस्" if ne else "Select Municipality"
83
+ return _render_options(data, placeholder)
84
+
22
85
  return JsonResponse(data, safe=False)