Features: 1) Migrate from Jazzmin to django-unfold for admin UI;
Fixes: 1) Remove deprecated Jazzmin configuration and replace with unfold dependencies; 2) Update DRF API title to use new PROJECT_NAME; 3) Fix import order and remove unused imports in core/viewsets.py; Extra: 1) Add PROJECT_NAME to base settings; 2) Update INSTALLED_APPS to include unfold-related apps; 3) Clean up unused config references.
This commit is contained in:
parent
fb84f1f89b
commit
43dc556063
18 changed files with 122 additions and 192 deletions
|
|
@ -217,21 +217,6 @@ services:
|
||||||
- --web.config.file=/etc/prometheus/web.yml
|
- --web.config.file=/etc/prometheus/web.yml
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
||||||
# supervisor:
|
|
||||||
# container_name: supervisor
|
|
||||||
# build:
|
|
||||||
# context: .
|
|
||||||
# dockerfile: ./Dockerfiles/supervisor.Dockerfile
|
|
||||||
# restart: always
|
|
||||||
# env_file:
|
|
||||||
# - .env
|
|
||||||
# ports:
|
|
||||||
# - "7777:7777"
|
|
||||||
# depends_on:
|
|
||||||
# app:
|
|
||||||
# condition: service_started
|
|
||||||
# logging: *default-logging
|
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
|
|
|
||||||
|
|
@ -993,6 +993,6 @@ class ConstanceConfig:
|
||||||
site.unregister([Config])
|
site.unregister([Config])
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore [list-item]
|
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore [list-item]
|
||||||
site.site_title = settings.CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment]
|
site.site_title = settings.PROJECT_NAME
|
||||||
site.site_header = "eVibes"
|
site.site_header = "eVibes"
|
||||||
site.index_title = settings.CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment]
|
site.index_title = settings.PROJECT_NAME
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import logging
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from constance import config
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models import Max, Min, QuerySet
|
from django.db.models import Max, Min, QuerySet
|
||||||
|
|
@ -139,7 +138,7 @@ class BrandType(DjangoObjectType): # type: ignore [misc]
|
||||||
lang = graphene_current_lang()
|
lang = graphene_current_lang()
|
||||||
base = f"https://{settings.BASE_DOMAIN}"
|
base = f"https://{settings.BASE_DOMAIN}"
|
||||||
canonical = f"{base}/{lang}/brand/{self.slug}"
|
canonical = f"{base}/{lang}/brand/{self.slug}"
|
||||||
title = f"{self.name} | {config.PROJECT_NAME}"
|
title = f"{self.name} | {settings.PROJECT_NAME}"
|
||||||
description = (self.description or "")[:180]
|
description = (self.description or "")[:180]
|
||||||
|
|
||||||
logo_url = None
|
logo_url = None
|
||||||
|
|
@ -265,7 +264,7 @@ class CategoryType(DjangoObjectType): # type: ignore [misc]
|
||||||
lang = graphene_current_lang()
|
lang = graphene_current_lang()
|
||||||
base = f"https://{settings.BASE_DOMAIN}"
|
base = f"https://{settings.BASE_DOMAIN}"
|
||||||
canonical = f"{base}/{lang}/catalog/{self.slug}"
|
canonical = f"{base}/{lang}/catalog/{self.slug}"
|
||||||
title = f"{self.name} | {config.PROJECT_NAME}"
|
title = f"{self.name} | {settings.PROJECT_NAME}"
|
||||||
description = (self.description or "")[:180]
|
description = (self.description or "")[:180]
|
||||||
|
|
||||||
og_image = graphene_abs(info.context, self.image.url) if getattr(self, "image", None) else ""
|
og_image = graphene_abs(info.context, self.image.url) if getattr(self, "image", None) else ""
|
||||||
|
|
@ -537,7 +536,7 @@ class ProductType(DjangoObjectType): # type: ignore [misc]
|
||||||
lang = graphene_current_lang()
|
lang = graphene_current_lang()
|
||||||
base = f"https://{settings.BASE_DOMAIN}"
|
base = f"https://{settings.BASE_DOMAIN}"
|
||||||
canonical = f"{base}/{lang}/product/{self.slug}"
|
canonical = f"{base}/{lang}/product/{self.slug}"
|
||||||
title = f"{self.name} | {config.PROJECT_NAME}"
|
title = f"{self.name} | {settings.PROJECT_NAME}"
|
||||||
description = (self.description or "")[:180]
|
description = (self.description or "")[:180]
|
||||||
|
|
||||||
first_img = self.images.order_by("priority").first()
|
first_img = self.images.order_by("priority").first()
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,5 @@ class DRFCoreViewsTests(TestCase):
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
return serializer.validated_data["access_token"]
|
return serializer.validated_data["access_token"]
|
||||||
|
|
||||||
|
|
||||||
# TODO: create tests for every possible HTTP method in core module with DRF stack
|
# TODO: create tests for every possible HTTP method in core module with DRF stack
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ def contact_us_email(contact_info) -> tuple[bool, str]:
|
||||||
)
|
)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
_(f"{config.PROJECT_NAME} | contact us initiated"),
|
_(f"{settings.PROJECT_NAME} | contact us initiated"),
|
||||||
render_to_string(
|
render_to_string(
|
||||||
"../templates/contact_us_email.html",
|
"../templates/contact_us_email.html",
|
||||||
{
|
{
|
||||||
|
|
@ -37,7 +37,7 @@ def contact_us_email(contact_info) -> tuple[bool, str]:
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
to=[config.EMAIL_FROM],
|
to=[config.EMAIL_FROM],
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
email.content_subtype = "html"
|
email.content_subtype = "html"
|
||||||
|
|
@ -70,7 +70,7 @@ def send_order_created_email(order_pk: str) -> tuple[bool, str]:
|
||||||
|
|
||||||
if not order.is_whole_digital:
|
if not order.is_whole_digital:
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
_(f"{config.PROJECT_NAME} | order confirmation"),
|
_(f"{settings.PROJECT_NAME} | order confirmation"),
|
||||||
render_to_string(
|
render_to_string(
|
||||||
"digital_order_created_email.html" if order.is_whole_digital else "shipped_order_created_email.html",
|
"digital_order_created_email.html" if order.is_whole_digital else "shipped_order_created_email.html",
|
||||||
{
|
{
|
||||||
|
|
@ -81,7 +81,7 @@ def send_order_created_email(order_pk: str) -> tuple[bool, str]:
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
to=[recipient],
|
to=[recipient],
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
email.content_subtype = "html"
|
email.content_subtype = "html"
|
||||||
|
|
@ -102,14 +102,14 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
|
||||||
activate(order.user.language)
|
activate(order.user.language)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
_(f"{config.PROJECT_NAME} | order delivered"),
|
_(f"{settings.PROJECT_NAME} | order delivered"),
|
||||||
render_to_string(
|
render_to_string(
|
||||||
template_name="../templates/digital_order_delivered_email.html",
|
template_name="../templates/digital_order_delivered_email.html",
|
||||||
context={
|
context={
|
||||||
"order_uuid": order.human_readable_id,
|
"order_uuid": order.human_readable_id,
|
||||||
"user_first_name": "" or order.user.first_name,
|
"user_first_name": "" or order.user.first_name,
|
||||||
"order_products": ops,
|
"order_products": ops,
|
||||||
"project_name": config.PROJECT_NAME,
|
"project_name": settings.PROJECT_NAME,
|
||||||
"contact_email": config.EMAIL_FROM,
|
"contact_email": config.EMAIL_FROM,
|
||||||
"total_price": round(sum(0.0 or op.buy_price for op in ops), 2), # type: ignore [misc]
|
"total_price": round(sum(0.0 or op.buy_price for op in ops), 2), # type: ignore [misc]
|
||||||
"display_system_attributes": order.user.has_perm("core.view_order"),
|
"display_system_attributes": order.user.has_perm("core.view_order"),
|
||||||
|
|
@ -117,7 +117,7 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
to=[order.user.email],
|
to=[order.user.email],
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
email.content_subtype = "html"
|
email.content_subtype = "html"
|
||||||
|
|
@ -185,20 +185,20 @@ def send_promocode_created_email(promocode_pk: str) -> tuple[bool, str]:
|
||||||
activate(promocode.user.language)
|
activate(promocode.user.language)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
_(f"{config.PROJECT_NAME} | promocode granted"),
|
_(f"{settings.PROJECT_NAME} | promocode granted"),
|
||||||
render_to_string(
|
render_to_string(
|
||||||
template_name="../templates/promocode_granted_email.html",
|
template_name="../templates/promocode_granted_email.html",
|
||||||
context={
|
context={
|
||||||
"promocode": promocode,
|
"promocode": promocode,
|
||||||
"user_first_name": "" or promocode.user.first_name,
|
"user_first_name": "" or promocode.user.first_name,
|
||||||
"project_name": config.PROJECT_NAME,
|
"project_name": settings.PROJECT_NAME,
|
||||||
"contact_email": config.EMAIL_FROM,
|
"contact_email": config.EMAIL_FROM,
|
||||||
"today": datetime.today(),
|
"today": datetime.today(),
|
||||||
"currency": settings.CURRENCY_CODE,
|
"currency": settings.CURRENCY_CODE,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
to=[promocode.user.email],
|
to=[promocode.user.email],
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
email.content_subtype = "html"
|
email.content_subtype = "html"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ def website_schema():
|
||||||
return {
|
return {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "WebSite",
|
"@type": "WebSite",
|
||||||
"name": config.PROJECT_NAME,
|
"name": settings.PROJECT_NAME,
|
||||||
"url": f"https://{settings.BASE_DOMAIN}/",
|
"url": f"https://{settings.BASE_DOMAIN}/",
|
||||||
"potentialAction": {
|
"potentialAction": {
|
||||||
"@type": "SearchAction",
|
"@type": "SearchAction",
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ import uuid
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from constance import config
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Prefetch, Q, OuterRef, Exists
|
from django.db.models import Exists, OuterRef, Prefetch, Q
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
|
@ -270,7 +269,7 @@ class CategoryViewSet(EvibesViewSet):
|
||||||
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
|
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
|
||||||
category = self.get_object()
|
category = self.get_object()
|
||||||
|
|
||||||
title = f"{category.name} | {config.PROJECT_NAME}"
|
title = f"{category.name} | {settings.PROJECT_NAME}"
|
||||||
description = (category.description or "")[:180]
|
description = (category.description or "")[:180]
|
||||||
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}"
|
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}"
|
||||||
og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else ""
|
og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else ""
|
||||||
|
|
@ -387,7 +386,7 @@ class BrandViewSet(EvibesViewSet):
|
||||||
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
|
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
|
||||||
brand = self.get_object()
|
brand = self.get_object()
|
||||||
|
|
||||||
title = f"{brand.name} | {config.PROJECT_NAME}"
|
title = f"{brand.name} | {settings.PROJECT_NAME}"
|
||||||
description = (brand.description or "")[:180]
|
description = (brand.description or "")[:180]
|
||||||
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}"
|
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}"
|
||||||
|
|
||||||
|
|
@ -529,7 +528,7 @@ class ProductViewSet(EvibesViewSet):
|
||||||
p = self.get_object()
|
p = self.get_object()
|
||||||
images = list(p.images.all()[:6])
|
images = list(p.images.all()[:6])
|
||||||
rating = {"value": p.rating, "count": p.feedbacks_count}
|
rating = {"value": p.rating, "count": p.feedbacks_count}
|
||||||
title = f"{p.name} | {config.PROJECT_NAME}"
|
title = f"{p.name} | {settings.PROJECT_NAME}"
|
||||||
description = (p.description or "")[:180]
|
description = (p.description or "")[:180]
|
||||||
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}"
|
canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}"
|
||||||
og = {
|
og = {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from celery.app import shared_task
|
from celery.app import shared_task
|
||||||
from constance import config
|
from constance import config
|
||||||
|
from django.conf import settings
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import activate
|
from django.utils.translation import activate
|
||||||
|
|
@ -24,20 +25,20 @@ def balance_deposit_email(transaction_pk: str) -> tuple[bool, str]:
|
||||||
activate(transaction.balance.user.language)
|
activate(transaction.balance.user.language)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
_(f"{config.PROJECT_NAME} | balance deposit"),
|
_(f"{settings.PROJECT_NAME} | balance deposit"),
|
||||||
render_to_string(
|
render_to_string(
|
||||||
template_name="../templates/balance_deposit_email.html",
|
template_name="../templates/balance_deposit_email.html",
|
||||||
context={
|
context={
|
||||||
"amount": transaction.amount,
|
"amount": transaction.amount,
|
||||||
"balance": transaction.balance.amount,
|
"balance": transaction.balance.amount,
|
||||||
"user_first_name": transaction.balance.user.first_name,
|
"user_first_name": transaction.balance.user.first_name,
|
||||||
"project_name": config.PROJECT_NAME,
|
"project_name": settings.PROJECT_NAME,
|
||||||
"contact_email": config.EMAIL_FROM,
|
"contact_email": config.EMAIL_FROM,
|
||||||
"today": datetime.today(),
|
"today": datetime.today(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
to=[transaction.balance.user.email],
|
to=[transaction.balance.user.email],
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
email.content_subtype = "html"
|
email.content_subtype = "html"
|
||||||
|
|
|
||||||
|
|
@ -21,21 +21,21 @@ def send_verification_email_task(user_pk: str) -> tuple[bool, str]:
|
||||||
|
|
||||||
activate(user.language)
|
activate(user.language)
|
||||||
|
|
||||||
email_subject = _(f"{config.PROJECT_NAME} | Activate Account")
|
email_subject = _(f"{settings.PROJECT_NAME} | Activate Account")
|
||||||
email_body = render_to_string(
|
email_body = render_to_string(
|
||||||
"../templates/user_verification_email.html",
|
"../templates/user_verification_email.html",
|
||||||
{
|
{
|
||||||
"user_first_name": user.first_name,
|
"user_first_name": user.first_name,
|
||||||
"activation_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/activate-user?uid={urlsafe_base64_encode(force_bytes(user.uuid))}"
|
"activation_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/activate-user?uid={urlsafe_base64_encode(force_bytes(user.uuid))}"
|
||||||
f"&token={urlsafe_base64_encode(force_bytes(user.activation_token))}",
|
f"&token={urlsafe_base64_encode(force_bytes(user.activation_token))}",
|
||||||
"project_name": config.PROJECT_NAME,
|
"project_name": settings.PROJECT_NAME,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
subject=email_subject,
|
subject=email_subject,
|
||||||
body=email_body,
|
body=email_body,
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
to=[user.email],
|
to=[user.email],
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
|
|
@ -60,7 +60,7 @@ def send_reset_password_email_task(user_pk: str) -> tuple[bool, str]:
|
||||||
|
|
||||||
activate(user.language)
|
activate(user.language)
|
||||||
|
|
||||||
email_subject = _(f"{config.PROJECT_NAME} | Reset Password")
|
email_subject = _(f"{settings.PROJECT_NAME} | Reset Password")
|
||||||
email_body = render_to_string(
|
email_body = render_to_string(
|
||||||
"../templates/user_reset_password_email.html",
|
"../templates/user_reset_password_email.html",
|
||||||
{
|
{
|
||||||
|
|
@ -68,14 +68,14 @@ def send_reset_password_email_task(user_pk: str) -> tuple[bool, str]:
|
||||||
"reset_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/reset-password?uid="
|
"reset_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/reset-password?uid="
|
||||||
f"{urlsafe_base64_encode(force_bytes(user.pk))}"
|
f"{urlsafe_base64_encode(force_bytes(user.pk))}"
|
||||||
f"&token={PasswordResetTokenGenerator().make_token(user)}",
|
f"&token={PasswordResetTokenGenerator().make_token(user)}",
|
||||||
"project_name": config.PROJECT_NAME,
|
"project_name": settings.PROJECT_NAME,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
email = EmailMessage(
|
email = EmailMessage(
|
||||||
subject=email_subject,
|
subject=email_subject,
|
||||||
body=email_body,
|
body=email_body,
|
||||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||||
to=[user.email],
|
to=[user.email],
|
||||||
connection=get_dynamic_email_connection(),
|
connection=get_dynamic_email_connection(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@ from .elasticsearch import * # noqa: F403
|
||||||
from .emailing import * # noqa: F403
|
from .emailing import * # noqa: F403
|
||||||
from .extensions import * # noqa: F403
|
from .extensions import * # noqa: F403
|
||||||
from .graphene import * # noqa: F403
|
from .graphene import * # noqa: F403
|
||||||
from .jazzmin import * # noqa: F403
|
from .unfold import * # noqa: F403
|
||||||
from .logconfig import * # noqa: F403
|
from .logconfig import * # noqa: F403
|
||||||
from .summernote import * # noqa: F403
|
from .summernote import * # noqa: F403
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
EVIBES_VERSION = "2025.4"
|
EVIBES_VERSION = "2025.4"
|
||||||
RELEASE_DATE = datetime(2025, 11, 9)
|
RELEASE_DATE = datetime(2025, 11, 9)
|
||||||
|
|
||||||
|
PROJECT_NAME = getenv("EVIBES_PROJECT_NAME", "eVibes")
|
||||||
|
|
||||||
BASE_DIR: Path = Path(__file__).resolve().parent.parent.parent
|
BASE_DIR: Path = Path(__file__).resolve().parent.parent.parent
|
||||||
INITIALIZED: bool = (BASE_DIR / ".initialized").exists()
|
INITIALIZED: bool = (BASE_DIR / ".initialized").exists()
|
||||||
|
|
||||||
|
|
@ -103,7 +105,9 @@ SITE_ID: int = 1
|
||||||
INSTALLED_APPS: list[str] = [
|
INSTALLED_APPS: list[str] = [
|
||||||
"django_prometheus",
|
"django_prometheus",
|
||||||
"constance",
|
"constance",
|
||||||
"jazzmin",
|
"unfold",
|
||||||
|
"unfold.contrib.filters",
|
||||||
|
"unfold.contrib.forms",
|
||||||
"modeltranslation",
|
"modeltranslation",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.admindocs",
|
"django.contrib.admindocs",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_noop as _
|
||||||
from django.utils.translation import gettext_noop
|
|
||||||
|
|
||||||
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
|
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
|
||||||
CONSTANCE_SUPERUSER_ONLY = False
|
CONSTANCE_SUPERUSER_ONLY = False
|
||||||
|
|
@ -19,11 +18,11 @@ CONSTANCE_ADDITIONAL_FIELDS = {
|
||||||
|
|
||||||
CONSTANCE_CONFIG = OrderedDict(
|
CONSTANCE_CONFIG = OrderedDict(
|
||||||
[
|
[
|
||||||
### General Options ###
|
### Legal Options ###
|
||||||
("PROJECT_NAME", (getenv("EVIBES_PROJECT_NAME"), _("Name of the project"))),
|
|
||||||
("COMPANY_NAME", (getenv("COMPANY_NAME"), _("Name of the company"))),
|
("COMPANY_NAME", (getenv("COMPANY_NAME"), _("Name of the company"))),
|
||||||
("COMPANY_ADDRESS", (getenv("COMPANY_ADDRESS"), _("Address of the company"))),
|
("COMPANY_ADDRESS", (getenv("COMPANY_ADDRESS"), _("Address of the company"))),
|
||||||
("COMPANY_PHONE_NUMBER", (getenv("COMPANY_PHONE_NUMBER"), _("Phone number of the company"))),
|
("COMPANY_PHONE_NUMBER", (getenv("COMPANY_PHONE_NUMBER"), _("Phone number of the company"))),
|
||||||
|
("TAX_RATE", (0, _("Tax rate in jurisdiction of your company. Leave 0 if you don't want to process taxes."))),
|
||||||
("EXCHANGE_RATE_API_KEY", (getenv("EXCHANGE_RATE_API_KEY", "example token"), _("Exchange rate API key"))),
|
("EXCHANGE_RATE_API_KEY", (getenv("EXCHANGE_RATE_API_KEY", "example token"), _("Exchange rate API key"))),
|
||||||
### Email Options ###
|
### Email Options ###
|
||||||
("EMAIL_BACKEND", ("django.core.mail.backends.smtp.EmailBackend", _("!!!DO NOT CHANGE!!!"))),
|
("EMAIL_BACKEND", ("django.core.mail.backends.smtp.EmailBackend", _("!!!DO NOT CHANGE!!!"))),
|
||||||
|
|
@ -52,14 +51,14 @@ CONSTANCE_CONFIG = OrderedDict(
|
||||||
|
|
||||||
CONSTANCE_CONFIG_FIELDSETS = OrderedDict(
|
CONSTANCE_CONFIG_FIELDSETS = OrderedDict(
|
||||||
{
|
{
|
||||||
gettext_noop("General Options"): (
|
_("Legal Options"): (
|
||||||
"PROJECT_NAME",
|
|
||||||
"COMPANY_NAME",
|
"COMPANY_NAME",
|
||||||
"COMPANY_ADDRESS",
|
"COMPANY_ADDRESS",
|
||||||
"COMPANY_PHONE_NUMBER",
|
"COMPANY_PHONE_NUMBER",
|
||||||
|
"TAX_RATE",
|
||||||
"EXCHANGE_RATE_API_KEY",
|
"EXCHANGE_RATE_API_KEY",
|
||||||
),
|
),
|
||||||
gettext_noop("Email Options"): (
|
_("Email Options"): (
|
||||||
"EMAIL_BACKEND",
|
"EMAIL_BACKEND",
|
||||||
"EMAIL_HOST",
|
"EMAIL_HOST",
|
||||||
"EMAIL_PORT",
|
"EMAIL_PORT",
|
||||||
|
|
@ -69,7 +68,7 @@ CONSTANCE_CONFIG_FIELDSETS = OrderedDict(
|
||||||
"EMAIL_HOST_PASSWORD",
|
"EMAIL_HOST_PASSWORD",
|
||||||
"EMAIL_FROM",
|
"EMAIL_FROM",
|
||||||
),
|
),
|
||||||
gettext_noop("Features Options"): (
|
_("Features Options"): (
|
||||||
"DAYS_TO_STORE_ANON_MSGS",
|
"DAYS_TO_STORE_ANON_MSGS",
|
||||||
"DAYS_TO_STORE_AUTH_MSGS",
|
"DAYS_TO_STORE_AUTH_MSGS",
|
||||||
"DISABLED_COMMERCE",
|
"DISABLED_COMMERCE",
|
||||||
|
|
@ -78,16 +77,15 @@ CONSTANCE_CONFIG_FIELDSETS = OrderedDict(
|
||||||
"ABSTRACT_API_KEY",
|
"ABSTRACT_API_KEY",
|
||||||
"HTTP_PROXY",
|
"HTTP_PROXY",
|
||||||
),
|
),
|
||||||
gettext_noop("SEO Options"): (
|
_("SEO Options"): (
|
||||||
"ADVERTSIMENT",
|
"ADVERTSIMENT",
|
||||||
"ANALYTICS",
|
"ANALYTICS",
|
||||||
),
|
),
|
||||||
gettext_noop("Debugging Options"): ("SAVE_VENDORS_RESPONSES",),
|
_("Debugging Options"): ("SAVE_VENDORS_RESPONSES",),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
EXPOSABLE_KEYS = [
|
EXPOSABLE_KEYS = [
|
||||||
"PROJECT_NAME",
|
|
||||||
"COMPANY_NAME",
|
"COMPANY_NAME",
|
||||||
"COMPANY_ADDRESS",
|
"COMPANY_ADDRESS",
|
||||||
"COMPANY_PHONE_NUMBER",
|
"COMPANY_PHONE_NUMBER",
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ from os import getenv
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from evibes.settings.base import DEBUG, EVIBES_VERSION, SECRET_KEY, BASE_DOMAIN
|
from evibes.settings.base import BASE_DOMAIN, DEBUG, EVIBES_VERSION, PROJECT_NAME, SECRET_KEY
|
||||||
from evibes.settings.constance import CONSTANCE_CONFIG
|
|
||||||
|
|
||||||
REST_FRAMEWORK: dict[str, str | int | list[str] | tuple[str, ...] | dict[str, bool]] = {
|
REST_FRAMEWORK: dict[str, str | int | list[str] | tuple[str, ...] | dict[str, bool]] = {
|
||||||
"DEFAULT_PAGINATION_CLASS": "evibes.pagination.CustomPagination",
|
"DEFAULT_PAGINATION_CLASS": "evibes.pagination.CustomPagination",
|
||||||
|
|
@ -99,7 +98,7 @@ Current API version: {EVIBES_VERSION}
|
||||||
|
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
"DEFAULT_GENERATOR_CLASS": "drf_spectacular_websocket.schemas.WsSchemaGenerator",
|
"DEFAULT_GENERATOR_CLASS": "drf_spectacular_websocket.schemas.WsSchemaGenerator",
|
||||||
"TITLE": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} API", # type: ignore [index]
|
"TITLE": f"{PROJECT_NAME} API", # type: ignore [index]
|
||||||
"DESCRIPTION": SPECTACULAR_DESCRIPTION,
|
"DESCRIPTION": SPECTACULAR_DESCRIPTION,
|
||||||
"VERSION": EVIBES_VERSION, # noqa: F405
|
"VERSION": EVIBES_VERSION, # noqa: F405
|
||||||
"TOS": "https://evibes.wiseless.xyz/terms-of-service",
|
"TOS": "https://evibes.wiseless.xyz/terms-of-service",
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,11 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
"""
|
||||||
|
Deprecated: Jazzmin settings (removed in favor of django-unfold).
|
||||||
|
|
||||||
from evibes.settings.base import EVIBES_VERSION, STOREFRONT_DOMAIN
|
This file is intentionally left as a stub to avoid accidental imports.
|
||||||
from evibes.settings.constance import CONSTANCE_CONFIG
|
If imported, raise an explicit error guiding developers to Unfold.
|
||||||
|
"""
|
||||||
|
|
||||||
JAZZMIN_SETTINGS = {
|
raise ImportError(
|
||||||
"site_title": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} Admin", # type: ignore [index]
|
"Jazzmin configuration has been removed. Use django-unfold instead. "
|
||||||
"site_header": str(CONSTANCE_CONFIG.get("PROJECT_NAME")[0]), # type: ignore [index]
|
"See evibes/settings/unfold.py and INSTALLED_APPS in evibes/settings/base.py."
|
||||||
"site_brand": str(CONSTANCE_CONFIG.get("PROJECT_NAME")[0]), # type: ignore [index]
|
)
|
||||||
"site_logo": "logo.png",
|
|
||||||
"login_logo": "logo.png",
|
|
||||||
"login_logo_dark": "logo.png",
|
|
||||||
"site_logo_classes": "",
|
|
||||||
"site_icon": "favicon.ico",
|
|
||||||
"welcome_sign": _("Whoa! Only admins allowed here!"),
|
|
||||||
"copyright": f"eVibes {EVIBES_VERSION} by Wiseless",
|
|
||||||
"search_model": None,
|
|
||||||
"user_avatar": "avatar",
|
|
||||||
"topmenu_links": [
|
|
||||||
{
|
|
||||||
"name": _("Home"),
|
|
||||||
"url": "admin:index",
|
|
||||||
"new_window": False,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": _("Storefront"),
|
|
||||||
"url": f"https://{STOREFRONT_DOMAIN}",
|
|
||||||
"new_window": False,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "GraphQL Docs",
|
|
||||||
"url": "graphql-platform",
|
|
||||||
"new_window": False,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Swagger",
|
|
||||||
"url": "swagger-ui-platform",
|
|
||||||
"new_window": False,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Redoc",
|
|
||||||
"url": "redoc-ui-platform",
|
|
||||||
"new_window": False,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": _("Taskboard"),
|
|
||||||
"url": "https://plane.wiseless.xyz/spaces/issues/dd33cb0ab9b04ef08a10f7eefae6d90c/?board=kanban",
|
|
||||||
"new_window": True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "GitLab",
|
|
||||||
"url": "https://gitlab.com/wiseless/evibes",
|
|
||||||
"new_window": True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": _("Support"),
|
|
||||||
"url": "https://t.me/fureunoir",
|
|
||||||
"new_window": True,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"usermenu_links": [],
|
|
||||||
"show_sidebar": True,
|
|
||||||
"navigation_expanded": True,
|
|
||||||
"hide_apps": ["django_celery_results", ""],
|
|
||||||
"hide_models": [],
|
|
||||||
"order_with_respect_to": ["vibes_auth", "core", "payments", "blog"],
|
|
||||||
"icons": {
|
|
||||||
"vibes_auth": "fas fa-users-cog",
|
|
||||||
"vibes_auth.user": "fas fa-user",
|
|
||||||
"vibes_auth.Group": "fas fa-users",
|
|
||||||
},
|
|
||||||
"default_icon_parents": "fas fa-chevron-circle-right",
|
|
||||||
"default_icon_children": "fas fa-circle",
|
|
||||||
"related_modal_active": False,
|
|
||||||
"use_google_fonts_cdn": True,
|
|
||||||
"show_ui_builder": True,
|
|
||||||
"changeform_format": "horizontal_tabs",
|
|
||||||
"language_chooser": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
JAZZMIN_UI_TWEAKS = {
|
|
||||||
"theme": "flatly",
|
|
||||||
"dark_mode_theme": "darkly",
|
|
||||||
}
|
|
||||||
|
|
|
||||||
28
evibes/settings/unfold.py
Normal file
28
evibes/settings/unfold.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""django-unfold configuration.
|
||||||
|
|
||||||
|
This module defines branding for the Django admin using django-unfold.
|
||||||
|
It intentionally avoids database-backed configuration (e.g., Constance)
|
||||||
|
so that it is safe during initial migrations and in all environments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from evibes.settings.base import PROJECT_NAME
|
||||||
|
|
||||||
|
# See django-unfold documentation for all available options.
|
||||||
|
# Only minimal, production-safe branding is configured here.
|
||||||
|
UNFOLD = {
|
||||||
|
# Text shown in the browser title bar and in the admin header
|
||||||
|
"SITE_TITLE": f"{PROJECT_NAME} Admin",
|
||||||
|
"SITE_HEADER": PROJECT_NAME,
|
||||||
|
# Optional URL the header/brand links to (leave default admin index)
|
||||||
|
# "SITE_URL": "/admin/",
|
||||||
|
# Logos and favicon served via Django staticfiles
|
||||||
|
# Files are expected at: engine/core/static/logo.png, favicon.ico, favicon.png
|
||||||
|
# Refer to them by their static URL path (relative), no leading slash.
|
||||||
|
"SITE_LOGO": "logo.png",
|
||||||
|
# If you use a different logo for dark theme, set SITE_LOGO_DARK
|
||||||
|
# Otherwise Unfold will reuse SITE_LOGO
|
||||||
|
# "SITE_LOGO_DARK": "logo.png",
|
||||||
|
"SITE_ICON": "favicon.ico",
|
||||||
|
# Sidebar behavior, search etc. (keep defaults minimal)
|
||||||
|
# Unfold automatically respects user OS light/dark theme; no forcing here.
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,6 @@ dependencies = [
|
||||||
"django-extensions==4.1",
|
"django-extensions==4.1",
|
||||||
"django-filter==25.2",
|
"django-filter==25.2",
|
||||||
"django-health-check==3.20.0",
|
"django-health-check==3.20.0",
|
||||||
"django-jazzmin==3.0.1",
|
|
||||||
"django-json-widget==2.1.0",
|
"django-json-widget==2.1.0",
|
||||||
"django-mailbox==4.10.1",
|
"django-mailbox==4.10.1",
|
||||||
"django-model-utils==5.0.0",
|
"django-model-utils==5.0.0",
|
||||||
|
|
@ -35,6 +34,7 @@ dependencies = [
|
||||||
"django-storages==1.14.6",
|
"django-storages==1.14.6",
|
||||||
"django-stubs==5.2.7",
|
"django-stubs==5.2.7",
|
||||||
"django-summernote==0.8.20.0",
|
"django-summernote==0.8.20.0",
|
||||||
|
"django-unfold>=0.71.0",
|
||||||
"django-widget-tweaks==1.5.0",
|
"django-widget-tweaks==1.5.0",
|
||||||
"django-md-field==0.1.0",
|
"django-md-field==0.1.0",
|
||||||
"djangorestframework==3.16.1",
|
"djangorestframework==3.16.1",
|
||||||
|
|
@ -87,7 +87,7 @@ linting = [
|
||||||
"isort==7.0.0",
|
"isort==7.0.0",
|
||||||
"mypy==1.18.2",
|
"mypy==1.18.2",
|
||||||
"mypy-extensions==1.1.0",
|
"mypy-extensions==1.1.0",
|
||||||
"ruff==0.14.4",
|
"ruff==0.14.5",
|
||||||
"celery-stubs==0.1.3",
|
"celery-stubs==0.1.3",
|
||||||
]
|
]
|
||||||
openai = ["openai==2.6.1"]
|
openai = ["openai==2.6.1"]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"name": "supervisor",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Supervisor is a custom dashboard application for eVibes",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "WISELESS team",
|
|
||||||
"license": "../LICENSE"
|
|
||||||
}
|
|
||||||
70
uv.lock
70
uv.lock
|
|
@ -874,18 +874,6 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e1/7f/49ba63f078015b0a52e09651b94ba16b41154ac7079c83153edd14e15ca0/django_health_check-3.20.0-py3-none-any.whl", hash = "sha256:bcb2b8f36f463cead0564a028345c5b17e2a2d18e9cc88ecd611b13a26521926", size = 31788, upload-time = "2025-06-16T09:22:32.069Z" },
|
{ url = "https://files.pythonhosted.org/packages/e1/7f/49ba63f078015b0a52e09651b94ba16b41154ac7079c83153edd14e15ca0/django_health_check-3.20.0-py3-none-any.whl", hash = "sha256:bcb2b8f36f463cead0564a028345c5b17e2a2d18e9cc88ecd611b13a26521926", size = 31788, upload-time = "2025-06-16T09:22:32.069Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "django-jazzmin"
|
|
||||||
version = "3.0.1"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "django" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/84/96/21b6255e90d92a3eb4e93bea9376635d54258e0353ebb913a55e40ae9254/django_jazzmin-3.0.1.tar.gz", hash = "sha256:67ae148bade41267a09ca8e4352ddefa6121795ebbac238bb9a6564ff841eb1b", size = 2053550, upload-time = "2024-10-08T17:40:59.771Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ad/5b/2f8c4b168e6c41bf1e4b14d787deb23d80f618f0693db913bbe208a4a907/django_jazzmin-3.0.1-py3-none-any.whl", hash = "sha256:12a0a4c1d4fd09c2eef22acf6a1f03112b515ba695c59faa8ea80efc81c1f21b", size = 2125957, upload-time = "2024-10-08T17:40:57.359Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-js-asset"
|
name = "django-js-asset"
|
||||||
version = "3.1.2"
|
version = "3.1.2"
|
||||||
|
|
@ -1058,6 +1046,18 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ec/09/7a808392a751a24ffa62bec00e3085a9c1a151d728c323a5bab229ea0e58/django_timezone_field-7.1-py3-none-any.whl", hash = "sha256:93914713ed882f5bccda080eda388f7006349f25930b6122e9b07bf8db49c4b4", size = 13177, upload-time = "2025-01-11T17:49:52.142Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/09/7a808392a751a24ffa62bec00e3085a9c1a151d728c323a5bab229ea0e58/django_timezone_field-7.1-py3-none-any.whl", hash = "sha256:93914713ed882f5bccda080eda388f7006349f25930b6122e9b07bf8db49c4b4", size = 13177, upload-time = "2025-01-11T17:49:52.142Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-unfold"
|
||||||
|
version = "0.71.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ec/5b/406eae1a429b15ba04f4dfaaf53aa64fb03bcfdc6bdd0753a41027aa3daa/django_unfold-0.71.0.tar.gz", hash = "sha256:995a296f1c15f172b0d8458ff12beb420ea7e9a666fa865a60ec03f70aaf4066", size = 1101347, upload-time = "2025-11-11T16:24:03.289Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/94/ad8ba84410655e0207ffcc7c6cba0875e9f79f914e3f2e2de883f706f7c9/django_unfold-0.71.0-py3-none-any.whl", hash = "sha256:76d4019aa9052ebe2e040d868be895d8581018fdf7debca943084aa0e79c2e31", size = 1213722, upload-time = "2025-11-11T16:24:01.985Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-widget-tweaks"
|
name = "django-widget-tweaks"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -1289,7 +1289,6 @@ dependencies = [
|
||||||
{ name = "django-extensions" },
|
{ name = "django-extensions" },
|
||||||
{ name = "django-filter" },
|
{ name = "django-filter" },
|
||||||
{ name = "django-health-check" },
|
{ name = "django-health-check" },
|
||||||
{ name = "django-jazzmin" },
|
|
||||||
{ name = "django-json-widget" },
|
{ name = "django-json-widget" },
|
||||||
{ name = "django-mailbox" },
|
{ name = "django-mailbox" },
|
||||||
{ name = "django-md-field" },
|
{ name = "django-md-field" },
|
||||||
|
|
@ -1302,6 +1301,7 @@ dependencies = [
|
||||||
{ name = "django-storages" },
|
{ name = "django-storages" },
|
||||||
{ name = "django-stubs" },
|
{ name = "django-stubs" },
|
||||||
{ name = "django-summernote" },
|
{ name = "django-summernote" },
|
||||||
|
{ name = "django-unfold" },
|
||||||
{ name = "django-widget-tweaks" },
|
{ name = "django-widget-tweaks" },
|
||||||
{ name = "djangorestframework" },
|
{ name = "djangorestframework" },
|
||||||
{ name = "djangorestframework-camel-case" },
|
{ name = "djangorestframework-camel-case" },
|
||||||
|
|
@ -1394,7 +1394,6 @@ requires-dist = [
|
||||||
{ name = "django-extensions", specifier = "==4.1" },
|
{ name = "django-extensions", specifier = "==4.1" },
|
||||||
{ name = "django-filter", specifier = "==25.2" },
|
{ name = "django-filter", specifier = "==25.2" },
|
||||||
{ name = "django-health-check", specifier = "==3.20.0" },
|
{ name = "django-health-check", specifier = "==3.20.0" },
|
||||||
{ name = "django-jazzmin", specifier = "==3.0.1" },
|
|
||||||
{ name = "django-json-widget", specifier = "==2.1.0" },
|
{ name = "django-json-widget", specifier = "==2.1.0" },
|
||||||
{ name = "django-mailbox", specifier = "==4.10.1" },
|
{ name = "django-mailbox", specifier = "==4.10.1" },
|
||||||
{ name = "django-md-field", specifier = "==0.1.0" },
|
{ name = "django-md-field", specifier = "==0.1.0" },
|
||||||
|
|
@ -1407,6 +1406,7 @@ requires-dist = [
|
||||||
{ name = "django-storages", specifier = "==1.14.6" },
|
{ name = "django-storages", specifier = "==1.14.6" },
|
||||||
{ name = "django-stubs", specifier = "==5.2.7" },
|
{ name = "django-stubs", specifier = "==5.2.7" },
|
||||||
{ name = "django-summernote", specifier = "==0.8.20.0" },
|
{ name = "django-summernote", specifier = "==0.8.20.0" },
|
||||||
|
{ name = "django-unfold", specifier = ">=0.71.0" },
|
||||||
{ name = "django-widget-tweaks", specifier = "==1.5.0" },
|
{ name = "django-widget-tweaks", specifier = "==1.5.0" },
|
||||||
{ name = "djangorestframework", specifier = "==3.16.1" },
|
{ name = "djangorestframework", specifier = "==3.16.1" },
|
||||||
{ name = "djangorestframework-camel-case", specifier = "==1.4.2" },
|
{ name = "djangorestframework-camel-case", specifier = "==1.4.2" },
|
||||||
|
|
@ -1444,7 +1444,7 @@ requires-dist = [
|
||||||
{ name = "python-slugify", specifier = "==8.0.4" },
|
{ name = "python-slugify", specifier = "==8.0.4" },
|
||||||
{ name = "redis", specifier = "==7.0.1" },
|
{ name = "redis", specifier = "==7.0.1" },
|
||||||
{ name = "requests", specifier = "==2.32.5" },
|
{ name = "requests", specifier = "==2.32.5" },
|
||||||
{ name = "ruff", marker = "extra == 'linting'", specifier = "==0.14.4" },
|
{ name = "ruff", marker = "extra == 'linting'", specifier = "==0.14.5" },
|
||||||
{ name = "sentry-sdk", extras = ["django", "celery", "opentelemetry"], specifier = "==2.44.0" },
|
{ name = "sentry-sdk", extras = ["django", "celery", "opentelemetry"], specifier = "==2.44.0" },
|
||||||
{ name = "six", specifier = "==1.17.0" },
|
{ name = "six", specifier = "==1.17.0" },
|
||||||
{ name = "sphinx", marker = "extra == 'docs'", specifier = "==8.2.3" },
|
{ name = "sphinx", marker = "extra == 'docs'", specifier = "==8.2.3" },
|
||||||
|
|
@ -3345,28 +3345,28 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.4"
|
version = "0.14.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/df/55/cccfca45157a2031dcbb5a462a67f7cf27f8b37d4b3b1cd7438f0f5c1df6/ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3", size = 5587844, upload-time = "2025-11-06T22:07:45.033Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/82/fa/fbb67a5780ae0f704876cb8ac92d6d76da41da4dc72b7ed3565ab18f2f52/ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1", size = 5615944, upload-time = "2025-11-13T19:58:51.155Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/b9/67240254166ae1eaa38dec32265e9153ac53645a6c6670ed36ad00722af8/ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518", size = 12606781, upload-time = "2025-11-06T22:07:01.841Z" },
|
{ url = "https://files.pythonhosted.org/packages/68/31/c07e9c535248d10836a94e4f4e8c5a31a1beed6f169b31405b227872d4f4/ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594", size = 13171630, upload-time = "2025-11-13T19:57:54.894Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/46/c8/09b3ab245d8652eafe5256ab59718641429f68681ee713ff06c5c549f156/ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4", size = 12946765, upload-time = "2025-11-06T22:07:05.858Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/5c/283c62516dca697cd604c2796d1487396b7a436b2f0ecc3fd412aca470e0/ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72", size = 13413925, upload-time = "2025-11-13T19:57:59.181Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/bb/1564b000219144bf5eed2359edc94c3590dd49d510751dad26202c18a17d/ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33", size = 11928120, upload-time = "2025-11-06T22:07:08.023Z" },
|
{ url = "https://files.pythonhosted.org/packages/b6/f3/aa319f4afc22cb6fcba2b9cdfc0f03bbf747e59ab7a8c5e90173857a1361/ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a", size = 12574040, upload-time = "2025-11-13T19:58:02.056Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/92/d5f1770e9988cc0742fefaa351e840d9aef04ec24ae1be36f333f96d5704/ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2", size = 12370877, upload-time = "2025-11-06T22:07:10.015Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/7f/cb5845fcc7c7e88ed57f58670189fc2ff517fe2134c3821e77e29fd3b0c8/ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f", size = 13009755, upload-time = "2025-11-13T19:58:05.172Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e2/29/e9282efa55f1973d109faf839a63235575519c8ad278cc87a182a366810e/ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5", size = 12408538, upload-time = "2025-11-06T22:07:13.085Z" },
|
{ url = "https://files.pythonhosted.org/packages/21/d2/bcbedbb6bcb9253085981730687ddc0cc7b2e18e8dc13cf4453de905d7a0/ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68", size = 12937641, upload-time = "2025-11-13T19:58:08.345Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8e/01/930ed6ecfce130144b32d77d8d69f5c610e6d23e6857927150adf5d7379a/ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e", size = 13141942, upload-time = "2025-11-06T22:07:15.386Z" },
|
{ url = "https://files.pythonhosted.org/packages/a4/58/e25de28a572bdd60ffc6bb71fc7fd25a94ec6a076942e372437649cbb02a/ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7", size = 13610854, upload-time = "2025-11-13T19:58:11.419Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6a/46/a9c89b42b231a9f487233f17a89cbef9d5acd538d9488687a02ad288fa6b/ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8", size = 14544306, upload-time = "2025-11-06T22:07:17.631Z" },
|
{ url = "https://files.pythonhosted.org/packages/7d/24/43bb3fd23ecee9861970978ea1a7a63e12a204d319248a7e8af539984280/ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78", size = 15061088, upload-time = "2025-11-13T19:58:14.551Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/96/9c6cf86491f2a6d52758b830b89b78c2ae61e8ca66b86bf5a20af73d20e6/ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649", size = 14210427, upload-time = "2025-11-06T22:07:19.832Z" },
|
{ url = "https://files.pythonhosted.org/packages/23/44/a022f288d61c2f8c8645b24c364b719aee293ffc7d633a2ca4d116b9c716/ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb", size = 14734717, upload-time = "2025-11-13T19:58:17.518Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/71/f4/0666fe7769a54f63e66404e8ff698de1dcde733e12e2fd1c9c6efb689cb5/ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850", size = 13658488, upload-time = "2025-11-06T22:07:22.32Z" },
|
{ url = "https://files.pythonhosted.org/packages/58/81/5c6ba44de7e44c91f68073e0658109d8373b0590940efe5bd7753a2585a3/ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2", size = 14028812, upload-time = "2025-11-13T19:58:20.533Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5", size = 13354908, upload-time = "2025-11-06T22:07:24.347Z" },
|
{ url = "https://files.pythonhosted.org/packages/ad/ef/41a8b60f8462cb320f68615b00299ebb12660097c952c600c762078420f8/ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19", size = 13825656, upload-time = "2025-11-13T19:58:23.345Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/60/f0b6990f740bb15c1588601d19d21bcc1bd5de4330a07222041678a8e04f/ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132", size = 13587803, upload-time = "2025-11-06T22:07:26.327Z" },
|
{ url = "https://files.pythonhosted.org/packages/7c/00/207e5de737fdb59b39eb1fac806904fe05681981b46d6a6db9468501062e/ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4", size = 13959922, upload-time = "2025-11-13T19:58:26.537Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c9/da/eaaada586f80068728338e0ef7f29ab3e4a08a692f92eb901a4f06bbff24/ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67", size = 12279654, upload-time = "2025-11-06T22:07:28.46Z" },
|
{ url = "https://files.pythonhosted.org/packages/bc/7e/fa1f5c2776db4be405040293618846a2dece5c70b050874c2d1f10f24776/ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1", size = 12932501, upload-time = "2025-11-13T19:58:29.822Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/66/d4/b1d0e82cf9bf8aed10a6d45be47b3f402730aa2c438164424783ac88c0ed/ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469", size = 12357520, upload-time = "2025-11-06T22:07:31.468Z" },
|
{ url = "https://files.pythonhosted.org/packages/67/d8/d86bf784d693a764b59479a6bbdc9515ae42c340a5dc5ab1dabef847bfaa/ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151", size = 12927319, upload-time = "2025-11-13T19:58:32.923Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/04/f4/53e2b42cc82804617e5c7950b7079d79996c27e99c4652131c6a1100657f/ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde", size = 12719431, upload-time = "2025-11-06T22:07:33.831Z" },
|
{ url = "https://files.pythonhosted.org/packages/ac/de/ee0b304d450ae007ce0cb3e455fe24fbcaaedae4ebaad6c23831c6663651/ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465", size = 13206209, upload-time = "2025-11-13T19:58:35.952Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a2/94/80e3d74ed9a72d64e94a7b7706b1c1ebaa315ef2076fd33581f6a1cd2f95/ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349", size = 13464394, upload-time = "2025-11-06T22:07:35.905Z" },
|
{ url = "https://files.pythonhosted.org/packages/33/aa/193ca7e3a92d74f17d9d5771a765965d2cf42c86e6f0fd95b13969115723/ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367", size = 13953709, upload-time = "2025-11-13T19:58:39.002Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/54/1a/a49f071f04c42345c793d22f6cf5e0920095e286119ee53a64a3a3004825/ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff", size = 12493429, upload-time = "2025-11-06T22:07:38.43Z" },
|
{ url = "https://files.pythonhosted.org/packages/cc/f1/7119e42aa1d3bf036ffc9478885c2e248812b7de9abea4eae89163d2929d/ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b", size = 12925808, upload-time = "2025-11-13T19:58:42.779Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bc/22/e58c43e641145a2b670328fb98bc384e20679b5774258b1e540207580266/ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c", size = 13635380, upload-time = "2025-11-06T22:07:40.496Z" },
|
{ url = "https://files.pythonhosted.org/packages/3b/9d/7c0a255d21e0912114784e4a96bf62af0618e2190cae468cd82b13625ad2/ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621", size = 14331546, upload-time = "2025-11-13T19:58:45.691Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/30/bd/4168a751ddbbf43e86544b4de8b5c3b7be8d7167a2a5cb977d274e04f0a1/ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb", size = 12663065, upload-time = "2025-11-06T22:07:42.603Z" },
|
{ url = "https://files.pythonhosted.org/packages/e5/80/69756670caedcf3b9be597a6e12276a6cf6197076eb62aad0c608f8efce0/ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4", size = 13433331, upload-time = "2025-11-13T19:58:48.434Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue