Sith/counter/api.py
2024-12-17 17:23:13 +01:00

145 lines
4.8 KiB
Python

#
# Copyright 2024 AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# You can find the source code of the website at https://github.com/ae-utbm/sith
#
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from django.conf import settings
from django.db.models import F
from django.shortcuts import get_object_or_404
from ninja import Query
from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema
from core.api_permissions import CanAccessLookup, CanView, IsInGroup, IsRoot
from counter.models import Counter, Product, ProductType
from counter.schemas import (
CounterFilterSchema,
CounterSchema,
ProductFilterSchema,
ProductSchema,
ProductTypeSchema,
ReorderProductTypeSchema,
SimpleProductSchema,
SimplifiedCounterSchema,
)
IsCounterAdmin = (
IsRoot
| IsInGroup(settings.SITH_GROUP_COUNTER_ADMIN_ID)
| IsInGroup(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
)
@api_controller("/counter")
class CounterController(ControllerBase):
@route.get("", response=list[CounterSchema], permissions=[IsRoot])
def fetch_all(self):
return Counter.objects.annotate_is_open()
@route.get("{counter_id}/", response=CounterSchema, permissions=[CanView])
def fetch_one(self, counter_id: int):
return self.get_object_or_exception(
Counter.objects.annotate_is_open(), pk=counter_id
)
@route.get("bar/", response=list[CounterSchema], permissions=[CanView])
def fetch_bars(self):
counters = list(Counter.objects.annotate_is_open().filter(type="BAR"))
for c in counters:
self.check_object_permissions(c)
return counters
@route.get(
"/search",
response=PaginatedResponseSchema[SimplifiedCounterSchema],
permissions=[CanAccessLookup],
)
@paginate(PageNumberPaginationExtra, page_size=50)
def search_counter(self, filters: Query[CounterFilterSchema]):
return filters.filter(Counter.objects.all())
@api_controller("/product")
class ProductController(ControllerBase):
@route.get(
"/search",
response=PaginatedResponseSchema[SimpleProductSchema],
permissions=[CanAccessLookup],
)
@paginate(PageNumberPaginationExtra, page_size=50)
def search_products(self, filters: Query[ProductFilterSchema]):
return filters.filter(
Product.objects.order_by(
F("product_type__order").asc(nulls_last=True),
"product_type",
"name",
).values()
)
@route.get(
"/search/detailed",
response=PaginatedResponseSchema[ProductSchema],
permissions=[IsCounterAdmin],
url_name="search_products_detailed",
)
@paginate(PageNumberPaginationExtra, page_size=50)
def search_products_detailed(self, filters: Query[ProductFilterSchema]):
"""Get the detailed information about the products."""
return filters.filter(
Product.objects.select_related("club")
.prefetch_related("buying_groups")
.select_related("product_type")
.order_by(
F("product_type__order").asc(nulls_last=True),
"product_type",
"name",
)
)
@api_controller("/product-type", permissions=[IsCounterAdmin])
class ProductTypeController(ControllerBase):
@route.get("", response=list[ProductTypeSchema], url_name="fetch_product_types")
def fetch_all(self):
return ProductType.objects.order_by("order")
@route.patch("/{type_id}/move")
def reorder(self, type_id: int, other_id: Query[ReorderProductTypeSchema]):
"""Change the order of a product type.
To use this route, give either the id of the product type
this one should be above of,
of the id of the product type this one should be below of.
Order affects the display order of the product types.
Examples:
```
GET /api/counter/product-type
=> [<1: type A>, <2: type B>, <3: type C>]
PATCH /api/counter/product-type/3/move?below=1
GET /api/counter/product-type
=> [<1: type A>, <3: type C>, <2: type B>]
```
"""
product_type: ProductType = self.get_object_or_exception(
ProductType, pk=type_id
)
other = get_object_or_404(ProductType, pk=other_id.above or other_id.below)
if other_id.below is not None:
product_type.below(other)
else:
product_type.above(other)