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.
- django_nepkit/__init__.py +20 -0
- django_nepkit/admin.py +243 -93
- django_nepkit/conf.py +34 -0
- django_nepkit/filters.py +113 -0
- django_nepkit/forms.py +9 -9
- django_nepkit/models.py +143 -162
- django_nepkit/serializers.py +127 -28
- django_nepkit/static/django_nepkit/js/address-chaining.js +105 -18
- django_nepkit/static/django_nepkit/js/nepal-data.js +1 -0
- django_nepkit/static/django_nepkit/js/nepali-datepicker-init.js +55 -46
- django_nepkit/utils.py +396 -46
- django_nepkit/validators.py +1 -1
- django_nepkit/views.py +66 -3
- django_nepkit/widgets.py +100 -31
- django_nepkit-0.2.0.dist-info/METADATA +308 -0
- django_nepkit-0.2.0.dist-info/RECORD +35 -0
- example/demo/admin.py +45 -21
- example/demo/models.py +41 -4
- example/demo/serializers.py +39 -0
- example/demo/urls.py +13 -2
- example/demo/views.py +125 -3
- example/example_project/settings.py +10 -0
- example/example_project/urls.py +1 -1
- example/manage.py +2 -2
- django_nepkit/templatetags/__init__.py +0 -0
- django_nepkit/templatetags/nepali.py +0 -74
- django_nepkit-0.1.0.dist-info/METADATA +0 -377
- django_nepkit-0.1.0.dist-info/RECORD +0 -39
- example/demo/migrations/0001_initial.py +0 -2113
- example/demo/migrations/0002_alter_person_phone_number.py +0 -18
- example/demo/migrations/0003_person_created_at_person_updated_at.py +0 -27
- example/demo/migrations/0004_alter_person_created_at_alter_person_updated_at.py +0 -23
- example/demo/migrations/0005_alter_person_created_at_alter_person_updated_at.py +0 -27
- example/demo/migrations/__init__.py +0 -0
- {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.0.dist-info}/WHEEL +0 -0
- {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
88
|
+
el.addEventListener('focus', applySoon);
|
|
89
|
+
el.addEventListener('click', applySoon);
|
|
66
90
|
});
|
|
67
91
|
}
|
|
68
92
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
//
|
|
91
|
-
if (
|
|
92
|
-
|
|
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
|
-
|
|
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 =
|
|
9
|
-
BS_DATETIME_FORMAT =
|
|
10
|
+
BS_DATE_FORMAT = nepkit_settings.BS_DATE_FORMAT
|
|
11
|
+
BS_DATETIME_FORMAT = nepkit_settings.BS_DATETIME_FORMAT
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
def
|
|
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,
|
|
18
|
+
if isinstance(value, cls):
|
|
25
19
|
return value
|
|
26
20
|
if isinstance(value, str):
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
285
|
+
return _convert(num)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def english_to_nepali_unicode(text: Any) -> str:
|
|
57
289
|
"""
|
|
58
|
-
|
|
290
|
+
Converts English text/numbers to Nepali Unicode.
|
|
291
|
+
Currently focuses on numbers.
|
|
59
292
|
"""
|
|
60
|
-
from nepali.
|
|
293
|
+
from nepali.number import english_to_nepali
|
|
61
294
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return
|
|
295
|
+
if text is None:
|
|
296
|
+
return ""
|
|
297
|
+
|
|
298
|
+
return english_to_nepali(text)
|
|
66
299
|
|
|
67
300
|
|
|
68
|
-
def
|
|
301
|
+
def normalize_address(address_string: str) -> dict[str, Optional[str]]:
|
|
69
302
|
"""
|
|
70
|
-
|
|
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
|
-
|
|
306
|
+
if not address_string:
|
|
307
|
+
return {"province": None, "district": None, "municipality": None}
|
|
73
308
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
django_nepkit/validators.py
CHANGED
|
@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|
|
3
3
|
from nepali import phone_number
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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)
|