You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

223 lines
8.7 KiB

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'<strong class="text-green-700 font-semibold dark:text-green-400">+{intcomma(f"{random.uniform(1, 9):.02f}")}%</strong>&nbsp;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)"},
],
}),
}