Features: 1) RUFFVCKED

This commit is contained in:
Egor Pavlovich Gorbunov 2025-07-03 02:53:01 +03:00
parent d17839abed
commit aa8f15d0ee
87 changed files with 6187 additions and 3160 deletions

View file

@ -5,6 +5,7 @@
<option name="myDocStringFormat" value="Google" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/blog/templates" />

View file

@ -18,56 +18,113 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='PostTag',
name="PostTag",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('tag_name', models.CharField(help_text='internal tag identifier for the post tag', max_length=255,
verbose_name='tag name')),
('name', models.CharField(help_text='user-friendly name for the post tag', max_length=255, unique=True,
verbose_name='tag display name')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
(
"tag_name",
models.CharField(
help_text="internal tag identifier for the post tag", max_length=255, verbose_name="tag name"
),
),
(
"name",
models.CharField(
help_text="user-friendly name for the post tag",
max_length=255,
unique=True,
verbose_name="tag display name",
),
),
],
options={
'verbose_name': 'post tag',
'verbose_name_plural': 'post tags',
"verbose_name": "post tag",
"verbose_name_plural": "post tags",
},
),
migrations.CreateModel(
name='Post',
name="Post",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('title', models.CharField()),
('content', markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name='content')),
('file', models.FileField(blank=True, null=True, upload_to='posts/')),
('slug', models.SlugField(allow_unicode=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='posts',
to=settings.AUTH_USER_MODEL)),
('tags', models.ManyToManyField(to='blog.posttag')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("title", models.CharField()),
("content", markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content")),
("file", models.FileField(blank=True, null=True, upload_to="posts/")),
("slug", models.SlugField(allow_unicode=True)),
(
"author",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="posts", to=settings.AUTH_USER_MODEL
),
),
("tags", models.ManyToManyField(to="blog.posttag")),
],
options={
'verbose_name': 'post',
'verbose_name_plural': 'posts',
"verbose_name": "post",
"verbose_name_plural": "posts",
},
),
]

View file

@ -5,20 +5,21 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
("blog", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name='post',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, blank=True, editable=False, populate_from='title', unique=True),
model_name="post",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True, blank=True, editable=False, populate_from="title", unique=True
),
),
migrations.AlterField(
model_name='post',
name='title',
field=models.CharField(help_text='post title', max_length=128, unique=True, verbose_name='title'),
model_name="post",
name="title",
field=models.CharField(help_text="post title", max_length=128, unique=True, verbose_name="title"),
),
]

