Actually fix wrong counter date

Le serveur ne percevait pas le changement de semestre, parce
que la valeur par défaut passée à la fonction `get_start_of_semester()` était une fonction appelée une seule fois, lors du lancement du serveur. Bref, c'était ça : https://beta.ruff.rs/docs/rules/function-call-in-default-argument/
This commit is contained in:
imperosol 2023-09-08 00:09:21 +02:00
parent 1f23de3fc7
commit a27a130f29
9 changed files with 142 additions and 93 deletions

View File

@ -1031,7 +1031,7 @@ thead {
}
tbody > tr {
&:nth-child(even) {
&:nth-child(even):not(.highlight) {
background: $primary-neutral-light-color;
}
&.clickable:hover {

View File

@ -17,16 +17,16 @@
import os
from datetime import date, timedelta
import freezegun
from django.core.cache import cache
from django.test import Client, TestCase
from django.urls import reverse
from django.core.management import call_command
from django.utils.timezone import now
from club.models import Membership
from core.models import User, Group, Page, AnonymousUser
from core.markdown import markdown
from core.utils import get_semester, get_start_of_semester
from core.models import AnonymousUser, Group, Page, User
from core.utils import get_semester_code, get_start_of_semester
from sith import settings
"""
@ -620,48 +620,73 @@ class UserIsInGroupTest(TestCase):
self.assertFalse(self.skia.is_in_group(name="This doesn't exist"))
class UtilsTest(TestCase):
class DateUtilsTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.autumn_month = settings.SITH_SEMESTER_START_AUTUMN[0]
cls.autumn_day = settings.SITH_SEMESTER_START_AUTUMN[1]
cls.spring_month = settings.SITH_SEMESTER_START_SPRING[0]
cls.spring_day = settings.SITH_SEMESTER_START_SPRING[1]
cls.autumn_semester_january = date(2025, 1, 4)
cls.autumn_semester_september = date(2024, 9, 4)
cls.autumn_first_day = date(2024, cls.autumn_month, cls.autumn_day)
cls.spring_semester_march = date(2023, 3, 4)
cls.spring_first_day = date(2023, cls.spring_month, cls.spring_day)
def test_get_semester(self):
autumn_month, autumn_day = settings.SITH_SEMESTER_START_AUTUMN
spring_month, spring_day = settings.SITH_SEMESTER_START_SPRING
"""
Test that the get_semester function returns the correct semester string
"""
self.assertEqual(get_semester_code(self.autumn_semester_january), "A24")
self.assertEqual(get_semester_code(self.autumn_semester_september), "A24")
self.assertEqual(get_semester_code(self.autumn_first_day), "A24")
t1_autumn_day = date(2025, 1, 4) # between 1st January and 15 February
t2_autumn_day = date(2024, 9, 1) # between 15 August and 31 December
t3_autumn_day = date(2024, autumn_month, autumn_day) # on 15 August
t1_spring_day = date(2023, 3, 1) # between 15 February and 15 August
t2_spring_day = date(2023, spring_month, spring_day) # on 15 February
self.assertEqual(get_semester(t1_autumn_day), "A24")
self.assertEqual(get_semester(t2_autumn_day), "A24")
self.assertEqual(get_semester(t3_autumn_day), "A24")
self.assertEqual(get_semester(t1_spring_day), "P23")
self.assertEqual(get_semester(t2_spring_day), "P23")
def test_get_start_of_semester(self):
autumn_month, autumn_day = settings.SITH_SEMESTER_START_AUTUMN
spring_month, spring_day = settings.SITH_SEMESTER_START_SPRING
t1_autumn_day = date(2025, 1, 4) # between 1st January and 15 February
t2_autumn_day = date(2024, 9, 1) # between 15 August and 31 December
t3_autumn_day = date(2024, autumn_month, autumn_day) # on 15 August
t1_spring_day = date(2023, 3, 1) # between 15 February and 15 August
t2_spring_day = date(2023, spring_month, spring_day) # on 15 February
self.assertEqual(get_semester_code(self.spring_semester_march), "P23")
self.assertEqual(get_semester_code(self.spring_first_day), "P23")
def test_get_start_of_semester_fixed_date(self):
"""
Test that the get_start_of_semester correctly the starting date of the semester.
"""
automn_2024 = date(2024, self.autumn_month, self.autumn_day)
self.assertEqual(
get_start_of_semester(t1_autumn_day), date(2024, autumn_month, autumn_day)
get_start_of_semester(self.autumn_semester_january), automn_2024
)
self.assertEqual(
get_start_of_semester(t2_autumn_day), date(2024, autumn_month, autumn_day)
)
self.assertEqual(
get_start_of_semester(t3_autumn_day), date(2024, autumn_month, autumn_day)
)
self.assertEqual(
get_start_of_semester(t1_spring_day), date(2023, spring_month, spring_day)
)
self.assertEqual(
get_start_of_semester(t2_spring_day), date(2023, spring_month, spring_day)
get_start_of_semester(self.autumn_semester_september), automn_2024
)
self.assertEqual(get_start_of_semester(self.autumn_first_day), automn_2024)
spring_2023 = date(2023, self.spring_month, self.spring_day)
self.assertEqual(get_start_of_semester(self.spring_semester_march), spring_2023)
self.assertEqual(get_start_of_semester(self.spring_first_day), spring_2023)
def test_get_start_of_semester_today(self):
"""
Test that the get_start_of_semester returns the start of the current semester
when no date is given
"""
with freezegun.freeze_time(self.autumn_semester_september):
self.assertEqual(get_start_of_semester(), self.autumn_first_day)
with freezegun.freeze_time(self.spring_semester_march):
self.assertEqual(get_start_of_semester(), self.spring_first_day)
def test_get_start_of_semester_changing_date(self):
"""
Test that the get_start_of_semester correctly gives the starting date of the semester,
even when the semester changes while the server isn't restarted.
"""
spring_2023 = date(2023, self.spring_month, self.spring_day)
autumn_2023 = date(2023, self.autumn_month, self.autumn_day)
mid_spring = spring_2023 + timedelta(days=45)
mid_autumn = autumn_2023 + timedelta(days=45)
with freezegun.freeze_time(mid_spring) as frozen_time:
self.assertEqual(get_start_of_semester(), spring_2023)
# forward time to the middle of the next semester
frozen_time.move_to(mid_autumn)
self.assertEqual(get_start_of_semester(), autumn_2023)

View File

@ -15,20 +15,19 @@
#
import os
import subprocess
import re
# Image utils
from io import BytesIO
import subprocess
from datetime import date
from PIL import ExifTags
# Image utils
from io import BytesIO
from typing import Optional
import PIL
from django.conf import settings
from django.core.files.base import ContentFile
from PIL import ExifTags
from django.utils import timezone
def get_git_revision_short_hash() -> str:
@ -44,50 +43,54 @@ def get_git_revision_short_hash() -> str:
return ""
def get_start_of_semester(today=date.today()) -> date:
def get_start_of_semester(today: Optional[date] = None) -> date:
"""
This function determines in which semester the given date falls and returns the start date of the corresponding semester.
If no date is given, today's date is used.
Return the date of the start of the semester of the given date.
If no date is given, return the start date of the current semester.
Parameters:
`today` (date, optional): The date to be tested. Defaults to `date.today()`.
The current semester is computed as follows:
Returns:
`date`: The start date of the semester to which the given date belongs.
- If the date is between 15/08 and 31/12 => Autumn semester.
- If the date is between 01/01 and 15/02 => Autumn semester of the previous year.
- If the date is between 15/02 and 15/08 => Spring semester
Context:
- If the date is between 15/08 and 31/12, it belongs to the autumn semester.
- If the date is between 01/01 and 15/02, it also belongs to the autumn semester, but for the year before the given date.
- Otherwise, if the date is between 15/02 and 15/08, it belongs to the spring semester.
:param today: the date to use to compute the semester. If None, use today's date.
:return: the date of the start of the semester
"""
autumn_month, autumn_day = settings.SITH_SEMESTER_START_AUTUMN
spring_month, spring_day = settings.SITH_SEMESTER_START_SPRING
if today is None:
today = timezone.now().date()
autumn = date(today.year, autumn_month, autumn_day)
spring = date(today.year, spring_month, spring_day)
autumn = date(today.year, *settings.SITH_SEMESTER_START_AUTUMN)
spring = date(today.year, *settings.SITH_SEMESTER_START_SPRING)
# between 15/08 (included) and 31/12 -> autumn semester
if today >= autumn:
if today >= autumn: # between 15/08 (included) and 31/12 -> autumn semester
return autumn
# between 15/02 (included) and 15/08 -> spring semester
if today >= spring:
if today >= spring: # between 15/02 (included) and 15/08 -> spring semester
return spring
# else : between 01/01 and 15/02 -> autumn semester where the year is the one before of the given date
# between 01/01 and 15/02 -> autumn semester of the previous year
return autumn.replace(year=autumn.year - 1)
def get_semester(d=date.today()):
def get_semester_code(d: Optional[date] = None) -> str:
"""
Return the semester code of the given date.
If no date is given, return the semester code of the current semester.
The semester code is an upper letter (A for autumn, P for spring),
followed by the last two digits of the year.
For example, the autumn semester of 2018 is "A18".
:param d: the date to use to compute the semester. If None, use today's date.
:return: the semester code corresponding to the given date
"""
if d is None:
d = timezone.now().date()
start = get_start_of_semester(d)
if (
start.month == settings.SITH_SEMESTER_START_AUTUMN[0]
and start.day == settings.SITH_SEMESTER_START_AUTUMN[1]
):
if (start.month, start.day) == settings.SITH_SEMESTER_START_AUTUMN:
return "A" + str(start.year)[-2:]
else:
return "P" + str(start.year)[-2:]
return "P" + str(start.year)[-2:]
def file_exist(path):

View File

@ -15,7 +15,7 @@
#
from __future__ import annotations
from typing import Tuple
from typing import Tuple, Optional
from django.db import models
from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists
@ -536,10 +536,7 @@ class Counter(models.Model):
.order_by("-perm_sum")
)
def get_stats_starting_date(self) -> date:
return get_start_of_semester()
def get_top_customers(self, since=get_start_of_semester()) -> QuerySet:
def get_top_customers(self, since: Optional[date] = 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.
@ -549,6 +546,8 @@ class Counter(models.Model):
- the nickname of the customer
- the amount of money spent by the customer
"""
if since is None:
since = get_start_of_semester()
return (
self.sellings.filter(date__gte=since)
.annotate(
@ -571,15 +570,17 @@ class Counter(models.Model):
.order_by("-selling_sum")
)
def get_total_sales(self, since=get_start_of_semester()) -> CurrencyField:
def get_total_sales(self, since=None) -> CurrencyField:
"""
Compute and return the total turnover of this counter
since the date specified in parameter (by default, since the start of the current
semester)
:param since: timestamp from which to perform the calculation
:type since: datetime | date
:type since: datetime | date | None
:return: Total revenue earned at this counter
"""
if since is None:
since = get_start_of_semester()
if isinstance(since, date):
since = datetime.combine(since, datetime.min.time())
total = self.sellings.filter(date__gte=since).aggregate(

View File

@ -11,7 +11,9 @@
{% block content %}
<h3>{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}</h3>
<h4>{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %} ({{ counter.get_stats_starting_date() }})</h4>
<h4>
{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %} ({{ current_semester }})
</h4>
<table>
<thead>
<tr>
@ -35,7 +37,9 @@
</tbody>
</table>
<h4>{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %} ({{ counter.get_stats_starting_date() }})</h4>
<h4>
{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %} ({{ current_semester }})
</h4>
<table>
<thead>
<tr>

View File

@ -48,7 +48,7 @@ import pytz
from datetime import timedelta, datetime
from http import HTTPStatus
from core.utils import get_start_of_semester
from core.utils import get_start_of_semester, get_semester_code
from core.views import CanViewMixin, TabedViewMixin, CanEditMixin
from core.views.forms import LoginForm
from core.models import User
@ -1361,6 +1361,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
kwargs.update(
{
"counter": counter,
"current_semester": get_semester_code(),
"total_sellings": counter.get_total_sales(since=semester_start),
"top_customers": counter.get_top_customers(since=semester_start)[:100],
"top_barman": office_hours[:100],

18
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "alabaster"
@ -550,6 +550,20 @@ files = [
{file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
]
[[package]]
name = "freezegun"
version = "1.2.2"
description = "Let your Python tests travel through time"
optional = false
python-versions = ">=3.6"
files = [
{file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"},
{file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"},
]
[package.dependencies]
python-dateutil = ">=2.7"
[[package]]
name = "idna"
version = "3.4"
@ -1558,4 +1572,4 @@ testing = ["coverage"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "9d38fb0dd50ef0a154bd6690afcb24be9fbdb2adbba1c4762b1ed0cdb9508eb2"
content-hash = "62519616aff5a472dac3dd8071a6404b1ee8eab12a197af717a0520f7ded0331"

View File

@ -59,6 +59,7 @@ testing = ["coverage"]
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
[tool.poetry.dev-dependencies]
freezegun = "^1.2.2" # used to test time-dependent code
django-debug-toolbar = "^4.0.0"
ipython = "^7.28.0"
black = "^23.3.0"

View File

@ -31,7 +31,7 @@ from django.core.exceptions import ValidationError
from datetime import timedelta, date
from core.models import User
from core.utils import get_start_of_semester, get_semester
from core.utils import get_start_of_semester, get_semester_code
from club.models import Club
@ -164,14 +164,14 @@ class TrombiUser(models.Model):
if m.description:
role += " (%s)" % m.description
if m.end_date:
end_date = get_semester(m.end_date)
end_date = get_semester_code(m.end_date)
else:
end_date = ""
TrombiClubMembership(
user=self,
club=str(m.club),
role=role[:64],
start=get_semester(m.start_date),
start=get_semester_code(m.start_date),
end=end_date,
).save()