django-nepkit 0.1.0__py3-none-any.whl → 0.2.1__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 (39) 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/constants.py +129 -0
  5. django_nepkit/filters.py +113 -0
  6. django_nepkit/forms.py +9 -9
  7. django_nepkit/lang_utils.py +52 -0
  8. django_nepkit/models.py +138 -161
  9. django_nepkit/serializers.py +127 -28
  10. django_nepkit/static/django_nepkit/js/address-chaining.js +129 -29
  11. django_nepkit/static/django_nepkit/js/nepal-data.js +1 -0
  12. django_nepkit/static/django_nepkit/js/nepali-datepicker-init.js +55 -46
  13. django_nepkit/utils.py +270 -46
  14. django_nepkit/validators.py +1 -1
  15. django_nepkit/views.py +127 -10
  16. django_nepkit/widgets.py +100 -31
  17. django_nepkit-0.2.1.dist-info/METADATA +308 -0
  18. django_nepkit-0.2.1.dist-info/RECORD +37 -0
  19. example/demo/admin.py +45 -21
  20. example/demo/models.py +41 -4
  21. example/demo/serializers.py +39 -0
  22. example/demo/urls.py +13 -2
  23. example/demo/views.py +125 -3
  24. example/example_project/settings.py +10 -0
  25. example/example_project/urls.py +1 -1
  26. example/manage.py +2 -2
  27. django_nepkit/templatetags/__init__.py +0 -0
  28. django_nepkit/templatetags/nepali.py +0 -74
  29. django_nepkit-0.1.0.dist-info/METADATA +0 -377
  30. django_nepkit-0.1.0.dist-info/RECORD +0 -39
  31. example/demo/migrations/0001_initial.py +0 -2113
  32. example/demo/migrations/0002_alter_person_phone_number.py +0 -18
  33. example/demo/migrations/0003_person_created_at_person_updated_at.py +0 -27
  34. example/demo/migrations/0004_alter_person_created_at_alter_person_updated_at.py +0 -23
  35. example/demo/migrations/0005_alter_person_created_at_alter_person_updated_at.py +0 -27
  36. example/demo/migrations/__init__.py +0 -0
  37. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/WHEEL +0 -0
  38. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/licenses/LICENSE +0 -0
  39. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/top_level.txt +0 -0
django_nepkit/widgets.py CHANGED
@@ -1,72 +1,141 @@
1
1
  from django import forms
2
- from django.urls import reverse_lazy
2
+ from django.urls import reverse, NoReverseMatch
3
+ from nepali.datetime import nepalidate
4
+ from django_nepkit.utils import BS_DATE_FORMAT
5
+
6
+ from django_nepkit.conf import nepkit_settings
3
7
 
4
8
 
5
9
  def _append_css_class(attrs, class_name: str):
6
- """
7
- Django-idiomatic helper to append a CSS class without clobbering existing ones.
8
- """
10
+ """Helper to add a CSS class safely."""
9
11
  existing = (attrs.get("class") or "").strip()
10
12
  attrs["class"] = (f"{existing} {class_name}").strip() if existing else class_name
11
13
  return attrs
12
14
 
13
15
 
14
- class ChainedSelectWidget(forms.Select):
15
- class Media:
16
- js = ("django_nepkit/js/address-chaining.js",)
16
+ class NepaliWidgetMixin:
17
+ def __init__(self, *args, **kwargs):
18
+ default_lang = nepkit_settings.DEFAULT_LANGUAGE
19
+ self.ne = kwargs.pop("ne", default_lang == "ne")
17
20
 
21
+ self.en = kwargs.pop("en", not self.ne)
22
+ self.htmx = kwargs.pop("htmx", False)
23
+
24
+ attrs = kwargs.get("attrs", {}) or {}
25
+
26
+ # Language settings
27
+ if self.ne:
28
+ attrs["data-ne"] = "true"
29
+ if self.en:
30
+ attrs["data-en"] = "true"
31
+
32
+ # Pass the date format to JavaScript
33
+ attrs["data-format"] = BS_DATE_FORMAT
34
+
35
+ self._configure_attrs(attrs)
18
36
 
19
- class ProvinceSelectWidget(ChainedSelectWidget):
20
- def __init__(self, *args, **kwargs):
21
- attrs = kwargs.get("attrs", {})
22
- _append_css_class(attrs, "nepkit-province-select")
23
37
  kwargs["attrs"] = attrs