View file

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_alter_post_slug_alter_post_title'),
("blog", "0002_alter_post_slug_alter_post_title"),
]
operations = [
migrations.AlterField(
model_name='post',
name='tags',
field=models.ManyToManyField(blank=True, related_name='posts', to='blog.posttag'),
model_name="post",
name="tags",
field=models.ManyToManyField(blank=True, related_name="posts", to="blog.posttag"),
),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("blog", "0003_alter_post_tags"),
]
@ -14,128 +13,92 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name="post",
name="content_ar_ar",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_cs_cz",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_da_dk",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_de_de",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_en_gb",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_en_us",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_es_es",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_fr_fr",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_hi_in",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_it_it",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_ja_jp",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_kk_kz",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_nl_nl",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_pl_pl",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_pt_br",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_ro_ro",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_ru_ru",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",
name="content_zh_hans",
field=markdown_field.fields.MarkdownField(
blank=True, null=True, verbose_name="content"
),
field=markdown_field.fields.MarkdownField(blank=True, null=True, verbose_name="content"),
),
migrations.AddField(
model_name="post",

View file

@ -31,10 +31,8 @@ class Post(NiceModel):
is_publicly_visible = True
author: ForeignKey = ForeignKey(
to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts"
)
title: CharField = CharField(
author = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts")
title = CharField(
unique=True, max_length=128, blank=False, null=False, help_text=_("post title"), verbose_name=_("title")
)
content: MarkdownField = MarkdownField(
@ -74,9 +72,9 @@ class Post(NiceModel):
blank=True,
null=True,
)
file: FileField = FileField(upload_to="posts/", blank=True, null=True)
slug: AutoSlugField = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
tags: ManyToManyField = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
file = FileField(upload_to="posts/", blank=True, null=True)
slug = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
tags = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
def __str__(self):
return f"{self.title} | {self.author.first_name} {self.author.last_name}"
@ -115,15 +113,16 @@ class PostTag(NiceModel):
"""
is_publicly_visible = True
posts: "Post"
tag_name: CharField = CharField(
tag_name = CharField(
blank=False,
null=False,
max_length=255,
help_text=_("internal tag identifier for the post tag"),
verbose_name=_("tag name"),
)
name: CharField = CharField(
name = CharField(
max_length=255,
help_text=_("user-friendly name for the post tag"),
verbose_name=_("tag display name"),

View file

@ -1,5 +1,4 @@
import uuid
from datetime import datetime
from django.db.models import BooleanField, Model, UUIDField
from django.utils.translation import gettext_lazy as _
@ -7,25 +6,21 @@ from django_extensions.db.fields import CreationDateTimeField, ModificationDateT
class NiceModel(Model):
id = None
uuid: uuid = UUIDField( # type: ignore
id: None = None
uuid = UUIDField(
verbose_name=_("unique id"),
help_text=_("unique id is used to surely identify any database object"),
primary_key=True,
default=uuid.uuid4,
editable=False,
)
is_active: bool = BooleanField( # type: ignore
is_active = BooleanField(
default=True,
verbose_name=_("is active"),
help_text=_("if set to false, this object can't be seen by users without needed permission"),
)
created: datetime = CreationDateTimeField( # type: ignore
_("created"), help_text=_("when the object first appeared on the database")
)
modified: datetime = ModificationDateTimeField( # type: ignore
_("modified"), help_text=_("when the object was last modified")
)
created = CreationDateTimeField(_("created"), help_text=_("when the object first appeared on the database"))
modified = ModificationDateTimeField(_("modified"), help_text=_("when the object was last modified"))
def save(self, **kwargs):
self.update_modified = kwargs.pop("update_modified", getattr(self, "update_modified", True))

View file

@ -1,4 +1,5 @@
from contextlib import suppress
from typing import ClassVar, Type
from constance.admin import Config
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
@ -38,7 +39,7 @@ from evibes.settings import CONSTANCE_CONFIG
class FieldsetsMixin:
general_fields: list = []
relation_fields: list = []
model: Model
model: ClassVar[Type[Model]]
def get_fieldsets(self, request, obj=None):
if request:
@ -172,7 +173,7 @@ class CategoryChildrenInline(TabularInline):
@register(AttributeGroup)
class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeGroup # type: ignore
model = AttributeGroup
list_display = ("name", "modified")
search_fields = ("uuid", "name")
readonly_fields = ("uuid", "modified", "created")
@ -183,7 +184,7 @@ class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Attribute)
class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Attribute # type: ignore
model = Attribute
list_display = ("name", "group", "value_type", "modified")
list_filter = ("value_type", "group", "is_active")
search_fields = ("uuid", "name", "group__name")
@ -196,7 +197,7 @@ class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(AttributeValue)
class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeValue # type: ignore
model = AttributeValue
list_display = ("attribute", "value", "modified")
list_filter = ("attribute__group", "is_active")
search_fields = ("uuid", "value", "attribute__name")
@ -209,7 +210,7 @@ class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Category)
class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin):
model = Category # type: ignore
model = Category
list_display = ("indented_title", "parent", "is_active", "modified")
# noinspection PyUnresolvedReferences
list_filter = ("is_active", "level", "created", "modified")
@ -224,7 +225,7 @@ class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin):
@register(Brand)
class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Brand # type: ignore
model = Brand
list_display = ("name",)
list_filter = ("categories", "is_active")
search_fields = ("uuid", "name", "categories__name")
@ -236,7 +237,7 @@ class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Product)
class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Product # type: ignore
model = Product
list_display = (
"name",
"partnumber",
@ -275,7 +276,7 @@ class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(ProductTag)
class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductTag # type: ignore
model = ProductTag
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
@ -286,7 +287,7 @@ class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(CategoryTag)
class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = CategoryTag # type: ignore
model = CategoryTag
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
@ -297,7 +298,7 @@ class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Vendor)
class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Vendor # type: ignore
model = Vendor
list_display = ("name", "markup_percent", "modified")
list_filter = ("markup_percent", "is_active")
search_fields = ("name",)
@ -310,7 +311,7 @@ class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Feedback)
class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Feedback # type: ignore
model = Feedback
list_display = ("order_product", "rating", "comment", "modified")
list_filter = ("rating", "is_active")
search_fields = ("order_product__product__name", "comment")
@ -322,7 +323,7 @@ class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Order)
class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Order # type: ignore
model = Order
list_display = (
"human_readable_id",
"user",
@ -350,7 +351,7 @@ class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(OrderProduct)
class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = OrderProduct # type: ignore
model = OrderProduct
list_display = ("order", "product", "quantity", "buy_price", "status", "modified")
list_filter = ("status",)
search_fields = ("order__user__email", "product__name")
@ -363,7 +364,7 @@ class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(PromoCode)
class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = PromoCode # type: ignore
model = PromoCode
list_display = (
"code",
"discount_percent",
@ -391,7 +392,7 @@ class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Promotion)
class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Promotion # type: ignore
model = Promotion
list_display = ("name", "discount_percent", "modified")
search_fields = ("name",)
readonly_fields = ("uuid", "modified", "created")
@ -403,7 +404,7 @@ class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Stock)
class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Stock # type: ignore
model = Stock
list_display = ("product", "vendor", "sku", "quantity", "price", "modified")
list_filter = ("vendor", "quantity")
search_fields = ("product__name", "vendor__name", "sku")
@ -423,7 +424,7 @@ class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Wishlist)
class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Wishlist # type: ignore
model = Wishlist
list_display = ("user", "modified")
search_fields = ("user__email",)
readonly_fields = ("uuid", "modified", "created")
@ -434,7 +435,7 @@ class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(ProductImage)
class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductImage # type: ignore
model = ProductImage
list_display = ("alt", "product", "priority", "modified")
list_filter = ("priority",)
search_fields = ("alt", "product__name")
@ -447,7 +448,7 @@ class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Address)
class AddressAdmin(FieldsetsMixin, GISModelAdmin):
model = Address # type: ignore
model = Address
list_display = ("street", "city", "region", "country", "user")
list_filter = ("country", "region")
search_fields = ("street", "city", "postal_code", "user__email")
@ -507,8 +508,10 @@ class ConstanceConfig:
_meta = Meta()
site.unregister([Config]) # type: ignore
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore
# noinspection PyTypeChecker
site.unregister([Config])
# noinspection PyTypeChecker
site.register([ConstanceConfig], BaseConstanceAdmin)
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0]
site.site_header = "eVibes"
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0]

View file

@ -185,8 +185,8 @@ def process_query(query: str = "", request: Request | None = None) -> dict[str,
results[idx].append(hit_result)
return results
except NotFoundError:
raise Http404
except NotFoundError as nfe:
raise Http404 from nfe
LANGUAGE_ANALYZER_MAP = {

View file

@ -96,8 +96,8 @@ class AddOrderProduct(BaseMutation):
order = order.add_product(product_uuid=product_uuid, attributes=format_attributes(attributes))
return AddOrderProduct(order=order)
except Order.DoesNotExist:
raise Http404(_(f"order {order_uuid} not found"))
except Order.DoesNotExist as dne:
raise Http404(_(f"order {order_uuid} not found")) from dne
class RemoveOrderProduct(BaseMutation):
@ -122,8 +122,8 @@ class RemoveOrderProduct(BaseMutation):
order = order.remove_product(product_uuid=product_uuid, attributes=format_attributes(attributes))
return AddOrderProduct(order=order)
except Order.DoesNotExist:
raise Http404(_(f"order {order_uuid} not found"))
except Order.DoesNotExist as dne:
raise Http404(_(f"order {order_uuid} not found")) from dne
class RemoveAllOrderProducts(BaseMutation):
@ -224,8 +224,8 @@ class BuyOrder(BaseMutation):
case _:
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
except Order.DoesNotExist:
raise Http404(_(f"order {order_uuid} not found"))
except Order.DoesNotExist as dne:
raise Http404(_(f"order {order_uuid} not found")) from dne
class BulkOrderAction(BaseMutation):
@ -271,8 +271,8 @@ class BulkOrderAction(BaseMutation):
return BulkOrderAction(order=order)
except Order.DoesNotExist:
raise Http404(_(f"order {order_uuid} not found"))
except Order.DoesNotExist as dne:
raise Http404(_(f"order {order_uuid} not found")) from dne
class BuyUnregisteredOrder(BaseMutation):
@ -344,8 +344,8 @@ class AddWishlistProduct(BaseMutation):
return AddWishlistProduct(wishlist=wishlist)
except Wishlist.DoesNotExist:
raise Http404(_(f"wishlist {wishlist_uuid} not found"))
except Wishlist.DoesNotExist as dne:
raise Http404(_(f"wishlist {wishlist_uuid} not found")) from dne
class RemoveWishlistProduct(BaseMutation):
@ -371,8 +371,8 @@ class RemoveWishlistProduct(BaseMutation):
return RemoveWishlistProduct(wishlist=wishlist)
except Wishlist.DoesNotExist:
raise Http404(_(f"wishlist {wishlist_uuid} not found"))
except Wishlist.DoesNotExist as dne:
raise Http404(_(f"wishlist {wishlist_uuid} not found")) from dne
class RemoveAllWishlistProducts(BaseMutation):
@ -398,8 +398,8 @@ class RemoveAllWishlistProducts(BaseMutation):
return RemoveAllWishlistProducts(wishlist=wishlist)
except Wishlist.DoesNotExist:
raise Http404(_(f"wishlist {wishlist_uuid} not found"))
except Wishlist.DoesNotExist as dne:
raise Http404(_(f"wishlist {wishlist_uuid} not found")) from dne
class BuyWishlist(BaseMutation):
@ -441,8 +441,8 @@ class BuyWishlist(BaseMutation):
case _:
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
except Wishlist.DoesNotExist:
raise Http404(_(f"wishlist {wishlist_uuid} not found"))
except Wishlist.DoesNotExist as dne:
raise Http404(_(f"wishlist {wishlist_uuid} not found")) from dne
class BuyProduct(BaseMutation):
@ -575,9 +575,9 @@ class DeleteAddress(BaseMutation):
raise PermissionDenied(permission_denied_message)
except Address.DoesNotExist:
except Address.DoesNotExist as dne:
name = "Address"
raise Http404(_(f"{name} does not exist: {uuid}"))
raise Http404(_(f"{name} does not exist: {uuid}")) from dne
class AutocompleteAddress(BaseMutation):

View file

@ -443,7 +443,7 @@ class PromoCodeType(DjangoObjectType):
description = _("promocodes")
def resolve_discount(self: PromoCode, _info) -> float:
return float(self.discount_percent) if self.discount_percent else float(self.discount_amount)
return float(self.discount_percent) if self.discount_percent else float(self.discount_amount) # type: ignore [arg-type]
def resolve_discount_type(self: PromoCode, _info) -> str:
return "percent" if self.discount_percent else "amount"

View file

@ -39,7 +39,7 @@ def load_po_sanitized(path: str) -> polib.POFile:
with open(path, encoding="utf-8") as f:
text = f.read()
except OSError as e:
raise CommandError(f"{path}: cannot read file ({e})")
raise CommandError(f"{path}: cannot read file ({e})") from e
# fix fuzzy flags and empty header entries
text = re.sub(r"^#,(?!\s)", "#, ", text, flags=re.MULTILINE)
parts = text.split("\n\n", 1)
@ -54,7 +54,7 @@ def load_po_sanitized(path: str) -> polib.POFile:
tmp.close()
return polib.pofile(tmp.name)
except Exception as e:
raise CommandError(f"{path}: syntax error after sanitization ({e})")
raise CommandError(f"{path}: syntax error after sanitization ({e})") from e
finally:
with contextlib.suppress(OSError):
os.unlink(tmp.name)
@ -97,7 +97,8 @@ class Command(BaseCommand):
root_path: str = options.get("root_path") or "/app/"
configs = list(apps.get_app_configs())
configs.append(RootDirectory())
# noinspection PyTypeChecker
configs.append(RootDirectory()) # type: ignore [arg-type]
for app_conf in configs:
if app_conf.label not in apps_to_scan:

View file

@ -138,7 +138,8 @@ class Command(BaseCommand):
self.stdout.write(self.style.MIGRATE_HEADING(f"→ Translating into {target_lang}"))
configs = list(apps.get_app_configs())
configs.append(RootDirectory())
# noinspection PyTypeChecker
configs.append(RootDirectory()) # type: ignore [arg-type]
for app_conf in configs:
if app_conf.label not in target_apps:
@ -212,8 +213,8 @@ class Command(BaseCommand):
protected = []
maps: list[list[str]] = []
for e in to_trans:
txt = source_map[e.msgid]
for entry in to_trans:
txt = source_map[entry.msgid]
p_txt, p_map = placeholderize(txt)
protected.append(p_txt)
maps.append(p_map)
@ -227,14 +228,14 @@ class Command(BaseCommand):
resp.raise_for_status()
result = resp.json()
except Exception as exc:
raise CommandError(f"DeepL error: {exc} {resp.text}")
raise CommandError(f"DeepL error: {exc} {resp.text}") from exc
trans = result.get("translations", [])
if len(trans) != len(to_trans):
raise CommandError(f"Got {len(trans)} translations, expected {len(to_trans)}")
for e, obj, pmap in zip(to_trans, trans, maps, strict=True):
e.msgstr = deplaceholderize(obj["text"], pmap)
for entry, obj, pmap in zip(to_trans, trans, maps, strict=True):
entry.msgstr = deplaceholderize(obj["text"], pmap)
new_po.save(tgt_path)
self.stdout.write(self.style.SUCCESS(f"Saved {tgt_path}"))

View file

@ -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) # type: ignore
stock.price = AbstractVendor.round_price_marketologically(stock.price)
stock.save()
except Exception as e:
self.stdout.write(self.style.WARNING(f"Couldn't fix price on {stock.uuid}"))

View file

@ -61,16 +61,16 @@ class Command(BaseCommand):
try:
module_path, model_name, field_name = target.rsplit(".", 2)
except ValueError:
except ValueError as e:
raise CommandError(
"Invalid target format. Use app.module.Model.field, e.g. core.models.Product.description"
)
) from e
try:
module = importlib.import_module(module_path)
model = getattr(module, model_name)
except (ImportError, AttributeError) as e:
raise CommandError(f"Could not import model '{model_name}' from '{module_path}': {e}")
raise CommandError(f"Could not import model '{model_name}' from '{module_path}': {e}") from e
dest_suffix = lang.replace("-", "_")
dest_field = f"{field_name}_{dest_suffix}"

View file

@ -9,20 +9,20 @@ logger = logging.getLogger("evibes")
class AddressManager(models.Manager):
def create(self, raw_data: str, **kwargs): # type: ignore
if not raw_data:
def create(self, **kwargs):
if not kwargs.get("raw_data"):
raise ValueError("'raw_data' (address string) must be provided.")
params: dict[str, str | int] = {
params: dict[str, str | int] = { # type: ignore [annotation-unchecked]
"format": "json",
"addressdetails": 1,
"q": raw_data,
"q": kwargs.get("raw_data"),
}
resp = requests.get(config.NOMINATIM_URL.rstrip("/") + "/search", params=params)
resp.raise_for_status()
results = resp.json()
if not results:
raise ValueError(f"No geocoding result for address: {raw_data}")
raise ValueError(f"No geocoding result for address: {kwargs.get('raw_data')}")
data = results[0]
addr = data.get("address", {})
@ -51,7 +51,7 @@ class AddressManager(models.Manager):
address_line_2 = ""
return super().get_or_create(
raw_data=raw_data,
raw_data=kwargs.get("raw_data"),
address_line=f"{address_line_1}, {address_line_2}",
street=street,
district=district,

File diff suppressed because it is too large Load diff

View file

@ -7,101 +7,205 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('core', '0001_initial'),
("core", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='order',
name='user',
field=models.ForeignKey(help_text='the user who placed the order', on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='user'),
model_name="order",
name="user",
field=models.ForeignKey(
help_text="the user who placed the order",
on_delete=django.db.models.deletion.CASCADE,
related_name="orders",
to=settings.AUTH_USER_MODEL,
verbose_name="user",
),
),
migrations.AddField(
model_name='orderproduct',
name='order',
field=models.ForeignKey(help_text='reference to the parent order that contains this product', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='order_products', to='core.order', verbose_name='parent order'),
model_name="orderproduct",
name="order",
field=models.ForeignKey(
help_text="reference to the parent order that contains this product",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="order_products",
to="core.order",
verbose_name="parent order",
),
),
migrations.AddField(
model_name='feedback',
name='order_product',
field=models.OneToOneField(help_text='references the specific product in an order that this feedback is about', on_delete=django.db.models.deletion.CASCADE, to='core.orderproduct', verbose_name='related order product'),
model_name="feedback",
name="order_product",
field=models.OneToOneField(
help_text="references the specific product in an order that this feedback is about",
on_delete=django.db.models.deletion.CASCADE,
to="core.orderproduct",
verbose_name="related order product",
),
),
migrations.AddField(
model_name='product',
name='brand',
field=models.ForeignKey(blank=True, help_text='optionally associate this product with a brand', null=True, on_delete=django.db.models.deletion.CASCADE, to='core.brand', verbose_name='brand'),
model_name="product",
name="brand",
field=models.ForeignKey(
blank=True,
help_text="optionally associate this product with a brand",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="core.brand",
verbose_name="brand",
),
),
migrations.AddField(
model_name='product',
name='category',
field=models.ForeignKey(help_text='category this product belongs to', on_delete=django.db.models.deletion.CASCADE, to='core.category', verbose_name='category'),
model_name="product",
name="category",
field=models.ForeignKey(
help_text="category this product belongs to",
on_delete=django.db.models.deletion.CASCADE,
to="core.category",
verbose_name="category",
),
),
migrations.AddField(
model_name='orderproduct',
name='product',
field=models.ForeignKey(blank=True, help_text='the specific product associated with this order line', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.product', verbose_name='associated product'),
model_name="orderproduct",
name="product",
field=models.ForeignKey(
blank=True,
help_text="the specific product associated with this order line",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="core.product",
verbose_name="associated product",
),
),
migrations.AddField(
model_name='attributevalue',
name='product',
field=models.ForeignKey(help_text="the specific product associated with this attribute's value", null=True, on_delete=django.db.models.deletion.CASCADE, related_name='attributes', to='core.product', verbose_name='associated product'),
model_name="attributevalue",
name="product",
field=models.ForeignKey(
help_text="the specific product associated with this attribute's value",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="attributes",
to="core.product",
verbose_name="associated product",
),
),
migrations.AddField(
model_name='productimage',
name='product',
field=models.ForeignKey(help_text='the product that this image represents', on_delete=django.db.models.deletion.CASCADE, related_name='images', to='core.product', verbose_name='associated product'),
model_name="productimage",
name="product",
field=models.ForeignKey(
help_text="the product that this image represents",
on_delete=django.db.models.deletion.CASCADE,
related_name="images",
to="core.product",
verbose_name="associated product",
),
),
migrations.AddField(
model_name='product',
name='tags',
field=models.ManyToManyField(blank=True, help_text='tags that help describe or group this product', to='core.producttag', verbose_name='product tags'),
model_name="product",
name="tags",
field=models.ManyToManyField(
blank=True,
help_text="tags that help describe or group this product",
to="core.producttag",
verbose_name="product tags",
),
),
migrations.AddField(
model_name='promocode',
name='user',
field=models.ForeignKey(blank=True, help_text='user assigned to this promocode if applicable', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='promocodes', to=settings.AUTH_USER_MODEL, verbose_name='assigned user'),
model_name="promocode",
name="user",
field=models.ForeignKey(
blank=True,
help_text="user assigned to this promocode if applicable",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="promocodes",
to=settings.AUTH_USER_MODEL,
verbose_name="assigned user",
),
),
migrations.AddField(
model_name='order',
name='promo_code',
field=models.ForeignKey(blank=True, help_text='optional promo code applied to this order', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.promocode', verbose_name='applied promo code'),
model_name="order",
name="promo_code",
field=models.ForeignKey(
blank=True,
help_text="optional promo code applied to this order",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="core.promocode",
verbose_name="applied promo code",
),
),
migrations.AddField(
model_name='promotion',
name='products',
field=models.ManyToManyField(blank=True, help_text='select which products are included in this promotion', to='core.product', verbose_name='included products'),
model_name="promotion",
name="products",
field=models.ManyToManyField(
blank=True,
help_text="select which products are included in this promotion",
to="core.product",
verbose_name="included products",
),
),
migrations.AddField(
model_name='stock',
name='product',
field=models.ForeignKey(blank=True, help_text='the product associated with this stock entry', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='stocks', to='core.product', verbose_name='associated product'),
model_name="stock",
name="product",
field=models.ForeignKey(
blank=True,
help_text="the product associated with this stock entry",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="stocks",
to="core.product",
verbose_name="associated product",
),
),
migrations.AddIndex(
model_name='vendor',
index=django.contrib.postgres.indexes.GinIndex(fields=['authentication'], name='core_vendor_authent_80dc1f_gin'),
model_name="vendor",
index=django.contrib.postgres.indexes.GinIndex(
fields=["authentication"], name="core_vendor_authent_80dc1f_gin"
),
),
migrations.AddField(
model_name='stock',
name='vendor',
field=models.ForeignKey(help_text='the vendor supplying this product stock', on_delete=django.db.models.deletion.CASCADE, to='core.vendor', verbose_name='associated vendor'),
model_name="stock",
name="vendor",
field=models.ForeignKey(
help_text="the vendor supplying this product stock",
on_delete=django.db.models.deletion.CASCADE,
to="core.vendor",
verbose_name="associated vendor",
),
),
migrations.AddField(
model_name='wishlist',
name='products',
field=models.ManyToManyField(blank=True, help_text='products that the user has marked as wanted', to='core.product', verbose_name='wishlisted products'),
model_name="wishlist",
name="products",
field=models.ManyToManyField(
blank=True,
help_text="products that the user has marked as wanted",
to="core.product",
verbose_name="wishlisted products",
),
),
migrations.AddField(
model_name='wishlist',
name='user',
field=models.OneToOneField(blank=True, help_text='user who owns this wishlist', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_related_wishlist', to=settings.AUTH_USER_MODEL, verbose_name='wishlist owner'),
model_name="wishlist",
name="user",
field=models.OneToOneField(
blank=True,
help_text="user who owns this wishlist",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="user_related_wishlist",
to=settings.AUTH_USER_MODEL,
verbose_name="wishlist owner",
),
),
migrations.AddIndex(
model_name='orderproduct',
index=django.contrib.postgres.indexes.GinIndex(fields=['notifications', 'attributes'], name='core_orderp_notific_cd27e9_gin'),
model_name="orderproduct",
index=django.contrib.postgres.indexes.GinIndex(
fields=["notifications", "attributes"], name="core_orderp_notific_cd27e9_gin"
),
),
]

View file

@ -4,105 +4,198 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_alter_attribute_name_alter_attribute_name_ar_ar_and_more'),
("core", "0003_alter_attribute_name_alter_attribute_name_ar_ar_and_more"),
]
operations = [
migrations.AlterField(
model_name='product',
name='name',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, verbose_name='product name'),
model_name="product",
name="name",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_ar_AR',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_ar_AR",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_cs_CZ',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_cs_CZ",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_da_DK',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_da_DK",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_de_DE',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_de_DE",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_en_GB',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_en_GB",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_en_US',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_en_US",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_es_ES',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_es_ES",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_fr_FR',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_fr_FR",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_hi_IN',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_hi_IN",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_it_IT',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_it_IT",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_ja_JP',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_ja_JP",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_kk_KZ',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_kk_KZ",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_nl_NL',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_nl_NL",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_pl_PL',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_pl_PL",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_pt_BR',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_pt_BR",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_ro_RO',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_ro_RO",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_ru_RU',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_ru_RU",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
migrations.AlterField(
model_name='product',
name='name_zh_hans',
field=models.CharField(help_text='provide a clear identifying name for the product', max_length=255, null=True, verbose_name='product name'),
model_name="product",
name="name_zh_hans",
field=models.CharField(
help_text="provide a clear identifying name for the product",
max_length=255,
null=True,
verbose_name="product name",
),
),
]

View file

@ -4,19 +4,23 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_alter_product_name_alter_product_name_ar_ar_and_more'),
("core", "0004_alter_product_name_alter_product_name_ar_ar_and_more"),
]
operations = [
migrations.RemoveField(
model_name='brand',
name='category',
model_name="brand",
name="category",
),
migrations.AddField(
model_name='brand',
name='categories',
field=models.ManyToManyField(blank=True, help_text='optional categories that this brand is associated with', to='core.category', verbose_name='associated categories'),
model_name="brand",
name="categories",
field=models.ManyToManyField(
blank=True,
help_text="optional categories that this brand is associated with",
to="core.category",
verbose_name="associated categories",
),
),
]

View file

@ -4,15 +4,28 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_remove_brand_category_brand_categories'),
("core", "0005_remove_brand_category_brand_categories"),
]
operations = [
migrations.AlterField(
model_name='order',
name='status',
field=models.CharField(choices=[('PENDING', 'pending'), ('FAILED', 'failed'), ('PAYMENT', 'payment'), ('CREATED', 'created'), ('DELIVERING', 'delivering'), ('FINISHED', 'finished'), ('MOMENTAL', 'momental')], default='PENDING', help_text='current status of the order in its lifecycle', max_length=64, verbose_name='order status'),
model_name="order",
name="status",
field=models.CharField(
choices=[
("PENDING", "pending"),
("FAILED", "failed"),
("PAYMENT", "payment"),
("CREATED", "created"),
("DELIVERING", "delivering"),
("FINISHED", "finished"),
("MOMENTAL", "momental"),
],
default="PENDING",
help_text="current status of the order in its lifecycle",
max_length=64,
verbose_name="order status",
),
),
]

View file

@ -7,16 +7,20 @@ import core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0006_alter_order_status'),
("core", "0006_alter_order_status"),
]
operations = [
migrations.AlterField(
model_name='category',
name='image',
field=models.ImageField(blank=True, help_text='upload an image representing this category', null=True,
upload_to='categories/',
validators=[core.validators.validate_category_image_dimensions],
verbose_name='category image'),
model_name="category",
name="image",
field=models.ImageField(
blank=True,
help_text="upload an image representing this category",
null=True,
upload_to="categories/",
validators=[core.validators.validate_category_image_dimensions],
verbose_name="category image",
),
),
]

View file

@ -9,33 +9,57 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_alter_category_image'),
("core", "0007_alter_category_image"),
]
operations = [
migrations.CreateModel(
name='DigitalAssetDownload',
name="DigitalAssetDownload",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('num_downloads', models.IntegerField(default=0)),
('order_product',
models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='download',
to='core.orderproduct')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("num_downloads", models.IntegerField(default=0)),
(
"order_product",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE, related_name="download", to="core.orderproduct"
),
),
],
options={
'verbose_name': 'download',
'verbose_name_plural': 'downloads',
"verbose_name": "download",
"verbose_name_plural": "downloads",
},
),
]

View file

@ -11,32 +11,57 @@ import core.utils
class Migration(migrations.Migration):
dependencies = [
('core', '0008_digitalassetdownload'),
("core", "0008_digitalassetdownload"),
]
operations = [
migrations.CreateModel(
name='Documentary',
name="Documentary",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('document', models.FileField(upload_to=core.utils.get_product_uuid_as_path)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documentaries',
to='core.product')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("document", models.FileField(upload_to=core.utils.get_product_uuid_as_path)),
(
"product",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="documentaries", to="core.product"
),
),
],
options={
'verbose_name': 'documentary',
'verbose_name_plural': 'documentaries',
"verbose_name": "documentary",
"verbose_name_plural": "documentaries",
},
),
]

View file

@ -4,15 +4,20 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_documentary'),
("core", "0009_documentary"),
]
operations = [
migrations.AddField(
model_name='product',
name='partnumber',
field=models.CharField(default=None, help_text='part number for this product', null=True, unique=True, verbose_name='part number'),
model_name="product",
name="partnumber",
field=models.CharField(
default=None,
help_text="part number for this product",
null=True,
unique=True,
verbose_name="part number",
),
),
]

View file

@ -7,138 +7,222 @@ import core.validators
class Migration(migrations.Migration):
dependencies = [
('core', '0010_product_partnumber'),
("core", "0010_product_partnumber"),
]
operations = [
migrations.AddField(
model_name='brand',
name='big_logo',
field=models.ImageField(blank=True, help_text='upload a big logo representing this brand', null=True,
upload_to='brands/',
validators=[core.validators.validate_category_image_dimensions],
verbose_name='brand big image'),
model_name="brand",
name="big_logo",
field=models.ImageField(
blank=True,
help_text="upload a big logo representing this brand",
null=True,
upload_to="brands/",
validators=[core.validators.validate_category_image_dimensions],
verbose_name="brand big image",
),
),
migrations.AddField(
model_name='brand',
name='description',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_ar_AR',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_ar_AR",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_cs_CZ',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_cs_CZ",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_da_DK',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_da_DK",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_de_DE',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_de_DE",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_en_GB',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_en_GB",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_en_US',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_en_US",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_es_ES',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_es_ES",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_fr_FR',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_fr_FR",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_hi_IN',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_hi_IN",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_it_IT',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_it_IT",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_ja_JP',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_ja_JP",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_kk_KZ',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_kk_KZ",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_nl_NL',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_nl_NL",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_pl_PL',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_pl_PL",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_pt_BR',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_pt_BR",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_ro_RO',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_ro_RO",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_ru_RU',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_ru_RU",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='description_zh_hans',
field=models.TextField(blank=True, help_text='add a detailed description of the brand', null=True,
verbose_name='brand description'),
model_name="brand",
name="description_zh_hans",
field=models.TextField(
blank=True,
help_text="add a detailed description of the brand",
null=True,
verbose_name="brand description",
),
),
migrations.AddField(
model_name='brand',
name='small_logo',
field=models.ImageField(blank=True, help_text='upload a logo representing this brand', null=True,
upload_to='brands/',
validators=[core.validators.validate_category_image_dimensions],
verbose_name='brand small image'),
model_name="brand",
name="small_logo",
field=models.ImageField(
blank=True,
help_text="upload a logo representing this brand",
null=True,
upload_to="brands/",
validators=[core.validators.validate_category_image_dimensions],
verbose_name="brand small image",
),
),
]

View file

@ -6,16 +6,23 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0011_brand_big_logo_brand_description_and_more'),
("core", "0011_brand_big_logo_brand_description_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='order',
name='user',
field=models.ForeignKey(blank=True, help_text='the user who placed the order', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='user'),
model_name="order",
name="user",
field=models.ForeignKey(
blank=True,
help_text="the user who placed the order",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="orders",
to=settings.AUTH_USER_MODEL,
verbose_name="user",
),
),
]

View file

@ -5,15 +5,21 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0012_alter_order_user'),
("core", "0012_alter_order_user"),
]
operations = [
migrations.AddField(
model_name='product',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, null=True, blank=True, editable=False, populate_from=('category.name', 'brand.name', 'name'), unique=True),
model_name="product",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True,
null=True,
blank=True,
editable=False,
populate_from=("category.name", "brand.name", "name"),
unique=True,
),
),
]

View file

@ -5,15 +5,21 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0013_product_slug'),
("core", "0013_product_slug"),
]
operations = [
migrations.AlterField(
model_name='product',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, blank=True, editable=False, null=True, populate_from=('uuid', 'category.name', 'brand.name', 'name'), unique=True),
model_name="product",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True,
blank=True,
editable=False,
null=True,
populate_from=("uuid", "category.name", "brand.name", "name"),
unique=True,
),
),
]

View file

@ -5,15 +5,21 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0014_alter_product_slug'),
("core", "0014_alter_product_slug"),
]
operations = [
migrations.AlterField(
model_name='product',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, blank=True, editable=False, null=True, populate_from=('category__name', 'name'), unique=True),
model_name="product",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True,
blank=True,
editable=False,
null=True,
populate_from=("category__name", "name"),
unique=True,
),
),
]

View file

@ -5,15 +5,21 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0015_alter_product_slug'),
("core", "0015_alter_product_slug"),
]
operations = [
migrations.AlterField(
model_name='product',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, blank=True, editable=False, null=True, populate_from=('uuid', 'category__name', 'name'), unique=True),
model_name="product",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True,
blank=True,
editable=False,
null=True,
populate_from=("uuid", "category__name", "name"),
unique=True,
),
),
]

View file

@ -7,15 +7,18 @@ import core.utils
class Migration(migrations.Migration):
dependencies = [
('core', '0016_alter_product_slug'),
("core", "0016_alter_product_slug"),
]
operations = [
migrations.AddField(
model_name='order',
name='human_readable_id',
field=models.CharField(default=core.utils.generate_human_readable_id,
help_text='a human-readable identifier for the order', max_length=8,
verbose_name='human readable id'),
model_name="order",
name="human_readable_id",
field=models.CharField(
default=core.utils.generate_human_readable_id,
help_text="a human-readable identifier for the order",
max_length=8,
verbose_name="human readable id",
),
),
]

View file

@ -8,11 +8,7 @@ def fix_duplicates(apps, schema_editor):
if schema_editor:
pass
Order = apps.get_model("core", "Order")
duplicates = (
Order.objects.values("human_readable_id")
.annotate(count=Count("uuid"))
.filter(count__gt=1)
)
duplicates = Order.objects.values("human_readable_id").annotate(count=Count("uuid")).filter(count__gt=1)
for duplicate in duplicates:
h_id = duplicate["human_readable_id"]
orders = Order.objects.filter(human_readable_id=h_id).order_by("uuid")
@ -20,6 +16,7 @@ def fix_duplicates(apps, schema_editor):
new_id = order.human_readable_id
while Order.objects.filter(human_readable_id=new_id).exists():
from core.utils import generate_human_readable_id
new_id = generate_human_readable_id()
order.human_readable_id = new_id
order.save()
@ -35,16 +32,20 @@ def reverse_func(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('core', '0017_order_human_readable_id'),
("core", "0017_order_human_readable_id"),
]
operations = [
migrations.RunPython(fix_duplicates, reverse_func),
migrations.AlterField(
model_name='order',
name='human_readable_id',
field=models.CharField(default=core.utils.generate_human_readable_id,
help_text='a human-readable identifier for the order', max_length=8, unique=True,
verbose_name='human readable id'),
model_name="order",
name="human_readable_id",
field=models.CharField(
default=core.utils.generate_human_readable_id,
help_text="a human-readable identifier for the order",
max_length=8,
unique=True,
verbose_name="human readable id",
),
),
]

View file

@ -11,46 +11,86 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_alter_order_human_readable_id'),
("core", "0018_alter_order_human_readable_id"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Address',
name="Address",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('street', models.CharField(max_length=255, null=True, verbose_name='street')),
('district', models.CharField(max_length=255, null=True, verbose_name='district')),
('city', models.CharField(max_length=100, null=True, verbose_name='city')),
('region', models.CharField(max_length=100, null=True, verbose_name='region')),
('postal_code', models.CharField(max_length=20, null=True, verbose_name='postal code')),
('country', models.CharField(max_length=40, null=True, verbose_name='country')),
('location', django.contrib.gis.db.models.fields.PointField(blank=True, geography=True,
help_text='geolocation point: (longitude, latitude)',
null=True, srid=4326)),
('raw_data', models.JSONField(blank=True, help_text='full JSON response from geocoder for this address',
null=True)),
('api_response',
models.JSONField(blank=True, help_text='stored JSON response from the geocoding service', null=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("street", models.CharField(max_length=255, null=True, verbose_name="street")),
("district", models.CharField(max_length=255, null=True, verbose_name="district")),
("city", models.CharField(max_length=100, null=True, verbose_name="city")),
("region", models.CharField(max_length=100, null=True, verbose_name="region")),
("postal_code", models.CharField(max_length=20, null=True, verbose_name="postal code")),
("country", models.CharField(max_length=40, null=True, verbose_name="country")),
(
"location",
django.contrib.gis.db.models.fields.PointField(
blank=True,
geography=True,
help_text="geolocation point: (longitude, latitude)",
null=True,
srid=4326,
),
),
(
"raw_data",
models.JSONField(
blank=True, help_text="full JSON response from geocoder for this address", null=True
),
),
(
"api_response",
models.JSONField(
blank=True, help_text="stored JSON response from the geocoding service", null=True
),
),
(
"user",
models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
'verbose_name': 'address',
'verbose_name_plural': 'addresses',
'indexes': [models.Index(fields=['location'], name='core_addres_locatio_eb6b39_idx')],
"verbose_name": "address",
"verbose_name_plural": "addresses",
"indexes": [models.Index(fields=["location"], name="core_addres_locatio_eb6b39_idx")],
},
),
]

View file

@ -5,7 +5,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0019_address'),
("core", "0019_address"),
]
operations = [
@ -17,29 +17,30 @@ class Migration(migrations.Migration):
),
reverse_sql=migrations.RunSQL.noop,
),
migrations.AddField(
model_name='order',
name='billing_address',
model_name="order",
name="billing_address",
field=models.ForeignKey(
blank=True, null=True,
blank=True,
null=True,
on_delete=models.deletion.CASCADE,
related_name='billing_address_order',
to='core.address',
verbose_name='billing address',
help_text='the billing address used for this order',
related_name="billing_address_order",
to="core.address",
verbose_name="billing address",
help_text="the billing address used for this order",
),
),
migrations.AddField(
model_name='order',
name='shipping_address',
model_name="order",
name="shipping_address",
field=models.ForeignKey(
blank=True, null=True,
blank=True,
null=True,
on_delete=models.deletion.CASCADE,
related_name='shipping_address_order',
to='core.address',
verbose_name='shipping address',
help_text='the shipping address used for this order',
related_name="shipping_address_order",
to="core.address",
verbose_name="shipping address",
help_text="the shipping address used for this order",
),
),
]

View file

@ -7,7 +7,7 @@ from django.db import migrations
def populate_slugs(apps, schema_editor):
if schema_editor:
pass
Category = apps.get_model('core', 'Category')
Category = apps.get_model("core", "Category")
for category in Category.objects.all():
try:
if not category.slug:
@ -18,15 +18,16 @@ def populate_slugs(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('core', '0021_rename_name_ar_ar_attribute_name_ar_ar_and_more'),
("core", "0021_rename_name_ar_ar_attribute_name_ar_ar_and_more"),
]
operations = [
migrations.AddField(
model_name='category',
name='slug',
field=django_extensions.db.fields.AutoSlugField(allow_unicode=True, blank=True, editable=False, null=True,
populate_from=('uuid', 'name'), unique=True),
model_name="category",
name="slug",
field=django_extensions.db.fields.AutoSlugField(
allow_unicode=True, blank=True, editable=False, null=True, populate_from=("uuid", "name"), unique=True
),
),
migrations.RunPython(populate_slugs, reverse_code=migrations.RunPython.noop),
]

View file

@ -5,14 +5,15 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0022_category_slug'),
("core", "0022_category_slug"),
]
operations = [
migrations.AddField(
model_name='address',
name='address_line',
field=models.TextField(blank=True, help_text='address line for the customer', null=True,
verbose_name='address line'),
model_name="address",
name="address_line",
field=models.TextField(
blank=True, help_text="address line for the customer", null=True, verbose_name="address line"
),
),
]

View file

@ -9,95 +9,256 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0023_address_address_line'),
("core", "0023_address_address_line"),
]
operations = [
migrations.CreateModel(
name='CategoryTag',
name="CategoryTag",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('tag_name', models.CharField(help_text='internal tag identifier for the product tag', max_length=255,
verbose_name='tag name')),
('name',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, unique=True,
verbose_name='tag display name')),
('name_en_gb',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_ar_ar',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_cs_cz',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_da_dk',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_de_de',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_en_us',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_es_es',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_fr_fr',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_hi_in',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_it_it',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_ja_jp',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_kk_kz',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_nl_nl',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_pl_pl',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_pt_br',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_ro_ro',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_ru_ru',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
('name_zh_hans',
models.CharField(help_text='user-friendly name for the product tag', max_length=255, null=True,
unique=True, verbose_name='tag display name')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
(
"tag_name",
models.CharField(
help_text="internal tag identifier for the product tag", max_length=255, verbose_name="tag name"
),
),
(
"name",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
unique=True,
verbose_name="tag display name",
),
),
(
"name_en_gb",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_ar_ar",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_cs_cz",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_da_dk",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_de_de",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_en_us",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_es_es",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_fr_fr",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_hi_in",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_it_it",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_ja_jp",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_kk_kz",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_nl_nl",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_pl_pl",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_pt_br",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_ro_ro",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_ru_ru",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
(
"name_zh_hans",
models.CharField(
help_text="user-friendly name for the product tag",
max_length=255,
null=True,
unique=True,
verbose_name="tag display name",
),
),
],
options={
'verbose_name': 'category tag',
'verbose_name_plural': 'category tags',
"verbose_name": "category tag",
"verbose_name_plural": "category tags",
},
bases=(django_prometheus.models.ExportModelOperationsMixin('category_tag'), models.Model),
bases=(django_prometheus.models.ExportModelOperationsMixin("category_tag"), models.Model),
),
migrations.AddField(
model_name='category',
name='tags',
field=models.ManyToManyField(blank=True, help_text='tags that help describe or group this category',
to='core.categorytag', verbose_name='category tags'),
model_name="category",
name="tags",
field=models.ManyToManyField(
blank=True,
help_text="tags that help describe or group this category",
to="core.categorytag",
verbose_name="category tags",
),
),
]

View file

@ -6,15 +6,19 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0024_categorytag_category_tags'),
("core", "0024_categorytag_category_tags"),
]
operations = [
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(help_text='category this product belongs to',
on_delete=django.db.models.deletion.CASCADE, related_name='products',
to='core.category', verbose_name='category'),
model_name="product",
name="category",
field=models.ForeignKey(
help_text="category this product belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="products",
to="core.category",
verbose_name="category",
),
),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0025_alter_product_category"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0026_brand_slug_alter_category_slug_alter_product_slug"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0027_brand_priority_alter_brand_slug"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0028_alter_category_slug_alter_product_slug"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0029_alter_category_slug"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0030_alter_category_slug"),
]

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0031_alter_product_slug"),
]

View file

@ -6,7 +6,6 @@ import core.utils.db
class Migration(migrations.Migration):
dependencies = [
("core", "0032_alter_brand_slug_alter_category_slug_and_more"),
]

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0033_alter_category_slug"),
]

View file

@ -6,7 +6,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0034_category_priority_alter_brand_priority"),
]

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
import logging
from contextlib import suppress
from typing import Collection, Any
from django.contrib.auth.models import AnonymousUser
from django.core.cache import cache
@ -113,7 +114,7 @@ class CategoryDetailSerializer(ModelSerializer):
return filterable_results
def get_children(self, obj) -> list:
def get_children(self, obj) -> Collection[Any]:
request = self.context.get("request")
if request is not None and request.user.has_perm("view_category"):
children = obj.children.all()

View file

@ -26,8 +26,8 @@ from core.serializers.utility import AddressSerializer
class AttributeGroupSimpleSerializer(ModelSerializer):
parent: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True) # type: ignore
children: PrimaryKeyRelatedField = PrimaryKeyRelatedField(many=True, read_only=True) # type: ignore
parent = PrimaryKeyRelatedField(read_only=True)
children = PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = AttributeGroup

View file

@ -89,7 +89,7 @@ class DoFeedbackSerializer(Serializer):
class CacheOperatorSerializer(Serializer):
key = CharField(required=True)
data = JSONField(required=False) # type: ignore
data = JSONField(required=False)
timeout = IntegerField(required=False)

View file

@ -1,5 +1,4 @@
{% load tz static i18n filters conditions %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View file

@ -1,24 +1,12 @@
import logging
from django.db.models import Model
from django.db.models.constants import LOOKUP_SEP
from django.utils.translation import gettext_lazy as _
from django_extensions.db.fields import AutoSlugField
from slugify import slugify
logger = logging.getLogger("evibes")
def list_to_queryset(model: Model, data: list):
if not isinstance(model, Model):
raise ValueError(_(f"{model} must be model"))
if not isinstance(data, list):
raise ValueError(_(f"{data} must be list object"))
pk_list = [obj.pk for obj in data]
return model.objects.filter(pk__in=pk_list)
def unicode_slugify_function(content):
return slugify(
text=str(content),

View file

@ -47,7 +47,10 @@ 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) # type: ignore
if not order.user:
return False, f"Order's user not found with the given pk: {order_pk}"
activate(order.user.language)
set_email_settings()
connection = mail.get_connection()
@ -64,7 +67,7 @@ def send_order_created_email(order_pk: str) -> tuple[bool, str]:
"total_price": order.total_price,
},
),
to=[order.user.email], # type: ignore
to=[order.user.email],
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
connection=connection,
)
@ -80,7 +83,7 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
if len(ops) <= 0:
return
activate(order.user.language) # type: ignore
activate(order.user.language)
set_email_settings()
connection = mail.get_connection()
@ -91,16 +94,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, # type: ignore
"user_first_name": order.user.first_name,
"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"), # type: ignore
"display_system_attributes": order.user.has_perm("core.view_order"),
"today": datetime.today(),
},
),
to=[order.user.email], # type: ignore
to=[order.user.email],
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
connection=connection,
)
@ -110,7 +113,7 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
def send_thank_you_email(ops: list[OrderProduct]):
if ops:
pass
activate(order.user.language) # type: ignore
activate(order.user.language)
set_email_settings()
@ -121,6 +124,9 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
except Order.DoesNotExist:
return False, f"Order not found with the given pk: {order_pk}"
if not order.user:
return False, f"Order's user not found with the given pk: {order_pk}"
digital_ops = []
for digital_op in order.order_products.filter(

View file

@ -145,7 +145,7 @@ class AbstractVendor:
return value, "string"
@staticmethod
def auto_resolver_helper(model: Brand | Category, resolving_name: str) -> Brand | Category | None:
def auto_resolver_helper(model: type[Brand] | type[Category], resolving_name: str) -> Brand | Category | None:
queryset = model.objects.filter(name=resolving_name)
if not queryset.exists():
if len(resolving_name) > 255:
@ -178,7 +178,7 @@ class AbstractVendor:
except Category.DoesNotExist:
pass
return self.auto_resolver_helper(Category, category_name) # type: ignore
return self.auto_resolver_helper(Category, category_name)
def auto_resolve_brand(self, brand_name: str):
if brand_name:
@ -196,7 +196,7 @@ class AbstractVendor:
except Brand.DoesNotExist:
pass
return self.auto_resolver_helper(Brand, brand_name) # type: ignore
return self.auto_resolver_helper(Brand, brand_name)
def resolve_price(
self,
@ -213,7 +213,7 @@ class AbstractVendor:
price = float(original_price)
if category and category.markup_percent:
price *= 1 + float(category.markup_percent) / 100.0 # type: ignore
price *= 1 + float(category.markup_percent) / 100.0
elif vendor and vendor.markup_percent:
price *= 1 + vendor.markup_percent / 100.0
@ -268,8 +268,8 @@ class AbstractVendor:
if vendor.is_active:
return vendor
raise VendorError(f"Vendor {self.vendor_name!r} is inactive...")
except Vendor.DoesNotExist:
raise Exception(f"No matching vendor found with name {self.vendor_name!r}...")
except Vendor.DoesNotExist as dne:
raise Exception(f"No matching vendor found with name {self.vendor_name!r}...") from dne
def get_products(self):
pass
@ -288,6 +288,7 @@ class AbstractVendor:
def prepare_for_stock_update(self, method: str = "deactivate") -> None:
products = self.get_products_queryset()
# noinspection PyUnreachableCode
match method:
case "deactivate":
products.update(is_active=False)
@ -301,6 +302,7 @@ class AbstractVendor:
def delete_inactives(self, inactivation_method: str = "deactivate"):
products = self.get_products_queryset()
# noinspection PyUnreachableCode
match inactivation_method:
case "deactivate":
products.filter(is_active=False).delete()
@ -339,7 +341,7 @@ class AbstractVendor:
defaults={"is_active": True},
)
except Attribute.MultipleObjectsReturned:
attribute = Attribute.objects.filter(name=key, group=attr_group).order_by("uuid").first() # type: ignore
attribute = Attribute.objects.filter(name=key, group=attr_group).order_by("uuid").first() # type: ignore [assignment]
attribute.is_active = True
attribute.value_type = attr_value_type
attribute.save()

View file

@ -102,9 +102,6 @@ class CustomGraphQLView(FileUploadGraphQLView):
This class serves as a customization extension of FileUploadGraphQLView that allows modification
or enhancement of specific behaviors, particularly the context handling for GraphQL requests.
Attributes
----------
None
"""
def get_context(self, request):
@ -520,8 +517,8 @@ def favicon_view(request, *args, **kwargs):
try:
favicon_path = os.path.join(settings.BASE_DIR, "static/favicon.png")
return FileResponse(open(favicon_path, "rb"), content_type="image/x-icon")
except FileNotFoundError:
raise Http404(_("favicon not found"))
except FileNotFoundError as fnfe:
raise Http404(_("favicon not found")) from fnfe
def index(request, *args, **kwargs):

View file

@ -2,7 +2,7 @@ import logging
import uuid
from uuid import UUID
from django.db.models import Q, QuerySet
from django.db.models import Q
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
@ -378,7 +378,7 @@ class ProductViewSet(EvibesViewSet):
lookup_val = kwargs.get(self.lookup_field)
try:
product = Product.objects.get(uuid=lookup_val)
feedbacks: QuerySet[Feedback] = ( # type: ignore
feedbacks = (
Feedback.objects.filter(order_product__product=product)
if request.user.has_perm("core.view_feedback")
else Feedback.objects.filter(order_product__product=product, is_active=True)

View file

@ -29,7 +29,7 @@ app.conf.update(
)
app.conf.task_routes = {
'core.tasks.update_products_task': {'queue': 'stock_updater'},
"core.tasks.update_products_task": {"queue": "stock_updater"},
}
app.config_from_object("django.conf:settings", namespace="CELERY")

View file

@ -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, # type: ignore
"page_size": None or self.page_size, # type: ignore
"total_items": None or self.page.paginator.count, # type: ignore
"total_pages": None or self.page.paginator.num_pages, # type: ignore [union-attr]
"page_size": None or self.page_size,
"total_items": None or self.page.paginator.count, # type: ignore [union-attr]
},
"data": data,
}

View file

@ -1,5 +1,5 @@
import logging
from os import getenv
from os import getenv, name
from pathlib import Path
EVIBES_VERSION = "2.9.0"
@ -9,7 +9,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent
SECRET_KEY = getenv("SECRET_KEY", "SUPER_SECRET_KEY")
DEBUG = bool(int(getenv("DEBUG", "1")))
ALLOWED_HOSTS = {
ALLOWED_HOSTS: set = {
"app",
"worker",
"beat",
@ -27,9 +27,9 @@ else:
for entry in getenv("ALLOWED_HOSTS", "").split(" "):
ALLOWED_HOSTS.add(entry)
ALLOWED_HOSTS = tuple(ALLOWED_HOSTS) # type: ignore
ALLOWED_HOSTS: tuple = tuple(ALLOWED_HOSTS)
CSRF_TRUSTED_ORIGINS = {
CSRF_TRUSTED_ORIGINS: set = {
"http://127.0.0.1",
"http://api.localhost",
"http://b2b.localhost",
@ -38,12 +38,12 @@ CSRF_TRUSTED_ORIGINS = {
for entry in getenv("CSRF_TRUSTED_ORIGINS", "").split(" "):
CSRF_TRUSTED_ORIGINS.add(entry)
CSRF_TRUSTED_ORIGINS = tuple(CSRF_TRUSTED_ORIGINS) # type: ignore
CSRF_TRUSTED_ORIGINS: tuple = tuple(CSRF_TRUSTED_ORIGINS)
if DEBUG:
CORS_ALLOW_ALL_ORIGINS = True
else:
CORS_ALLOWED_ORIGINS = {
CORS_ALLOWED_ORIGINS: set = {
"http://127.0.0.1",
"http://api.localhost",
"http://b2b.localhost",
@ -51,7 +51,7 @@ else:
for entry in getenv("CORS_ALLOWED_ORIGINS", "").split(" "):
CORS_ALLOWED_ORIGINS.add(entry)
CORS_ALLOWED_ORIGINS = tuple(CORS_ALLOWED_ORIGINS) # type: ignore
CORS_ALLOWED_ORIGINS: tuple = tuple(CORS_ALLOWED_ORIGINS)
CORS_ALLOW_METHODS = (
"DELETE",
@ -342,3 +342,7 @@ STORAGES: dict[str, dict[str, str | int | bool | None]] = {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
}
if name == "nt":
GDAL_LIBRARY_PATH = r"C:\OSGeo4W\bin\gdal311.dll"
GEOS_LIBRARY_PATH = r"C:\OSGeo4W\bin\geos_c.dll"

View file

@ -1,11 +1,11 @@
from evibes.settings.base import * # noqa: F403
from evibes.settings.base import getenv
DATABASES = {
"default": {
"ENGINE": "django_prometheus.db.backends.postgis",
"NAME": getenv("POSTGRES_DB"), # noqa: F405
"USER": getenv("POSTGRES_USER"), # noqa: F405
"PASSWORD": getenv("POSTGRES_PASSWORD"), # noqa: F405
"NAME": getenv("POSTGRES_DB"),
"USER": getenv("POSTGRES_USER"),
"PASSWORD": getenv("POSTGRES_PASSWORD"),
"HOST": "database",
"PORT": 5432,
}

View file

@ -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] # 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
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]

View file

@ -4,9 +4,9 @@ from evibes.settings.base import EVIBES_VERSION
from evibes.settings.constance import CONSTANCE_CONFIG
JAZZMIN_SETTINGS = {
"site_title": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} Admin", # type: ignore
"site_header": str(CONSTANCE_CONFIG.get("PROJECT_NAME")[0]), # type: ignore
"site_brand": str(CONSTANCE_CONFIG.get("PROJECT_NAME")[0]), # type: ignore
"site_title": f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]} Admin", # type: ignore [index]
"site_header": str(CONSTANCE_CONFIG.get("PROJECT_NAME")[0]), # type: ignore [index]
"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",
@ -18,21 +18,21 @@ JAZZMIN_SETTINGS = {
"user_avatar": "avatar",
"topmenu_links": [
{"name": _("Home"), "url": "admin:index"},
{"name": _("Storefront"), "url": f"https://{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}", "new_window": True},
{"name": _("Storefront"), "url": f"https://{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}", "new_window": True}, # type: ignore [index]
{"name": "GitLab", "url": "https://gitlab.com/wiseless/evibes", "new_window": True},
{
"name": _("GraphQL Docs"),
"url": f"https://api.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/graphql", # type: ignore
"url": f"https://api.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/graphql", # type: ignore [index]
"new_window": True,
},
{
"name": _("Platform REST Docs"),
"url": f"https://api.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/docs/swagger", # type: ignore
"url": f"https://api.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/docs/swagger", # type: ignore [index]
"new_window": True,
},
{
"name": _("B2B REST Docs"),
"url": f"https://b2b.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/docs/swagger", # type: ignore
"url": f"https://b2b.{CONSTANCE_CONFIG.get('BASE_DOMAIN')[0]}/docs/swagger", # type: ignore [index]
"new_window": True,
},
{"name": _("Support"), "url": "https://t.me/fureunoir", "new_window": True},

View file

@ -6,8 +6,11 @@ class SkipVariableDoesNotExistFilter(logging.Filter): # noqa: F405
if record.exc_info:
exc_type, exc_instance, _ = record.exc_info
try:
if exc_type.__name__ == "VariableDoesNotExist": # type: ignore
return False
if exc_type is not None:
if exc_type.__name__ == "VariableDoesNotExist":
return False
else:
return True
except AttributeError:
return True
return "VariableDoesNotExist" not in record.getMessage()

View file

@ -11,8 +11,9 @@ class TransactionType(DjangoObjectType):
process = GenericScalar()
def resolve_process(self: Transaction, info) -> dict:
if info.context.user == self.balance.user:
return self.process
if self.balance is not None:
if info.context.user == self.balance.user:
return self.process
return {}
class Meta:

View file

@ -9,55 +9,96 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Balance',
name="Balance",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('amount', models.FloatField(default=0)),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("amount", models.FloatField(default=0)),
],
options={
'verbose_name': 'balance',
'verbose_name_plural': 'balances',
"verbose_name": "balance",
"verbose_name_plural": "balances",
},
),
migrations.CreateModel(
name='Transaction',
name="Transaction",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('is_active', models.BooleanField(default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name='is active')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('amount', models.FloatField()),
('currency', models.CharField(max_length=3)),
('payment_method', models.CharField(max_length=20)),
('process', models.JSONField(default=dict, verbose_name='processing details')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="if set to false, this object can't be seen by users without needed permission",
verbose_name="is active",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
("amount", models.FloatField()),
("currency", models.CharField(max_length=3)),
("payment_method", models.CharField(max_length=20)),
("process", models.JSONField(default=dict, verbose_name="processing details")),
],
options={
'verbose_name': 'transaction',
'verbose_name_plural': 'transactions',
"verbose_name": "transaction",
"verbose_name_plural": "transactions",
},
),
]

View file

@ -10,32 +10,42 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('core', '0002_initial'),
('payments', '0001_initial'),
("core", "0002_initial"),
("payments", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='balance',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payments_balance',
to=settings.AUTH_USER_MODEL, blank=True, null=True),
model_name="balance",
name="user",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="payments_balance",
to=settings.AUTH_USER_MODEL,
blank=True,
null=True,
),
),
migrations.AddField(
model_name='transaction',
name='balance',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payments.balance'),
model_name="transaction",
name="balance",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="payments.balance"),
),
migrations.AddField(
model_name='transaction',
name='order',
field=models.ForeignKey(blank=True, help_text='order to process after paid', null=True,
on_delete=django.db.models.deletion.CASCADE, related_name='payments_transactions',
to='core.order'),
model_name="transaction",
name="order",
field=models.ForeignKey(
blank=True,
help_text="order to process after paid",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="payments_transactions",
to="core.order",
),
),
migrations.AddIndex(
model_name='transaction',
index=django.contrib.postgres.indexes.GinIndex(fields=['process'], name='payments_tr_process_d5b008_gin'),
model_name="transaction",
index=django.contrib.postgres.indexes.GinIndex(fields=["process"], name="payments_tr_process_d5b008_gin"),
),
]

View file

@ -5,15 +5,20 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('payments', '0002_initial'),
("payments", "0002_initial"),
]
operations = [
migrations.AlterField(
model_name='transaction',
name='balance',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='payments.balance'),
model_name="transaction",
name="balance",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="transactions",
to="payments.balance",
),
),
]

