mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-21 21:53:30 +00:00
Fix immutable default variable in get_start_of_semester
(#656)
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/ --------- Co-authored-by: imperosol <thgirod@hotmail.com>
This commit is contained in:
parent
544b0248b2
commit
38295e591d
@ -1031,7 +1031,7 @@ thead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody > tr {
|
tbody > tr {
|
||||||
&:nth-child(even) {
|
&:nth-child(even):not(.highlight) {
|
||||||
background: $primary-neutral-light-color;
|
background: $primary-neutral-light-color;
|
||||||
}
|
}
|
||||||
&.clickable:hover {
|
&.clickable:hover {
|
||||||
|
@ -15,17 +15,18 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
import freezegun
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.core.management import call_command
|
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from club.models import Membership
|
from club.models import Membership
|
||||||
from core.models import User, Group, Page, AnonymousUser
|
|
||||||
from core.markdown import markdown
|
from core.markdown import markdown
|
||||||
|
from core.models import AnonymousUser, Group, Page, User
|
||||||
|
from core.utils import get_semester_code, get_start_of_semester
|
||||||
from sith import settings
|
from sith import settings
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -617,3 +618,75 @@ class UserIsInGroupTest(TestCase):
|
|||||||
returns False
|
returns False
|
||||||
"""
|
"""
|
||||||
self.assertFalse(self.skia.is_in_group(name="This doesn't exist"))
|
self.assertFalse(self.skia.is_in_group(name="This doesn't exist"))
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
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")
|
||||||
|
|
||||||
|
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(self.autumn_semester_january), automn_2024
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
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)
|
||||||
|
@ -15,20 +15,19 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
# Image utils
|
|
||||||
|
|
||||||
from io import BytesIO
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from PIL import ExifTags
|
# Image utils
|
||||||
|
from io import BytesIO
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import PIL
|
import PIL
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
from PIL import ExifTags
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
def get_git_revision_short_hash() -> str:
|
def get_git_revision_short_hash() -> str:
|
||||||
@ -44,34 +43,54 @@ def get_git_revision_short_hash() -> str:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def get_start_of_semester(d=date.today()):
|
def get_start_of_semester(today: Optional[date] = None) -> date:
|
||||||
"""
|
"""
|
||||||
This function computes the start date of the semester with respect to the given date (default is today),
|
Return the date of the start of the semester of the given date.
|
||||||
and the start date given in settings.SITH_START_DATE.
|
If no date is given, return the start date of the current semester.
|
||||||
It takes the nearest past start date.
|
|
||||||
Exemples: with SITH_START_DATE = (8, 15)
|
The current semester is computed as follows:
|
||||||
Today -> Start date
|
|
||||||
2015-03-17 -> 2015-02-15
|
- If the date is between 15/08 and 31/12 => Autumn semester.
|
||||||
2015-01-11 -> 2014-08-15
|
- 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
|
||||||
|
|
||||||
|
: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
|
||||||
"""
|
"""
|
||||||
today = d
|
if today is None:
|
||||||
year = today.year
|
today = timezone.now().date()
|
||||||
start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1])
|
|
||||||
start2 = start.replace(month=(start.month + 6) % 12)
|
autumn = date(today.year, *settings.SITH_SEMESTER_START_AUTUMN)
|
||||||
spring, autumn = min(start, start2), max(start, start2)
|
spring = date(today.year, *settings.SITH_SEMESTER_START_SPRING)
|
||||||
if today > autumn: # autumn semester
|
|
||||||
|
if today >= autumn: # between 15/08 (included) and 31/12 -> autumn semester
|
||||||
return autumn
|
return autumn
|
||||||
if today > spring: # spring semester
|
if today >= spring: # between 15/02 (included) and 15/08 -> spring semester
|
||||||
return spring
|
return spring
|
||||||
return autumn.replace(year=year - 1) # autumn semester of last year
|
# 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)
|
start = get_start_of_semester(d)
|
||||||
if start.month <= 6:
|
|
||||||
return "P" + str(start.year)[-2:]
|
if (start.month, start.day) == settings.SITH_SEMESTER_START_AUTUMN:
|
||||||
else:
|
|
||||||
return "A" + str(start.year)[-2:]
|
return "A" + str(start.year)[-2:]
|
||||||
|
return "P" + str(start.year)[-2:]
|
||||||
|
|
||||||
|
|
||||||
def file_exist(path):
|
def file_exist(path):
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Tuple
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists
|
from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists
|
||||||
@ -536,7 +536,7 @@ class Counter(models.Model):
|
|||||||
.order_by("-perm_sum")
|
.order_by("-perm_sum")
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
Return a QuerySet querying the money spent by customers of this counter
|
||||||
since the specified date, ordered by descending amount of money spent.
|
since the specified date, ordered by descending amount of money spent.
|
||||||
@ -546,6 +546,8 @@ class Counter(models.Model):
|
|||||||
- the nickname of the customer
|
- the nickname of the customer
|
||||||
- the amount of money spent by the customer
|
- the amount of money spent by the customer
|
||||||
"""
|
"""
|
||||||
|
if since is None:
|
||||||
|
since = get_start_of_semester()
|
||||||
return (
|
return (
|
||||||
self.sellings.filter(date__gte=since)
|
self.sellings.filter(date__gte=since)
|
||||||
.annotate(
|
.annotate(
|
||||||
@ -557,7 +559,8 @@ class Counter(models.Model):
|
|||||||
)
|
)
|
||||||
.annotate(nickname=F("customer__user__nick_name"))
|
.annotate(nickname=F("customer__user__nick_name"))
|
||||||
.annotate(promo=F("customer__user__promo"))
|
.annotate(promo=F("customer__user__promo"))
|
||||||
.values("customer__user", "name", "nickname")
|
.annotate(user=F("customer__user"))
|
||||||
|
.values("user", "promo", "name", "nickname")
|
||||||
.annotate(
|
.annotate(
|
||||||
selling_sum=Sum(
|
selling_sum=Sum(
|
||||||
F("unit_price") * F("quantity"), output_field=CurrencyField()
|
F("unit_price") * F("quantity"), output_field=CurrencyField()
|
||||||
@ -567,15 +570,17 @@ class Counter(models.Model):
|
|||||||
.order_by("-selling_sum")
|
.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
|
Compute and return the total turnover of this counter
|
||||||
since the date specified in parameter (by default, since the start of the current
|
since the date specified in parameter (by default, since the start of the current
|
||||||
semester)
|
semester)
|
||||||
:param since: timestamp from which to perform the calculation
|
: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
|
:return: Total revenue earned at this counter
|
||||||
"""
|
"""
|
||||||
|
if since is None:
|
||||||
|
since = get_start_of_semester()
|
||||||
if isinstance(since, date):
|
if isinstance(since, date):
|
||||||
since = datetime.combine(since, datetime.min.time())
|
since = datetime.combine(since, datetime.min.time())
|
||||||
total = self.sellings.filter(date__gte=since).aggregate(
|
total = self.sellings.filter(date__gte=since).aggregate(
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}</h3>
|
<h3>{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}</h3>
|
||||||
<h4>{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %}</h4>
|
<h4>
|
||||||
|
{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %} ({{ current_semester }})
|
||||||
|
</h4>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -35,7 +37,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h4>{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %}</h4>
|
<h4>
|
||||||
|
{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %} ({{ current_semester }})
|
||||||
|
</h4>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
from datetime import date, timedelta
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
@ -322,42 +323,49 @@ class CounterStatsTest(TestCase):
|
|||||||
Test the result of Counter.get_top_customers() is correct
|
Test the result of Counter.get_top_customers() is correct
|
||||||
"""
|
"""
|
||||||
top = iter(self.counter.get_top_customers())
|
top = iter(self.counter.get_top_customers())
|
||||||
self.assertEqual(
|
expected_results = [
|
||||||
next(top),
|
|
||||||
{
|
{
|
||||||
"customer__user": self.sli.id,
|
"user": self.sli.id,
|
||||||
"name": f"{self.sli.first_name} {self.sli.last_name}",
|
"name": f"{self.sli.first_name} {self.sli.last_name}",
|
||||||
|
"promo": self.sli.promo,
|
||||||
"nickname": self.sli.nick_name,
|
"nickname": self.sli.nick_name,
|
||||||
"selling_sum": 2000,
|
"selling_sum": 2000,
|
||||||
},
|
},
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
next(top),
|
|
||||||
{
|
{
|
||||||
"customer__user": self.skia.id,
|
"user": self.skia.id,
|
||||||
"name": f"{self.skia.first_name} {self.skia.last_name}",
|
"name": f"{self.skia.first_name} {self.skia.last_name}",
|
||||||
|
"promo": self.skia.promo,
|
||||||
"nickname": self.skia.nick_name,
|
"nickname": self.skia.nick_name,
|
||||||
"selling_sum": 1000,
|
"selling_sum": 1000,
|
||||||
},
|
},
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
next(top),
|
|
||||||
{
|
{
|
||||||
"customer__user": self.krophil.id,
|
"user": self.krophil.id,
|
||||||
"name": f"{self.krophil.first_name} {self.krophil.last_name}",
|
"name": f"{self.krophil.first_name} {self.krophil.last_name}",
|
||||||
|
"promo": self.krophil.promo,
|
||||||
"nickname": self.krophil.nick_name,
|
"nickname": self.krophil.nick_name,
|
||||||
"selling_sum": 100,
|
"selling_sum": 100,
|
||||||
},
|
},
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
next(top),
|
|
||||||
{
|
{
|
||||||
"customer__user": self.root.id,
|
"user": self.root.id,
|
||||||
"name": f"{self.root.first_name} {self.root.last_name}",
|
"name": f"{self.root.first_name} {self.root.last_name}",
|
||||||
|
"promo": self.root.promo,
|
||||||
"nickname": self.root.nick_name,
|
"nickname": self.root.nick_name,
|
||||||
"selling_sum": 2,
|
"selling_sum": 2,
|
||||||
},
|
},
|
||||||
)
|
]
|
||||||
|
|
||||||
|
for result in expected_results:
|
||||||
|
self.assertEqual(
|
||||||
|
next(top),
|
||||||
|
{
|
||||||
|
"user": result["user"],
|
||||||
|
"name": result["name"],
|
||||||
|
"promo": result["promo"],
|
||||||
|
"nickname": result["nickname"],
|
||||||
|
"selling_sum": result["selling_sum"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIsNone(next(top, None))
|
self.assertIsNone(next(top, None))
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ import pytz
|
|||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
from http import HTTPStatus
|
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 import CanViewMixin, TabedViewMixin, CanEditMixin
|
||||||
from core.views.forms import LoginForm
|
from core.views.forms import LoginForm
|
||||||
from core.models import User
|
from core.models import User
|
||||||
@ -1354,13 +1354,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Add stats to the context"""
|
"""Add stats to the context"""
|
||||||
counter = self.object
|
counter: Counter = self.object
|
||||||
semester_start = get_start_of_semester()
|
semester_start = get_start_of_semester()
|
||||||
office_hours = counter.get_top_barmen()
|
office_hours = counter.get_top_barmen()
|
||||||
kwargs = super(CounterStatView, self).get_context_data(**kwargs)
|
kwargs = super(CounterStatView, self).get_context_data(**kwargs)
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
"counter": counter,
|
"counter": counter,
|
||||||
|
"current_semester": get_semester_code(),
|
||||||
"total_sellings": counter.get_total_sales(since=semester_start),
|
"total_sellings": counter.get_total_sales(since=semester_start),
|
||||||
"top_customers": counter.get_top_customers(since=semester_start)[:100],
|
"top_customers": counter.get_top_customers(since=semester_start)[:100],
|
||||||
"top_barman": office_hours[:100],
|
"top_barman": office_hours[:100],
|
||||||
|
18
poetry.lock
generated
18
poetry.lock
generated
@ -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]]
|
[[package]]
|
||||||
name = "alabaster"
|
name = "alabaster"
|
||||||
@ -550,6 +550,20 @@ files = [
|
|||||||
{file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
|
{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]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.4"
|
version = "3.4"
|
||||||
@ -1558,4 +1572,4 @@ testing = ["coverage"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "9d38fb0dd50ef0a154bd6690afcb24be9fbdb2adbba1c4762b1ed0cdb9508eb2"
|
content-hash = "62519616aff5a472dac3dd8071a6404b1ee8eab12a197af717a0520f7ded0331"
|
||||||
|
@ -59,6 +59,7 @@ testing = ["coverage"]
|
|||||||
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
|
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
freezegun = "^1.2.2" # used to test time-dependent code
|
||||||
django-debug-toolbar = "^4.0.0"
|
django-debug-toolbar = "^4.0.0"
|
||||||
ipython = "^7.28.0"
|
ipython = "^7.28.0"
|
||||||
black = "^23.3.0"
|
black = "^23.3.0"
|
||||||
|
@ -327,7 +327,8 @@ SITH_CLUB_ROOT_PAGE = "clubs"
|
|||||||
|
|
||||||
# Define the date in the year serving as reference for the subscriptions calendar
|
# Define the date in the year serving as reference for the subscriptions calendar
|
||||||
# (month, day)
|
# (month, day)
|
||||||
SITH_START_DATE = (8, 15) # 15th August
|
SITH_SEMESTER_START_AUTUMN = (8, 15) # 15 August
|
||||||
|
SITH_SEMESTER_START_SPRING = (2, 15) # 15 February
|
||||||
|
|
||||||
# Used to determine the valid promos
|
# Used to determine the valid promos
|
||||||
SITH_SCHOOL_START_YEAR = 1999
|
SITH_SCHOOL_START_YEAR = 1999
|
||||||
|
@ -114,12 +114,12 @@ class Subscription(models.Model):
|
|||||||
return "No user - " + str(self.pk)
|
return "No user - " + str(self.pk)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_start(d=None, duration=1, user=None):
|
def compute_start(d: date = None, duration: int = 1, user: User = None) -> date:
|
||||||
"""
|
"""
|
||||||
This function computes the start date of the subscription with respect to the given date (default is today),
|
This function computes the start date of the subscription with respect to the given date (default is today),
|
||||||
and the start date given in settings.SITH_START_DATE.
|
and the start date given in settings.SITH_SEMESTER_START_AUTUMN.
|
||||||
It takes the nearest past start date.
|
It takes the nearest past start date.
|
||||||
Exemples: with SITH_START_DATE = (8, 15)
|
Exemples: with SITH_SEMESTER_START_AUTUMN = (8, 15)
|
||||||
Today -> Start date
|
Today -> Start date
|
||||||
2015-03-17 -> 2015-02-15
|
2015-03-17 -> 2015-02-15
|
||||||
2015-01-11 -> 2014-08-15
|
2015-01-11 -> 2014-08-15
|
||||||
@ -135,9 +135,9 @@ class Subscription(models.Model):
|
|||||||
return get_start_of_semester(d)
|
return get_start_of_semester(d)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_end(duration, start=None, user=None):
|
def compute_end(duration: int, start: date = None, user: User = None) -> date:
|
||||||
"""
|
"""
|
||||||
This function compute the end date of the subscription given a start date and a duration in number of semestre
|
This function compute the end date of the subscription given a start date and a duration in number of semester
|
||||||
Exemple:
|
Exemple:
|
||||||
Start - Duration -> End date
|
Start - Duration -> End date
|
||||||
2015-09-18 - 1 -> 2016-03-18
|
2015-09-18 - 1 -> 2016-03-18
|
||||||
@ -153,7 +153,7 @@ class Subscription(models.Model):
|
|||||||
days=math.ceil((6 * duration - round(6 * duration)) * 30),
|
days=math.ceil((6 * duration - round(6 * duration)) * 30),
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_be_edited_by(self, user):
|
def can_be_edited_by(self, user: User):
|
||||||
return user.is_board_member or user.is_root
|
return user.is_board_member or user.is_root
|
||||||
|
|
||||||
def is_valid_now(self):
|
def is_valid_now(self):
|
||||||
|
@ -31,7 +31,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from datetime import timedelta, date
|
from datetime import timedelta, date
|
||||||
|
|
||||||
from core.models import User
|
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
|
from club.models import Club
|
||||||
|
|
||||||
|
|
||||||
@ -164,14 +164,14 @@ class TrombiUser(models.Model):
|
|||||||
if m.description:
|
if m.description:
|
||||||
role += " (%s)" % m.description
|
role += " (%s)" % m.description
|
||||||
if m.end_date:
|
if m.end_date:
|
||||||
end_date = get_semester(m.end_date)
|
end_date = get_semester_code(m.end_date)
|
||||||
else:
|
else:
|
||||||
end_date = ""
|
end_date = ""
|
||||||
TrombiClubMembership(
|
TrombiClubMembership(
|
||||||
user=self,
|
user=self,
|
||||||
club=str(m.club),
|
club=str(m.club),
|
||||||
role=role[:64],
|
role=role[:64],
|
||||||
start=get_semester(m.start_date),
|
start=get_semester_code(m.start_date),
|
||||||
end=end_date,
|
end=end_date,
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user