2025.4 BETA

This commit is contained in:
Egor Pavlovich Gorbunov 2025-11-10 08:36:57 +03:00
parent c09c0d8753
commit 3fbe6883c7
189 changed files with 3768 additions and 3905 deletions

View file

@ -75,7 +75,7 @@ static/
media/
!engine/core/static
!engine/blog/static
!engine/authv/static
!engine/vibes_auth/static
!engine/payments/static
# Environment file

2
.gitignore vendored
View file

@ -111,7 +111,7 @@ media/
# Allow checked-in static from apps
!engine/core/static/
!engine/payments/static/
!engine/authv/static/
!engine/vibes_auth/static/
!engine/blog/static/
# Webassets

View file

@ -1,52 +0,0 @@
from __future__ import annotations
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from engine.authv.messaging.models import ChatMessage, ChatThread, ThreadStatus
# noinspection PyUnusedLocal
@admin.register(ChatThread)
class ChatThreadAdmin(admin.ModelAdmin):
list_display = (
"uuid",
"user",
"email",
"assigned_to",
"status",
"last_message_at",
"is_active",
"created",
"modified",
)
list_filter = (
"status",
"is_active",
("assigned_to", admin.EmptyFieldListFilter),
)
search_fields = ("uuid", "email", "user__email", "user__username")
autocomplete_fields = ("user", "assigned_to")
actions = (
"close_threads",
"open_threads",
"delete_selected",
)
readonly_fields = ("created", "modified")
@admin.action(description=_("Close selected threads"))
def close_threads(self, request, queryset): # type: ignore[no-untyped-def]
queryset.update(status=ThreadStatus.CLOSED)
@admin.action(description=_("Open selected threads"))
def open_threads(self, request, queryset): # type: ignore[no-untyped-def]
queryset.update(status=ThreadStatus.OPEN)
@admin.register(ChatMessage)
class ChatMessageAdmin(admin.ModelAdmin):
list_display = ("uuid", "thread", "sender_type", "sender_user", "sent_at")
list_filter = ("sender_type",)
search_fields = ("uuid", "thread__uuid", "sender_user__email")
autocomplete_fields = ("thread", "sender_user")
readonly_fields = ("created", "modified")

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-28 11:56
import uuid
import django.db.models.deletion

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-28 12:07
import django_extensions.db.fields
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-28 12:39
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-29 13:09
import markdown_field.fields
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-19 11:59
import markdown_field.fields
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-07 12:46
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-21 09:24
from django.db import migrations, models

View file

@ -1,5 +1,4 @@
import logging
from typing import Optional
import requests
from constance import config
@ -165,7 +164,7 @@ class AmoCRM:
def process_order_changes(self, order: Order) -> str:
with transaction.atomic():
try:
link: Optional[OrderCrmLink] = OrderCrmLink.objects.get(order=order)
link: OrderCrmLink | None = OrderCrmLink.objects.get(order=order)
except OrderCrmLink.MultipleObjectsReturned:
link = OrderCrmLink.objects.filter(order=order).first()
except OrderCrmLink.DoesNotExist:

View file