24
38
  super().__init__(*args, **kwargs)
25
39
 
40
+ def _configure_attrs(self, attrs):
41
+ """Override in subclasses or use class attributes."""
42
+ css_class = getattr(self, "css_class", None)
43
+ if css_class:
44
+ _append_css_class(attrs, css_class)
45
+
46
+ def get_context(self, name, value, attrs):
47
+ context = super().get_context(name, value, attrs)
48
+ widget_attrs = context.get("widget", {}).get("attrs", {})
49
+
50
+ # Handle URLs for data fetching
51
+ url_name = getattr(self, "_url_name", None)
52
+ if url_name:
53
+ try:
54
+ widget_attrs["data-url"] = reverse(url_name)
55
+ except NoReverseMatch:
56
+ pass
57
+
58
+ # Handle HTMX dynamic updates
59
+ if self.htmx:
60
+ hx_url_name = getattr(self, "_hx_url_name", None)
61
+ if hx_url_name:
62
+ try:
63
+ widget_attrs["hx-get"] = reverse(hx_url_name)
64
+ widget_attrs["hx-target"] = getattr(self, "_hx_target", "")
65
+ widget_attrs["hx-trigger"] = "change"
66
+ except NoReverseMatch:
67
+ pass
68
+
69
+ return context
70
+
71
+
72
+ class ChainedSelectWidget(forms.Select):
73
+ class Media:
74
+ js = (
75
+ "django_nepkit/js/nepal-data.js",
76
+ "django_nepkit/js/address-chaining.js",
77
+ )
78
+
79
+
80
+ class ProvinceSelectWidget(NepaliWidgetMixin, ChainedSelectWidget):
81
+ css_class = "nepkit-province-select"
26
82
 
27
- class DistrictSelectWidget(ChainedSelectWidget):
28
83
  def __init__(self, *args, **kwargs):
29
- attrs = kwargs.get("attrs", {})
30
- _append_css_class(attrs, "nepkit-district-select")
31
- attrs["data-url"] = reverse_lazy("django_nepkit:district-list")
32
- kwargs["attrs"] = attrs
33
84
  super().__init__(*args, **kwargs)
85
+ if self.htmx:
86
+ self._hx_url_name = "django_nepkit:district-list"
87
+ self._hx_target = ".nepkit-district-select"
88
+
34
89
 
90
+ class DistrictSelectWidget(NepaliWidgetMixin, ChainedSelectWidget):
91
+ css_class = "nepkit-district-select"
92
+ _url_name = "django_nepkit:district-list"
35
93
 
36
- class MunicipalitySelectWidget(ChainedSelectWidget):
37
94
  def __init__(self, *args, **kwargs):
38
- attrs = kwargs.get("attrs", {})
39
- _append_css_class(attrs, "nepkit-municipality-select")
40
- attrs["data-url"] = reverse_lazy("django_nepkit:municipality-list")
41
- kwargs["attrs"] = attrs
42
95
  super().__init__(*args, **kwargs)
96
+ if self.htmx:
97
+ self._hx_url_name = "django_nepkit:municipality-list"
98
+ self._hx_target = ".nepkit-municipality-select"
99
+
43
100
 
101
+ class MunicipalitySelectWidget(NepaliWidgetMixin, ChainedSelectWidget):
102
+ css_class = "nepkit-municipality-select"
103
+ _url_name = "django_nepkit:municipality-list"
44
104
 
45
- class NepaliDatePickerWidget(forms.TextInput):
105
+
106
+ class NepaliDatePickerWidget(NepaliWidgetMixin, forms.TextInput):
46
107
  input_type = "text"
47
108
 
48
109
  class Media:
49
110
  css = {
50
111
  "all": (
51
- "https://unpkg.com/nepali-date-picker@2.0.2/dist/nepaliDatePicker.min.css",
112
+ "https://nepalidatepicker.sajanmaharjan.com.np/v5/nepali.datepicker/css/nepali.datepicker.v5.0.6.min.css",
52
113
  )
53
114
  }
54
115
  js = (
55
116
  "https://code.jquery.com/jquery-3.5.1.slim.min.js",
56
- "https://unpkg.com/nepali-date-picker@2.0.2/dist/nepaliDatePicker.min.js",
117
+ "https://nepalidatepicker.sajanmaharjan.com.np/v5/nepali.datepicker/js/nepali.datepicker.v5.0.6.min.js",
57
118
  "django_nepkit/js/nepali-datepicker-init.js",
58
119
  )
