import json import random from functools import lru_cache from django import forms from django.conf import settings from django.contrib.humanize.templatetags.humanize import intcomma from django.urls import reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from django.views.generic import RedirectView from django.utils.translation import get_language # Unfold Imports from unfold.sites import UnfoldAdminSite # --------------------------------------------------------- # 1. Helper Functions # --------------------------------------------------------- def is_dovoodi_panel(request): """ Returns True if the user is accessing the Dovoodi admin panel. Checks if '/dovoodi/' exists anywhere in the path to handle i18n prefixes (e.g., /en/dovoodi/admin, /fa/dovoodi/admin). """ return '/dovoodi/' in request.path def is_main_panel(request): """Returns True if the user is accessing the Main (Imam Javad) admin panel.""" return not is_dovoodi_panel(request) def admin_url_generator(request, url_name): """ Dynamically generates admin URLs based on the current active panel. Usage in settings.py: lambda request: admin_url_generator(request, "app_model_changelist") """ # 1. Determine the current namespace using the robust check if is_dovoodi_panel(request): namespace = 'dovoodi_admin' else: # Default to the main admin namespace = 'imam_javad_admin' # 2. Construct the view name full_view_name = f"{namespace}:{url_name}" # 3. Resolve the URL try: return reverse(full_view_name) except Exception: return "#" def dashboard_callback(request, context): context.update(random_data()) return context def variables(request): return {"plausible_domain": getattr(settings, 'PLAUSIBLE_DOMAIN', '')} # --------------------------------------------------------- # 2. Custom Login Form # --------------------------------------------------------- class LoginForm: """Lazy login form to avoid circular imports during settings loading""" @staticmethod def get_form(): # Import AuthenticationForm only when needed from unfold.forms import AuthenticationForm class CustomLoginForm(AuthenticationForm): password = forms.CharField(widget=forms.PasswordInput(render_value=True)) def __init__(self, request=None, *args, **kwargs): super().__init__(request, *args, **kwargs) # Change the label of the username field to "Email" self.fields["username"].label = "Email" return CustomLoginForm # --------------------------------------------------------- # 3. Admin Site Definitions # --------------------------------------------------------- class FormulaAdminSite(UnfoldAdminSite): """Main Admin for Imam Jawad""" site_header = "Imam Jawad Admin" site_title = "Imam Jawad Admin" index_title = "System Administration" site_subheader = "Imam Jawad School" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Set login form after initialization to avoid circular import self.login_form = LoginForm.get_form() class DovoodiAdminSite(UnfoldAdminSite): """Secondary Admin for Dovoodi""" site_header = "Dovoodi Admin" site_title = "Dovoodi Admin" index_title = "System Administration" site_subheader = "Dovodbi Application" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Set login form after initialization to avoid circular import self.login_form = LoginForm.get_form() # Simple admin site placeholders that will be replaced after Django setup class AdminSitePlaceholder(UnfoldAdminSite): """Placeholder that behaves like an admin site until Django is fully loaded""" def __init__(self, site_class, name): # 1. Store config for lazy loading self._site_class = site_class self._name = name self._real_instance = None # 2. THE FIX: Copy visual attributes immediately so Templates see them! self.site_header = getattr(site_class, 'site_header', 'Django Admin') self.site_title = getattr(site_class, 'site_title', 'Django Site') self.index_title = getattr(site_class, 'index_title', 'Site Administration') self.site_subheader = getattr(site_class, 'site_subheader', '') def _get_real_instance(self): if self._real_instance is None: # Force creation of real admin site instance for proper CSS loading self._real_instance = self._site_class(name=self._name) # Copy critical attributes immediately for template access self.login_form = self._real_instance.login_form self.login_template = self._real_instance.login_template # Copy any other attributes that templates might need for attr in ['site_header', 'site_title', 'index_title', 'site_subheader']: if hasattr(self._real_instance, attr): setattr(self, attr, getattr(self._real_instance, attr)) return self._real_instance def __getattr__(self, name): # Delegate all attribute access to the real instance for proper CSS and template loading return getattr(self._get_real_instance(), name) def __call__(self, *args, **kwargs): return self._get_real_instance()(*args, **kwargs) def get_urls(self): return self._get_real_instance().get_urls() @property def urls(self): return self._get_real_instance().urls def each_context(self, request): return self._get_real_instance().each_context(request) # Create placeholder instances that will be replaced with real instances when Django is ready project_admin_site = AdminSitePlaceholder(FormulaAdminSite, 'imam_javad_admin') dovoodi_admin_site = AdminSitePlaceholder(DovoodiAdminSite, 'dovoodi_admin') # Function to replace placeholders with real instances when Django is ready def replace_placeholders_with_real_sites(): global project_admin_site, dovoodi_admin_site if isinstance(project_admin_site, AdminSitePlaceholder): project_admin_site = FormulaAdminSite(name='imam_javad_admin') if isinstance(dovoodi_admin_site, AdminSitePlaceholder): dovoodi_admin_site = DovoodiAdminSite(name='dovoodi_admin') # The placeholders will be replaced with real instances when first accessed # This ensures proper CSS loading for admin templates class HomeView(RedirectView): def get_redirect_url(self, *args, **kwargs): host = self.request.get_host() # دریافت زبان فعلی (پیش‌فرض: en) language = get_language() or 'en' # دامنه‌های داوودی dovoodi_domains = ['dovodi.newhorizonco.uk', 'dovoodi.newhorizonco.uk'] # تصمیم‌گیری بر اساس دامنه و برگرداندن URL با prefix زبانی if any(domain in host for domain in dovoodi_domains): return f'/{language}/dovoodi/admin/' else: return f'/{language}/imam-javad/admin/' # --------------------------------------------------------- # 4. Dummy Data for Dashboard Charts # --------------------------------------------------------- @lru_cache def random_data(): WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] # Generate some fake data positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] average = [r[1] - random.randint(3, 5) for r in positive] performance_positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] performance_negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] return { "navigation": [ {"title": _("Dashboard"), "link": "/", "active": True}, {"title": _("Analytics"), "link": "#"}, {"title": _("Settings"), "link": "#"}, ], "kpi": [ { "title": "Total Revenue", "metric": f"${intcomma(f'{random.uniform(1000, 9999):.02f}')}", "footer": mark_safe(f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress'), "chart": json.dumps({"labels": [WEEKDAYS[day % 7] for day in range(1, 28)], "datasets": [{"data": average, "borderColor": "#9333ea"}]}), }, ], "chart": json.dumps({ "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], "datasets": [ {"label": "Revenue", "data": positive, "backgroundColor": "var(--color-primary-700)"}, ], }), }