@ -86,8 +86,8 @@ from engine.core.utils import get_project_parameters
from engine.core.utils.languages import get_flag_by_language
from engine.core.utils.messages import permission_denied_message
from engine.payments.graphene.mutations import Deposit
from engine.authv.filters import UserFilter
from engine.authv.graphene.mutations import (
from engine.vibes_auth.filters import UserFilter
from engine.vibes_auth.graphene.mutations import (
ActivateUser,
ConfirmResetPassword,
CreateUser,
@ -99,8 +99,8 @@ from engine.authv.graphene.mutations import (
UploadAvatar,
VerifyJSONWebToken,
)
from engine.authv.graphene.object_types import UserType
from engine.authv.models import User
from engine.vibes_auth.graphene.object_types import UserType
from engine.vibes_auth.models import User
logger = logging.getLogger(__name__)
@ -207,7 +207,7 @@ class Query(ObjectType):
@staticmethod
def resolve_users(_parent, info, **_kwargs):
if info.context.user.has_perm("authv.view_user"):
if info.context.user.has_perm("vibes_auth.view_user"):
return User.objects.all()
users = User.objects.filter(uuid=info.context.user.pk)
return users if users.exists() else User.objects.none()

View file

@ -662,7 +662,7 @@ msgstr ""
"deepl_translate -l en-gb -l ar-ar -l cs-cz -l da-dk -l de-de-de -l en-us -l "
"es-es -l fr-fr -l hi-in -l it-it -l ja-jp -l kk-kz -l nl-nl -l nl-nl -l pl-"
"pl -l pt-br -l ro-ro -l ru-ru -l zh-hans -l zh-ans -a core -a geo -a geo -a "
"payments -a authv -a blog"
"payments -a vibes_auth -a blog"
#: engine/core/docs/drf/viewsets.py:592
msgid "limit the results amount, 1 < limit < 10, default: 5"

View file

@ -711,7 +711,7 @@ msgstr ""
"docker compose exec app poetry run python manage.py deepl_translate -l en-gb"
" -l ar-ar -l cs-cz -l da-dk -l de-de -l en-us -l es-es -l fr-fr -l hi-in -l "
"it-it -l ja-jp -l kk-kz -l nl-nl -l pl -l pt-br -l ro-ro -l ru-ru -l zh-hans"
" -a core -a geo -a payments -a authv -a blog"
" -a core -a geo -a payments -a vibes_auth -a blog"
#: engine/core/docs/drf/viewsets.py:592
msgid "limit the results amount, 1 < limit < 10, default: 5"

View file

@ -647,7 +647,7 @@ msgstr ""
"docker compose exec app poetry run python manage.py deepl_translate -l en-gb"
" -l ar-ar -l cs-cz -l da-dk -l de-de -l en-us -l es-es -l fr-fr -l hi-in -l "
"it-it -l ja-jp -l kk-kz -l n-nl -l pl-pl -l pt-br -l ro-ro -l ru-ru -l zh-"
"hans -a core -a geo -a payments -a authv -a blog"
"hans -a core -a geo -a payments -a vibes_auth -a blog"
#: engine/core/docs/drf/viewsets.py:592
msgid "limit the results amount, 1 < limit < 10, default: 5"

View file

@ -700,7 +700,7 @@ msgstr ""
"docker compose exec app poetry run python manage.py deepl_translate -l en-gb"
" -l ar-ar -l cs-cz -l da-dk -l de-de -l en-us -l es-es -l fr-fr -l hi-in -l "
"it-it -l ja-jp -l kk-kz -l nl-nl -l pl-pl -l pt-br -l ro-ro -l ru-ru -l zh-"
"hans -a core -a geo -a plăți -a authv -a blog"
"hans -a core -a geo -a plăți -a vibes_auth -a blog"
#: engine/core/docs/drf/viewsets.py:592
msgid "limit the results amount, 1 < limit < 10, default: 5"

View file

@ -39,4 +39,4 @@ DEEPL_TARGET_LANGUAGES_MAPPING = {
"zh-hans": "ZH-HANS",
}
TRANSLATABLE_APPS = ["core", "authv", "blog", "payments", "root"]
TRANSLATABLE_APPS = ["core", "vibes_auth", "blog", "payments", "root"]

View file

@ -4,7 +4,7 @@ from typing import Any
from django.core.management.base import BaseCommand
from engine.core.models import Vendor
from engine.authv.models import Group
from engine.vibes_auth.models import Group
from django.contrib.auth.models import Permission
logger = logging.getLogger(__name__)

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 11:38
import uuid
import django.core.validators

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 11:38
import django.contrib.postgres.indexes
import django.db.models.deletion
from django.conf import settings

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 12:09
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 20:13
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-16 12:53
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-20 15:27
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-24 14:04
from django.db import migrations, models
import engine.core.validators

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-04-10 15:55
import uuid
import django.db.models.deletion

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-04-15 09:15
import uuid
import django.db.models.deletion

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-17 14:22
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-18 11:34
from django.db import migrations, models
import engine.core.validators

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-28 11:56
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-30 13:29
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-30 13:42
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-30 14:03
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-05-05 12:56
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-05-06 13:58
from django.db import migrations, models
import engine.core.utils

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-05-20 04:57
import uuid
import django.contrib.gis.db.models.fields

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-05-20 19:06
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-05-21 09:35
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-05-28 19:06
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-10 02:42
import uuid
import django_extensions.db.fields

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-17 08:25
import django.db.models.deletion
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-18 19:21
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-20 02:25
import django_extensions.db.fields
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 16:04
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 16:29
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 16:34
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 16:40
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 17:14
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 17:38
from django.db import migrations
import engine.core.utils.db

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-21 21:40
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-06-29 13:09
import engine.core.utils.db
import django_extensions.db.fields
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-07-28 08:55
import engine.core.utils
from django.conf import settings
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-01 17:33
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-01 17:36
import engine.core.utils
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-06 22:05
import django.db.models.deletion
import django_extensions.db.fields
import django_prometheus.models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-06 22:16
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-19 11:59
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-09-22 11:10
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-13 12:15
import engine.core.utils
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-17 11:27
from django.conf import settings
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-18 19:41
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-18 21:16
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-21 09:24
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2.7 on 2025-10-24 23:17
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2.7 on 2025-10-26 14:10
from django.db import migrations

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2.7 on 2025-10-26 16:59
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2.7 on 2025-11-01 23:45
from django.db import migrations, models

View file

@ -2,7 +2,7 @@ import datetime
import json
import logging
from contextlib import suppress
from typing import Any, Optional, Self, Iterable
from typing import Any, Iterable, Self
from constance import config
from django.conf import settings
@ -36,8 +36,8 @@ from django.db.models import (
TextField,
URLField,
)
from django.db.models.indexes import Index
from django.db.models.functions import Length
from django.db.models.indexes import Index
from django.http import Http404
from django.utils import timezone
from django.utils.encoding import force_bytes
@ -64,8 +64,8 @@ from engine.core.utils import (
from engine.core.utils.db import TweakedAutoSlugField, unicode_slugify_function
from engine.core.utils.lists import FAILED_STATUSES
from engine.core.validators import validate_category_image_dimensions
from evibes.utils.misc import create_object
from engine.payments.models import Transaction
from evibes.utils.misc import create_object
logger = logging.getLogger(__name__)
@ -369,10 +369,10 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel): #
.distinct()
)
per_cat: dict[int, dict[int, dict[str, Any]]] = {}
per_cat: dict[Any, Any] = {}
for cat_id, attr_id, attr_name, value_type, value in rows:
cat_bucket = per_cat.get(cat_id)
if cat_bucket is None:
cat_bucket = per_cat.get(cat_id, "")
if not cat_bucket:
cat_bucket = {}
per_cat[cat_id] = cat_bucket
bucket = cat_bucket.get(attr_id)
@ -405,7 +405,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel): #
.distinct()
)
by_attr: dict[int, dict] = {}
by_attr: dict[Any, Any] = {}
for attr_id, attr_name, value_type, value in rows:
bucket = by_attr.get(attr_id)
if bucket is None:
@ -913,7 +913,7 @@ class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel): # type: igno
verbose_name=_("wishlisted products"),
)
user = OneToOneField(
"authv.User",
"vibes_auth.User",
on_delete=CASCADE,
blank=True,
null=True,
@ -1107,7 +1107,7 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel): # type: ig
verbose_name=_("usage timestamp"),
)
user = ForeignKey(
"authv.User",
"vibes_auth.User",
on_delete=CASCADE,
help_text=_("user assigned to this promocode if applicable"),
verbose_name=_("assigned user"),
@ -1236,7 +1236,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
verbose_name=_("attributes"),
)
user = ForeignKey(
"authv.User",
"vibes_auth.User",
on_delete=CASCADE,
help_text=_("the user who placed the order"),
verbose_name=_("user"),
@ -1680,6 +1680,49 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
return None
class Feedback(ExportModelOperationsMixin("feedback"), NiceModel): # type: ignore [misc]
__doc__ = _( # type: ignore
"Manages user feedback for products. "
"This class is designed to capture and store user feedback for specific products "
"that they have purchased. It contains attributes to store user comments, "
"a reference to the related product in the order, and a user-assigned rating. The "
"class uses database fields to effectively model and manage feedback data."
)
is_publicly_visible = True
comment = TextField(
blank=True,
null=True,
help_text=_("user-provided comments about their experience with the product"),
verbose_name=_("feedback comments"),
)
order_product = OneToOneField(
"core.OrderProduct",
on_delete=CASCADE,
blank=False,
null=False,
help_text=_("references the specific product in an order that this feedback is about"),
verbose_name=_("related order product"),
)
rating = FloatField(
blank=True,
null=True,
help_text=_("user-assigned rating for the product"),
verbose_name=_("product rating"),
validators=[MinValueValidator(0), MaxValueValidator(10)],
)
def __str__(self) -> str:
if self.order_product and self.order_product.order and self.order_product.order.user:
return f"{self.rating} by {self.order_product.order.user.email}"
return f"{self.rating} | {self.uuid}"
class Meta:
verbose_name = _("feedback")
verbose_name_plural = _("feedbacks")
class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel): # type: ignore [misc]
__doc__ = _( # type: ignore
"Represents products associated with orders and their attributes. "
@ -1810,7 +1853,7 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel): # t
return DigitalAssetDownload.objects.create(order_product=self).url
return ""
def do_feedback(self, rating=10, comment="", action="add") -> Optional["Feedback"] | int:
def do_feedback(self, rating=10, comment="", action="add") -> Feedback | int | None:
if not self.order:
raise ValueError(_("order product must have an order"))
if action not in ["add", "remove"]:
@ -1912,46 +1955,3 @@ class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceMo
return (
f"https://api.{config.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}"
)
class Feedback(ExportModelOperationsMixin("feedback"), NiceModel): # type: ignore [misc]
__doc__ = _( # type: ignore
"Manages user feedback for products. "
"This class is designed to capture and store user feedback for specific products "
"that they have purchased. It contains attributes to store user comments, "
"a reference to the related product in the order, and a user-assigned rating. The "
"class uses database fields to effectively model and manage feedback data."
)
is_publicly_visible = True
comment = TextField(
blank=True,
null=True,
help_text=_("user-provided comments about their experience with the product"),
verbose_name=_("feedback comments"),
)
order_product = OneToOneField(
"core.OrderProduct",
on_delete=CASCADE,
blank=False,
null=False,
help_text=_("references the specific product in an order that this feedback is about"),
verbose_name=_("related order product"),
)
rating = FloatField(
blank=True,
null=True,
help_text=_("user-assigned rating for the product"),
verbose_name=_("product rating"),
validators=[MinValueValidator(0), MaxValueValidator(10)],
)
def __str__(self) -> str:
if self.order_product and self.order_product.order and self.order_product.order.user:
return f"{self.rating} by {self.order_product.order.user.email}"
return f"{self.rating} | {self.uuid}"
class Meta:
verbose_name = _("feedback")
verbose_name_plural = _("feedbacks")

