Aller au contenu

Schemas

SimpleUserSchema

Bases: ModelSchema

A schema with the minimum amount of information to represent a user.

Counter

Bases: Model

gen_token()

Generate a new token for this counter.

Source code in counter/models.py
def gen_token(self) -> None:
    """Generate a new token for this counter."""
    self.token = "".join(
        random.choice(string.ascii_letters + string.digits) for _ in range(30)
    )
    self.save()

barmen_list()

Returns the barman list as list of User.

Source code in counter/models.py
@cached_property
def barmen_list(self) -> list[User]:
    """Returns the barman list as list of User."""
    return [
        p.user for p in self.permanencies.filter(end=None).select_related("user")
    ]

get_random_barman()

Return a random user being currently a barman.

Source code in counter/models.py
def get_random_barman(self) -> User:
    """Return a random user being currently a barman."""
    return random.choice(self.barmen_list)

update_activity()

Update the barman activity to prevent timeout.

Source code in counter/models.py
def update_activity(self) -> None:
    """Update the barman activity to prevent timeout."""
    self.permanencies.filter(end=None).update(activity=timezone.now())

can_refill()

Show if the counter authorize the refilling with physic money.

Source code in counter/models.py
def can_refill(self) -> bool:
    """Show if the counter authorize the refilling with physic money."""
    if self.type != "BAR":
        return False
    if self.id in SITH_COUNTER_OFFICES:
        # If the counter is either 'AE' or 'BdF', refills are authorized
        return True
    # at least one of the barmen is in the AE board
    ae = Club.objects.get(unix_name=SITH_MAIN_CLUB["unix_name"])
    return any(ae.get_membership_for(barman) for barman in self.barmen_list)

get_top_barmen()

Return a QuerySet querying the office hours stats of all the barmen of all time of this counter, ordered by descending number of hours.

Each element of the QuerySet corresponds to a barman and has the following data
  • the full name (first name + last name) of the barman
  • the nickname of the barman
  • the promo of the barman
  • the total number of office hours the barman did attend
Source code in counter/models.py
def get_top_barmen(self) -> QuerySet:
    """Return a QuerySet querying the office hours stats of all the barmen of all time
    of this counter, ordered by descending number of hours.

    Each element of the QuerySet corresponds to a barman and has the following data :
        - the full name (first name + last name) of the barman
        - the nickname of the barman
        - the promo of the barman
        - the total number of office hours the barman did attend
    """
    return (
        self.permanencies.exclude(end=None)
        .annotate(
            name=Concat(F("user__first_name"), Value(" "), F("user__last_name"))
        )
        .annotate(nickname=F("user__nick_name"))
        .annotate(promo=F("user__promo"))
        .values("user", "name", "nickname", "promo")
        .annotate(perm_sum=Sum(F("end") - F("start")))
        .exclude(perm_sum=None)
        .order_by("-perm_sum")
    )

get_top_customers(since=None)

Return a QuerySet querying the money spent by customers of this counter since the specified date, ordered by descending amount of money spent.

Each element of the QuerySet corresponds to a customer and has the following data :

  • the full name (first name + last name) of the customer
  • the nickname of the customer
  • the amount of money spent by the customer

Parameters:

Name Type Description Default
since datetime | date | None

timestamp from which to perform the calculation

None
Source code in counter/models.py
def get_top_customers(self, since: datetime | date | None = None) -> QuerySet:
    """Return a QuerySet querying the money spent by customers of this counter
    since the specified date, ordered by descending amount of money spent.

    Each element of the QuerySet corresponds to a customer and has the following data :

    - the full name (first name + last name) of the customer
    - the nickname of the customer
    - the amount of money spent by the customer

    Args:
        since: timestamp from which to perform the calculation
    """
    if since is None:
        since = get_start_of_semester()
    if isinstance(since, date):
        since = datetime(since.year, since.month, since.day, tzinfo=tz.utc)
    return (
        self.sellings.filter(date__gte=since)
        .annotate(
            name=Concat(
                F("customer__user__first_name"),
                Value(" "),
                F("customer__user__last_name"),
            )
        )
        .annotate(nickname=F("customer__user__nick_name"))
        .annotate(promo=F("customer__user__promo"))
        .annotate(user=F("customer__user"))
        .values("user", "promo", "name", "nickname")
        .annotate(
            selling_sum=Sum(
                F("unit_price") * F("quantity"), output_field=CurrencyField()
            )
        )
        .filter(selling_sum__gt=0)
        .order_by("-selling_sum")
    )

get_total_sales(since=None)

Compute and return the total turnover of this counter since the given date.

By default, the date is the start of the current semester.

Parameters:

Name Type Description Default
since datetime | date | None

timestamp from which to perform the calculation

None

Returns:

Type Description
CurrencyField

Total revenue earned at this counter.

Source code in counter/models.py
def get_total_sales(self, since: datetime | date | None = None) -> CurrencyField:
    """Compute and return the total turnover of this counter since the given date.

    By default, the date is the start of the current semester.

    Args:
        since: timestamp from which to perform the calculation

    Returns:
        Total revenue earned at this counter.
    """
    if since is None:
        since = get_start_of_semester()
    if isinstance(since, date):
        since = datetime(since.year, since.month, since.day, tzinfo=tz.utc)
    return self.sellings.filter(date__gte=since).aggregate(
        total=Sum(
            F("quantity") * F("unit_price"),
            default=0,
            output_field=CurrencyField(),
        )
    )["total"]

Product

Bases: Model

A product, with all its related information.

is_owned_by(user)

Method to see if that object can be edited by the given user.

Source code in counter/models.py
def is_owned_by(self, user):
    """Method to see if that object can be edited by the given user."""
    if user.is_anonymous:
        return False
    return user.is_in_group(
        pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID
    ) or user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)

can_be_sold_to(user)

Check if whether the user given in parameter has the right to buy this product or not.

This must be not confused with the Customer.can_buy() method as the present method returns an information about the relation between a User and a Product, whereas the other tells something about a Customer (and not a user, they are not the same model).

Returns:

Type Description
bool

True if the user can buy this product else False

Warning

This performs a db query, thus you can quickly have a N+1 queries problem if you call it in a loop. Hopefully, you can avoid that if you prefetch the buying_groups :

user = User.objects.get(username="foobar")
products = [
    p
    for p in Product.objects.prefetch_related("buying_groups")
    if p.can_be_sold_to(user)
]
Source code in counter/models.py
def can_be_sold_to(self, user: User) -> bool:
    """Check if whether the user given in parameter has the right to buy
    this product or not.

    This must be not confused with the Customer.can_buy()
    method as the present method returns an information
    about the relation between a User and a Product,
    whereas the other tells something about a Customer
    (and not a user, they are not the same model).

    Returns:
        True if the user can buy this product else False

    Warning:
        This performs a db query, thus you can quickly have
        a N+1 queries problem if you call it in a loop.
        Hopefully, you can avoid that if you prefetch the buying_groups :

        ```python
        user = User.objects.get(username="foobar")
        products = [
            p
            for p in Product.objects.prefetch_related("buying_groups")
            if p.can_be_sold_to(user)
        ]
        ```
    """
    buying_groups = list(self.buying_groups.all())
    if not buying_groups:
        return True
    return any(user.is_in_group(pk=group.id) for group in buying_groups)

CounterSchema

Bases: ModelSchema

CounterFilterSchema

Bases: FilterSchema

SimplifiedCounterSchema

Bases: ModelSchema

ProductSchema

Bases: ModelSchema