59
120
 
60
- def __init__(self, *args, **kwargs):
61
- attrs = kwargs.get("attrs", {})
62
- # Ensure we don't have vDateField class which triggers Django admin calendar
121
+ def _configure_attrs(self, attrs):
63
122
  classes = attrs.get("class", "")
64
123
  if "vDateField" in classes:
65
124
  classes = classes.replace("vDateField", "")
125
+ attrs["class"] = classes
66
126
 
67
- attrs["class"] = (classes or "").strip()
68
127
  _append_css_class(attrs, "nepkit-datepicker")
69
128
  attrs["autocomplete"] = "off"
70
- attrs["placeholder"] = "YYYY-MM-DD"
71
- kwargs["attrs"] = attrs
72
- super().__init__(*args, **kwargs)
129
+ attrs["placeholder"] = (
130
+ BS_DATE_FORMAT.replace("%Y", "YYYY").replace("%m", "MM").replace("%d", "DD")
131
+ )
132
+
133
+ def format_value(self, value):
134
+ if value is None:
135
+ return None
136
+
137
+ if self.ne and isinstance(value, nepalidate):
138
+ if hasattr(value, "strftime_ne"):
139
+ return value.strftime_ne(BS_DATE_FORMAT)
140
+
141
+ return super().format_value(value)
@@ -0,0 +1,308 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-nepkit
3
+ Version: 0.2.1
4
+ Summary: Django Nepali date, time, phone, and address fields with helpers.
5
+ Home-page: https://github.com/S4NKALP/django-nepkit
6
+ Author: Sankalp Tharu
7
+ Author-email: Sankalp Tharu <sankalptharu50028@gmail.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/S4NKALP/django-nepkit
10
+ Keywords: django,django-fields,django-forms,nepal,nepali,nepali-date,bikram-sambat,nepali-calendar,timezone,asia-kathmandu,phone-number,address,province,district,municipality
11
+ Classifier: Framework :: Django
12
+ Classifier: Framework :: Django :: 4.2
13
+ Classifier: Framework :: Django :: 5.0
14
+ Classifier: Framework :: Django :: 5.1
15
+ Classifier: Framework :: Django :: 6.0
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Natural Language :: English
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: django>=4.2
29
+ Requires-Dist: django-filter>=25.2
30
+ Requires-Dist: djangorestframework>=3.16.1
31
+ Requires-Dist: nepali>=1.2.0
32
+ Provides-Extra: drf
33
+ Requires-Dist: djangorestframework>=3.14; extra == "drf"
34
+ Requires-Dist: django-filter>=23.1; extra == "drf"
35
+ Dynamic: author
36
+ Dynamic: home-page
37
+ Dynamic: license-file
38
+ Dynamic: requires-python
39
+
40
+ # 🇳🇵 django-nepkit
41
+
42
+ <div align="center">
43
+
44
+ [![PyPI version](https://badge.fury.io/py/django-nepkit.svg)](https://badge.fury.io/py/django-nepkit)
45
+ [![Python Versions](https://img.shields.io/pypi/pyversions/django-nepkit.svg)](https://pypi.org/project/django-nepkit/)
46
+ [![Django Versions](https://img.shields.io/badge/Django-3.2%20%7C%204.2%20%7C%205.0%20%7C%206.0-blue.svg)](https://www.djangoproject.com/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
+
49
+ **A toolkit for handling BS dates, regional locations, and validation in the local context.**
50
+
51
+ </div>
52
+
53
+ Building software for local requirements comes with unique challenges, from handling BS dates to managing the regional administrative hierarchy. `django-nepkit` provides solutions for these requirements directly within the Django ecosystem.
54
+
55
+ ![Showcase](docs/showcase.gif)
56
+
57
+ ---
58
+
59
+ ## 🎯 Features
60
+
61
+ - **📅 BS Date Support**: Model fields for `nepalidate` and `nepalidatetime` objects.
62
+ - **🗺️ Regional Locations**: Pre-defined Provinces, Districts, and Municipalities.
63
+ - **📱 Phone Validation**: Patterns for local mobile and landline numbers.
64
+ - **💰 Currency Formatting**: `NepaliCurrencyField` with automatic Lakhs/Crores comma placement.
65
+ - **🔤 Numbers to Words**: Convert digits into Nepali text representation.
66
+ - **🔌 Admin Integration**: Automatic setup for datepickers and localized list displays.
67
+ - **🚀 API Support**: DRF Serializers and Filtering backends for BS searching and ordering.
68
+ - **⚡ Location Chaining**: Address linking via client side JS or server driven HTMX.
69
+ - **🔍 Address Normalization**: Utility to extract structured locations from raw strings.
70
+
71
+ ---
72
+
73
+ ## 🛠 Setup
74
+
75
+ Installation:
76
+
77
+ ```bash
78
+ pip install django-nepkit
79
+ ```
80
+
81
+ ### 1. Basic Configuration
82
+
83
+ Add it to your `INSTALLED_APPS`:
84
+
85
+ ```python
86
+ INSTALLED_APPS = [
87
+ # ...
88
+ "django_nepkit",
89
+ ]
90
+ ```
91
+
92
+ ### 2. Global Control
93
+
94
+ Configure behavior in your `settings.py`:
95
+
96
+ ```python
97
+ NEPKIT = {
98
+ "DEFAULT_LANGUAGE": "en", # "en" or "ne"
99
+ "ADMIN_DATEPICKER": True, # Toggle the datepicker
100
+ "TIME_FORMAT": 12, # 12 or 24 hour display
101
+ "DATE_INPUT_FORMATS": ["%Y-%m-%d", "%d/%m/%Y", "%d-%m-%Y"], # Input formats
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 🚀 Core Usage
108
+
109
+ ### 1. Model Implementation
110
+
111
+ Fields store `YYYY-MM-DD` strings for database stability while providing BS objects in Python.
112
+
113
+ ```python
114
+ from django_nepkit import NepaliDateField, NepaliPhoneNumberField
115
+
116
+ class Profile(models.Model):
117
+ name = models.CharField(max_length=100)
118
+ birth_date = NepaliDateField() # BS Date support
119
+ phone = NepaliPhoneNumberField() # Local pattern validation
120
+ ```
121
+
122
+ ### 2. Admin Integration
123
+
124
+ Use `NepaliModelAdmin` for automatic formatting and datepicker support.
125
+
126
+ ```python
127
+ from django_nepkit import NepaliModelAdmin, NepaliDateFilter
128
+
129
+ @admin.register(Profile)
130
+ class ProfileAdmin(NepaliModelAdmin):
131
+ list_display = ("name", "birth_date", "phone")
132
+ list_filter = (("birth_date", NepaliDateFilter),)
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 🗺️ Address Management
138
+
139
+ Manage the **Province → District → Municipality** hierarchy.
140
+
141
+ ### Client-Side Chaining (Standard)
142
+
143
+ Cascading selects in the Django Admin without extra configuration.
144
+
145
+ ```python
146
+ from django_nepkit import ProvinceField, DistrictField, MunicipalityField
147
+
148
+ class Address(models.Model):
149
+ province = ProvinceField()
150
+ district = DistrictField()
151
+ municipality = MunicipalityField()
152
+
153
+ ### Address Normalization
154
+
155
+ Standardize raw strings into structured location data (Province, District, Municipality).
156
+
157
+ ```python
158
+ from django_nepkit.utils import normalize_address
159
+
160
+ # Supports English or Nepali input
161
+ result = normalize_address("House 123, Bharatpur, Chitwan")
162
+ # Returns: {'province': 'Bagmati Province', 'district': 'Chitawan', 'municipality': 'Bharatpur Metropolitan City'}
163
+
164
+ result_ne = normalize_address("विराटनगर, कोशी")
165
+ # Returns: {'province': 'कोशी प्रदेश', 'district': 'मोरङ', 'municipality': 'विराटनगर महानगरपालिका'}
166
+ ```
167
+ ```
168
+
169
+ ### Server Side Chaining (HTMX)
170
+
171
+ Enable `htmx=True` for a server driven experience.
172
+
173
+ > [!IMPORTANT]
174
+ > **HTMX Setup:**
175
+ > Include the required URLs in your main `urls.py`:
176
+ >
177
+ > ```python
178
+ > path("nepkit/", include("django_nepkit.urls")),
179
+ > ```
180
+
181
+ ---
182
+
183
+ ## 🔌 API & DRF Support
184
+
185
+ Search and ordering work natively. BS year/month filtering is supported.
186
+
187
+ ```python
188
+ from django_nepkit.filters import NepaliDateYearFilter
189
+
190
+ class ProfileFilter(filters.FilterSet):
191
+ # Filter by BS Year (e.g., /api/profiles/?year=2081)
192
+ year = NepaliDateYearFilter(field_name="birth_date")
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 💰 Formatting & Helpers
198
+
199
+ ### 1. Currency & Numbers
200
+ Use `NepaliCurrencyField` for automatic formatting in the admin and templates.
201
+
202
+ ```python
203
+ from django_nepkit import NepaliCurrencyField
204
+
205
+ class Transaction(models.Model):
206
+ amount = NepaliCurrencyField() # Defaults to 19 digits, 2 decimals
207
+ ```
208
+
209
+ ### 2. Template Filters
210
+ Load the tags to use localized formatting in your templates.
211
+
212
+ ```html
213
+ {% load nepali %}
214
+
215
+ <!-- Comma formatting: Rs. 1,12,000.00 -->
216
+ <p>{{ 112000 | nepali_currency }}</p>
217
+
218
+ <!-- Numbers to Words: एक सय तेईस -->
219
+ <p>{{ 123 | nepali_words }}</p>
220
+
221
+ <!-- English to Nepali Digits: १२३ -->
222
+ <p>{{ "123" | nepali_unicode }}</p>
223
+ ```
224
+
225
+ ---
226
+
227
+ ## 🕒 Technical Design
228
+
229
+ This library is engineered for performance, data integrity, and local compliance.
230
+
231
+ ### 1. The "Source of Truth" Strategy
232
+ We avoid on-the-fly `AD to BS` conversion during database queries because it is computationally expensive and prone to logical drift (due to lunar calendar offsets).
233
+ - **Storage**: All BS dates are stored as `VARCHAR(10)` in `YYYY-MM-DD` format.
234
+ - **Sorting**: Because `YYYY` is at the start, string based database sorting (ascending/descending) accurately matches chronological order.
235
+ - **Indexability**: Standard B-Tree indexes work perfectly on these fields without requiring custom database functions.
236
+ - **Timezone Safety**: Dates are stored without time components, making them immune to server side timezone shifts during saving.
237
+
238
+ ### 2. Python Object Mapping
239
+ While data is stored as strings, it is automatically hydrated into rich Python objects.
240
+ - **`nepali-datetime` Integration**: Values are cast to `nepalidate` or `nepalidatetime` objects when retrieved from the database.
241
+ - **Validation**: Fields use specialized validators (e.g., `validate_nepali_phone_number`) that leverage official regional patterns.
242
+
243
+ ### 3. Frontend Architecture
244
+ - **Automatic Initialization**: The library includes a lightweight JS observer that automatically initializes the datepicker for any field with the `.nepkit-datepicker` class.
245
+ - **Theme Support**: The datepicker dynamically adapts its skin based on the Django Admin's dark/light mode state.
246
+
247
+ ---
248
+
249
+ ## ❓ FAQ
250
+
251
+ **Q: How do I handle Null or Optional dates?**
252
+
253
+ Just like standard Django fields, pass `null=True, blank=True` to any `django-nepkit` field. The library handles empty strings and `None` values gracefully.
254
+
255
+ **Q: Can I change the database storage format?**
256
+
257
+ No. The `YYYY-MM-DD` format is hardcoded to ensure database level sorting and indexing work consistently. However, you can change the **display** format via global settings or template filters.
258
+
259
+ **Q: Can I use Devanagari output?**
260
+
261
+ Yes. Pass `ne=True` to fields, forms, or serializers.
262
+
263
+ **Q: Can I display the datepicker in English?**
264
+
265
+ Yes. By default, if you pass `en=True` (or if `DEFAULT_LANGUAGE` is set to `"en"`), the datepicker will display month and day names in English instead of Devanagari script.
266
+
267
+ ```python
268
+ birth_date = NepaliDateField(en=True) # English month/day names in picker
269
+ ```
270
+
271
+ **Q: Is the location data up to date?**
272
+
273
+ Yes. Province, District, and Municipality data is sourced from the `nepali` Python package, which is regularly updated to reflect administrative changes in Nepal.
274
+
275
+ **Q: Does it work with standard Django Forms?**
276
+
277
+ Yes. `NepaliDateField` uses a specialized `NepaliDateFormField` that automatically handles input parsing and error reporting.
278
+
279
+ **Q: How do I migrate existing English (AD) dates to BS?**
280
+
281
+ We recommend staying on standard `DateField` for AD data. If you must convert to BS, use our [Migration Script](docs/migration_guide.py) to perform a bulk data transformation safely.
282
+
283
+ **Q: Why use VARCHAR instead of a native DateField?**
284
+
285
+ Native `DateField` in most SQL engines is locked to the Gregorian calendar. Using `VARCHAR` allows us to treat the BS date as the primary data point, avoiding the "off-by-one" conversion errors common when syncing two disparate calendars.
286
+
287
+ ---
288
+
289
+ ## 🤝 Community
290
+
291
+ We welcome contributions and feedback from the community.
292
+
293
+ 1. **Clone**: `git clone https://github.com/S4NKALP/django-nepkit`
294
+ 2. **Setup**: `uv sync`
295
+ 3. **Test**: `uv run pytest`
296
+
297
+ ---
298
+
299
+ ## 🙏 Credits
300
+
301
+ This library is built on top of excellent open source projects:
302
+
303
+ - **[nepali](https://github.com/opensource-nepal/py-nepali)** by [@opensource-nepal](https://github.com/opensource-nepal) - Provides the core `nepalidate`, `nepalidatetime` objects and regional location data (Provinces, Districts, Municipalities).
304
+ - **[Nepali Datepicker](https://nepalidatepicker.sajanmaharjan.com.np/)** by [Sajan Maharjan](https://github.com/sajanm/nepali-date-picker) - Powers the beautiful BS date picker widget in the Django Admin.
305
+
306
+ ---
307
+
308
+ MIT License. Designed for the local Django ecosystem.
@@ -0,0 +1,37 @@
1
+ django_nepkit/__init__.py,sha256=20cQUKdAjNtQYkAJU0g4OSJa90Ru3M9reNfV0ZgG3vc,1164
2
+ django_nepkit/admin.py,sha256=xqQHybYb9T4_NYvCqDV3VYt0tbwWQDc8dwTOrkBmj0Q,12714
3
+ django_nepkit/conf.py,sha256=urr8gwBj2YZgL2Po_C7339YafoQ_PMOWb1MEh-vPdfs,981
4
+ django_nepkit/constants.py,sha256=cVq-ZFkDCBwETU2IDEtAMz2H-oLzSw8TtncRm954Jlg,3507
5
+ django_nepkit/filters.py,sha256=dw5Yej9cLI-cZxlVHSaNUHq63hnnzhkjqmgLPTvbpd8,3939
6
+ django_nepkit/forms.py,sha256=yrRE1gzkxGyqrAl0ETcOWnNsTyhPxlS_nlq4ex5a3UY,1675
7
+ django_nepkit/lang_utils.py,sha256=OY0EdWCHL3pVz4rLijCPv1qs8DMVK8ypL4sv8Dw4w3w,1264
8
+ django_nepkit/models.py,sha256=_u1Y0_q9liReodkDHl-kt8D9b_HxP_GwJo2koLRNBWg,8406
9
+ django_nepkit/serializers.py,sha256=P8dTLOLEaGpc2OuetGXh9LZnbwF1l4VBWQU7y5hwWPA,6487
10
+ django_nepkit/urls.py,sha256=mPF_xEaOqNrInggBhUbwwMq-FTR7rJ0CfT0fQPx41t8,297
11
+ django_nepkit/utils.py,sha256=md3VpScslatEqtaIHo1_kbwvmlVPO45G_Qoikd_oFsE,9106
12
+ django_nepkit/validators.py,sha256=HGd4PwXY7vLh8j2r2pnAHM-rfwVWCa4UDfuW79JYxJ8,407
13
+ django_nepkit/views.py,sha256=u0Hv343omi51pPF0jOLbwBn9cFkDK-Svt9aXtDrHCxM,3864
14
+ django_nepkit/widgets.py,sha256=36Q5kkEn4FuFw--CiAOBrp2mzYSaknCmoLpTFFEDgYU,4588
15
+ django_nepkit/static/django_nepkit/css/admin-nepali-datepicker.css,sha256=g9ago3O0UfoNwRD02AmWSdxiHmXxkQ7mpi94Nn1rO3E,834
16
+ django_nepkit/static/django_nepkit/js/address-chaining.js,sha256=-_eSgL38nc2XUvKv-Ns7cOQyTxra_roRb0z1R0sBKN0,6503
17
+ django_nepkit/static/django_nepkit/js/admin-jquery-bridge.js,sha256=6iqIuSqgYssKtt61WSjye3GBpYHh3AKKYUtGL6Uh3S8,319
18
+ django_nepkit/static/django_nepkit/js/nepal-data.js,sha256=llFe3CAloAoUU505dnNpKgS9Qhqp0-hmZdLbkBDZkz4,168476
19
+ django_nepkit/static/django_nepkit/js/nepali-datepicker-init.js,sha256=b1X15_AAyw5DzHHArGjpF9BFA4RM5IVw9CgzAmPAf40,4356
20
+ django_nepkit-0.2.1.dist-info/licenses/LICENSE,sha256=76985cvIL0AXuhZd5L2C9tbdxKMZX7IOejjvZI8kXMo,1070
21
+ example/manage.py,sha256=wZWUpuXgYEHBxZR-sysETawIaMhr83HQglQTeZdadpQ,752
22
+ example/demo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ example/demo/admin.py,sha256=pNlA7_GlDpNQRIb7VoTEO1fa0JD-EwLA6VK72aQR504,1635
24
+ example/demo/apps.py,sha256=sXxYSi4dEGg-HDkM7C-TfLv26KbSUj-8HTeFS86pW8w,83
25
+ example/demo/models.py,sha256=zZhoFfYK3zCEapOvVgxbs2bS-OKD3kuFUFvrRDaL_-Y,1854
26
+ example/demo/serializers.py,sha256=V07_sXGPPicCJc1RMecd0jQFvldFPaHpjJuSArj5M7U,1034
27
+ example/demo/tests.py,sha256=qWDvA9ZhVCQ1rPbkoFify7o_fDirXMUdYMxF12q3WIM,26
28
+ example/demo/urls.py,sha256=0zB7KRRTE7HHDnTbE5yibpH9eyJhBekNguj_nuY_gcE,786
29
+ example/demo/views.py,sha256=ow6hQwnhTD1KjEkM4R7qRcuHcnJJ4pOXsU-JRH0Ocn4,4380
30
+ example/example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ example/example_project/settings.py,sha256=nq5lGLEY1cl99XhfS60BpIVfhVFzUhHb_qfNPm5TqSY,2334
32
+ example/example_project/urls.py,sha256=AK8SdvMN2K0QvdzhMr5BUMzl0SrEUhVmMP-3uLEqnU4,217
33
+ example/example_project/wsgi.py,sha256=OK4x37NxQNVNWV4ynLqWP546tR4eALIFn7pyHc8XDNw,176
34
+ django_nepkit-0.2.1.dist-info/METADATA,sha256=4FIFav_TWnqaPd-AY5Zsp3lZ0uI1b0ZFZIishqIrsqM,11008
35
+ django_nepkit-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
36
+ django_nepkit-0.2.1.dist-info/top_level.txt,sha256=Rs68WqPeyCvsWQStPNLl6vu10wCIfrFrc7xhkRcFzss,22
37
+ django_nepkit-0.2.1.dist-info/RECORD,,
example/demo/admin.py CHANGED
@@ -1,42 +1,66 @@
1
1
  from django.contrib import admin
2
2
 
3
- from django_nepkit import NepaliModelAdmin
3
+ from django_nepkit import (
4
+ NepaliModelAdmin,
5
+ NepaliAdminMixin,
6
+ NepaliDateFilter,
7
+ NepaliMonthFilter,
8
+ )
4
9
 
5
- from .models import Person
10
+ from .models import Person, Citizen, AuditedPerson, Transaction
6
11
 
7
12
 
13
+ # Example 1: Basic setup
8
14
  @admin.register(Person)
9
15
  class PersonAdmin(NepaliModelAdmin):
10
16
  list_display = (
11
17
  "name",
12
- "display_birth_date",
18
+ "birth_date",
19
+ "birth_date_ne",
13
20
  "phone_number",
14
21
  "province",
22
+ "province_ne",
15
23
  "district",
24
+ "district_ne",
16
25
  "municipality",
17
- "display_created_at",
18
- "display_updated_at",
26
+ "municipality_ne",
27
+ "created_at",
28
+ "updated_at",
29
+ )
30
+ # Added Year and Month filters
31
+ list_filter = (
32
+ ("birth_date", NepaliDateFilter),
33
+ ("birth_date", NepaliMonthFilter),
34
+ "province",
19
35
  )
20
- list_filter = ("birth_date", "province", "district")
21
36
  search_fields = ("name", "phone_number")
22
37
 
23
- def display_birth_date(self, obj):
24
- """Display birth date with Nepali month names"""
25
- return self.format_nepali_date(obj.birth_date)
26
38
 
27
- display_birth_date.short_description = "Birth Date"
28
- display_birth_date.admin_order_field = "birth_date"
39
+ # Example 2: Using a Mixin
40
+ class BaseAuditAdmin(admin.ModelAdmin):
41
+ """A fake base class for testing."""
42
+
43
+ pass
44
+
45
+
46
+ # Use the Mixin if you already have a base Admin class
47
+ @admin.register(AuditedPerson)
48
+ class CustomPersonAdmin(NepaliAdminMixin, BaseAuditAdmin):
49
+ list_display = ("name", "birth_date", "created_at")
50
+ list_filter = (
51
+ ("created_at", NepaliMonthFilter), # Filter by Nepali Month
52
+ )
29
53
 
30
- def display_created_at(self, obj):
31
- """Display created date with Nepali month names"""
32
- return self.format_nepali_datetime(obj.created_at)
33
54
 
34
- display_created_at.short_description = "Created At"
35
- display_created_at.admin_order_field = "created_at"
55
+ # Example 3: HTMX support
56
+ @admin.register(Citizen)
57
+ class CitizenAdmin(NepaliModelAdmin):
58
+ list_display = ("name", "province", "district", "municipality")
59
+ # Filters still work with HTMX
60
+ list_filter = ("province", "district")
36
61
 
37
- def display_updated_at(self, obj):
38
- """Display updated date with Nepali month names"""
39
- return self.format_nepali_datetime(obj.updated_at)
40
62
 
41
- display_updated_at.short_description = "Updated At"
42
- display_updated_at.admin_order_field = "updated_at"
63
+ @admin.register(Transaction)
64
+ class TransactionAdmin(NepaliModelAdmin):
65
+ list_display = ("title", "amount", "date")
66
+ list_filter = (("date", NepaliDateFilter),)
example/demo/models.py CHANGED
@@ -7,21 +7,58 @@ from django_nepkit import (
7
7
  ProvinceField,
8
8
  DistrictField,
9
9
  MunicipalityField,
10
+ NepaliCurrencyField,
10
11
  )
11
12
 
12
13
 
13
14
  class Person(models.Model):
14
15
  name = models.CharField(max_length=100)
15
- birth_date = NepaliDateField()
16
- registration_time = NepaliTimeField(auto_now_add=True)
16
+ # Dates and times
17
+ birth_date = NepaliDateField() # English by default
18
+ birth_date_ne = NepaliDateField(ne=True, blank=True, null=True) # Using Devanagari
19
+ registration_time = NepaliTimeField(auto_now_add=True) # English digits by default
17
20
  phone_number = NepaliPhoneNumberField()
18
21
 
19
- # Address chaining
20
- province = ProvinceField()
22
+ # Location fields
23
+ province = ProvinceField() # English names by default
24
+ province_ne = ProvinceField(ne=True, blank=True, null=True) # In Devanagari
21
25
  district = DistrictField()
26
+ district_ne = DistrictField(ne=True, blank=True, null=True)
22
27
  municipality = MunicipalityField()
28
+ municipality_ne = MunicipalityField(ne=True, blank=True, null=True)
29
+
23
30
  created_at = NepaliDateTimeField(auto_now_add=True)
24
31
  updated_at = NepaliDateTimeField(auto_now=True)
25
32
 
26
33
  def __str__(self):
27
34
  return self.name
35
+
36
+
37
+ class Citizen(models.Model):
38
+ name = models.CharField(max_length=100)
39
+
40
+ # Chaining works automatically
41
+ province = ProvinceField()
42
+ district = DistrictField()
43
+ municipality = MunicipalityField()
44
+
45
+ def __str__(self):
46
+ return self.name
47
+
48
+
49
+ class AuditedPerson(models.Model):
50
+ name = models.CharField(max_length=100)
51
+ birth_date = NepaliDateField()
52
+ created_at = NepaliDateTimeField(auto_now_add=True)
53
+
54
+ def __str__(self):
55
+ return self.name
56
+
57
+
58
+ class Transaction(models.Model):
59
+ title = models.CharField(max_length=100)
60
+ amount = NepaliCurrencyField()
61
+ date = NepaliDateField(auto_now_add=True)
62
+
63
+ def __str__(self):
64
+ return f"{self.title} - {self.amount}"