import json from django.contrib import admin from django.db import models from django.http import JsonResponse from django.shortcuts import get_object_or_404 from django.urls import path from django.utils.dateformat import format as date_format from django.utils.translation import gettext_lazy as _ from django.utils.safestring import mark_safe from django.utils.html import format_html from dj_language.models import Language from django import forms from import_export import fields, widgets, resources from import_export.admin import ImportExportModelAdmin from unfold.decorators import display from unfold.admin import ModelAdmin, TabularInline from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm from unfold.contrib.filters.admin import ( RangeDateFilter, RangeNumericFilter, SingleNumericFilter, ChoicesDropdownFilter ) from apps.dobodbi_calendar.models import CalendarOccasions from utils.json_editor_field import JsonEditorWidget from utils.admin import dovoodi_admin_site from utils.schema import get_calender_dates_schema class CalendarOccasionsForm(forms.ModelForm): class Meta: model = CalendarOccasions fields = '__all__' widgets = { 'dates': JsonEditorWidget(attrs={ 'schema': get_calender_dates_schema(), 'title': _('Dates'), }), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class CalendarOccasionsAdmin(ModelAdmin): form = CalendarOccasionsForm change_list_template = "admin/dobodbi_calendar/calendar_event_list.html" ordering = ('-id',) list_display = [ "title", "display_occasion_type", "display_event_type", "is_global", "is_yearly", "display_dates", ] list_filter = [ ("occasion_type", ChoicesDropdownFilter), ("event_type", ChoicesDropdownFilter), "is_global", "is_yearly", ("created_at", RangeDateFilter), ] search_fields = [ "title", "dates", ] fieldsets = ( ("Basic Information", { "fields": ( "title", "is_global", "is_yearly", "occasion_type", "event_type", ), "description": "Main information about the calendar occasion", }), ("Dates Configuration", { "fields": ("dates",), "classes": ("collapse",), "description": "Configure dates for this occasion", }), ("Metadata", { "fields": ("created_at", "updated_at"), "classes": ("collapse",), "description": "Metadata information", }), ) readonly_fields = ["created_at", "updated_at"] # سفارشی‌سازی اکشن‌ها actions = ["make_global", "make_not_global"] # متدهای اکشن @admin.action(description="Mark selected occasions as global") def make_global(self, request, queryset): queryset.update(is_global=True) @admin.action(description="Mark selected occasions as not global") def make_not_global(self, request, queryset): queryset.update(is_global=False) @display(description="Occasion Type", label=True) def display_occasion_type(self, obj): return obj.get_occasion_type_display() @display(description="Event Type", label=True) def display_event_type(self, obj): return obj.get_event_type_display() @display(description="Dates") def display_dates(self, obj): from django.utils.html import format_html return "\n".join([f"{i['month']}/{i['day']}" for i in obj.dates]) def get_urls(self): urls = super().get_urls() custom_urls = [ path( "manage/create/", self.admin_site.admin_view(self.create_event_view), name="dobodbi_calendar_calendaroccasions_create", ), path( "manage//update/", self.admin_site.admin_view(self.update_event_view), name="dobodbi_calendar_calendaroccasions_update", ), ] return custom_urls + urls def changelist_view(self, request, extra_context=None): events = CalendarOccasions.objects.all().order_by("-updated_at", "-id") event_rows = [ { "id": event.id, "title": event.title, "occasion_type": event.occasion_type, "occasion_type_label": event.get_occasion_type_display(), "event_type": event.event_type or "", "event_type_label": event.get_event_type_display() if event.event_type else "-", "is_global": event.is_global, "is_yearly": event.is_yearly, "dates": event.dates or [], "dates_preview": ", ".join( [ f"{date_item.get('year', '')}/{date_item.get('month', '')}/{date_item.get('day', '')}".strip("/") for date_item in (event.dates or []) ] ), "updated_at": date_format(event.updated_at, "Y-m-d H:i"), "created_at": date_format(event.created_at, "Y-m-d H:i"), } for event in events ] extra_context = extra_context or {} extra_context.update( { "calendar_events": event_rows, "calendar_event_type_choices": [ {"value": value, "label": label} for value, label in CalendarOccasions.EventType.choices ], "calendar_occasion_type_choices": [ {"value": value, "label": label} for value, label in CalendarOccasions.OccasionType.choices ], } ) return super().changelist_view(request, extra_context=extra_context) def create_event_view(self, request): if request.method != "POST": return JsonResponse({"ok": False, "error": "Method not allowed."}, status=405) form = CalendarOccasionsForm(request.POST) if form.is_valid(): event = form.save() return JsonResponse({"ok": True, "id": event.id}) return JsonResponse({"ok": False, "errors": form.errors.get_json_data()}, status=400) def update_event_view(self, request, event_id): if request.method != "POST": return JsonResponse({"ok": False, "error": "Method not allowed."}, status=405) event = get_object_or_404(CalendarOccasions, pk=event_id) form = CalendarOccasionsForm(request.POST, instance=event) if form.is_valid(): form.save() return JsonResponse({"ok": True, "id": event.id}) return JsonResponse({"ok": False, "errors": form.errors.get_json_data()}, status=400) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super().get_search_results(request, queryset, search_term) try: # امکان جستجو در فیلد JSON import json json.loads(search_term) queryset |= self.model.objects.filter(dates__contains=search_term) except ValueError: pass return queryset, use_distinct dovoodi_admin_site.register(CalendarOccasions, CalendarOccasionsAdmin)