Author SHA1 Message Date
Emma Eilefsen Glenna d5de4597c3 Fix: "Insufficient Funds" when user has exact amount 2024-02-15 19:36:04 +00:00
38 changed files with 1323 additions and 679 deletions

No files matched your search

-19
View File
@@ -1,19 +0,0 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
+8 -5
View File
@@ -2,27 +2,30 @@ variables:
PIP_CACHE_DIR: "${CI_PROJECT_DIR}/.cache/pip"
default:
image: nikolaik/python-nodejs:python3.9-nodejs20
image: nikolaik/python-nodejs:python3.8-nodejs16
cache:
paths:
- .cache/pip
- .venv
before_script:
- poetry config virtualenvs.in-project true
- poetry install
django-checks:
stage: build
script:
- uv run manage.py migrate
- uv run manage.py check
- poetry run python manage.py migrate
- poetry run python manage.py check
tests:
stage: test
script:
- uv run bin/test
- poetry run bin/test
lint:
stage: test
script:
- uv run bin/pre-commit
- poetry run bin/lint
deploy:
stage: deploy
+16 -10
View File
@@ -1,12 +1,18 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.11.7'
- repo: local
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/adamchainz/django-upgrade
rev: "1.24.0"
hooks:
- id: django-upgrade
args: [--target-version, "4.2"]
- id: black
name: black
entry: black
args: [--check]
language: system
require_serial: true
types: [python]
- id: isort
name: isort
entry: isort
args: [--check]
language: system
types: [python]
+7 -3
View File
@@ -4,9 +4,11 @@ Before starting, make sure you have a working python and nodejs setup with pyenv
```bash
apt-get install libmysqlclient-dev
poetry install
poetry shell
pre-commit install
uv run manage.py migrate
uv run bin/run
python manage.py migrate
bin/run
cp .env-default .env
# in another terminal
@@ -17,7 +19,7 @@ npm start
## References
- We use JS code generation to work with queries/mutations in our graphql API: <https://github.com/dotansimha/graphql-typed-document-node>
- We use JS code generation to work with queries/mutations in our graphql API: https://github.com/dotansimha/graphql-typed-document-node
## TODO
@@ -26,8 +28,10 @@ npm start
- inventory views
- Stats: Implement users tab
## TODO-old
- Larger pictures
- Compact GUI for max 768 vertical pixels
- Search right of in title
Regular → Executable
View File
File mode changed.
+2 -9
View File
@@ -1,20 +1,13 @@
from django.contrib import admin
from bbs.models import (
InventoryTransaction,
Order,
OrderLine,
Product,
Transaction,
UserProfile,
)
from bbs.models import *
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
pass
admin.site.register(Product, ProductAdmin)
admin.site.register(InventoryTransaction)
admin.site.register(Order)
admin.site.register(OrderLine)
+3 -1
View File
@@ -1,3 +1,5 @@
from typing import List
import strawberry
@@ -17,6 +19,6 @@ class FieldError(Error):
@strawberry.type
class FormErrors:
fields: list[FieldError]
fields: List[FieldError]
message: str = ""
error: bool = True
+9 -14
View File
@@ -1,4 +1,4 @@
from typing import Annotated, Union
from typing import List
import strawberry
from django.conf import settings
@@ -27,18 +27,16 @@ class CreateOrderSuccess:
@strawberry.type
class InsufficientFunds(Error): # noqa: N818
class InsufficientFunds(Error):
amount_lacking: float
CreateOrderResponse = Annotated[
Union[CreateOrderSuccess, InsufficientFunds, FormErrors], strawberry.union("CreateOrderResponse") # noqa: UP007
]
CreateOrderResponse = strawberry.union("CreateOrderResponse", [CreateOrderSuccess, InsufficientFunds, FormErrors])
@strawberry.mutation
@transaction.atomic
def create_order(customer_id: ID, order_lines: list[OrderLineInput], is_external: bool = False) -> CreateOrderResponse: # noqa: FBT001
def create_order(customer_id: ID, order_lines: List[OrderLineInput], is_external: bool = False) -> CreateOrderResponse:
# clean and validate
try:
user = UserModel.objects.get(pk=customer_id)
@@ -68,10 +66,9 @@ def create_order(customer_id: ID, order_lines: list[OrderLineInput], is_external
product.inventory_amount -= ol.amount
product.save()
if not is_external and user.profile.balance <= order_sum:
if not is_external and user.profile.balance < order_sum:
return InsufficientFunds(
message="Du har ikke nok penger på bok.",
amount_lacking=abs(order_sum - user.profile.balance),
message="Du har ikke nok penger på bok.", amount_lacking=abs(order_sum - user.profile.balance)
)
# create
@@ -94,14 +91,12 @@ class CreateDepositSuccess:
@strawberry.type
class AboveMaxSaldo(Error): # noqa: N818
class AboveMaxSaldo(Error):
max_saldo: float
above: float
CreateDepositResponse = Annotated[
Union[CreateDepositSuccess, AboveMaxSaldo, FormErrors], strawberry.union("CreateDepositResponse") # noqa: UP007
]
CreateDepositResponse = strawberry.union("CreateDepositResponse", [CreateDepositSuccess, AboveMaxSaldo, FormErrors])
@strawberry.mutation
@@ -123,7 +118,7 @@ def create_deposit(user_id: ID, amount: int) -> CreateDepositResponse:
fields=[FieldError(field="amount", message=message)],
)
# 1337 is special <3
if amount + user.profile.balance > settings.BBS_SALDO_MAX and amount + user.profile.balance != 1337: # noqa: PLR2004
if amount + user.profile.balance > settings.BBS_SALDO_MAX and amount + user.profile.balance != 1337:
return AboveMaxSaldo(
message="Du prøver å sette inn for mye penger",
max_saldo=settings.BBS_SALDO_MAX,
+6 -7
View File
@@ -1,12 +1,14 @@
import strawberry
import strawberry_django
from django.contrib.auth.models import User as DjangoUser
from django.contrib.auth import get_user_model
from django.db import transaction
from strawberry import auto
from strawberry_django import auto
from bbs import models
from bbs.api.types.users import User
DjangoUser = get_user_model()
@strawberry_django.input(DjangoUser)
class UserInput:
@@ -17,12 +19,9 @@ class UserInput:
@strawberry.mutation
@transaction.atomic
def register_mutation(input: UserInput) -> User: # noqa: A002
def register_mutation(input: UserInput) -> User:
user = DjangoUser.objects.create_user(
first_name=input.first_name,
last_name=input.last_name,
email=input.email,
username=input.email,
first_name=input.first_name, last_name=input.last_name, email=input.email, username=input.email
)
models.UserProfile.objects.create(user=user)
return user
+3 -1
View File
@@ -1,3 +1,5 @@
from typing import List
import strawberry
import strawberry_django
@@ -6,4 +8,4 @@ from bbs.api.types.inventory import Transaction, TransactionOrdering
@strawberry.type
class InventoryQueries:
transaction_list: list[Transaction] = strawberry_django.field(pagination=True, ordering=TransactionOrdering)
transaction_list: List[Transaction] = strawberry_django.field(pagination=True, order=TransactionOrdering)
+8 -17
View File
@@ -1,23 +1,17 @@
from typing import List
import strawberry
import strawberry_django
from bbs import models
from bbs.api.types.shop import (
Order,
OrderOrdering,
OrderStats,
OrderStatsByTime,
Point,
Product,
ProductOrderStat,
)
from bbs.api.types.shop import Order, OrderOrdering, OrderStats, OrderStatsByTime, Point, Product, ProductOrderStat
@strawberry.type
class ShopQueries:
all_products: list[Product] = strawberry_django.field()
all_orders: list[Order] = strawberry_django.field()
order_list: list[Order] = strawberry_django.field(pagination=True, ordering=OrderOrdering)
all_products: List[Product] = strawberry_django.field()
all_orders: List[Order] = strawberry_django.field()
order_list: List[Order] = strawberry_django.field(pagination=True, order=OrderOrdering)
@strawberry.field
def order_stats(self) -> OrderStats:
@@ -32,11 +26,8 @@ class ShopQueries:
)
@strawberry.field
def product_stats(self) -> list[ProductOrderStat]:
def product_stats(self) -> List[ProductOrderStat]:
return [
ProductOrderStat(
product_name=stat["product_name"],
data=[Point(**p) for p in stat["data"]],
)
ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]])
for stat in models.OrderLine.objects.product_order_stats().as_series()
]
+3 -1
View File
@@ -1,3 +1,5 @@
from typing import List
import strawberry
import strawberry_django
from django.contrib.auth.models import User as DjangoUser
@@ -8,7 +10,7 @@ from bbs.api.types.users import User
@strawberry.type
class UsersQueries:
all_users: list[User] = strawberry_django.field()
all_users: List[User] = strawberry_django.field()
@strawberry.field
def user(self, user_id: ID) -> User:
+2 -2
View File
@@ -1,6 +1,6 @@
import strawberry
from django.core.exceptions import ObjectDoesNotExist
from strawberry.extensions import SchemaExtension
from strawberry.extensions import Extension
from bbs.api.mutations.shop import ShopMutations
from bbs.api.mutations.users import UsersMutations
@@ -19,7 +19,7 @@ class Mutation(ShopMutations, UsersMutations):
pass
class HandleNotFound(SchemaExtension):
class HandleNotFound(Extension):
def on_request_end(self):
result = self.execution_context.result
if result.errors:
+4 -9
View File
@@ -1,16 +1,11 @@
from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django
from strawberry import auto
from strawberry_django import auto
from bbs import models
if TYPE_CHECKING:
from .users import User
from bbs.api.types.users import User
@strawberry_django.order_type(models.Transaction)
@strawberry_django.ordering.order(models.Transaction)
class TransactionOrdering:
created: auto
@@ -18,6 +13,6 @@ class TransactionOrdering:
@strawberry_django.type(models.Transaction)
class Transaction:
id: auto
user: Annotated["User", strawberry.lazy(".users")]
user: "User"
amount: auto
created: auto
+11 -14
View File
@@ -1,17 +1,14 @@
from typing import TYPE_CHECKING, Annotated
from typing import List
import strawberry
import strawberry_django
from strawberry import auto
from strawberry_django import auto
from bbs import models
from bbs.api.types.utils import JSONScalar
if TYPE_CHECKING:
from .users import User
@strawberry_django.filter_type(models.Product)
@strawberry_django.filters.filter(models.Product)
class ProductFilter:
id: auto
active: auto
@@ -28,7 +25,7 @@ class Product:
user_counts: JSONScalar
@strawberry_django.order_type(models.Order)
@strawberry_django.ordering.order(models.Order)
class OrderOrdering:
created: auto
@@ -36,10 +33,10 @@ class OrderOrdering:
@strawberry_django.type(models.Order)
class Order:
id: auto
customer: Annotated["User", strawberry.lazy(".users")]
customer: strawberry.LazyType["User", "bbs.api.types.users"]
order_sum: auto
created: auto
orderlines: list["OrderLine"]
orderlines: List["OrderLine"]
is_external: bool
@@ -60,10 +57,10 @@ class OrderStatsByTime:
@strawberry.type
class OrderStats:
yearly: list[OrderStatsByTime]
monthly: list[OrderStatsByTime]
daily: list[OrderStatsByTime]
hourly: list[OrderStatsByTime]
yearly: List[OrderStatsByTime]
monthly: List[OrderStatsByTime]
daily: List[OrderStatsByTime]
hourly: List[OrderStatsByTime]
@strawberry.type
@@ -75,4 +72,4 @@ class Point:
@strawberry.type
class ProductOrderStat:
product_name: str
data: list[Point]
data: List[Point]
+9 -9
View File
@@ -1,14 +1,17 @@
from datetime import datetime
from typing import List
from decimal import Decimal
import strawberry
import strawberry_django
from django.contrib.auth.models import User as DjangoUser
from strawberry import auto
from django.contrib.auth import get_user_model
from strawberry_django import auto
from bbs import models
from bbs.api.types.shop import Point, ProductOrderStat
UserModel = get_user_model()
@strawberry.type
class TopMonth:
@@ -26,21 +29,18 @@ class UserProfile:
order_sum_total: Decimal
@strawberry.field()
def top_months(self) -> list[TopMonth]:
def top_months(self) -> List[TopMonth]:
return [TopMonth(**month) for month in self.top_months()]
@strawberry.field()
def product_order_stats(self) -> list[ProductOrderStat]:
def product_order_stats(self) -> List[ProductOrderStat]:
return [
ProductOrderStat(
product_name=stat["product_name"],
data=[Point(**p) for p in stat["data"]],
)
ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]])
for stat in self.product_order_stats().as_series()
]
@strawberry_django.type(DjangoUser)
@strawberry_django.type(UserModel)
class User:
id: auto
first_name: auto
+2 -8
View File
@@ -5,18 +5,12 @@ from bbs.models import InventoryTransaction, Product
MAX_DIGITS = 9
DECIMAL_PLACES = 1
class InventoryTransactionForm(forms.ModelForm):
unit_price = forms.DecimalField(
max_digits=MAX_DIGITS,
decimal_places=DECIMAL_PLACES,
required=True,
localize=True,
)
unit_price = forms.DecimalField(max_digits=MAX_DIGITS, decimal_places=DECIMAL_PLACES, required=True, localize=True)
def __init__(self, *args, **kwargs):
# Only show active products
super().__init__(*args, **kwargs)
super(InventoryTransactionForm, self).__init__(*args, **kwargs)
self.fields["product"].queryset = Product.objects.filter(active=True)
class Meta:
+2 -2
View File
@@ -13,11 +13,11 @@ class Command(BaseCommand):
def add_arguments(self, parser: CommandParser):
parser.add_argument("--filename", default="schema.graphql", help="`-` prints to stdout")
def handle(self, *args, filename=None, **options): # noqa: ARG002
def handle(self, *args, filename=None, **options):
schema_output = print_schema(schema_symbol)
if filename != "-":
with Path(settings.BASE_DIR, filename).open("w+") as fp:
fp.write(schema_output)
self.stdout.write(self.style.SUCCESS("OK"))
else:
pass
print(schema_output)
+4 -3
View File
@@ -10,10 +10,11 @@ class Command(BaseCommand):
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--force", action="store_true")
def handle(self, *args, dry_run=False, force=False, **options): # noqa: ARG002
def handle(self, *args, dry_run=False, force=False, **options):
for profile in UserProfile.objects.all().order_by("id"):
if dry_run:
pass
elif force or not profile.image:
print(profile.user.email, profile.image_url())
else:
if force or not profile.image:
profile.image = profile.image_url()
profile.save()
+2 -1
View File
@@ -3,6 +3,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
@@ -179,6 +180,6 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name="orderline",
unique_together={("order", "product")},
unique_together=set([("order", "product")]),
),
]
@@ -4,6 +4,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bbs", "0001_initial"),
]
+5 -4
View File
@@ -4,14 +4,15 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bbs", "0002_alter_inventorytransaction_amount_and_more"),
('bbs', '0002_alter_inventorytransaction_amount_and_more'),
]
operations = [
migrations.AlterField(
model_name="order",
name="order_sum",
field=models.DecimalField(db_column="sum", decimal_places=1, max_digits=9),
model_name='order',
name='order_sum',
field=models.DecimalField(db_column='sum', decimal_places=1, max_digits=9),
),
]
+18 -21
View File
@@ -1,7 +1,8 @@
import hashlib # noqa: EXE002
import hashlib
from datetime import datetime
from decimal import Decimal
from itertools import groupby
from typing import Type
from decimal import Decimal
from django.conf import settings
from django.db import models
@@ -86,26 +87,21 @@ class Product(models.Model):
return _format_counts(per_user)
def __str__(self): # noqa: DJ012
def __str__(self):
return self.name
class Meta: # noqa: DJ012
class Meta:
ordering = ["name"]
class OrderManager(models.Manager):
def count_by_created(self, extract_class: type[Extract]):
def count_by_created(self, extract_class: Type[Extract]):
period = extract_class("created")
return self.get_queryset().annotate(period=period).values("period").annotate(count=Count("id"))
def count_by_created_period(self, period: str):
periods = {
"yearly": ExtractYear,
"monthly": ExtractMonth,
"daily": ExtractIsoWeekDay,
"hourly": ExtractHour,
}
assert period in periods # noqa: S101
periods = {"yearly": ExtractYear, "monthly": ExtractMonth, "daily": ExtractIsoWeekDay, "hourly": ExtractHour}
assert period in periods
return self.count_by_created(periods[period])
@@ -128,7 +124,7 @@ class Order(models.Model):
def reciept(self):
return ", ".join([str(ol) for ol in self.orderlines.all()])
def __str__(self): # noqa: DJ012
def __str__(self):
return f"{self.customer or EXTERNAL_USER}: {self.order_sum} kr"
@@ -167,10 +163,10 @@ class OrderLine(models.Model):
def price(self):
return self.amount * self.unit_price
def __str__(self): # noqa: DJ012
def __str__(self):
return f"{self.amount} {self.product} ({self.unit_price} kr per)"
class Meta: # noqa: DJ012
class Meta:
unique_together = ("order", "product")
@@ -182,7 +178,7 @@ class Transaction(models.Model):
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.id}: {self.user}: {self.amount}"
return f"{self.id}: { self.user}: {self.amount}"
class InventoryTransaction(models.Model):
@@ -210,7 +206,7 @@ class InventoryTransaction(models.Model):
def price(self):
return self.amount * self.unit_price
def __str__(self): # noqa: DJ012
def __str__(self):
return f"{self.id}: {self.user}: {self.product}. {self.amount:.0f} stykker ({self.unit_price} kr per)"
@@ -224,7 +220,8 @@ class UserProfile(models.Model):
def last_purchase_date(self) -> datetime:
last = self.last_purchase()
return last.created if last else datetime.min # noqa: DTZ901
last_p = last.created if last else datetime.min
return last_p
def top_months(self, limit=5):
return (
@@ -243,16 +240,16 @@ class UserProfile(models.Model):
def image_url(self) -> str:
if self.user.email:
md5_hash = hashlib.md5(self.user.email.lower().strip().encode()).hexdigest() # noqa: S324
md5_hash = hashlib.md5(self.user.email.lower().strip().encode()).hexdigest()
return f"https://www.gravatar.com/avatar/{md5_hash}?d=identicon"
return ""
def save(self, **kwargs): # noqa: DJ012
def save(self, **kwargs):
if not self.image:
self.image = self.image_url()
super().save(**kwargs)
def __str__(self): # noqa: DJ012
def __str__(self):
return f"{self.user}"
+14 -13
View File
@@ -1,13 +1,12 @@
# Django settings for bbs project. # noqa: EXE002
import contextlib
# Django settings for bbs project.
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # noqa: PTH120
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DEBUG = True
ADMINS = (
# ('Your Name', 'your_email@example.com'), # noqa: ERA001
# ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
@@ -21,18 +20,18 @@ DATABASES = {
TIME_ZONE = "Europe/Oslo"
LANGUAGE_CODE = "nb"
USE_I18N = True
USE_TZ = True
USE_L10N = True
SITE_ID = 1
# Media and uploads
MEDIA_ROOT = os.path.join(BASE_DIR, "media") # noqa: PTH118
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
# Static files
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") # noqa: PTH118
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STATIC_URL = "/static/"
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),) # noqa: PTH118
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
@@ -40,13 +39,13 @@ STATICFILES_FINDERS = (
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = "osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss" # noqa: S105
SECRET_KEY = "osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
os.path.join(BASE_DIR, "templates"), # noqa: PTH118
os.path.join(BASE_DIR, "templates"),
],
"APP_DIRS": True,
"OPTIONS": {
@@ -98,7 +97,7 @@ LOGIN_REDIRECT_URL = "/"
SESSION_COOKIE_AGE = 1209600 # 15 minutes
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
BBS_SALDO_MAX = 1000
BBS_SALDO_MAX = 600
BBS_LIMIT_DEPOSITS = False
# Thumbnails
@@ -142,5 +141,7 @@ CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
]
with contextlib.suppress(ImportError):
from .local_settings import * # noqa: F403
try:
from .local_settings import *
except ImportError:
pass
+4 -8
View File
@@ -1,4 +1,4 @@
import pytest # noqa: INP001
import pytest
from django.test import Client
from strawberry.django.test import GraphQLTestClient
@@ -7,21 +7,17 @@ from bbs.models import Order, Product
@pytest.fixture(name="gql_client")
def gql_client_fixture(client: Client):
return GraphQLTestClient(client)
yield GraphQLTestClient(client)
@pytest.fixture(name="product")
def product_fix(db): # noqa: ARG001
def product_fix(db):
return Product.objects.create(name="borg", sale_price_ext=35, sale_price_int=30)
def test_create_order(product, gql_client):
assert Order.objects.count() == 0
variables = {
"customerId": "external",
"isExternal": True,
"orderLines": [{"productId": product.id, "amount": 1}],
}
variables = dict(customerId="external", isExternal=True, orderLines=[{"productId": product.id, "amount": 1}])
query = """
mutation CreateOrder($customerId: ID!, $isExternal: Boolean!, $orderLines: [OrderLineInput!]!) {
createOrder(customerId: $customerId, isExternal: $isExternal, orderLines: $orderLines) {
+6 -11
View File
@@ -2,8 +2,7 @@ from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from django.urls import path, re_path
from strawberry.django.views import GraphQLView
from bbs.api.schema import schema
@@ -13,17 +12,13 @@ admin.autodiscover()
urlpatterns = [
# TODO: migrate reports to SPA
path("inventory/", inventory, name="inventory"),
path("inventory/add/", inventory_add, name="inventory-add"),
path("report", report, name="report"),
re_path(r"^inventory/$", inventory, name="inventory"),
re_path(r"^inventory/add/$", inventory_add, name="inventory-add"),
re_path(r"^report$", report, name="report"),
path("accounts/login/", LoginView.as_view(), name="django.contrib.auth.views.login"),
path(
"accounts/logout/",
LogoutView.as_view(),
name="django.contrib.auth.views.logout",
),
path("accounts/logout/", LogoutView.as_view(), name="django.contrib.auth.views.logout"),
path("admin/", admin.site.urls),
path("graphql/", csrf_exempt(GraphQLView.as_view(schema=schema)), name="graphql"),
path("graphql/", GraphQLView.as_view(schema=schema), name="graphql"),
]
urlpatterns += static("/media", document_root=settings.MEDIA_ROOT)
+14 -13
View File
@@ -10,12 +10,12 @@ from bbs.models import InventoryTransaction, Order, Product, Transaction, UserPr
def report(request):
# FIXME: split this up in smaller pieces
datetime_now = datetime.now() # noqa: DTZ005
start_time = datetime.now() - timedelta(days=30) # noqa: DTZ005
datetime_now = datetime.now()
start_time = datetime.now() - timedelta(days=30)
# start of day, 1 month
start_time = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
# end of day
end_time = datetime.now().replace(hour=23, minute=59, second=59, microsecond=999) # noqa: DTZ005
end_time = datetime.now().replace(hour=23, minute=59, second=59, microsecond=999)
if request.GET.get("start_time", False) or request.GET.get("end_time", False):
form = DateRangeForm(request.GET)
@@ -50,14 +50,14 @@ def report(request):
k: {
"name": k,
"transactions": group,
"transactions_sum": sum(x["transactions__price"] for x in group),
"transactions_units": sum(x["transactions__amount"] for x in group),
"transactions_sum": sum(map(lambda x: x["transactions__price"], group)),
"transactions_units": sum(map(lambda x: x["transactions__amount"], group)),
}
}
)
itrans = InventoryTransaction.objects.filter(created__range=[start_time, end_time])
inv_in = sum(x[1].get("transactions_sum", 0) for x in inventory_products.items())
inv_in = sum(map(lambda x: x[1].get("transactions_sum", 0), inventory_products.items()))
# out
out_products = (
@@ -75,8 +75,8 @@ def report(request):
product = {
"name": k,
"orderlines": group,
"orderlines_sum": sum(x["orderlines__price"] for x in group),
"orderlines_units": sum(x["orderlines__amount"] for x in group),
"orderlines_sum": sum(map(lambda x: x["orderlines__price"], group)),
"orderlines_units": sum(map(lambda x: x["orderlines__amount"], group)),
}
if k in inventory_products:
# add diff
@@ -88,7 +88,7 @@ def report(request):
p[1]["value_diff"] = p[1].get("transactions_sum", 0) - p[1].get("orderlines_sum", 0)
p[1]["units_diff"] = p[1].get("transactions_units", 0) - p[1].get("orderlines_units", 0)
inv_out = sum(x[1].get("orderlines_sum", 0) for x in inventory_products.items())
inv_out = sum(map(lambda x: x[1].get("orderlines_sum", 0), inventory_products.items()))
inv_diff = inv_in - inv_out
@@ -110,7 +110,7 @@ def report(request):
purchases_diff = purchases_end - purchases_start
saldo_diff = saldo_end - saldo_start
return render(request, "report.html", locals()) # noqa: DJ003
return render(request, "report.html", locals())
def inventory(request):
@@ -120,7 +120,7 @@ def inventory(request):
wholesale_value = sum([p.wholesale_value for p in products_active])
num_products = sum(products_active.values_list("inventory_amount", flat=True))
return render(request, "inventory.html", locals()) # noqa: DJ003
return render(request, "inventory.html", locals())
def inventory_add(request):
@@ -133,12 +133,13 @@ def inventory_add(request):
itrans.user = request.user
itrans.product.save()
itrans.save()
messages.success(request, f"Satt inn {itrans.amount} stk {itrans.product}.")
messages.success(request, "Satt inn {0} stk {1}.".format(itrans.amount, itrans.product))
return redirect("inventory-add")
else:
messages.error(request, "Feil med skjemaet, se under.")
form = InventoryTransactionForm(data=request.POST)
else:
form = InventoryTransactionForm()
return render(request, "inventory_add.html", locals()) # noqa: DJ003
return render(request, "inventory_add.html", locals())
+3 -5
View File
@@ -1,8 +1,6 @@
#!/bin/bash
set -eu
source "$HOME/.local/bin/env"
export VITE_API_URL="https://bbs.neuf.no/api"
PROJECT_PATH=/var/www/neuf.no/bbs
PROCESS_NAME="bbs.neuf.no"
@@ -12,9 +10,9 @@ cd "$PROJECT_PATH" || exit 1
git pull
# Backend
uv sync
umask 022; uv run manage.py collectstatic --noinput # Collect static
uv run manage.py migrate # Run DB migrations
poetry install
umask 022; poetry run python manage.py collectstatic --noinput # Collect static
poetry run python manage.py migrate # Run DB migrations
# Frontend
bin/build-frontend
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
SRC_DIR="bbs"
black $SRC_DIR
isort $SRC_DIR
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
SRC_DIR="bbs"
black --check $SRC_DIR
isort --diff $SRC_DIR
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
uvx pre-commit run --all-files
+1 -1
View File
@@ -1 +1 @@
v20
v16
+2 -1
View File
@@ -52,7 +52,8 @@
"vite-plugin-svgr": "^1.1.0"
},
"engines": {
"node": "20"
"node": ">=16",
"npm": ">=7"
}
},
"node_modules/@ampproject/remapping": {
+2 -1
View File
@@ -60,6 +60,7 @@
"vite-plugin-svgr": "^1.1.0"
},
"engines": {
"node": "20"
"node": ">=16",
"npm": ">=7"
}
}
-3
View File
@@ -1,3 +0,0 @@
[tools]
python = "3.9"
uv = "latest"
Generated
+1103
View File
File diff suppressed because it is too large. Load diff
+37 -49
View File
@@ -1,55 +1,43 @@
[project]
name = "bbs"
version = "0.1.0"
description = ""
authors = [{ name = "Nikolai R Kristiansen", email = "nikolaik@gmail.com" }]
requires-python = ">=3.9,<3.10"
license = "MIT"
dependencies = [
"django-cors-headers>=3.10.0,<4",
"django-extensions>=3.1.3,<4",
"django-rename-app>=0.1.3,<0.2",
"Django==4.2.*",
"easy-thumbnails>=2.8.1,<3",
"gunicorn>=20.1.0,<21",
"mysqlclient>=2.0.3,<3",
"Pillow>=9.0.0,<10",
"sentry-sdk>=1.5.5,<2",
"strawberry-graphql-django==0.60.*",
"strawberry-graphql==0.270.*",
]
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "bbs.settings"
[dependency-groups]
dev = [
"ruff",
"pytest-django",
"pytest",
]
[tool.ruff]
[tool.black]
line-length = 120
target-version = "py310"
target_version = ['py39']
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"D", # pydocstyle: Allow undocumented code
"ANN", # flake8-annotations: Allow untyped code
"TD", "FIX", # Allow TODOs and FIXMEs
"RUF012", # FIXME: Fix later https://docs.astral.sh/ruff/rules/mutable-class-default/
"FBT002", # Allow boolean args
"G004", # Allow logging using f-string
"EM101", "EM102", # Allow string in exceptions (prioritice code readability vs exception time readability)
"TRY003", # Allow long exception messages
"E501", # Allow long lines
"COM812", "ISC001", # Avoid conflicting with ruff formatter
]
[tool.isort]
profile = "black"
line_length = 120
[tool.ruff.lint.per-file-ignores]
# Testing code can deviate a bit more
"bbs/tests/*.py" = ["S101", "S106", "PLR2004"]
"**/test_*.py" = ["S101", "S106", "PLR2004"]
"**/tests.py" = ["S101", "S106", "PLR2004"]
[tool.poetry]
name = "bbs"
version = "0.1.0"
description = ""
authors = ["Nikolai R Kristiansen <nikolaik@gmail.com>"]
license = "MIT"
[tool.poetry.dependencies]
python = "~3.8"
django-extensions = "^3.1.3"
Django = "^4.0.0"
easy-thumbnails = "^2.8.1"
gunicorn = "^20.1.0"
mysqlclient = "^2.0.3"
Pillow = "^9.0.0"
sentry-sdk = "^1.5.5"
strawberry-graphql = "^0.97.0"
strawberry-graphql-django = "^0.2.5"
django-cors-headers = "^3.10.0"
django-rename-app = "^0.1.3"
[tool.poetry.dev-dependencies]
pytest = "*"
black = "*"
pytest-django = "*"
pre-commit = "*"
isort = "*"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Generated
-411
View File
@@ -1,411 +0,0 @@
version = 1
revision = 2
requires-python = "==3.9.*"
[[package]]
name = "asgiref"
version = "3.8.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" },
]
[[package]]
name = "bbs"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "django" },
{ name = "django-cors-headers" },
{ name = "django-extensions" },
{ name = "django-rename-app" },
{ name = "easy-thumbnails" },
{ name = "gunicorn" },
{ name = "mysqlclient" },
{ name = "pillow" },
{ name = "sentry-sdk" },
{ name = "strawberry-graphql" },
{ name = "strawberry-graphql-django" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
{ name = "pytest-django" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "django", specifier = "==4.2.*" },
{ name = "django-cors-headers", specifier = ">=3.10.0,<4" },
{ name = "django-extensions", specifier = ">=3.1.3,<4" },
{ name = "django-rename-app", specifier = ">=0.1.3,<0.2" },
{ name = "easy-thumbnails", specifier = ">=2.8.1,<3" },
{ name = "gunicorn", specifier = ">=20.1.0,<21" },
{ name = "mysqlclient", specifier = ">=2.0.3,<3" },
{ name = "pillow", specifier = ">=9.0.0,<10" },
{ name = "sentry-sdk", specifier = ">=1.5.5,<2" },
{ name = "strawberry-graphql", specifier = "==0.270.*" },
{ name = "strawberry-graphql-django", specifier = "==0.60.*" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pytest" },
{ name = "pytest-django" },
{ name = "ruff" },
]
[[package]]
name = "certifi"
version = "2025.4.26"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "django"
version = "4.2.21"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c1/bb/2fad5edc1af2945cb499a2e322ac28e4714fc310bd5201ed1f5a9f73a342/django-4.2.21.tar.gz", hash = "sha256:b54ac28d6aa964fc7c2f7335138a54d78980232011e0cd2231d04eed393dcb0d", size = 10424638, upload-time = "2025-05-07T14:07:07.992Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/4f/aeaa3098da18b625ed672f3da6d1cd94e188d1b2cc27c2c841b2f9666282/django-4.2.21-py3-none-any.whl", hash = "sha256:1d658c7bf5d31c7d0cac1cab58bc1f822df89255080fec81909256c30e6180b3", size = 7993839, upload-time = "2025-05-07T14:07:01.318Z" },
]
[[package]]
name = "django-cors-headers"
version = "3.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bc/f4/8b8c3d5e9a0aeea43576fbe623599052a7699abb54378ddb44adb1ef1ed3/django_cors_headers-3.14.0.tar.gz", hash = "sha256:5fbd58a6fb4119d975754b2bc090f35ec160a8373f276612c675b00e8a138739", size = 24892, upload-time = "2023-02-25T07:20:18.41Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/45/f1/972b3a183192841b811e4b7496de0a31c09ae1d83a5391743c8ae0cbd86e/django_cors_headers-3.14.0-py3-none-any.whl", hash = "sha256:684180013cc7277bdd8702b80a3c5a4b3fcae4abb2bf134dceb9f5dfe300228e", size = 13187, upload-time = "2023-02-25T07:20:16.376Z" },
]
[[package]]
name = "django-extensions"
version = "3.2.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8a/f1/318684c9466968bf9a9c221663128206e460c1a67f595055be4b284cde8a/django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a", size = 277216, upload-time = "2023-06-05T17:09:01.447Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/7e/ba12b9660642663f5273141018d2bec0a1cae1711f4f6d1093920e157946/django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401", size = 229868, upload-time = "2023-06-05T17:08:58.197Z" },
]
[[package]]
name = "django-rename-app"
version = "0.1.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/70/f6/0cb0c7169584e8f87c229e6336ce98f90cbb911ae374e2a7683c25e872b4/django_rename_app-0.1.7.tar.gz", hash = "sha256:f1236dacfbaf06d63aa53a665333c9e0b4943ef41019aae7626dd5fb5ffa69b5", size = 8291, upload-time = "2023-11-08T08:22:05.25Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/4b/6a15f0a25003f7c563218722ee02968f008bedbc4e9d0c08fe9344421d5c/django_rename_app-0.1.7-py2.py3-none-any.whl", hash = "sha256:68ed0dbc9fc167b72cf2f3ba6b464d7981a42a0e09070476cf94f1f603f7a433", size = 8455, upload-time = "2023-11-08T08:22:03.782Z" },
]
[[package]]
name = "easy-thumbnails"
version = "2.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
{ name = "pillow" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/3b/5fe5c2c20cb08a34759cbd1ca9b8394b121785ebcb033c010a4b27b65a7a/easy_thumbnails-2.10.tar.gz", hash = "sha256:d009462fcd791edf2496e5ed46d044712e1a06b7d4600e4cf2812002e9904ef5", size = 80721, upload-time = "2024-09-12T12:41:00.361Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/ef/de1f3a7421d7a9ce2314ee7bc0d37a8264a50872e2cd5af1240798b0bbba/easy_thumbnails-2.10-py3-none-any.whl", hash = "sha256:9e446fb9da1ca0cbc99bdd1fe8fdd5c38784323d64393a37e16d13cd736980b2", size = 78939, upload-time = "2024-09-12T12:40:59.135Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
]
[[package]]
name = "graphql-core"
version = "3.2.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c4/16/7574029da84834349b60ed71614d66ca3afe46e9bf9c7b9562102acb7d4f/graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab", size = 505353, upload-time = "2025-01-26T16:36:27.374Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/4f/7297663840621022bc73c22d7d9d80dbc78b4db6297f764b545cd5dd462d/graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f", size = 203416, upload-time = "2025-01-26T16:36:24.868Z" },
]
[[package]]
name = "gunicorn"
version = "20.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/28/5b/0d1f0296485a6af03366604142ea8f19f0833894db3512a40ed07b2a56dd/gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8", size = 370601, upload-time = "2021-03-27T01:54:37.202Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/dd/5b190393e6066286773a67dfcc2f9492058e9b57c4867a95f1ba5caf0a83/gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", size = 79531, upload-time = "2021-04-27T12:16:23.375Z" },
]
[[package]]
name = "iniconfig"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
[[package]]
name = "mysqlclient"
version = "2.2.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/61/68/810093cb579daae426794bbd9d88aa830fae296e85172d18cb0f0e5dd4bc/mysqlclient-2.2.7.tar.gz", hash = "sha256:24ae22b59416d5fcce7e99c9d37548350b4565baac82f95e149cac6ce4163845", size = 91383, upload-time = "2025-01-10T12:06:00.763Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/e0/524b0777524e0d410f71987f556dd6a0e3273fdb06cd6e91e96afade7220/mysqlclient-2.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:199dab53a224357dd0cb4d78ca0e54018f9cee9bf9ec68d72db50e0a23569076", size = 207857, upload-time = "2025-01-10T11:56:33.666Z" },
{ url = "https://files.pythonhosted.org/packages/20/40/b5d03494c1caa8f4da171db41d8d9d5b0d8959f893761597d97420083362/mysqlclient-2.2.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:977e35244fe6ef44124e9a1c2d1554728a7b76695598e4b92b37dc2130503069", size = 207965, upload-time = "2025-01-10T11:56:37.252Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pillow"
version = "9.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/00/d5/4903f310765e0ff2b8e91ffe55031ac6af77d982f0156061e20a4d1a8b2d/Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1", size = 50488147, upload-time = "2023-04-01T09:31:37.159Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/46/672289c0ff87733fb93854dedf3a8d65642a25c0bfc88e7f6d722f9161a5/Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66", size = 3395750, upload-time = "2023-04-01T09:30:10.542Z" },
{ url = "https://files.pythonhosted.org/packages/a9/70/9259e93534d01f846f7d0501f19bb7d8cc1751741bc20826fc8d3a20fe32/Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e", size = 3077133, upload-time = "2023-04-01T09:30:13.386Z" },
{ url = "https://files.pythonhosted.org/packages/95/62/8a943681db5f6588498ed86aa1568dd31c63f6afdabe50841589fc662c68/Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115", size = 3112534, upload-time = "2023-04-01T09:30:15.616Z" },
{ url = "https://files.pythonhosted.org/packages/f2/43/0892913d499c8df2c88dee69d59e77de19e0c51754a9be82023880641c09/Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3", size = 3386725, upload-time = "2023-04-01T09:30:19.221Z" },
{ url = "https://files.pythonhosted.org/packages/ff/fc/48a51c0fe2a00d5def57b9981a1e0f8339b516351da7a51500383d833bc8/Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef", size = 3308605, upload-time = "2023-04-01T09:30:29.279Z" },
{ url = "https://files.pythonhosted.org/packages/af/b7/f9faf80e3c93b02712c5748f10c75a8948e74eca61ec2408f7e1d4c9dd16/Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705", size = 3164057, upload-time = "2023-04-01T09:30:32.484Z" },
{ url = "https://files.pythonhosted.org/packages/3b/2b/57915b8af178e2c20bfd403ffed4521947881f9dbbfbaba48210dc59b9d7/Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1", size = 3415613, upload-time = "2023-04-01T09:30:34.978Z" },
{ url = "https://files.pythonhosted.org/packages/e7/2a/f3ed578595f8486ee2cc07434460097d89aedd406a3db849b890ca8ec416/Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a", size = 3350667, upload-time = "2023-04-01T09:30:37.849Z" },
{ url = "https://files.pythonhosted.org/packages/28/a2/f2d0d584d45100a5419fd70a1233ade8f12469ffe6e8e3acd40364beaadb/Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865", size = 3414552, upload-time = "2023-04-01T09:30:48.682Z" },
{ url = "https://files.pythonhosted.org/packages/51/3a/a6701b987007aaa43559b7d8510629845b25686f09a0eb29f8946a62d767/Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964", size = 2229361, upload-time = "2023-04-01T09:30:51.739Z" },
{ url = "https://files.pythonhosted.org/packages/69/72/48cc52bff8731cf72bc4101e34dc44807a410c171f921afb582a511da50e/Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d", size = 2538580, upload-time = "2023-04-01T09:30:54.345Z" },
{ url = "https://files.pythonhosted.org/packages/24/35/92032a00f41bea9bf93f19d48f15daac27d1365c0038fe22dc4e7fc7c8b0/Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572", size = 3349772, upload-time = "2023-04-01T09:31:13.438Z" },
{ url = "https://files.pythonhosted.org/packages/50/ce/d39869c22904558ce32e664904cf72f13a9d47703b72392e881d9e7b6082/Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe", size = 3281583, upload-time = "2023-04-01T09:31:16.7Z" },
{ url = "https://files.pythonhosted.org/packages/7a/75/4a382d1567efc6f4e3054f693167f8ce2d1ad939c5f6f12aa5c50f74b997/Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1", size = 3222603, upload-time = "2023-04-01T09:31:19.548Z" },
{ url = "https://files.pythonhosted.org/packages/51/d2/c10f72c44e000d08e41f822083cf322bb59afa7ed01ae7e3e47875b47600/Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7", size = 3298174, upload-time = "2023-04-01T09:31:23.005Z" },
{ url = "https://files.pythonhosted.org/packages/02/4a/d362f7f44f1e5801c6726f0eaaeaf869d0d43c554b717072b2c5540cefb4/Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799", size = 2538628, upload-time = "2023-04-01T09:31:26.082Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pytest"
version = "8.3.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "tomli" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" },
]
[[package]]
name = "pytest-django"
version = "4.11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/fb/55d580352db26eb3d59ad50c64321ddfe228d3d8ac107db05387a2fadf3a/pytest_django-4.11.1.tar.gz", hash = "sha256:a949141a1ee103cb0e7a20f1451d355f83f5e4a5d07bdd4dcfdd1fd0ff227991", size = 86202, upload-time = "2025-04-03T18:56:09.338Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/be/ac/bd0608d229ec808e51a21044f3f2f27b9a37e7a0ebaca7247882e67876af/pytest_django-4.11.1-py3-none-any.whl", hash = "sha256:1b63773f648aa3d8541000c26929c1ea63934be1cfa674c76436966d73fe6a10", size = 25281, upload-time = "2025-04-03T18:56:07.678Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "ruff"
version = "0.11.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289, upload-time = "2025-05-29T13:31:40.037Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597, upload-time = "2025-05-29T13:30:57.539Z" },
{ url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154, upload-time = "2025-05-29T13:31:00.865Z" },
{ url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048, upload-time = "2025-05-29T13:31:03.413Z" },
{ url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062, upload-time = "2025-05-29T13:31:05.539Z" },
{ url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152, upload-time = "2025-05-29T13:31:07.986Z" },
{ url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067, upload-time = "2025-05-29T13:31:10.57Z" },
{ url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807, upload-time = "2025-05-29T13:31:12.88Z" },
{ url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261, upload-time = "2025-05-29T13:31:15.236Z" },
{ url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601, upload-time = "2025-05-29T13:31:18.68Z" },
{ url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186, upload-time = "2025-05-29T13:31:21.216Z" },
{ url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032, upload-time = "2025-05-29T13:31:23.417Z" },
{ url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370, upload-time = "2025-05-29T13:31:25.777Z" },
{ url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529, upload-time = "2025-05-29T13:31:28.396Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642, upload-time = "2025-05-29T13:31:30.647Z" },
{ url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511, upload-time = "2025-05-29T13:31:32.917Z" },
{ url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573, upload-time = "2025-05-29T13:31:35.782Z" },
{ url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770, upload-time = "2025-05-29T13:31:38.009Z" },
]
[[package]]
name = "sentry-sdk"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/28/02c0cd9184f9108e3c52519f9628b215077a3854240e0b17ae845e664855/sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26", size = 244774, upload-time = "2024-07-26T13:48:32.375Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/9f/105366a122efa93f0cb1914f841747d160788e4d022d0488d2d44c2ba26c/sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1", size = 267163, upload-time = "2024-07-26T13:48:29.38Z" },
]
[[package]]
name = "setuptools"
version = "80.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "sqlparse"
version = "0.5.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
]
[[package]]
name = "strawberry-graphql"
version = "0.270.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "graphql-core" },
{ name = "packaging" },
{ name = "python-dateutil" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/87/07/b4c411266038f9d367ce3307cf28cac74772771dfe6b4a87219311d59c90/strawberry_graphql-0.270.4.tar.gz", hash = "sha256:cca958a6070bcc2aca73a938cd80c0c7b4d39aecc8140a228608025896be990c", size = 207381, upload-time = "2025-05-29T10:40:31.544Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/de/3c7bbd77374fe814d671c607a61d6ba6cc6c25983e7cda6fa391e5c8450b/strawberry_graphql-0.270.4-py3-none-any.whl", hash = "sha256:93c6bd3bde85641ec81655c939b2daa09583e41048f94984a523539c20a8550d", size = 301421, upload-time = "2025-05-29T10:40:28.811Z" },
]
[[package]]
name = "strawberry-graphql-django"
version = "0.60.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "django" },
{ name = "strawberry-graphql" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/83/a0a902db2080469ea5f468d8077063d1c81cca99c89ae0bce6411e2e1bed/strawberry_graphql_django-0.60.0.tar.gz", hash = "sha256:a5d210bd4458be298f86d8b1b8d01ce0208291b5b6b227cd0cfd97c51cf71ee8", size = 84511, upload-time = "2025-05-24T10:01:20.863Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/34/a7/86c098eb3407170230fe95053e1ee0283e5bac5323d90f04e6544fdd6692/strawberry_graphql_django-0.60.0-py3-none-any.whl", hash = "sha256:6c37e9e0fe43a947732f15b43900720171e77f2f66915456a54e18bb32779772", size = 105807, upload-time = "2025-05-24T10:01:19.178Z" },
]
[[package]]
name = "tomli"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
]
[[package]]
name = "typing-extensions"
version = "4.13.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" },
]
[[package]]
name = "tzdata"
version = "2025.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
]
[[package]]
name = "urllib3"
version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
]