Features: 1) Added vendor, product tag, category tag models and metadata; 2) Introduced proper noinspection comments for Mypy warnings; 3) Extended Markdown linting rules.
Fixes: 1) Corrected `ForeignKey` type assertions across models; 2) Resolved typos and formatting inconsistencies in `.env` and README; 3) Fixed explicit boolean checks in user manager methods. Extra: Updated type hints in multiple models, serializers, and views.
This commit is contained in:
parent
328ccaa615
commit
a33be30098
30 changed files with 716 additions and 690 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -87,6 +87,7 @@ wheels/
|
|||
share/python-wheels/
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
desktop.ini
|
||||
|
||||
# Node build artifacts
|
||||
npm-debug.log*
|
||||
|
|
|
|||
|
|
@ -103,12 +103,12 @@ decomment previously commented lines and enjoy eVibes over HTTPS!
|
|||
|
||||
### .env
|
||||
|
||||
After .env file generation, you may want to edit some of its values, such as macroservices' API keys, database password,
|
||||
After .env file generation, you may want to edit some of its values, such as macroservices` API keys, database password,
|
||||
redis password, etc.
|
||||
|
||||
## Usage
|
||||
|
||||
- Add necessary subdomains to DNS-settings of your domain, those are:
|
||||
- Add the necessary subdomains to DNS-settings of your domain, those are:
|
||||
|
||||
1. @.your-domain.com
|
||||
2. www.your-domain.com
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ from .models import Post, PostTag
|
|||
|
||||
@admin.register(Post)
|
||||
class PostAdmin(admin.ModelAdmin):
|
||||
def preview_html(self, obj):
|
||||
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
|
||||
# noinspection DjangoSafeString
|
||||
return mark_safe(html)
|
||||
|
||||
preview_html.short_description = _("rendered HTML") # type: ignore
|
||||
|
||||
form = PostAdminForm
|
||||
list_display = ("title", "author", "slug", "created", "modified")
|
||||
list_filter = ("author", "tags", "created", "modified")
|
||||
|
|
@ -33,13 +40,6 @@ class PostAdmin(admin.ModelAdmin):
|
|||
),
|
||||
)
|
||||
|
||||
def preview_html(self, obj):
|
||||
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
|
||||
# noinspection DjangoSafeString
|
||||
return mark_safe(html)
|
||||
|
||||
preview_html.short_description = _("rendered HTML") # type: ignore
|
||||
|
||||
|
||||
@admin.register(PostTag)
|
||||
class PostTagAdmin(admin.ModelAdmin):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# noinspection PyUnresolvedReferences
|
||||
from constance.admin import Config
|
||||
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
|
||||
from django.apps import apps
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ class BulkOrderAction(BaseMutation):
|
|||
elif order_hr_id:
|
||||
order = Order.objects.get(user=user, human_readable_id=order_hr_id)
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
match action:
|
||||
case "add":
|
||||
order = order.bulk_add_products(products)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class Command(BaseCommand):
|
|||
for product in Product.objects.filter(stocks__isnull=False):
|
||||
for stock in product.stocks.all():
|
||||
try:
|
||||
stock.price = AbstractVendor.round_price_marketologically(stock.price)
|
||||
stock.price = AbstractVendor.round_price_marketologically(stock.price) # type: ignore
|
||||
stock.save()
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.WARNING(f"Couldn't fix price on {stock.uuid}"))
|
||||
|
|
|
|||
|
|
@ -40,13 +40,13 @@ class Command(BaseCommand):
|
|||
"-t",
|
||||
"--target",
|
||||
required=True,
|
||||
help=("Dotted path to the field to translate, e.g. core.models.Product.description"),
|
||||
help="Dotted path to the field to translate, e.g. core.models.Product.description",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--language",
|
||||
required=True,
|
||||
help=("Modeltranslation language code to translate into, e.g. de-de, fr-fr, zh-hans"),
|
||||
help="Modeltranslation language code to translate into, e.g. de-de, fr-fr, zh-hans",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
|
|
|||
1066
core/models.py
1066
core/models.py
File diff suppressed because it is too large
Load diff
|
|
@ -27,8 +27,8 @@ from core.serializers.utility import AddressSerializer
|
|||
|
||||
|
||||
class AttributeGroupSimpleSerializer(ModelSerializer):
|
||||
parent: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
children: PrimaryKeyRelatedField = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
parent: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True) # type: ignore
|
||||
children: PrimaryKeyRelatedField = PrimaryKeyRelatedField(many=True, read_only=True) # type: ignore
|
||||
|
||||
class Meta:
|
||||
model = AttributeGroup
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<!--suppress JSSuspiciousNameCombination -->
|
||||
<!--suppress JSSuspiciousNameCombination, JSUnresolvedReference -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ def process_promotions() -> tuple[bool, str]:
|
|||
|
||||
:return: A tuple where the first element is a boolean indicating success, and the second
|
||||
element is a message describing the operation's outcome.
|
||||
:rtype: tuple[bool, str]
|
||||
"""
|
||||
if not config.ABSTRACT_API_KEY or config.ABSTRACT_API_KEY == "example key":
|
||||
return False, "Abstract features disabled."
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ def send_order_created_email(order_pk: str) -> tuple[bool, str]:
|
|||
except Order.DoesNotExist:
|
||||
return False, f"Order not found with the given pk: {order_pk}"
|
||||
|
||||
activate(order.user.language)
|
||||
activate(order.user.language) # type: ignore
|
||||
|
||||
set_email_settings()
|
||||
connection = mail.get_connection()
|
||||
|
|
@ -64,7 +64,7 @@ def send_order_created_email(order_pk: str) -> tuple[bool, str]:
|
|||
"total_price": order.total_price,
|
||||
},
|
||||
),
|
||||
to=[order.user.email],
|
||||
to=[order.user.email], # type: ignore
|
||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||
connection=connection,
|
||||
)
|
||||
|
|
@ -80,7 +80,7 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
|
|||
if len(ops) <= 0:
|
||||
return
|
||||
|
||||
activate(order.user.language)
|
||||
activate(order.user.language) # type: ignore
|
||||
|
||||
set_email_settings()
|
||||
connection = mail.get_connection()
|
||||
|
|
@ -91,16 +91,16 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
|
|||
template_name="digital_order_delivered_email.html",
|
||||
context={
|
||||
"order_uuid": order.human_readable_id,
|
||||
"user_first_name": order.user.first_name,
|
||||
"user_first_name": order.user.first_name, # type: ignore
|
||||
"order_products": ops,
|
||||
"project_name": config.PROJECT_NAME,
|
||||
"contact_email": config.EMAIL_FROM,
|
||||
"total_price": round(sum(op.buy_price for op in ops), 2),
|
||||
"display_system_attributes": order.user.has_perm("core.view_order"),
|
||||
"display_system_attributes": order.user.has_perm("core.view_order"), # type: ignore
|
||||
"today": datetime.today(),
|
||||
},
|
||||
),
|
||||
to=[order.user.email],
|
||||
to=[order.user.email], # type: ignore
|
||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||
connection=connection,
|
||||
)
|
||||
|
|
@ -108,7 +108,7 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
|
|||
email.send()
|
||||
|
||||
def send_thank_you_email(ops: list[OrderProduct]):
|
||||
activate(order.user.language)
|
||||
activate(order.user.language) # type: ignore
|
||||
|
||||
set_email_settings()
|
||||
|
||||
|
|
|
|||
2
core/vendors/__init__.py
vendored
2
core/vendors/__init__.py
vendored
|
|
@ -197,7 +197,7 @@ class AbstractVendor:
|
|||
price = float(original_price)
|
||||
|
||||
if category and category.markup_percent:
|
||||
price *= 1 + category.markup_percent / 100.0
|
||||
price *= 1 + float(category.markup_percent) / 100.0 # type: ignore
|
||||
elif vendor and vendor.markup_percent:
|
||||
price *= 1 + vendor.markup_percent / 100.0
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class CustomLocaleMiddleware(LocaleMiddleware):
|
|||
request.LANGUAGE_CODE = normalized
|
||||
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
class GrapheneJWTAuthorizationMiddleware:
|
||||
def resolve(self, next, root, info, **args):
|
||||
context = info.context
|
||||
|
|
@ -92,6 +93,7 @@ class BlockInvalidHostMiddleware:
|
|||
return self.get_response(request)
|
||||
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
class GrapheneLoggingErrorsDebugMiddleware:
|
||||
def resolve(self, next, root, info, **args):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ class CustomPagination(PageNumberPagination):
|
|||
{
|
||||
"links": {"forward": self.get_next_link(), "backward": self.get_previous_link()},
|
||||
"counts": {
|
||||
"total_pages": None or self.page.paginator.num_pages,
|
||||
"page_size": None or self.page_size,
|
||||
"total_items": None or self.page.paginator.count,
|
||||
"total_pages": None or self.page.paginator.num_pages, # type: ignore
|
||||
"page_size": None or self.page_size, # type: ignore
|
||||
"total_items": None or self.page.paginator.count, # type: ignore
|
||||
},
|
||||
"data": data,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ CURRENCIES: tuple[tuple[str, str], ...] = (
|
|||
("zh-hans", "CNY"),
|
||||
)
|
||||
|
||||
CURRENCY_CODE: str = dict(CURRENCIES).get(LANGUAGE_CODE)
|
||||
CURRENCY_CODE: str | None = dict(CURRENCIES).get(LANGUAGE_CODE)
|
||||
|
||||
MODELTRANSLATION_FALLBACK_LANGUAGES: tuple = (LANGUAGE_CODE, "en-us", "de-de")
|
||||
|
||||
|
|
@ -274,7 +274,7 @@ INTERNAL_IPS: list[str] = [
|
|||
"127.0.0.1",
|
||||
]
|
||||
|
||||
DAISY_SETTINGS: dict[str, str | list[str] | dict[str, str] | None | bool | int | float | list[dict[str, str]]] = {
|
||||
DAISY_SETTINGS: dict = {
|
||||
"SITE_LOGO": "/static/favicon.ico",
|
||||
"EXTRA_STYLES": [],
|
||||
"EXTRA_SCRIPTS": [],
|
||||
|
|
@ -283,6 +283,10 @@ DAISY_SETTINGS: dict[str, str | list[str] | dict[str, str] | None | bool | int |
|
|||
"DONT_SUPPORT_ME": True,
|
||||
"SIDEBAR_FOOTNOTE": "eVibes by Wiseless",
|
||||
"APPS_REORDER": {
|
||||
"django_celery_results": {
|
||||
"hide": True,
|
||||
"app": "django_celery_results",
|
||||
},
|
||||
"django_celery_beat": {
|
||||
"icon": "fa fa-solid fa-timeline",
|
||||
"hide": False,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from evibes.settings.base import * # noqa: F403
|
||||
from evibes.settings.constance import CONSTANCE_CONFIG
|
||||
|
||||
REST_FRAMEWORK: dict[str, int | str | dict[str, str | bool]] = {
|
||||
REST_FRAMEWORK: dict = {
|
||||
"DEFAULT_PAGINATION_CLASS": "evibes.pagination.CustomPagination",
|
||||
"PAGE_SIZE": 30,
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||
|
|
@ -45,6 +45,7 @@ SIMPLE_JWT: dict[str, timedelta | str | bool] = {
|
|||
}
|
||||
|
||||
# type: ignore
|
||||
# noinspection Mypy
|
||||
SPECTACULAR_B2B_DESCRIPTION = _(f"""
|
||||
Welcome to the {CONSTANCE_CONFIG.get("PROJECT_NAME")[0]} B2B API documentation.
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ Current API version: {EVIBES_VERSION}
|
|||
""") # noqa: E501, F405
|
||||
|
||||
# type: ignore
|
||||
# noinspection Mypy
|
||||
SPECTACULAR_PLATFORM_DESCRIPTION = _(f"""
|
||||
Welcome to the {CONSTANCE_CONFIG.get("PROJECT_NAME")[0]} Platform API documentation.
|
||||
|
||||
|
|
@ -95,6 +97,7 @@ The {CONSTANCE_CONFIG.get("PROJECT_NAME")[0]} API is the central hub for managin
|
|||
Current API version: {EVIBES_VERSION}
|
||||
""") # noqa: E501, F405
|
||||
|
||||
# noinspection Mypy
|
||||
SPECTACULAR_PLATFORM_SETTINGS = {
|
||||
"TITLE": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} API",
|
||||
"DESCRIPTION": SPECTACULAR_PLATFORM_DESCRIPTION,
|
||||
|
|
@ -146,6 +149,7 @@ SPECTACULAR_PLATFORM_SETTINGS = {
|
|||
},
|
||||
}
|
||||
|
||||
# noinspection Mypy
|
||||
SPECTACULAR_B2B_SETTINGS = {
|
||||
"TITLE": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} API",
|
||||
"DESCRIPTION": SPECTACULAR_B2B_DESCRIPTION,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from evibes.settings import CONSTANCE_CONFIG
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||
EMAIL_HOST = CONSTANCE_CONFIG.get("EMAIL_HOST")[0]
|
||||
EMAIL_PORT = CONSTANCE_CONFIG.get("EMAIL_PORT")[0]
|
||||
EMAIL_USE_TLS = CONSTANCE_CONFIG.get("EMAIL_USE_TLS")[0]
|
||||
EMAIL_USE_SSL = CONSTANCE_CONFIG.get("EMAIL_USE_SSL")[0]
|
||||
EMAIL_HOST_USER = CONSTANCE_CONFIG.get("EMAIL_HOST_USER")[0]
|
||||
EMAIL_HOST_PASSWORD = CONSTANCE_CONFIG.get("EMAIL_HOST_PASSWORD")[0]
|
||||
EMAIL_HOST = CONSTANCE_CONFIG.get("EMAIL_HOST")[0] # type: ignore
|
||||
EMAIL_PORT = CONSTANCE_CONFIG.get("EMAIL_PORT")[0] # type: ignore
|
||||
EMAIL_USE_TLS = CONSTANCE_CONFIG.get("EMAIL_USE_TLS")[0] # type: ignore
|
||||
EMAIL_USE_SSL = CONSTANCE_CONFIG.get("EMAIL_USE_SSL")[0] # type: ignore
|
||||
EMAIL_HOST_USER = CONSTANCE_CONFIG.get("EMAIL_HOST_USER")[0] # type: ignore
|
||||
EMAIL_HOST_PASSWORD = CONSTANCE_CONFIG.get("EMAIL_HOST_PASSWORD")[0] # type: ignore
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LOGGING = {
|
|||
"formatters": {
|
||||
"color": {
|
||||
"()": "colorlog.ColoredFormatter",
|
||||
"format": ("%(asctime)s %(log_color)s[%(levelname)s]%(reset)s %(name)s: %(message)s"),
|
||||
"format": "%(asctime)s %(log_color)s[%(levelname)s]%(reset)s %(name)s: %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
"log_colors": {
|
||||
"DEBUG": "cyan",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# noinspection PyUnresolvedReferences
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ from django.db.models import CASCADE, CharField, FloatField, ForeignKey, JSONFie
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.abstract import NiceModel
|
||||
from core.models import Order
|
||||
from vibes_auth.models import User
|
||||
|
||||
|
||||
class Balance(NiceModel):
|
||||
amount: FloatField = FloatField(null=False, blank=False, default=0)
|
||||
user: OneToOneField = OneToOneField(
|
||||
amount: float = FloatField(null=False, blank=False, default=0)
|
||||
user: User = OneToOneField(
|
||||
to="vibes_auth.User", on_delete=CASCADE, blank=True, null=True, related_name="payments_balance"
|
||||
)
|
||||
|
||||
|
|
@ -26,11 +28,11 @@ class Balance(NiceModel):
|
|||
|
||||
|
||||
class Transaction(NiceModel):
|
||||
amount: FloatField = FloatField(null=False, blank=False)
|
||||
balance: ForeignKey = ForeignKey(Balance, on_delete=CASCADE, blank=True, null=True, related_name="transactions")
|
||||
currency: CharField = CharField(max_length=3, null=False, blank=False)
|
||||
payment_method: CharField = CharField(max_length=20, null=True, blank=True)
|
||||
order: ForeignKey = ForeignKey(
|
||||
amount: float = FloatField(null=False, blank=False)
|
||||
balance: Balance = ForeignKey(Balance, on_delete=CASCADE, blank=True, null=True, related_name="transactions")
|
||||
currency: str = CharField(max_length=3, null=False, blank=False)
|
||||
payment_method: str = CharField(max_length=20, null=True, blank=True)
|
||||
order: Order = ForeignKey(
|
||||
"core.Order",
|
||||
on_delete=CASCADE,
|
||||
blank=True,
|
||||
|
|
@ -38,7 +40,7 @@ class Transaction(NiceModel):
|
|||
help_text=_("order to process after paid"),
|
||||
related_name="payments_transactions",
|
||||
)
|
||||
process: JSONField = JSONField(verbose_name=_("processing details"), default=dict)
|
||||
process: dict = JSONField(verbose_name=_("processing details"), default=dict)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.balance.user.email} | {self.amount}"
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ class TransactionProcessSerializer(ModelSerializer):
|
|||
order_uuid = SerializerMethodField(read_only=True, required=False)
|
||||
|
||||
def get_order_hr_id(self, obj: Transaction):
|
||||
return obj.order.human_readable_id if obj.order else None
|
||||
return obj.order.human_readable_id if obj.order else None # type: ignore
|
||||
|
||||
def get_order_uuid(self, obj: Transaction):
|
||||
return obj.order.uuid if obj.order else None
|
||||
return obj.order.uuid if obj.order else None # type: ignore
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from payments.views import CallbackAPIView, DepositView
|
|||
###############################################################################
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class BalanceModelTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user_model = get_user_model()
|
||||
|
|
@ -34,9 +35,10 @@ class BalanceModelTests(TestCase):
|
|||
self.balance.save()
|
||||
self.balance.refresh_from_db()
|
||||
# round(10.129, 2) == 10.13
|
||||
self.assertAlmostEqual(self.balance.amount, 10.13, places=2)
|
||||
self.assertAlmostEqual(float(self.balance.amount), 10.13, places=2)
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class TransactionModelTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user_model = get_user_model()
|
||||
|
|
@ -66,6 +68,7 @@ class TransactionModelTests(TestCase):
|
|||
###############################################################################
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class DepositViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
|
|
@ -115,6 +118,7 @@ class CallbackViewTests(TestCase):
|
|||
###############################################################################
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class SignalTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user_model = get_user_model()
|
||||
|
|
@ -133,6 +137,7 @@ class SignalTests(TestCase):
|
|||
###############################################################################
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class GraphQLDepositTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user_model = get_user_model()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ def get_rates(provider: str):
|
|||
if not provider:
|
||||
raise ValueError(_("a provider to get rates from is required"))
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
match provider:
|
||||
case "cbr":
|
||||
return get_rates_cbr()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ def balance_email(user_pk: str) -> tuple[bool, str]:
|
|||
|
||||
email = EmailMessage(
|
||||
"eVibes | Successful Order",
|
||||
render_to_string("balance.html", {"user": user, "current_year": timezone.now().year, "config": config}),
|
||||
render_to_string("balance_deposit_email.html",
|
||||
{"user": user, "current_year": timezone.now().year, "config": config}),
|
||||
to=[user.email],
|
||||
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class CallbackAPIView(APIView):
|
|||
logger.debug(request.__dict__)
|
||||
try:
|
||||
gateway = kwargs.get("gateway", "")
|
||||
# noinspection PyUnreachableCode
|
||||
match gateway:
|
||||
case "gateway":
|
||||
# Gateway.process_callback(request.data)
|
||||
|
|
|
|||
173
pyproject.toml
173
pyproject.toml
|
|
@ -7,110 +7,111 @@ readme = "README.md"
|
|||
package-mode = false
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
aiosmtpd = "1.4.6"
|
||||
celery = { version = "5.5.2", optional = true }
|
||||
celery-prometheus-exporter = { version = "1.7.0", optional = true }
|
||||
colorlog = "6.9.0"
|
||||
cryptography = "44.0.3"
|
||||
django = "5.2"
|
||||
django-cacheops = "7.2"
|
||||
django-celery-beat = { version = "2.8.0", optional = true }
|
||||
django-celery-results = { version = "2.6.0", optional = true }
|
||||
django-constance = "4.3.2"
|
||||
django-cors-headers = "4.7.0"
|
||||
django-daisy = "1.0.23"
|
||||
django-dbbackup = "4.2.1"
|
||||
django-elasticsearch-dsl = "8.0"
|
||||
django-elasticsearch-dsl-drf = "0.22.5"
|
||||
django-extensions = "4.1"
|
||||
django-filter = "25.1"
|
||||
django-health-check = "3.19.1"
|
||||
django-hosts = "6.0"
|
||||
django-json-widget = "2.0.1"
|
||||
django-mailbox = "4.10.1"
|
||||
django-model-utils = "5.0.0"
|
||||
django-modeltranslation = "0.19.14"
|
||||
django-mptt = "0.17.0"
|
||||
django-prometheus = "^2.3.1"
|
||||
django-redis = "5.4.0"
|
||||
django-ratelimit = "4.1.0"
|
||||
django-storages = "1.14.6"
|
||||
django-stubs = "5.2.0"
|
||||
django-widget-tweaks = "1.5.0"
|
||||
django-md-field = "0.1.0"
|
||||
djangorestframework = "3.16.0"
|
||||
aiosmtpd = "1.4.6"
|
||||
celery = { version = "5.5.2", optional = true }
|
||||
celery-prometheus-exporter = { version = "1.7.0", optional = true }
|
||||
colorlog = "6.9.0"
|
||||
cryptography = "44.0.3"
|
||||
django = "5.2"
|
||||
django-cacheops = "7.2"
|
||||
django-celery-beat = { version = "2.8.0", optional = true }
|
||||
django-celery-results = { version = "2.6.0", optional = true }
|
||||
django-constance = "4.3.2"
|
||||
django-cors-headers = "4.7.0"
|
||||
django-daisy = "1.0.23"
|
||||
django-dbbackup = "4.2.1"
|
||||
django-elasticsearch-dsl = "8.0"
|
||||
django-elasticsearch-dsl-drf = "0.22.5"
|
||||
django-extensions = "4.1"
|
||||
django-filter = "25.1"
|
||||
django-health-check = "3.19.1"
|
||||
django-hosts = "6.0"
|
||||
django-json-widget = "2.0.1"
|
||||
django-mailbox = "4.10.1"
|
||||
django-model-utils = "5.0.0"
|
||||
django-modeltranslation = "0.19.14"
|
||||
django-mptt = "0.17.0"
|
||||
django-prometheus = "^2.3.1"
|
||||
django-redis = "5.4.0"
|
||||
django-ratelimit = "4.1.0"
|
||||
django-storages = "1.14.6"
|
||||
django-stubs = "5.2.0"
|
||||
django-widget-tweaks = "1.5.0"
|
||||
django-md-field = "0.1.0"
|
||||
djangorestframework = "3.16.0"
|
||||
djangorestframework-camel-case = "1.4.2"
|
||||
djangorestframework-recursive = "0.1.2"
|
||||
djangorestframework-simplejwt = { extras = ["crypto"], version = "5.5.0" }
|
||||
djangorestframework-stubs = "3.16.0"
|
||||
djangorestframework-xml = "2.0.0"
|
||||
djangorestframework-yaml = "2.0.0"
|
||||
drf-spectacular = { extras = ["sidecar"], version = "0.28.0" }
|
||||
elasticsearch-dsl = "8.18.0"
|
||||
filetype = "1.2.0"
|
||||
graphene-django = "3.2.3"
|
||||
graphene-file-upload = "1.3.0"
|
||||
gunicorn = "23.0.0"
|
||||
httpx = "0.28.1"
|
||||
jupyter = { version = "1.1.1", optional = true }
|
||||
mypy = "1.16.1"
|
||||
openai = { version = "1.77.0", optional = true }
|
||||
paramiko = "3.5.1"
|
||||
pillow = "11.2.1"
|
||||
polib = "1.2.0"
|
||||
poetry-core = "2.1.3"
|
||||
python = ">=3.12,<3.13"
|
||||
psutil = "6.1.1"
|
||||
psycopg2 = "2.9.10"
|
||||
pygraphviz = { version = "1.14", optional = true, markers = "sys_platform != 'win32'"}
|
||||
pymdown-extensions = "10.15"
|
||||
redis = "6.0.0"
|
||||
requests = "2.32.3"
|
||||
ruff = "0.11.8"
|
||||
sentry-sdk = { extras = ["django", "celery", "opentelemetry", "redis"], version = "2.27.0" }
|
||||
six = "1.17.0"
|
||||
swapper = "1.4.0"
|
||||
zeep = "4.3.1"
|
||||
djangorestframework-recursive = "0.1.2"
|
||||
djangorestframework-simplejwt = { extras = ["crypto"], version = "5.5.0" }
|
||||
djangorestframework-stubs = "3.16.0"
|
||||
djangorestframework-xml = "2.0.0"
|
||||
djangorestframework-yaml = "2.0.0"
|
||||
drf-spectacular = { extras = ["sidecar"], version = "0.28.0" }
|
||||
elasticsearch-dsl = "8.18.0"
|
||||
filetype = "1.2.0"
|
||||
graphene-django = "3.2.3"
|
||||
graphene-file-upload = "1.3.0"
|
||||
gunicorn = "23.0.0"
|
||||
httpx = "0.28.1"
|
||||
jupyter = { version = "1.1.1", optional = true }
|
||||
mypy = "1.16.1"
|
||||
openai = { version = "1.77.0", optional = true }
|
||||
paramiko = "3.5.1"
|
||||
pillow = "11.2.1"
|
||||
polib = "1.2.0"
|
||||
poetry-core = "2.1.3"
|
||||
python = ">=3.12,<3.13"
|
||||
psutil = "6.1.1"
|
||||
psycopg2 = "2.9.10"
|
||||
pygraphviz = { version = "1.14", optional = true, markers = "sys_platform != 'win32'" }
|
||||
pymdown-extensions = "10.15"
|
||||
redis = "6.0.0"
|
||||
requests = "2.32.3"
|
||||
ruff = "0.11.8"
|
||||
sentry-sdk = { extras = ["django", "celery", "opentelemetry", "redis"], version = "2.27.0" }
|
||||
six = "1.17.0"
|
||||
swapper = "1.4.0"
|
||||
zeep = "4.3.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "25.1.0"
|
||||
celery-stubs = "0.1.3"
|
||||
isort = "5.13.2"
|
||||
flake8 = "7.2.0"
|
||||
mypy-extensions = "1.1.0"
|
||||
pytest = "8.4.1"
|
||||
pytest-django = "4.11.1"
|
||||
coverage = "7.8.2"
|
||||
pre-commit = "4.2.0"
|
||||
safety = "3.5.2"
|
||||
bandit = "1.2.0"
|
||||
black = "25.1.0"
|
||||
celery-stubs = "0.1.3"
|
||||
isort = "5.13.2"
|
||||
flake8 = "7.2.0"
|
||||
mypy-extensions = "1.1.0"
|
||||
pytest = "8.4.1"
|
||||
pytest-django = "4.11.1"
|
||||
coverage = "7.8.2"
|
||||
pre-commit = "4.2.0"
|
||||
safety = "3.5.2"
|
||||
bandit = "1.2.0"
|
||||
|
||||
[tool.poetry.extras]
|
||||
graph = ["pygraphviz"]
|
||||
worker = ["celery", "django-celery-beat", "django-celery-results", "celery-prometheus-exporter"]
|
||||
openai = ["openai"]
|
||||
graph = ["pygraphviz"]
|
||||
worker = ["celery", "django-celery-beat", "django-celery-results", "celery-prometheus-exporter"]
|
||||
openai = ["openai"]
|
||||
jupyter = ["jupyter"]
|
||||
docs = ["sphinx", "sphinx-rtd-theme", "m2r2"]
|
||||
docs = ["sphinx", "sphinx-rtd-theme", "m2r2"]
|
||||
testing = ["pytest", "pytest-django", "coverage"]
|
||||
linting = ["black", "isort", "flake8", "bandit"]
|
||||
|
||||
[tool.mypy]
|
||||
disable_error_code = ["import-untyped", "misc"]
|
||||
exclude = ["*/migrations/*"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
target-version = "py38"
|
||||
exclude = ["migrations", "media", "static", "storefront"]
|
||||
line-length = 120
|
||||
target-version = "py38"
|
||||
exclude = ["migrations", "media", "static", "storefront"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "W", "F", "B", "I", "RUF", "UP", "N", "A", "COM", "C4", "DJ001", "RSE", "SIM", "ISC", "TID252", "PGH004"]
|
||||
ignore = ["B904", "RUF001", "RUF002", "RUF003", "RUF005", "RUF012", "A003", "A002", "COM812", "S603"]
|
||||
select = ["E", "W", "F", "B", "I", "RUF", "UP", "N", "A", "COM", "C4", "DJ001", "RSE", "SIM", "ISC", "TID252", "PGH004"]
|
||||
ignore = ["B904", "RUF001", "RUF002", "RUF003", "RUF005", "RUF012", "A003", "A002", "COM812", "S603"]
|
||||
per-file-ignores = { "__init__.py" = ["E402", "F401"] }
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
|
|
@ -41,6 +41,7 @@ class UserManager(BaseUserManager):
|
|||
|
||||
def _create_user(self, email, password, **extra_fields):
|
||||
email = self.normalize_email(email)
|
||||
# noinspection PyShadowingNames
|
||||
user = self.model(email=email, **extra_fields)
|
||||
user.password = make_password(password)
|
||||
user.save(using=self._db)
|
||||
|
|
@ -55,10 +56,11 @@ class UserManager(BaseUserManager):
|
|||
def create_superuser(self, email=None, password=None, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", True)
|
||||
extra_fields.setdefault("is_superuser", True)
|
||||
if extra_fields.get("is_staff") is not True:
|
||||
if not extra_fields.get("is_staff"):
|
||||
raise ValueError("Superuser must have is_staff=True.")
|
||||
if extra_fields.get("is_superuser") is not True:
|
||||
if not extra_fields.get("is_superuser"):
|
||||
raise ValueError("Superuser must have is_superuser=True.")
|
||||
# noinspection PyShadowingNames
|
||||
user = self._create_user(email, password, **extra_fields)
|
||||
user.is_active = True
|
||||
user.is_verified = True
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import uuid
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
|
@ -29,8 +30,8 @@ class User(AbstractUser, NiceModel):
|
|||
def get_uuid_as_path(self, *args):
|
||||
return str(self.uuid) + "/" + args[0]
|
||||
|
||||
email: EmailField = EmailField(_("email"), unique=True, help_text=_("user email address"))
|
||||
phone_number: CharField = CharField(
|
||||
email: str = EmailField(_("email"), unique=True, help_text=_("user email address"))
|
||||
phone_number: str = CharField(
|
||||
_("phone_number"),
|
||||
max_length=20,
|
||||
unique=True,
|
||||
|
|
@ -42,9 +43,9 @@ class User(AbstractUser, NiceModel):
|
|||
],
|
||||
)
|
||||
username = None
|
||||
first_name: CharField = CharField(_("first_name"), max_length=150, blank=True, null=True)
|
||||
last_name: CharField = CharField(_("last_name"), max_length=150, blank=True, null=True)
|
||||
avatar: ImageField = ImageField(
|
||||
first_name: str = CharField(_("first_name"), max_length=150, blank=True, null=True)
|
||||
last_name: str = CharField(_("last_name"), max_length=150, blank=True, null=True)
|
||||
avatar = ImageField(
|
||||
null=True,
|
||||
verbose_name=_("avatar"),
|
||||
upload_to=get_uuid_as_path,
|
||||
|
|
@ -52,27 +53,28 @@ class User(AbstractUser, NiceModel):
|
|||
help_text=_("user profile image"),
|
||||
)
|
||||
|
||||
is_verified: BooleanField = BooleanField(
|
||||
is_verified: bool = BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("is verified"),
|
||||
help_text=_("user verification status"),
|
||||
)
|
||||
is_active: BooleanField = BooleanField(
|
||||
is_active: bool = BooleanField(
|
||||
_("is_active"),
|
||||
default=False,
|
||||
help_text=_("unselect this instead of deleting accounts"),
|
||||
)
|
||||
is_subscribed: BooleanField = BooleanField(
|
||||
is_subscribed: bool = BooleanField(
|
||||
verbose_name=_("is_subscribed"), help_text=_("user's newsletter subscription status"), default=False
|
||||
)
|
||||
|
||||
activation_token: UUIDField = UUIDField(default=uuid4, verbose_name=_("activation token"))
|
||||
language: CharField = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7)
|
||||
attributes: JSONField = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True)
|
||||
activation_token: uuid = UUIDField(default=uuid4, verbose_name=_("activation token"))
|
||||
language: str = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7)
|
||||
attributes: dict = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True)
|
||||
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS = []
|
||||
objects = UserManager() # type: ignore
|
||||
# noinspection PyClassVar
|
||||
objects = UserManager()
|
||||
|
||||
def add_to_recently_viewed(self, product_uuid):
|
||||
recently_viewed = self.recently_viewed
|
||||
|
|
|
|||
|
|
@ -122,12 +122,12 @@ class TokenObtainSerializer(Serializer):
|
|||
with suppress(KeyError):
|
||||
authenticate_kwargs["request"] = self.context["request"]
|
||||
|
||||
self.user = authenticate(**authenticate_kwargs)
|
||||
self.user = authenticate(**authenticate_kwargs) # type: ignore
|
||||
|
||||
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
|
||||
raise AuthenticationFailed(
|
||||
self.error_messages["no_active_account"],
|
||||
_("no active account"),
|
||||
_("no active account"), # type: ignore
|
||||
)
|
||||
|
||||
return {}
|
||||
|
|
@ -145,15 +145,15 @@ class TokenObtainPairSerializer(TokenObtainSerializer):
|
|||
|
||||
logger.debug("Data validated")
|
||||
|
||||
refresh = self.get_token(self.user)
|
||||
refresh = self.get_token(self.user) # type: ignore
|
||||
data["refresh"] = str(refresh)
|
||||
data["access"] = str(refresh.access_token)
|
||||
data["access"] = str(refresh.access_token) # type: ignore
|
||||
data["user"] = UserSerializer(self.user).data
|
||||
|
||||
logger.debug("Data formed")
|
||||
|
||||
if api_settings.UPDATE_LAST_LOGIN:
|
||||
update_last_login(self.user, self.user)
|
||||
update_last_login(self.user, self.user) # type: ignore
|
||||
logger.debug("Updated last login")
|
||||
|
||||
logger.debug("Returning data")
|
||||
|
|
@ -181,7 +181,7 @@ class TokenRefreshSerializer(Serializer):
|
|||
|
||||
data["refresh"] = str(refresh)
|
||||
user = User.objects.get(uuid=refresh.payload["user_uuid"])
|
||||
data["user"] = UserSerializer(user).data
|
||||
data["user"] = UserSerializer(user).data # type: ignore
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ class TokenVerifySerializer(Serializer):
|
|||
except User.DoesNotExist:
|
||||
raise ValidationError(_("user does not exist"))
|
||||
|
||||
attrs["user"] = UserSerializer(user).data
|
||||
attrs["user"] = UserSerializer(user).data # type: ignore
|
||||
return attrs
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue