Files
django-nepkit/django_nepkit/admin.py
T

212 lines
7.1 KiB
Python

from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from nepali.datetime import nepalidate, nepalidatetime
from django_nepkit.models import NepaliDateField
from django_nepkit.utils import (
try_parse_nepali_date,
try_parse_nepali_datetime,
)
def format_nepali_date(date_value, format_string="%B %d, %Y"):
"""
Format a nepalidate object with Nepali month names.
Args:
date_value: A nepalidate object or string in YYYY-MM-DD format
format_string: strftime format string (default: '%B %d, %Y')
%B = Full month name (Baishak, Jestha, etc.)
%b = Short month name
%d = Day of month
%Y = Year
Returns:
Formatted date string with Nepali month names, or empty string if invalid
"""
if date_value is None:
return ""
try:
parsed = try_parse_nepali_date(date_value)
if parsed is not None:
return parsed.strftime(format_string)
if isinstance(date_value, nepalidate):
return date_value.strftime(
format_string
) # defensive; should be covered above
except (ValueError, TypeError, AttributeError):
pass
return str(date_value) if date_value else ""
def format_nepali_datetime(datetime_value, format_string="%B %d, %Y %I:%M %p"):
"""
Format a nepalidatetime object with Nepali month names.
Default output uses **12-hour time with AM/PM**.
"""
if datetime_value is None:
return ""
try:
parsed = try_parse_nepali_datetime(datetime_value)
if parsed is not None:
return parsed.strftime(format_string)
if isinstance(datetime_value, nepalidatetime):
return datetime_value.strftime(
format_string
) # defensive; should be covered above
except (ValueError, TypeError, AttributeError):
pass
return str(datetime_value) if datetime_value else ""
class NepaliDateFilter(admin.FieldListFilter):
"""
A list filter for NepaliDateField to filter by BS Year.
"""
def __init__(self, field, request, params, model, model_admin, field_path):
self.parameter_name = f"{field_path}_bs_year"
super().__init__(field, request, params, model, model_admin, field_path)
self.title = _("Nepali Date (Year)")
def expected_parameters(self):
return [self.parameter_name]
def choices(self, changelist):
yield {
"selected": self.used_parameters.get(self.parameter_name) is None,
"query_string": changelist.get_query_string(remove=[self.parameter_name]),
"display": _("All"),
}
current_year = nepalidate.today().year
for year in range(current_year - 10, current_year + 2):
yield {
"selected": self.used_parameters.get(self.parameter_name) == str(year),
"query_string": changelist.get_query_string(
{self.parameter_name: str(year)}
),
"display": str(year),
}
def queryset(self, request, queryset):
value = self.used_parameters.get(self.parameter_name)
if value:
year = int(value)
# Convert BS year range to AD date range
start_date_bs = nepalidate(year, 1, 1)
# Find last day of the year. Some BS years end on the 30th,
# so we try 31 first and then fall back to 30 if that date
# is not valid.
try:
end_date_bs = nepalidate(year, 12, 31)
except ValueError:
end_date_bs = nepalidate(year, 12, 30)
start_date_ad = start_date_bs.to_date()
end_date_ad = end_date_bs.to_date()
return queryset.filter(
**{f"{self.field_path}__range": (start_date_ad, end_date_ad)}
)
return queryset
# Register NepaliDateFilter as the default list filter for NepaliDateField
admin.FieldListFilter.register(
lambda f: isinstance(f, NepaliDateField),
NepaliDateFilter,
take_priority=True,
)
class NepaliAdminMixin:
"""
Mixin for Django admin classes that provides Nepali date utilities.
Makes format_nepali_date and NepaliDateFilter available without explicit imports.
"""
def format_nepali_date(self, date_value, format_string="%B %d, %Y"):
"""
Format a nepalidate object with Nepali month names.
Available as a method on admin classes using this mixin.
"""
return format_nepali_date(date_value, format_string)
def format_nepali_datetime(
self, datetime_value, format_string="%B %d, %Y %I:%M %p"
):
return format_nepali_datetime(datetime_value, format_string)
class NepaliModelAdmin(NepaliAdminMixin, admin.ModelAdmin):
"""
Base ModelAdmin class with Nepali date utilities built-in.
Use this instead of admin.ModelAdmin to have format_nepali_date available.
Example:
from django_nepkit import NepaliModelAdmin, NepaliDateFilter
@admin.register(MyModel)
class MyModelAdmin(NepaliModelAdmin):
list_filter = (('date_field', NepaliDateFilter),)
def display_date(self, obj):
return self.format_nepali_date(obj.date_field)
"""
# Make NepaliDateFilter available as a class attribute
NepaliDateFilter = NepaliDateFilter
# Ensure admin forms render Nepali fields with the proper widget,
# even if a project doesn't provide custom ModelForms.
def formfield_for_dbfield(self, db_field, request, **kwargs):
"""
Force Nepali widgets in Django admin without requiring user forms.
"""
try:
from django_nepkit.models import NepaliDateField, NepaliDateTimeField
from django_nepkit.widgets import NepaliDatePickerWidget
except Exception:
return super().formfield_for_dbfield(db_field, request, **kwargs)
if isinstance(db_field, (NepaliDateField, NepaliDateTimeField)):
kwargs.setdefault("widget", NepaliDatePickerWidget)
return super().formfield_for_dbfield(db_field, request, **kwargs)
class Media:
"""
Django admin ships jQuery as `django.jQuery` (not `window.jQuery`).
The Nepali date picker library expects a global `jQuery`, so we bridge it.
"""
css = {
"all": (
"https://nepalidatepicker.sajanmaharjan.com.np/v5/nepali.datepicker/css/nepali.datepicker.v5.0.6.min.css",
"django_nepkit/css/admin-nepali-datepicker.css",
)
}
js = (
# Bridge admin's `django.jQuery` -> `window.jQuery`
"django_nepkit/js/admin-jquery-bridge.js",
# Date picker lib
"https://nepalidatepicker.sajanmaharjan.com.np/v5/nepali.datepicker/js/nepali.datepicker.v5.0.6.min.js",
# Init
"django_nepkit/js/nepali-datepicker-init.js",
)
# Exporting for easy usage
__all__ = [
"NepaliDateFilter",
"format_nepali_date",
"format_nepali_datetime",
"NepaliAdminMixin",
"NepaliModelAdmin",
]