View file

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('payments', '0003_alter_transaction_balance'),
("payments", "0003_alter_transaction_balance"),
]
operations = [
migrations.AlterField(
model_name='transaction',
name='payment_method',
model_name="transaction",
name="payment_method",
field=models.CharField(blank=True, max_length=20, null=True),
),
]

View file

@ -7,13 +7,11 @@ from core.abstract import NiceModel
class Transaction(NiceModel):
amount: float = FloatField(null=False, blank=False) # type: ignore
balance: "Balance" = ForeignKey(
"payments.Balance", on_delete=CASCADE, blank=True, null=True, related_name="transactions"
) # type: ignore
currency: str = CharField(max_length=3, null=False, blank=False) # type: ignore
payment_method: str = CharField(max_length=20, null=True, blank=True) # type: ignore
order = ForeignKey( # type: ignore
amount = FloatField(null=False, blank=False)
balance = ForeignKey("payments.Balance", on_delete=CASCADE, blank=True, null=True, related_name="transactions")
currency = CharField(max_length=3, null=False, blank=False)
payment_method = CharField(max_length=20, null=True, blank=True)
order = ForeignKey(
"core.Order",
on_delete=CASCADE,
blank=True,
@ -21,7 +19,7 @@ class Transaction(NiceModel):
help_text=_("order to process after paid"),
related_name="payments_transactions",
)
process: dict = JSONField(verbose_name=_("processing details"), default=dict) # type: ignore
process = JSONField(verbose_name=_("processing details"), default=dict)
def __str__(self):
return f"{self.balance.user.email} | {self.amount}"
@ -48,8 +46,8 @@ class Transaction(NiceModel):
class Balance(NiceModel):
amount: float = FloatField(null=False, blank=False, default=0) # type: ignore
user = OneToOneField( # type: ignore
amount = FloatField(null=False, blank=False, default=0)
user = OneToOneField(
to="vibes_auth.User", on_delete=CASCADE, blank=True, null=True, related_name="payments_balance"
)
transactions: QuerySet["Transaction"]

View file

@ -20,10 +20,10 @@ class TransactionProcessSerializer(ModelSerializer):
order_uuid = SerializerMethodField(read_only=True, required=False)
def get_order_hr_id(self, obj: Transaction) -> str | None:
return obj.order.human_readable_id if obj.order else None # type: ignore
return obj.order.human_readable_id if obj.order else None
def get_order_uuid(self, obj: Transaction) -> str | None:
return str(obj.order.uuid) if obj.order else None # type: ignore
return str(obj.order.uuid) if obj.order else None
class Meta:
model = Transaction

View file

@ -25,7 +25,7 @@ def process_transaction_changes(instance, created, **_kwargs):
case _:
gateway = AbstractGateway()
gateway.process_transaction(instance)
except Exception as e: # noqa:
except Exception as e:
instance.process = {"status": "NOGATEWAY", "error": str(e)}
if not created:
status = instance.process.get("status", "").lower()

View file

@ -1,5 +1,4 @@
{% load tz static i18n filters conditions %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
@ -61,12 +60,6 @@
color: #888;
}
.order-table {
width: 100%;
margin-top: 20px;
border-collapse: collapse;
}
.order-table th, .order-table td {
border: 1px solid #ddd;
padding: 8px;

View file

@ -19,7 +19,10 @@ def balance_deposit_email(transaction_pk: str) -> tuple[bool, str]:
except Transaction.DoesNotExist:
return False, f"Transaction not found with the given pk: {transaction_pk}"
activate(transaction.balance.user.language) # type: ignore
if not transaction.balance or not transaction.balance.user:
return False, f"Balance not found for the given transaction pk: {transaction_pk}"
activate(transaction.balance.user.language)
set_email_settings()
connection = mail.get_connection()
@ -31,13 +34,13 @@ def balance_deposit_email(transaction_pk: str) -> tuple[bool, str]:
context={
"amount": transaction.amount,
"balance": transaction.balance.amount,
"user_first_name": transaction.balance.user.first_name, # type: ignore
"user_first_name": transaction.balance.user.first_name,
"project_name": config.PROJECT_NAME,
"contact_email": config.EMAIL_FROM,
"today": datetime.today(),
},
),
to=[transaction.balance.user.email], # type: ignore
to=[transaction.balance.user.email],
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
connection=connection,
)

View file

@ -103,18 +103,21 @@ testing = ["pytest", "pytest-django", "coverage"]
linting = ["black", "isort", "flake8", "bandit"]
[tool.mypy]
disable_error_code = ["import-untyped", "misc"]
exclude = ["*/migrations/*", "./evibes/settings/drf.py"]
disable_error_code = ["no-redef", "import-untyped"]
exclude = ["*/migrations/*"]
plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main"]
[tool.django-stubs]
django_settings_module = "evibes.settings"
[tool.ruff]
line-length = 120
target-version = "py312"
exclude = ["migrations", "media", "static", "storefront"]
exclude = ["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"]
per-file-ignores = { "__init__.py" = ["E402", "F401"] }
select = ["E4", "E7", "E9", "F", "B", "Q"]
ignore = ["RUF012", "A002", "A003"]
[tool.ruff.format]
quote-style = "double"

View file

@ -102,9 +102,9 @@ class UpdateUser(BaseMutation):
try:
user = User.objects.get(uuid=uuid)
except User.DoesNotExist:
except User.DoesNotExist as dne:
name = "User"
raise Http404(_(f"{name} does not exist: {uuid}"))
raise Http404(_(f"{name} does not exist: {uuid}")) from dne
if not (info.context.user.has_perm("vibes_auth.change_user") or info.context.user == user):
raise PermissionDenied(permission_denied_message)
@ -176,8 +176,8 @@ class DeleteUser(BaseMutation):
else:
raise BadRequest("uuid or email must be specified")
return DeleteUser(success=True)
except User.DoesNotExist:
raise Http404(f"User with the given uuid: {uuid} or email: {email} does not exist.")
except User.DoesNotExist as dne:
raise Http404(f"User with the given uuid: {uuid} or email: {email} does not exist.") from dne
raise PermissionDenied(permission_denied_message)
@ -201,7 +201,7 @@ class ObtainJSONWebToken(BaseMutation):
access_token=serializer.validated_data["access"],
)
except Exception as e:
raise PermissionDenied(f"invalid credentials provided: {e!s}")
raise PermissionDenied(f"invalid credentials provided: {e!s}") from e
class RefreshJSONWebToken(BaseMutation):
@ -222,7 +222,7 @@ class RefreshJSONWebToken(BaseMutation):
user=User.objects.get(uuid=serializer.validated_data["user"]["uuid"]),
)
except Exception as e:
raise PermissionDenied(f"invalid refresh token provided: {e!s}")
raise PermissionDenied(f"invalid refresh token provided: {e!s}") from e
class VerifyJSONWebToken(BaseMutation):
@ -270,7 +270,7 @@ class ActivateUser(BaseMutation):
user.save()
except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e:
raise BadRequest(_(f"something went wrong: {e!s}"))
raise BadRequest(_(f"something went wrong: {e!s}")) from e
return ActivateUser(success=True)
@ -322,7 +322,7 @@ class ConfirmResetPassword(BaseMutation):
return ConfirmResetPassword(success=True)
except (TypeError, ValueError, OverflowError, ValidationError, User.DoesNotExist) as e:
raise BadRequest(_(f"something went wrong: {e!s}"))
raise BadRequest(_(f"something went wrong: {e!s}")) from e
class UploadAvatar(BaseMutation):
@ -339,6 +339,6 @@ class UploadAvatar(BaseMutation):
info.context.user.avatar = avatar
info.context.user.save()
except Exception as e:
raise BadRequest(str(e))
raise BadRequest(str(e)) from e
return UploadAvatar(user=info.context.user)

