Compare commits

..

No commits in common. "114204b1052b040f399e75c2a24ffff77045234e" and "1f39535f05fd5068dcdbed390cc30442c8dbc2be" have entirely different histories.

6 changed files with 19 additions and 105 deletions

View file

@ -1,6 +1,5 @@
import logging
from contextlib import suppress
from decimal import Decimal
from typing import Any
from django.conf import settings
@ -19,9 +18,6 @@ from graphene import (
String,
relay,
)
from graphene import (
Decimal as GrapheneDecimal,
)
from graphene.types.generic import GenericScalar
from graphene_django import DjangoObjectType
from graphene_django.filter import (
@ -540,7 +536,7 @@ class ProductType(DjangoObjectType):
attribute_groups = DjangoFilterConnectionField(
AttributeGroupType, description=_("attribute groups")
)
price = GrapheneDecimal(description=_("price"))
price = Float(description=_("price"))
quantity = Float(description=_("quantity"))
feedbacks_count = Int(description=_("number of feedbacks"))
personal_orders_only = Boolean(description=_("only available for personal orders"))
@ -548,7 +544,7 @@ class ProductType(DjangoObjectType):
rating = Float(
description=_("rating value from 1 to 10, inclusive, or 0 if not set.")
)
discount_price = GrapheneDecimal(description=_("discount price"))
discount_price = Float(description=_("discount price"))
class Meta:
model = Product
@ -578,8 +574,8 @@ class ProductType(DjangoObjectType):
def resolve_description(self: Product, _info) -> str:
return render_markdown(self.description or "")
def resolve_price(self: Product, _info) -> Decimal:
return self.price or Decimal("0.0")
def resolve_price(self: Product, _info) -> float:
return self.price or 0.0
def resolve_rating(self: Product, _info) -> float:
return self.rating or 0.0
@ -659,7 +655,7 @@ class ProductType(DjangoObjectType):
def resolve_video(self: Product, info) -> str:
return info.context.build_absolute_uri(self.video.url) if self.video else ""
def resolve_discount_price(self: Product, _info) -> Decimal:
def resolve_discount_price(self: Product, _info) -> float | None:
return self.discount_price

View file

@ -770,13 +770,9 @@ class Product(NiceModel):
).order_by("-discount_percent")
@cached_property
def discount_price(self) -> Decimal:
def discount_price(self) -> Decimal | None:
promo = self.promos.first()
return (
Decimal((self.price / 100) * promo.discount_percent)
if promo
else Decimal("0.0")
)
return (self.price / 100) * promo.discount_percent if promo else None
@property
def rating(self) -> float:

View file

@ -4,10 +4,8 @@ import os
import traceback
from contextlib import suppress
from datetime import date, timedelta
from decimal import Decimal
from os import getenv
import orjson
import requests
from constance import config
from django.conf import settings
@ -137,7 +135,11 @@ class CustomGraphQLView(FileUploadGraphQLView):
def get_context(self, request):
return request
def json_encode(self, request, d, pretty=False):
@staticmethod
def json_encode(request, d, pretty=False):
from decimal import Decimal
import orjson
def _default(obj):
if isinstance(obj, Decimal):

View file

@ -1,4 +1,3 @@
import datetime
import logging
from django import forms
@ -8,11 +7,9 @@ from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import path, reverse
from django.utils.translation import gettext_lazy as _
from health_check.exceptions import HealthCheckException
from unfold.sites import UnfoldAdminSite
from engine.vibes_auth.utils.otp import generate_otp_code, send_admin_otp_email_task
from schon.health_checks import DynamicMail
logger = logging.getLogger(__name__)
@ -34,18 +31,6 @@ class OTPVerifyForm(forms.Form):
class SchonAdminSite(UnfoldAdminSite):
@staticmethod
def _email_backend_configured() -> bool:
"""Use DynamicMail health check to verify the SMTP backend is reachable."""
from asgiref.sync import async_to_sync
try:
check = DynamicMail(timeout=datetime.timedelta(seconds=5))
async_to_sync(check.run)()
except (HealthCheckException, Exception):
return False
return True
def login(self, request: HttpRequest, extra_context=None) -> HttpResponse:
if request.method == "POST":
email = request.POST.get("username", "")
@ -53,23 +38,13 @@ class SchonAdminSite(UnfoldAdminSite):
user = authenticate(request, username=email, password=password)
if user is not None and user.is_staff: # ty: ignore[unresolved-attribute]
if self._email_backend_configured():
code = generate_otp_code(user)
send_admin_otp_email_task.delay(user_pk=str(user.pk), code=code)
request.session["_2fa_user_id"] = str(user.pk)
messages.info(
request,
_("A verification code has been sent to your email."),
)
return HttpResponseRedirect(reverse("admin:verify-otp"))
logger.warning(
"Email backend is not configured — "
"skipping OTP, logging in user %s directly.",
user.pk,
code = generate_otp_code(user)
send_admin_otp_email_task.delay(user_pk=str(user.pk), code=code)
request.session["_2fa_user_id"] = str(user.pk)
messages.info(
request, _("A verification code has been sent to your email.")
)
login(request, user)
return HttpResponseRedirect(reverse("admin:index"))
return HttpResponseRedirect(reverse("admin:verify-otp"))
return super().login(request, extra_context)

View file

@ -1,55 +0,0 @@
"""Custom health checks that read runtime config from constance."""
import asyncio
import dataclasses
import datetime
import logging
import smtplib
from health_check.base import HealthCheck
from health_check.exceptions import ServiceUnavailable
logger = logging.getLogger(__name__)
@dataclasses.dataclass
class DynamicMail(HealthCheck):
"""
Check that the SMTP backend configured via constance can open a connection.
Unlike the built-in ``health_check.Mail``, this check reads host, port,
credentials, and TLS/SSL flags from ``constance.config`` so it always
reflects whatever an administrator has set at runtime.
Args:
timeout: Timeout for the SMTP connection attempt.
"""
timeout: datetime.timedelta = dataclasses.field(
default=datetime.timedelta(seconds=15), repr=False
)
def _check_connection(self) -> None:
"""Blocking SMTP probe — runs inside a thread."""
from engine.core.utils import get_dynamic_email_connection
connection = get_dynamic_email_connection()
connection.timeout = int(self.timeout.total_seconds())
logger.debug("Trying to open dynamic mail connection.")
try:
connection.open()
except smtplib.SMTPException as e:
raise ServiceUnavailable(
"Failed to open connection with SMTP server"
) from e
except ConnectionRefusedError as e:
raise ServiceUnavailable("Connection refused") from e
except OSError as e:
raise ServiceUnavailable("Could not connect to mail server") from e
finally:
connection.close()
logger.debug("Dynamic mail connection established successfully.")
async def run(self) -> None:
await asyncio.to_thread(self._check_connection)

View file

@ -29,7 +29,7 @@ urlpatterns = [
"health_check.Cache",
"health_check.DNS",
"health_check.Database",
"schon.health_checks.DynamicMail",
"health_check.Mail",
"health_check.Storage",
"health_check.contrib.psutil.Disk",
"health_check.contrib.psutil.Memory",