View file

@ -91,7 +91,7 @@ class CategoryDetailSerializer(ModelSerializer):
else:
children = obj.children.filter(is_active=True)
return CategorySimpleSerializer(children, many=True, context=self.context).data if obj.children.exists() else []
return CategorySimpleSerializer(children, many=True, context=self.context).data if obj.children.exists() else [] # type: ignore [return-value]
class BrandDetailSerializer(ModelSerializer):

View file

@ -66,7 +66,7 @@ class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg]
else:
children = obj.children.filter(is_active=True)
return CategorySimpleSerializer(children, many=True, context=self.context).data if obj.children.exists() else []
return CategorySimpleSerializer(children, many=True, context=self.context).data if obj.children.exists() else [] # type: ignore [return-value]
class BrandSimpleSerializer(ModelSerializer): # type: ignore [type-arg]

View file

@ -20,7 +20,7 @@ from engine.core.utils import (
)
from engine.core.utils.emailing import send_order_created_email, send_order_finished_email, send_promocode_created_email
from evibes.utils.misc import create_object
from engine.authv.models import User
from engine.vibes_auth.models import User
logger = logging.getLogger(__name__)

View file

@ -5,7 +5,7 @@ import shutil
import uuid
from datetime import date, timedelta
from time import sleep
from typing import Any, Type
from typing import Any
import requests
from celery.app import shared_task
@ -40,10 +40,11 @@ def update_products_task() -> tuple[bool, str]:
if not update_products_task_running:
cache.set("update_products_task_running", True, 86400)
vendors: list[Type[AbstractVendor]] = get_vendors_integrations()
vendors: list[AbstractVendor] = get_vendors_integrations()
for vendor in vendors:
try:
# noinspection PyArgumentList
vendor.update_stock()
except VendorInactiveError:
logger.info("Skipping %s due to inactivity", str(vendor))
@ -70,9 +71,10 @@ def update_orderproducts_task() -> tuple[bool, str]:
message confirming the successful execution of the task.
:rtype: Tuple[bool, str]
"""
vendors: list[Type[AbstractVendor]] = get_vendors_integrations()
vendors: list[AbstractVendor] = get_vendors_integrations()
for vendor in vendors:
# noinspection PyArgumentList
vendor.update_order_products_statuses()
return True, "Success"

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy as _
from graphene import Context
from rest_framework.request import Request
from engine.authv.models import User
from engine.vibes_auth.models import User
logger = logging.getLogger(__name__)

View file

@ -1,5 +1,4 @@
import logging
from typing import Type
from engine.core.models import Vendor
from engine.core.vendors import AbstractVendor
@ -8,8 +7,8 @@ from evibes.utils.misc import create_object
logger = logging.getLogger(__name__)
def get_vendors_integrations(name: str | None = None) -> list[Type[AbstractVendor]]:
vendors_integrations: list[Type[AbstractVendor]] = []
def get_vendors_integrations(name: str | None = None) -> list[AbstractVendor]:
vendors_integrations: list[AbstractVendor] = []
vendors = Vendor.objects.filter(is_active=True, integration_path__isnull=False)
if name:
@ -17,7 +16,7 @@ def get_vendors_integrations(name: str | None = None) -> list[Type[AbstractVendo
for vendor in vendors:
try:
module_name, class_name = vendor.integration_path.rsplit(".", 1)
module_name, class_name = vendor.integration_path.rsplit(".", 1) # type: ignore [union-attr]
vendors_integrations.append(create_object(module_name, class_name))
except Exception as e:
logger.warning("Couldn't load integration for vendor %s: %s", vendor.name, e)

View file

@ -95,7 +95,7 @@ class AbstractVendor:
deletions on inactive objects.
"""
def __init__(self, vendor_name: str | None = None, currency: str = "USD") -> None:
def __init__(self, vendor_name: str = "", currency: str = "USD") -> None:
self.vendor_name = vendor_name
self.currency = currency
self.blocked_attributes: list[Any] = []

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 11:38
import uuid
import django_extensions.db.fields

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.5 on 2025-03-10 11:38
import django.contrib.postgres.indexes
import django.db.models.deletion
from django.conf import settings

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-29 11:32
import django.db.models.deletion
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.1.8 on 2025-04-30 13:29
from django.db import migrations, models

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2 on 2025-10-21 09:24
import django.db.models.deletion
import django_extensions.db.fields
import uuid

View file

@ -1,5 +1,3 @@
# Generated by Django 5.2.7 on 2025-10-24 23:17
from django.db import migrations, models

View file

@ -1,5 +1,4 @@
from datetime import datetime, time
from typing import Type
from constance import config
from django.conf import settings
@ -9,20 +8,20 @@ from django.db.models import (
CharField,
FloatField,
ForeignKey,
Index,
JSONField,
OneToOneField,
PositiveIntegerField,
QuerySet,
Sum,
Index,
)
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from engine.core.abstract import NiceModel
from evibes.utils.misc import create_object
from engine.payments.gateways import AbstractGateway
from engine.payments.managers import GatewayManager
from evibes.utils.misc import create_object
class Transaction(NiceModel):
@ -172,7 +171,7 @@ class Gateway(NiceModel):
def can_be_used(self, value: bool):
self.__dict__["can_be_used"] = value
def get_integration_class_object(self, raise_exc: bool = True) -> Type[AbstractGateway] | None:
def get_integration_class_object(self, raise_exc: bool = True) -> AbstractGateway | None:
if not self.integration_path:
if raise_exc:
raise ValueError(_("gateway integration path is not set"))

View file

@ -7,7 +7,7 @@ from django.dispatch import receiver
from engine.payments.models import Balance, Transaction, Gateway
from engine.payments.utils.emailing import balance_deposit_email
from engine.authv.models import User
from engine.vibes_auth.models import User
logger = logging.getLogger(__name__)
@ -27,7 +27,7 @@ def process_transaction_changes(instance: Transaction, created: bool, **kwargs:
instance.gateway = Gateway.objects.can_be_used().first()
try:
gateway = instance.gateway.get_integration_class_object()
gateway.process_transaction(instance)
gateway.process_transaction(instance) # type: ignore [union-attr]
except Exception as e:
instance.process = {"status": "ERRORED", "error": str(e)}
logger.error(f"Error processing transaction {instance.uuid}: {e}\n{traceback.format_exc()}")

View file

@ -28,8 +28,9 @@ from rest_framework_simplejwt.token_blacklist.models import (
from engine.core.admin import ActivationActionsMixin
from engine.core.models import Order
from engine.payments.models import Balance
from engine.authv.forms import UserForm
from engine.authv.models import BlacklistedToken, Group, OutstandingToken, User
from engine.vibes_auth.forms import UserForm
from engine.vibes_auth.messaging.models import ChatMessage, ChatThread, ThreadStatus
from engine.vibes_auth.models import BlacklistedToken, Group, OutstandingToken, User
class BalanceInline(admin.TabularInline): # type: ignore [type-arg]
@ -113,6 +114,52 @@ class UserAdmin(ActivationActionsMixin, BaseUserAdmin): # type: ignore [misc, t
super().save_model(request, obj, form, change)
# noinspection PyUnusedLocal
@admin.register(ChatThread)
class ChatThreadAdmin(admin.ModelAdmin):
list_display = (
"uuid",
"user",
"email",
"assigned_to",
"status",
"last_message_at",
"is_active",
"created",
"modified",
)
list_filter = (
"status",
"is_active",
("assigned_to", admin.EmptyFieldListFilter),
)
search_fields = ("uuid", "email", "user__email", "user__username")
autocomplete_fields = ("user", "assigned_to")
actions = (
"close_threads",
"open_threads",
"delete_selected",
)
readonly_fields = ("created", "modified")
@admin.action(description=_("Close selected threads"))
def close_threads(self, request, queryset): # type: ignore[no-untyped-def]
queryset.update(status=ThreadStatus.CLOSED)
@admin.action(description=_("Open selected threads"))
def open_threads(self, request, queryset): # type: ignore[no-untyped-def]
queryset.update(status=ThreadStatus.OPEN)
@admin.register(ChatMessage)
class ChatMessageAdmin(admin.ModelAdmin):
list_display = ("uuid", "thread", "sender_type", "sender_user", "sent_at")
list_filter = ("sender_type",)
search_fields = ("uuid", "thread__uuid", "sender_user__email")
autocomplete_fields = ("thread", "sender_user")
readonly_fields = ("created", "modified")
class GroupAdmin(BaseGroupAdmin):
pass

View file

@ -4,11 +4,11 @@ from django.utils.translation import gettext_lazy as _
class VibesAuthConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "engine.authv"
name = "engine.vibes_auth"
verbose_name = _("authentication")
icon = "fa fa-solid fa-user"
priority = 89
hide = False
def ready(self) -> None:
import engine.authv.signals # noqa: F401
import engine.vibes_auth.signals # noqa: F401

View file

@ -3,7 +3,7 @@ from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers, status
from engine.core.docs.drf import error
from engine.authv.serializers import (
from engine.vibes_auth.serializers import (
TokenObtainPairSerializer,
TokenRefreshSerializer,
TokenVerifySerializer,

View file

@ -3,7 +3,7 @@ from drf_spectacular.utils import extend_schema
from rest_framework import status
from engine.core.docs.drf import BASE_ERRORS
from engine.authv.serializers import (
from engine.vibes_auth.serializers import (
ActivateEmailSerializer,
ConfirmPasswordResetSerializer,
MergeRecentlyViewedSerializer,

View file

@ -1,6 +1,6 @@
import django_filters
from engine.authv.models import User
from engine.vibes_auth.models import User
class UserFilter(django_filters.FilterSet):

View file

@ -1,7 +1,7 @@
from django.forms import ModelForm
from engine.core.widgets import JSONTableWidget
from engine.authv.models import User
from engine.vibes_auth.models import User
class UserForm(ModelForm): # type: ignore [type-arg]

View file

@ -15,15 +15,15 @@ from graphene_file_upload.scalars import Upload
from engine.core.graphene import BaseMutation
from engine.core.utils.messages import permission_denied_message
from engine.authv.graphene.object_types import UserType
from engine.authv.models import User
from engine.authv.serializers import (
from engine.vibes_auth.graphene.object_types import UserType
from engine.vibes_auth.models import User
from engine.vibes_auth.serializers import (
TokenObtainPairSerializer,
TokenRefreshSerializer,
TokenVerifySerializer,
)
from engine.authv.utils.emailing import send_reset_password_email_task
from engine.authv.validators import is_valid_email, is_valid_phone_number
from engine.vibes_auth.utils.emailing import send_reset_password_email_task
from engine.vibes_auth.validators import is_valid_email, is_valid_phone_number
logger = logging.getLogger(__name__)
@ -109,7 +109,7 @@ class UpdateUser(BaseMutation):
name = "User"
raise Http404(_(f"{name} does not exist: {uuid}")) from dne
if not (info.context.user.has_perm("authv.change_user") or info.context.user == user):
if not (info.context.user.has_perm("vibes_auth.change_user") or info.context.user == user):
raise PermissionDenied(permission_denied_message)
email = kwargs.get("email")
@ -158,7 +158,7 @@ class UpdateUser(BaseMutation):
"is_staff",
"is_active",
"is_superuser",
] or info.context.user.has_perm("authv.change_user"):
] or info.context.user.has_perm("vibes_auth.change_user"):
setattr(user, attr, value)
user.save()
@ -174,7 +174,7 @@ class DeleteUser(BaseMutation):
success = Boolean()
def mutate(self, info, uuid=None, email=None):
if info.context.user.has_perm("authv.delete_user"):
if info.context.user.has_perm("vibes_auth.delete_user"):
try:
if uuid is not None:
User.objects.get(uuid=uuid).delete()

View file

@ -10,7 +10,7 @@ from engine.core.graphene.object_types import OrderType, ProductType, WishlistTy
from engine.core.models import Product, Wishlist
from engine.payments.graphene.object_types import BalanceType
from engine.payments.models import Balance
from engine.authv.models import User
from engine.vibes_auth.models import User
class GroupType(DjangoObjectType):

Some files were not shown because too many files have changed in this diff Show more