View file

@ -16,87 +16,179 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
],
name="Group",
fields=[],
options={
'verbose_name': 'group',
'verbose_name_plural': 'groups',
'proxy': True,
'indexes': [],
'constraints': [],
"verbose_name": "group",
"verbose_name_plural": "groups",
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=('auth.group',),
bases=("auth.group",),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
("objects", django.contrib.auth.models.GroupManager()),
],
),
migrations.CreateModel(
name='User',
name="User",
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status')),
('is_staff', models.BooleanField(default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False,
help_text='unique id is used to surely identify any database object',
primary_key=True, serialize=False, verbose_name='unique id')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True,
help_text='when the object first appeared on the database',
verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True,
help_text='when the object was last modified',
verbose_name='modified')),
('email',
models.EmailField(help_text='user email address', max_length=254, unique=True, verbose_name='email')),
('phone_number',
models.CharField(blank=True, help_text='user phone number', max_length=20, null=True, unique=True,
validators=[vibes_auth.validators.validate_phone_number],
verbose_name='phone_number')),
('first_name', models.CharField(blank=True, max_length=150, null=True, verbose_name='first_name')),
('last_name', models.CharField(blank=True, max_length=150, null=True, verbose_name='last_name')),
('avatar', models.ImageField(blank=True, help_text='user profile image', null=True,
upload_to=vibes_auth.models.User.get_uuid_as_path, verbose_name='avatar')),
('is_verified',
models.BooleanField(default=False, help_text='user verification status', verbose_name='is verified')),
('is_active', models.BooleanField(default=False, help_text='unselect this instead of deleting accounts',
verbose_name='is_active')),
('is_subscribed', models.BooleanField(default=False, help_text="user's newsletter subscription status",
verbose_name='is_subscribed')),
('activation_token', models.UUIDField(default=uuid.uuid4, verbose_name='activation token')),
('language', models.CharField(
choices=[('en-GB', 'English (British)'), ('ar-AR', 'العربية'), ('cs-CZ', 'Česky'),
('da-DK', 'Dansk'), ('de-DE', 'Deutsch'), ('en-US', 'English (American)'),
('es-ES', 'Español'), ('fr-FR', 'Français'), ('hi-IN', 'हिंदी'), ('it-IT', 'Italiano'),
('ja-JP', '日本語'), ('kk-KZ', 'Қазақ'), ('nl-NL', 'Nederlands'), ('pl-PL', 'Polska'),
('pt-BR', 'Português'), ('ro-RO', 'Română'), ('ru-RU', 'Русский'),
('zh-hans', '简体中文')], default='en-GB', max_length=7)),
('attributes', models.JSONField(blank=True, default=dict, null=True, verbose_name='attributes')),
('groups', models.ManyToManyField(blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set', related_query_name='user', to='auth.group',
verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
related_name='user_set', related_query_name='user',
to='auth.permission', verbose_name='user permissions')),
("password", models.CharField(max_length=128, verbose_name="password")),
("last_login", models.DateTimeField(blank=True, null=True, verbose_name="last login")),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
("date_joined", models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined")),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="unique id is used to surely identify any database object",
primary_key=True,
serialize=False,
verbose_name="unique id",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True,
help_text="when the object first appeared on the database",
verbose_name="created",
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, help_text="when the object was last modified", verbose_name="modified"
),
),
(
"email",
models.EmailField(
help_text="user email address", max_length=254, unique=True, verbose_name="email"
),
),
(
"phone_number",
models.CharField(
blank=True,
help_text="user phone number",
max_length=20,
null=True,
unique=True,
validators=[vibes_auth.validators.validate_phone_number],
verbose_name="phone_number",
),
),
("first_name", models.CharField(blank=True, max_length=150, null=True, verbose_name="first_name")),
("last_name", models.CharField(blank=True, max_length=150, null=True, verbose_name="last_name")),
(
"avatar",
models.ImageField(
blank=True,
help_text="user profile image",
null=True,
upload_to=vibes_auth.models.User.get_uuid_as_path,
verbose_name="avatar",
),
),
(
"is_verified",
models.BooleanField(
default=False, help_text="user verification status", verbose_name="is verified"
),
),
(
"is_active",
models.BooleanField(
default=False, help_text="unselect this instead of deleting accounts", verbose_name="is_active"
),
),
(
"is_subscribed",
models.BooleanField(
default=False, help_text="user's newsletter subscription status", verbose_name="is_subscribed"
),
),
("activation_token", models.UUIDField(default=uuid.uuid4, verbose_name="activation token")),
(
"language",
models.CharField(
choices=[
("en-GB", "English (British)"),
("ar-AR", "العربية"),
("cs-CZ", "Česky"),
("da-DK", "Dansk"),
("de-DE", "Deutsch"),
("en-US", "English (American)"),
("es-ES", "Español"),
("fr-FR", "Français"),
("hi-IN", "हिंदी"),
("it-IT", "Italiano"),
("ja-JP", "日本語"),
("kk-KZ", "Қазақ"),
("nl-NL", "Nederlands"),
("pl-PL", "Polska"),
("pt-BR", "Português"),
("ro-RO", "Română"),
("ru-RU", "Русский"),
("zh-hans", "简体中文"),
],
default="en-GB",
max_length=7,
),
),
("attributes", models.JSONField(blank=True, default=dict, null=True, verbose_name="attributes")),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'swappable': 'AUTH_USER_MODEL',
"verbose_name": "user",
"verbose_name_plural": "users",
"swappable": "AUTH_USER_MODEL",
},
managers=[
('objects', vibes_auth.managers.UserManager()),
("objects", vibes_auth.managers.UserManager()),
],
),
]

View file

@ -4,37 +4,34 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('token_blacklist', '0012_alter_outstandingtoken_user'),
('vibes_auth', '0001_initial'),
("token_blacklist", "0012_alter_outstandingtoken_user"),
("vibes_auth", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='BlacklistedToken',
fields=[
],
name="BlacklistedToken",
fields=[],
options={
'verbose_name': 'blacklisted token',
'verbose_name_plural': 'blacklisted tokens',
'proxy': True,
'indexes': [],
'constraints': [],
"verbose_name": "blacklisted token",
"verbose_name_plural": "blacklisted tokens",
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=('token_blacklist.blacklistedtoken',),
bases=("token_blacklist.blacklistedtoken",),
),
migrations.CreateModel(
name='OutstandingToken',
fields=[
],
name="OutstandingToken",
fields=[],
options={
'verbose_name': 'outstanding token',
'verbose_name_plural': 'outstanding tokens',
'proxy': True,
'indexes': [],
'constraints': [],
"verbose_name": "outstanding token",
"verbose_name_plural": "outstanding tokens",
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=('token_blacklist.outstandingtoken',),
bases=("token_blacklist.outstandingtoken",),
),
]

View file

@ -5,54 +5,53 @@ from django.db.models.functions import Lower
def forwards(apps, schema_editor):
if schema_editor:
pass
User = apps.get_model('vibes_auth', 'User')
User.objects.all().update(language=Lower('language'))
User = apps.get_model("vibes_auth", "User")
User.objects.all().update(language=Lower("language"))
def backwards(apps, schema_editor):
if schema_editor:
pass
User = apps.get_model('vibes_auth', 'User')
User = apps.get_model("vibes_auth", "User")
for u in User.objects.all():
parts = u.language.split('-', 1)
parts = u.language.split("-", 1)
if len(parts) == 2:
u.language = f"{parts[0].lower()}-{parts[1].upper()}"
u.save(update_fields=['language'])
u.save(update_fields=["language"])
class Migration(migrations.Migration):
dependencies = [
('vibes_auth', '0002_blacklistedtoken_outstandingtoken'),
("vibes_auth", "0002_blacklistedtoken_outstandingtoken"),
]
operations = [
migrations.RunPython(forwards, backwards),
migrations.AlterField(
model_name='user',
name='language',
model_name="user",
name="language",
field=models.CharField(
choices=[
('en-gb', 'English (British)'),
('ar-ar', 'العربية'),
('cs-cz', 'Česky'),
('da-dk', 'Dansk'),
('de-de', 'Deutsch'),
('en-us', 'English (American)'),
('es-es', 'Español'),
('fr-fr', 'Français'),
('hi-in', 'हिंदी'),
('it-it', 'Italiano'),
('ja-jp', '日本語'),
('kk-kz', 'Қазақ'),
('nl-nl', 'Nederlands'),
('pl-pl', 'Polska'),
('pt-br', 'Português'),
('ro-ro', 'Română'),
('ru-ru', 'Русский'),
('zh-hans', '简体中文'),
("en-gb", "English (British)"),
("ar-ar", "العربية"),
("cs-cz", "Česky"),
("da-dk", "Dansk"),
("de-de", "Deutsch"),
("en-us", "English (American)"),
("es-es", "Español"),
("fr-fr", "Français"),
("hi-in", "हिंदी"),
("it-it", "Italiano"),
("ja-jp", "日本語"),
("kk-kz", "Қазақ"),
("nl-nl", "Nederlands"),
("pl-pl", "Polska"),
("pt-br", "Português"),
("ro-ro", "Română"),
("ru-ru", "Русский"),
("zh-hans", "简体中文"),
],
default='en-gb',
default="en-gb",
max_length=7,
),
),

View file

@ -1,4 +1,3 @@
import uuid
from uuid import uuid4
from django.contrib.auth.models import AbstractUser
@ -21,6 +20,7 @@ from rest_framework_simplejwt.token_blacklist.models import (
OutstandingToken as BaseOutstandingToken,
)
from blog.models import Post
from core.abstract import NiceModel
from core.models import Order, Wishlist
from evibes.settings import LANGUAGE_CODE, LANGUAGES
@ -83,8 +83,8 @@ class User(AbstractUser, NiceModel):
def get_uuid_as_path(self, *args):
return str(self.uuid) + "/" + args[0]
email: str = EmailField(_("email"), unique=True, help_text=_("user email address")) # type: ignore
phone_number: str = CharField( # type: ignore
email = EmailField(_("email"), unique=True, help_text=_("user email address"))
phone_number = CharField(
_("phone_number"),
max_length=20,
unique=True,
@ -95,9 +95,10 @@ class User(AbstractUser, NiceModel):
validate_phone_number,
],
)
username = None
first_name: str = CharField(_("first_name"), max_length=150, blank=True, null=True) # type: ignore
last_name: str = CharField(_("last_name"), max_length=150, blank=True, null=True) # type: ignore
username: None = None # type: ignore [assignment]
posts: "Post"
first_name = CharField(_("first_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
last_name = CharField(_("last_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
avatar = ImageField(
null=True,
verbose_name=_("avatar"),
@ -106,28 +107,28 @@ class User(AbstractUser, NiceModel):
help_text=_("user profile image"),
)
is_verified: bool = BooleanField( # type: ignore
is_verified = BooleanField(
default=False,
verbose_name=_("is verified"),
help_text=_("user verification status"),
)
is_active: bool = BooleanField( # type: ignore
is_active = BooleanField(
_("is_active"),
default=False,
help_text=_("unselect this instead of deleting accounts"),
)
is_subscribed: bool = BooleanField( # type: ignore
is_subscribed = BooleanField(
verbose_name=_("is_subscribed"), help_text=_("user's newsletter subscription status"), default=False
)
activation_token: uuid = UUIDField(default=uuid4, verbose_name=_("activation token")) # type: ignore
language: str = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7) # type: ignore
attributes: dict = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True) # type: ignore
activation_token = UUIDField(default=uuid4, verbose_name=_("activation token"))
language = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7)
attributes = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
# noinspection PyClassVar
objects = UserManager() # type: ignore
objects = UserManager() # type: ignore [misc, assignment]
payments_balance: "Balance"
user_related_wishlist: "Wishlist"
@ -190,11 +191,6 @@ class OutstandingToken(BaseOutstandingToken):
It does not add additional fields or logic to the base model but allows for
overloading or extending its default functionality as required.
Attributes:
Meta (class): Contains metadata for the model, including options such as
whether the model is a proxy, the human-readable name for the model, and
the plural form of that name.
"""
class Meta:
@ -214,8 +210,6 @@ class BlacklistedToken(BaseBlacklistedToken):
meta options for the model, affecting its database and administrative
representation.
Attributes:
None
"""
class Meta:

View file

@ -112,7 +112,7 @@ class TokenObtainSerializer(Serializer):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.user = None
self.user: User | None = None
self.fields[self.username_field] = CharField(write_only=True)
self.fields["password"] = PasswordField()
@ -124,19 +124,22 @@ class TokenObtainSerializer(Serializer):
with suppress(KeyError):
authenticate_kwargs["request"] = self.context["request"]
self.user = authenticate(**authenticate_kwargs) # type: ignore
self.user: User | None = authenticate(**authenticate_kwargs)
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
raise AuthenticationFailed(
self.error_messages["no_active_account"],
_("no active account"), # type: ignore
str(_("no active account")),
)
return {}
@classmethod
def get_token(cls, user: AuthUser) -> Token:
return cls.token_class.for_user(user) # type: ignore
if cls.token_class is not None:
return cls.token_class.for_user(user)
else:
raise RuntimeError(_("must set token_class attribute on class."))
class TokenObtainPairSerializer(TokenObtainSerializer):
@ -147,15 +150,19 @@ class TokenObtainPairSerializer(TokenObtainSerializer):
logger.debug("Data validated")
refresh = self.get_token(self.user) # type: ignore
if self.user is None:
raise ValidationError(_("no active account"))
refresh = self.get_token(self.user)
data["refresh"] = str(refresh)
data["access"] = str(refresh.access_token) # type: ignore
# noinspection PyUnresolvedReferences
data["access"] = str(refresh.access_token) # type: ignore [attr-defined]
data["user"] = UserSerializer(self.user).data
logger.debug("Data formed")
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(self.user, self.user) # type: ignore
update_last_login(self.user, self.user)
logger.debug("Updated last login")
logger.debug("Returning data")
@ -183,7 +190,8 @@ class TokenRefreshSerializer(Serializer):
data["refresh"] = str(refresh)
user = User.objects.get(uuid=refresh.payload["user_uuid"])
data["user"] = UserSerializer(user).data # type: ignore
# noinspection PyTypeChecker
data["user"] = UserSerializer(user).data
return data
@ -204,18 +212,19 @@ class TokenVerifySerializer(Serializer):
try:
payload = UntypedToken(attrs["token"]).payload
except TokenError:
raise ValidationError(_("invalid token"))
except TokenError as te:
raise ValidationError(_("invalid token")) from te
try:
user_uuid = payload["user_uuid"]
user = User.objects.get(uuid=user_uuid)
except KeyError:
raise ValidationError(_("no user uuid claim present in token"))
except User.DoesNotExist:
raise ValidationError(_("user does not exist"))
except KeyError as ke:
raise ValidationError(_("no user uuid claim present in token")) from ke
except User.DoesNotExist as dne:
raise ValidationError(_("user does not exist")) from dne
attrs["user"] = UserSerializer(user).data # type: ignore
# noinspection PyTypeChecker
attrs["user"] = UserSerializer(user).data
return attrs

View file

@ -52,7 +52,7 @@ def send_verification_email_task(user_pk: str) -> tuple[bool, str]:
return False, f"Something went wrong while sending an email: {e!s}"
else:
return True, user.uuid
return True, str(user.uuid)
@shared_task(queue="default")
@ -95,4 +95,4 @@ def send_reset_password_email_task(user_pk: str) -> tuple[bool, str]:
return False, f"Something went wrong while sending an email: {e!s}"
else:
return True, user.uuid
return True, str(user.uuid)

View file

@ -47,8 +47,8 @@ class TokenObtainPairView(TokenViewBase):
subject to rate limiting depending on the global DEBUG setting.
"""
serializer_class = TokenObtainPairSerializer # type: ignore
_serializer_class = TokenObtainPairSerializer # type: ignore
serializer_class = TokenObtainPairSerializer
_serializer_class = TokenObtainPairSerializer
@method_decorator(ratelimit(key="ip", rate="10/h" if not DEBUG else "888/h"))
def post(self, request, *args, **kwargs):
@ -82,8 +82,8 @@ class TokenRefreshView(TokenViewBase):
whether the application is in DEBUG mode or not.
"""
serializer_class = TokenRefreshSerializer # type: ignore
_serializer_class = TokenRefreshSerializer # type: ignore
serializer_class = TokenRefreshSerializer
_serializer_class = TokenRefreshSerializer
@method_decorator(ratelimit(key="ip", rate="10/h" if not DEBUG else "888/h"))
def post(self, request, *args, **kwargs):
@ -104,8 +104,8 @@ class TokenVerifyView(TokenViewBase):
error response.
"""
serializer_class = TokenVerifySerializer # type: ignore
_serializer_class = TokenVerifySerializer # type: ignore
serializer_class = TokenVerifySerializer
_serializer_class = TokenVerifySerializer
def post(self, request, *args, **kwargs):
try: