From 6c1fa6de0bd44a21646ecccf4ae703ddacdf0f43 Mon Sep 17 00:00:00 2001 From: thomas girod <56346771+imperosol@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:32:05 +0100 Subject: [PATCH] remove-useless-queries-counter-stats (#519) --- core/static/core/style.scss | 43 +- core/templates/core/user_detail.jinja | 86 ++-- core/templatetags/renderer.py | 55 +-- core/utils.py | 15 +- counter/models.py | 87 +++- counter/templates/counter/stats.jinja | 97 ++-- counter/tests.py | 208 +++++++- counter/views.py | 93 +--- locale/fr/LC_MESSAGES/django.po | 662 +++++++++++++------------- sith/settings.py | 2 + 10 files changed, 790 insertions(+), 558 deletions(-) diff --git a/core/static/core/style.scss b/core/static/core/style.scss index 3279d0d7..f4840082 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -1198,18 +1198,38 @@ blockquote h5:first-child { } table { - width: 100%; - font-size: 0.9em; + width: 90%; + margin: 15px auto; + border-collapse: collapse; + border-spacing: 0; + border-radius: 5px; + -moz-border-radius: 5px; + overflow: hidden; + box-shadow: rgba(60, 64, 67, .3) 0 1px 3px 0, rgba(60, 64, 67, .15) 0 4px 8px 3px; +} + +@media screen and (max-width: 500px){ + table { + width: 100%; + } } th { padding: 4px; } +td, th { + vertical-align: middle; + text-align: center; + padding: 5px 10px; + > ul { + margin-top: 0; + } +} + td { padding: 4px; margin: 5px; - border: solid 1px $primary-neutral-color; border-collapse: collapse; vertical-align: top; overflow: hidden; @@ -1219,18 +1239,29 @@ td { } } +th, thead td { + text-align: center; + border-top: none; +} + thead { - font-weight: bold; + background-color: #354a5f; + color: white; } tbody > tr { &:nth-child(even) { background: $primary-neutral-light-color; } - &:hover { + &.clickable:hover { + cursor: pointer; background: $secondary-neutral-light-color; width: 100%; } + &.highlight { + color: $primary-dark-color; + font-style: italic; + } } sup { @@ -2350,4 +2381,4 @@ $pedagogy-white-text: #f0f0f0; } } } -} +} \ No newline at end of file diff --git a/core/templates/core/user_detail.jinja b/core/templates/core/user_detail.jinja index b2e834e9..a8978229 100644 --- a/core/templates/core/user_detail.jinja +++ b/core/templates/core/user_detail.jinja @@ -5,8 +5,12 @@ {% trans user_name=profile.get_display_name() %}{{ user_name }}'s profile{% endtrans %} {% endblock %} +{% block additional_js %} + +{% endblock %} + {% block content %} -
+
@@ -15,7 +19,7 @@
« {{ profile.nick_name }} »
{% endif %} - + {% if profile.quote %}
@@ -146,24 +150,35 @@
{% endif %} {% if profile.was_subscribed and (user == profile or user.can_read_subscription_history)%} -
-
{% trans %}Subscription history{% endtrans %}
- - - - - - - - {% for sub in profile.subscriptions.all() %} - - - - - - - {% endfor %} -
{% trans %}Subscription start{% endtrans %}{% trans %}Subscription end{% endtrans %}{% trans %}Subscription type{% endtrans %}{% trans %}Payment method{% endtrans %}
{{ sub.subscription_start }}{{ sub.subscription_end }}{{ sub.subscription_type }}{{ sub.get_payment_method_display() }}
+
+
+ + {% trans %}Subscription history{% endtrans %} + + + + +
+
+ + + + + + + + + + {% for sub in profile.subscriptions.all() %} + + + + + + + {% endfor %} +
{% trans %}Subscription start{% endtrans %}{% trans %}Subscription end{% endtrans %}{% trans %}Subscription type{% endtrans %}{% trans %}Payment method{% endtrans %}
{{ sub.subscription_start }}{{ sub.subscription_end }}{{ sub.subscription_type }}{{ sub.get_payment_method_display() }}
+
{% endif %} @@ -177,13 +192,25 @@ {% if profile.gifts.exists() %} -
-
-
{% trans %}Last given gift :{% endtrans %} {{ profile.gifts.order_by('-date').first() }}
-
+ {% set gifts = profile.gifts.order_by("-date")|list %} +
+
+
+ + {% trans %}Last given gift :{% endtrans %} {{ gifts[0] }} + + + + +
+
@@ -229,12 +256,5 @@ $(function(){ active: false }); }); -$(function(){ - $("#drop_subscriptions").accordion({ - heightStyle: "content", - collapsible: true, - active: false - }); -}); {% endblock %} diff --git a/core/templatetags/renderer.py b/core/templatetags/renderer.py index 656efca3..2ee19a45 100644 --- a/core/templatetags/renderer.py +++ b/core/templatetags/renderer.py @@ -23,11 +23,13 @@ # # +import datetime import phonenumbers from django import template from django.template.defaultfilters import stringfilter from django.utils.safestring import mark_safe +from django.utils.translation import ngettext from core.scss.processor import ScssProcessor from core.markdown import markdown as md @@ -54,41 +56,26 @@ def phonenumber(value, country="FR", format=phonenumbers.PhoneNumberFormat.NATIO return value -@register.filter() -@stringfilter -def datetime_format_python_to_PHP(python_format_string): - """ - Given a python datetime format string, attempts to convert it to the nearest PHP datetime format string possible. - """ - python2PHP = { - "%a": "D", - "%a": "D", - "%A": "l", - "%b": "M", - "%B": "F", - "%c": "", - "%d": "d", - "%H": "H", - "%I": "h", - "%j": "z", - "%m": "m", - "%M": "i", - "%p": "A", - "%S": "s", - "%U": "", - "%w": "w", - "%W": "W", - "%x": "", - "%X": "", - "%y": "y", - "%Y": "Y", - "%Z": "e", - } +@register.filter(name="truncate_time") +def truncate_time(value, time_unit): + value = str(value) + return { + "millis": lambda: value.split(".")[0], + "seconds": lambda: value.rsplit(":", maxsplit=1)[0], + "minutes": lambda: value.split(":", maxsplit=1)[0], + "hours": lambda: value.rsplit(" ")[0], + }[time_unit]() - php_format_string = python_format_string - for py, php in python2PHP.items(): - php_format_string = php_format_string.replace(py, php) - return php_format_string + +@register.filter(name="format_timedelta") +def format_timedelta(value: datetime.timedelta) -> str: + days = value.days + if days == 0: + return str(value) + remainder = value - datetime.timedelta(days=days) + return ngettext( + "%(nb_days)d day, %(remainder)s", "%(nb_days)d days, %(remainder)s", days + ) % {"nb_days": days, "remainder": str(remainder)} @register.simple_tag() diff --git a/core/utils.py b/core/utils.py index bb45d726..0c5fa10c 100644 --- a/core/utils.py +++ b/core/utils.py @@ -31,7 +31,6 @@ from datetime import date from PIL import ExifTags -# from exceptions import IOError import PIL from django.conf import settings @@ -52,14 +51,12 @@ def get_start_of_semester(d=date.today()): year = today.year start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1]) start2 = start.replace(month=(start.month + 6) % 12) - if start > start2: - start, start2 = start2, start - if today < start: - return start2.replace(year=year - 1) - elif today < start2: - return start - else: - return start2 + spring, autumn = min(start, start2), max(start, start2) + if today > autumn: # autumn semester + return autumn + if today > spring: # spring semester + return spring + return autumn.replace(year=year - 1) # autumn semester of last year def get_semester(d=date.today()): diff --git a/counter/models.py b/counter/models.py index ceec55ab..2d19cc00 100644 --- a/counter/models.py +++ b/counter/models.py @@ -22,13 +22,12 @@ # # from __future__ import annotations -from django.db.models import Sum, F from typing import Tuple from django.db import models -from django.db.models import OuterRef, Exists -from django.db.models.functions import Length +from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists +from django.db.models.functions import Concat, Length from django.utils.translation import gettext_lazy as _ from django.utils import timezone from django.conf import settings @@ -37,14 +36,14 @@ from django.core.validators import MinLengthValidator from django.forms import ValidationError from django.utils.functional import cached_property -from datetime import timedelta, date +from datetime import timedelta, date, datetime import random import string import os import base64 -import datetime from dict2xml import dict2xml +from core.utils import get_start_of_semester from sith.settings import SITH_COUNTER_OFFICES, SITH_MAIN_CLUB from club.models import Club, Membership from accounting.models import CurrencyField @@ -92,8 +91,9 @@ class Customer(models.Model): don't mix them) and a Product. """ subscription = self.user.subscriptions.order_by("subscription_end").last() - time_diff = date.today() - subscription.subscription_end - return subscription is not None and time_diff < timedelta(days=90) + if subscription is None: + return False + return (date.today() - subscription.subscription_end) < timedelta(days=90) @classmethod def get_or_create(cls, user: User) -> Tuple[Customer, bool]: @@ -491,7 +491,7 @@ class Counter(models.Model): """ return self.is_open() and ( (timezone.now() - self.permanencies.order_by("-activity").first().activity) - > datetime.timedelta(minutes=settings.SITH_COUNTER_MINUTE_INACTIVE) + > timedelta(minutes=settings.SITH_COUNTER_MINUTE_INACTIVE) ) def barman_list(self): @@ -517,6 +517,77 @@ class Counter(models.Model): is_ae_member = True return is_ae_member + 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") + ) + + def get_top_customers(self, since=get_start_of_semester()) -> 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 + """ + 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")) + .values("customer__user", "name", "nickname") + .annotate( + selling_sum=Sum( + F("unit_price") * F("quantity"), output_field=CurrencyField() + ) + ) + .filter(selling_sum__gt=0) + .order_by("-selling_sum") + ) + + def get_total_sales(self, since=get_start_of_semester()) -> 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 + :return: Total revenue earned at this counter + """ + if isinstance(since, date): + since = datetime.combine(since, datetime.min.time()) + total = self.sellings.filter(date__gte=since).aggregate( + total=Sum(F("quantity") * F("unit_price"), output_field=CurrencyField()) + )["total"] + return total if total is not None else CurrencyField(0) + class Refilling(models.Model): """ diff --git a/counter/templates/counter/stats.jinja b/counter/templates/counter/stats.jinja index 011b8987..03b7f4e0 100644 --- a/counter/templates/counter/stats.jinja +++ b/counter/templates/counter/stats.jinja @@ -2,43 +2,34 @@ {% from 'core/macros.jinja' import user_profile_link %} {% block title %} -{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %} + {% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %} +{% endblock %} + +{% block jquery_css %} + {# Remove jquery_css #} {% endblock %} {% block content %} -

{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}

+

{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}

{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %}

- - - - - - - - + + + + + + + - {% for r in top %} - {% set customer=Customer.objects.filter(user__id=r.customer__user).first() %} - {% if customer.user == user %} - - {% else %} - - {% endif %} + {% for customer in top_customers %} + - - - - - + + + + {% endfor %} @@ -47,23 +38,20 @@

{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %}

{% trans %}Nb{% endtrans %}{% trans %}User{% endtrans %}{% trans %}Promo{% endtrans %}{% trans %}Clubs{% endtrans %}{% trans %}Total{% endtrans %}{% trans %}Percentage{% endtrans %}
{% trans %}User{% endtrans %}{% trans %}Promo{% endtrans %}{% trans %}Total{% endtrans %}{% trans %}Percentage{% endtrans %}
{{ loop.index }}{{ customer.user.get_display_name() }}{{ customer.user.promo or '' }} - {% for m in customer.user.memberships.filter(club__parent=None, end_date=None, - role__gt=settings.SITH_MAXIMUM_FREE_ROLE).all() -%} - {%- if loop.index>1 -%}, {% endif -%} - {{ m.club.name }} - {%- endfor %} - {{ r.selling_sum }} €{{ '%.2f'|format(100 * r.selling_sum / total_sellings) }}{{ customer.name }} {% if customer.nickname %} ({{ customer.nickname }}) {% endif %}{{ customer.promo or '' }}{{ "%.2f"|format(customer.selling_sum) }} €{{ '%.2f'|format(100 * customer.selling_sum / total_sellings) }}%
- - - - - + + + + + + - {% for r in top_barman_semester %} - {% set u=User.objects.filter(id=r.user).first() %} - {% if u == user %} - - {% else %} - - {% endif %} + {% for barman in top_barman_semester %} + - - + + + {% endfor %} @@ -72,23 +60,20 @@

{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }} (all semesters){% endtrans %}

{% trans %}Nb{% endtrans %}{% trans %}User{% endtrans %}{% trans %}Time{% endtrans %}
{% trans %}User{% endtrans %}{% trans %}Promo{% endtrans %}{% trans %}Time{% endtrans %}
{{ loop.index }}{{ u.get_display_name() }}{{ r.perm_sum }}{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}{{ barman.promo or '' }}{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}
- - - - - + + + + + + - {% for r in top_barman %} - {% set u=User.objects.filter(id=r.user).first() %} - {% if u == user %} - - {% else %} - - {% endif %} + {% for barman in top_barman %} + - - + + + {% endfor %} diff --git a/counter/tests.py b/counter/tests.py index d8772d9e..521127ed 100644 --- a/counter/tests.py +++ b/counter/tests.py @@ -28,9 +28,13 @@ import string from django.test import TestCase from django.urls import reverse from django.core.management import call_command +from django.utils import timezone +from django.utils.timezone import timedelta +from club.models import Club from core.models import User -from counter.models import Counter, Customer, BillingInfo +from counter.models import Counter, Customer, BillingInfo, Permanency, Selling, Product +from sith.settings import SITH_MAIN_CLUB class CounterTest(TestCase): @@ -164,15 +168,211 @@ class CounterTest(TestCase): class CounterStatsTest(TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): + super().setUpClass() call_command("populate") - self.counter = Counter.objects.filter(id=2).first() + cls.counter = Counter.objects.filter(id=2).first() + cls.krophil = User.objects.get(username="krophil") + cls.skia = User.objects.get(username="skia") + cls.sli = User.objects.get(username="sli") + cls.root = User.objects.get(username="root") + cls.subscriber = User.objects.get(username="subscriber") + cls.old_subscriber = User.objects.get(username="old_subscriber") + cls.counter.sellers.add(cls.sli) + cls.counter.sellers.add(cls.root) + cls.counter.sellers.add(cls.skia) + cls.counter.sellers.add(cls.krophil) - def test_unauthorised_user_fail(self): + barbar = Product.objects.get(code="BARB") + + # remove everything to make sure the fixtures bring no side effect + Permanency.objects.all().delete() + Selling.objects.all().delete() + + now = timezone.now() + # total of sli : 5 hours + Permanency.objects.create( + user=cls.sli, start=now, end=now + timedelta(hours=1), counter=cls.counter + ) + Permanency.objects.create( + user=cls.sli, + start=now + timedelta(hours=4), + end=now + timedelta(hours=6), + counter=cls.counter, + ) + Permanency.objects.create( + user=cls.sli, + start=now + timedelta(hours=7), + end=now + timedelta(hours=9), + counter=cls.counter, + ) + + # total of skia : 16 days, 2 hours, 35 minutes and 54 seconds + Permanency.objects.create( + user=cls.skia, start=now, end=now + timedelta(hours=1), counter=cls.counter + ) + Permanency.objects.create( + user=cls.skia, + start=now + timedelta(days=4, hours=1), + end=now + timedelta(days=20, hours=2, minutes=35, seconds=54), + counter=cls.counter, + ) + + # total of root : 1 hour + 20 hours (but the 20 hours were on last year) + Permanency.objects.create( + user=cls.root, + start=now + timedelta(days=5), + end=now + timedelta(days=5, hours=1), + counter=cls.counter, + ) + Permanency.objects.create( + user=cls.root, + start=now - timedelta(days=300, hours=20), + end=now - timedelta(days=300), + counter=cls.counter, + ) + + # total of krophil : 0 hour + s = Selling( + label=barbar.name, + product=barbar, + club=Club.objects.get(name=SITH_MAIN_CLUB["name"]), + counter=cls.counter, + unit_price=2, + seller=cls.skia, + ) + + krophil_customer = Customer.get_or_create(cls.krophil)[0] + sli_customer = Customer.get_or_create(cls.sli)[0] + skia_customer = Customer.get_or_create(cls.skia)[0] + root_customer = Customer.get_or_create(cls.root)[0] + + # moderate drinker. Total : 100 € + s.quantity = 50 + s.customer = krophil_customer + s.save(allow_negative=True) + + # Sli is a drunkard. Total : 2000 € + s.quantity = 100 + s.customer = sli_customer + for _ in range(10): + # little trick to make sure the instance is duplicated in db + s.pk = None + s.save(allow_negative=True) # save ten different sales + + # Skia is a heavy drinker too. Total : 1000 € + s.customer = skia_customer + for _ in range(5): + s.pk = None + s.save(allow_negative=True) + + # Root is quite an abstemious one. Total : 2 € + s.pk = None + s.quantity = 1 + s.customer = root_customer + s.save(allow_negative=True) + + def test_not_authenticated_user_fail(self): # Test with not login user response = self.client.get(reverse("counter:stats", args=[self.counter.id])) self.assertTrue(response.status_code == 403) + def test_unauthorized_user_fails(self): + user = User.objects.get(username="public") + self.client.login(username=user.username, password="plop") + response = self.client.get(reverse("counter:stats", args=[self.counter.id])) + self.assertTrue(response.status_code == 403) + + def test_get_total_sales(self): + """ + Test the result of the Counter.get_total_sales() method + """ + total = self.counter.get_total_sales() + self.assertEqual(total, 3102) + + def test_top_barmen(self): + """ + Test the result of Counter.get_top_barmen() is correct + """ + top = iter(self.counter.get_top_barmen()) + self.assertEqual( + next(top), + { + "user": self.skia.id, + "name": f"{self.skia.first_name} {self.skia.last_name}", + "promo": self.skia.promo, + "nickname": self.skia.nick_name, + "perm_sum": timedelta(days=16, hours=2, minutes=35, seconds=54), + }, + ) + self.assertEqual( + next(top), + { + "user": self.root.id, + "name": f"{self.root.first_name} {self.root.last_name}", + "promo": self.root.promo, + "nickname": self.root.nick_name, + "perm_sum": timedelta(hours=21), + }, + ) + self.assertEqual( + next(top), + { + "user": self.sli.id, + "name": f"{self.sli.first_name} {self.sli.last_name}", + "promo": self.sli.promo, + "nickname": self.sli.nick_name, + "perm_sum": timedelta(hours=5), + }, + ) + self.assertIsNone( + next(top, None), msg="barmen with no office hours should not be in the top" + ) + + def test_top_customer(self): + """ + Test the result of Counter.get_top_customers() is correct + """ + top = iter(self.counter.get_top_customers()) + self.assertEqual( + next(top), + { + "customer__user": self.sli.id, + "name": f"{self.sli.first_name} {self.sli.last_name}", + "nickname": self.sli.nick_name, + "selling_sum": 2000, + }, + ) + self.assertEqual( + next(top), + { + "customer__user": self.skia.id, + "name": f"{self.skia.first_name} {self.skia.last_name}", + "nickname": self.skia.nick_name, + "selling_sum": 1000, + }, + ) + self.assertEqual( + next(top), + { + "customer__user": self.krophil.id, + "name": f"{self.krophil.first_name} {self.krophil.last_name}", + "nickname": self.krophil.nick_name, + "selling_sum": 100, + }, + ) + self.assertEqual( + next(top), + { + "customer__user": self.root.id, + "name": f"{self.root.first_name} {self.root.last_name}", + "nickname": self.root.nick_name, + "selling_sum": 2, + }, + ) + self.assertIsNone(next(top, None)) + class BillingInfoTest(TestCase): @classmethod diff --git a/counter/views.py b/counter/views.py index 2b9bf392..e4cbd120 100644 --- a/counter/views.py +++ b/counter/views.py @@ -48,13 +48,15 @@ from django.utils import timezone from django import forms from django.utils.translation import gettext_lazy as _ from django.conf import settings -from django.db import DataError, transaction, models +from django.db import DataError, transaction +import json import re import pytz -from datetime import date, timedelta, datetime +from datetime import timedelta, datetime from http import HTTPStatus +from core.utils import get_start_of_semester from core.views import CanViewMixin, TabedViewMixin, CanEditMixin from core.views.forms import LoginForm from core.models import User @@ -68,7 +70,6 @@ from counter.forms import ( CashSummaryFormBase, EticketForm, ) -from subscription.models import Subscription from counter.models import ( Counter, Customer, @@ -80,7 +81,6 @@ from counter.models import ( CashRegisterSummary, CashRegisterSummaryItem, Eticket, - Permanency, BillingInfo, ) from accounting.models import CurrencyField @@ -1365,80 +1365,21 @@ class CounterStatView(DetailView, CounterAdminMixin): def get_context_data(self, **kwargs): """Add stats to the context""" - from django.db.models import Sum, Case, When, F - + counter = self.object + semester_start = get_start_of_semester() + office_hours = counter.get_top_barmen() kwargs = super(CounterStatView, self).get_context_data(**kwargs) - kwargs["Customer"] = Customer - kwargs["User"] = User - semester_start = Subscription.compute_start(d=date.today(), duration=3) - kwargs["total_sellings"] = Selling.objects.filter( - date__gte=semester_start, counter=self.object - ).aggregate( - total_sellings=Sum( - F("quantity") * F("unit_price"), output_field=CurrencyField() - ) - )[ - "total_sellings" - ] - kwargs["top"] = ( - Selling.objects.values("customer__user") - .annotate( - selling_sum=Sum( - Case( - When( - counter=self.object, - date__gte=semester_start, - unit_price__gt=0, - then=F("unit_price") * F("quantity"), - ), - output_field=CurrencyField(), - ) - ) - ) - .exclude(selling_sum=None) - .order_by("-selling_sum") - .all()[:100] + kwargs.update( + { + "counter": counter, + "total_sellings": counter.get_total_sales(since=semester_start), + "top_customers": counter.get_top_customers(since=semester_start)[:100], + "top_barman": office_hours[:100], + "top_barman_semester": ( + office_hours.filter(start__gt=semester_start)[:100] + ), + } ) - kwargs["top_barman"] = ( - Permanency.objects.values("user") - .annotate( - perm_sum=Sum( - Case( - When( - counter=self.object, - end__gt=datetime(year=1999, month=1, day=1), - then=F("end") - F("start"), - ), - output_field=models.DateTimeField(), - ) - ) - ) - .exclude(perm_sum=None) - .order_by("-perm_sum") - .all()[:100] - ) - kwargs["top_barman_semester"] = ( - Permanency.objects.values("user") - .annotate( - perm_sum=Sum( - Case( - When( - counter=self.object, - start__gt=semester_start, - end__gt=datetime(year=1999, month=1, day=1), - then=F("end") - F("start"), - ), - output_field=models.DateTimeField(), - ) - ) - ) - .exclude(perm_sum=None) - .order_by("-perm_sum") - .all()[:100] - ) - - kwargs["sith_date"] = settings.SITH_START_DATE[0] - kwargs["semester_start"] = semester_start return kwargs def dispatch(self, request, *args, **kwargs): diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index bc9dccc4..977437bc 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-02 11:02+0100\n" +"POT-Creation-Date: 2023-03-07 23:04+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Skia \n" "Language-Team: AE info \n" @@ -18,8 +18,8 @@ msgstr "" #: accounting/models.py:61 accounting/models.py:110 accounting/models.py:143 #: accounting/models.py:216 club/models.py:48 com/models.py:279 -#: com/models.py:296 counter/models.py:196 counter/models.py:229 -#: counter/models.py:317 forum/models.py:58 launderette/models.py:38 +#: com/models.py:296 counter/models.py:222 counter/models.py:255 +#: counter/models.py:369 forum/models.py:58 launderette/models.py:38 #: launderette/models.py:93 launderette/models.py:131 stock/models.py:40 #: stock/models.py:63 stock/models.py:105 stock/models.py:133 msgid "name" @@ -66,8 +66,8 @@ msgid "account number" msgstr "numero de compte" #: accounting/models.py:116 accounting/models.py:147 club/models.py:275 -#: com/models.py:75 com/models.py:266 com/models.py:302 counter/models.py:247 -#: counter/models.py:319 trombi/models.py:217 +#: com/models.py:75 com/models.py:266 com/models.py:302 counter/models.py:273 +#: counter/models.py:371 trombi/models.py:217 msgid "club" msgstr "club" @@ -88,12 +88,12 @@ msgstr "Compte club" msgid "%(club_account)s on %(bank_account)s" msgstr "%(club_account)s sur %(bank_account)s" -#: accounting/models.py:214 club/models.py:281 counter/models.py:753 +#: accounting/models.py:214 club/models.py:281 counter/models.py:878 #: election/models.py:18 launderette/models.py:194 msgid "start date" msgstr "date de début" -#: accounting/models.py:215 club/models.py:282 counter/models.py:754 +#: accounting/models.py:215 club/models.py:282 counter/models.py:879 #: election/models.py:19 msgid "end date" msgstr "date de fin" @@ -106,8 +106,8 @@ msgstr "est fermé" msgid "club account" msgstr "compte club" -#: accounting/models.py:225 accounting/models.py:289 counter/models.py:60 -#: counter/models.py:475 +#: accounting/models.py:225 accounting/models.py:289 counter/models.py:64 +#: counter/models.py:600 msgid "amount" msgstr "montant" @@ -129,19 +129,19 @@ msgstr "classeur" #: accounting/models.py:290 core/models.py:862 core/models.py:1400 #: core/models.py:1448 core/models.py:1477 core/models.py:1501 -#: counter/models.py:485 counter/models.py:578 counter/models.py:789 +#: counter/models.py:610 counter/models.py:703 counter/models.py:914 #: eboutic/models.py:67 eboutic/models.py:236 forum/models.py:311 #: forum/models.py:408 stock/models.py:104 msgid "date" msgstr "date" -#: accounting/models.py:291 counter/models.py:198 counter/models.py:790 +#: accounting/models.py:291 counter/models.py:224 counter/models.py:915 #: pedagogy/models.py:219 stock/models.py:107 msgid "comment" msgstr "commentaire" -#: accounting/models.py:293 counter/models.py:487 counter/models.py:580 -#: subscription/models.py:66 +#: accounting/models.py:293 counter/models.py:612 counter/models.py:705 +#: subscription/models.py:65 msgid "payment method" msgstr "méthode de paiement" @@ -149,7 +149,7 @@ msgstr "méthode de paiement" msgid "cheque number" msgstr "numéro de chèque" -#: accounting/models.py:303 eboutic/models.py:328 +#: accounting/models.py:303 eboutic/models.py:329 msgid "invoice" msgstr "facture" @@ -167,7 +167,7 @@ msgstr "type comptable" #: accounting/models.py:328 accounting/models.py:475 accounting/models.py:510 #: accounting/models.py:545 core/models.py:1476 core/models.py:1502 -#: counter/models.py:544 +#: counter/models.py:669 msgid "label" msgstr "étiquette" @@ -180,9 +180,9 @@ msgstr "type de cible" #: club/templates/club/club_old_members.jinja:8 #: club/templates/club/mailing.jinja:41 #: counter/templates/counter/cash_summary_list.jinja:32 -#: counter/templates/counter/stats.jinja:15 -#: counter/templates/counter/stats.jinja:52 -#: counter/templates/counter/stats.jinja:77 +#: counter/templates/counter/stats.jinja:19 +#: counter/templates/counter/stats.jinja:43 +#: counter/templates/counter/stats.jinja:65 #: launderette/templates/launderette/launderette_admin.jinja:44 msgid "User" msgstr "Utilisateur" @@ -211,7 +211,7 @@ msgstr "Utilisateur" msgid "Club" msgstr "Club" -#: accounting/models.py:339 core/views/user.py:279 +#: accounting/models.py:339 core/views/user.py:277 msgid "Account" msgstr "Compte" @@ -219,7 +219,7 @@ msgstr "Compte" msgid "Company" msgstr "Entreprise" -#: accounting/models.py:341 core/models.py:230 sith/settings.py:391 +#: accounting/models.py:341 core/models.py:230 sith/settings.py:393 #: stock/templates/stock/shopping_list_items.jinja:37 msgid "Other" msgstr "Autre" @@ -266,7 +266,7 @@ msgstr "" "Vous devez fournir soit un type comptable simplifié ou un type comptable " "standard" -#: accounting/models.py:467 counter/models.py:239 pedagogy/models.py:46 +#: accounting/models.py:467 counter/models.py:265 pedagogy/models.py:46 msgid "code" msgstr "code" @@ -426,7 +426,7 @@ msgstr "Nouveau compte club" #: com/templates/com/weekmail.jinja:61 core/templates/core/file.jinja:38 #: core/templates/core/group_list.jinja:24 core/templates/core/page.jinja:35 #: core/templates/core/poster_list.jinja:40 -#: core/templates/core/user_tools.jinja:43 core/views/user.py:229 +#: core/templates/core/user_tools.jinja:43 core/views/user.py:227 #: counter/templates/counter/cash_summary_list.jinja:53 #: counter/templates/counter/counter_list.jinja:17 #: counter/templates/counter/counter_list.jinja:33 @@ -530,7 +530,7 @@ msgid "Effective amount" msgstr "Montant effectif" #: accounting/templates/accounting/club_account_details.jinja:36 -#: sith/settings.py:437 +#: sith/settings.py:439 msgid "Closed" msgstr "Fermé" @@ -599,7 +599,7 @@ msgstr "Classeur : " #: accounting/templates/accounting/journal_statement_accounting.jinja:30 #: core/templates/core/user_account.jinja:38 #: core/templates/core/user_account_detail.jinja:10 -#: counter/templates/counter/counter_click.jinja:26 +#: counter/templates/counter/counter_click.jinja:32 msgid "Amount: " msgstr "Montant : " @@ -617,9 +617,6 @@ msgid "New operation" msgstr "Nouvelle opération" #: accounting/templates/accounting/journal_details.jinja:32 -#: counter/templates/counter/stats.jinja:14 -#: counter/templates/counter/stats.jinja:51 -#: counter/templates/counter/stats.jinja:76 msgid "Nb" msgstr "No" @@ -670,7 +667,7 @@ msgid "Done" msgstr "Effectuées" #: accounting/templates/accounting/journal_details.jinja:41 -#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:1072 +#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:1084 #: pedagogy/templates/pedagogy/moderation.jinja:13 #: pedagogy/templates/pedagogy/uv_detail.jinja:138 #: trombi/templates/trombi/comment.jinja:4 @@ -967,15 +964,15 @@ msgstr "Date de fin" #: club/forms.py:166 club/templates/club/club_sellings.jinja:21 #: core/templates/core/user_account_detail.jinja:18 #: core/templates/core/user_account_detail.jinja:51 -#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:156 +#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:158 msgid "Counter" msgstr "Comptoir" -#: club/forms.py:174 counter/views.py:770 +#: club/forms.py:174 counter/views.py:782 msgid "Products" msgstr "Produits" -#: club/forms.py:179 counter/views.py:775 +#: club/forms.py:179 counter/views.py:787 msgid "Archived products" msgstr "Produits archivés" @@ -1045,7 +1042,7 @@ msgstr "Vous ne pouvez pas faire de boucles dans les clubs" msgid "A club with that unix_name already exists" msgstr "Un club avec ce nom UNIX existe déjà." -#: club/models.py:267 counter/models.py:744 counter/models.py:780 +#: club/models.py:267 counter/models.py:869 counter/models.py:905 #: eboutic/models.py:63 eboutic/models.py:232 election/models.py:192 #: launderette/models.py:145 launderette/models.py:213 sas/models.py:244 #: trombi/models.py:213 @@ -1057,8 +1054,8 @@ msgstr "nom d'utilisateur" msgid "role" msgstr "rôle" -#: club/models.py:289 core/models.py:81 counter/models.py:197 -#: counter/models.py:230 election/models.py:15 election/models.py:120 +#: club/models.py:289 core/models.py:81 counter/models.py:223 +#: counter/models.py:256 election/models.py:15 election/models.py:120 #: election/models.py:197 forum/models.py:59 forum/models.py:240 msgid "description" msgstr "description" @@ -1209,7 +1206,7 @@ msgid "Barman" msgstr "Barman" #: club/templates/club/club_sellings.jinja:23 -#: counter/templates/counter/counter_click.jinja:23 +#: counter/templates/counter/counter_click.jinja:29 #: counter/templates/counter/last_ops.jinja:22 #: counter/templates/counter/last_ops.jinja:47 #: counter/templates/counter/refilling_list.jinja:12 @@ -1228,7 +1225,7 @@ msgstr "Quantité" #: core/templates/core/user_account_detail.jinja:22 #: counter/templates/counter/cash_summary_list.jinja:35 #: counter/templates/counter/last_ops.jinja:50 -#: counter/templates/counter/stats.jinja:18 +#: counter/templates/counter/stats.jinja:21 #: subscription/templates/subscription/stats.jinja:40 #: subscription/templates/subscription/stats.jinja:48 msgid "Total" @@ -1362,7 +1359,7 @@ msgstr "Anciens membres" msgid "History" msgstr "Historique" -#: club/views.py:125 core/templates/core/base.jinja:123 core/views/user.py:222 +#: club/views.py:125 core/templates/core/base.jinja:129 core/views/user.py:220 #: sas/templates/sas/picture.jinja:95 trombi/views.py:63 msgid "Tools" msgstr "Outils" @@ -1645,7 +1642,7 @@ msgid "Calls to moderate" msgstr "Appels à modérer" #: com/templates/com/news_admin_list.jinja:242 -#: core/templates/core/base.jinja:177 +#: core/templates/core/base.jinja:183 msgid "Events" msgstr "Événements" @@ -2404,7 +2401,7 @@ msgstr "Connexion" msgid "Register" msgstr "S'enregister" -#: core/templates/core/base.jinja:85 core/templates/core/base.jinja:86 +#: core/templates/core/base.jinja:91 core/templates/core/base.jinja:92 #: forum/templates/forum/macros.jinja:171 #: forum/templates/forum/macros.jinja:175 #: matmat/templates/matmat/search_form.jinja:37 @@ -2414,64 +2411,64 @@ msgstr "S'enregister" msgid "Search" msgstr "Recherche" -#: core/templates/core/base.jinja:112 +#: core/templates/core/base.jinja:118 msgid "View more" msgstr "Voir plus" -#: core/templates/core/base.jinja:116 +#: core/templates/core/base.jinja:122 #: forum/templates/forum/last_unread.jinja:17 msgid "Mark all as read" msgstr "Marquer tout commme lu" -#: core/templates/core/base.jinja:126 +#: core/templates/core/base.jinja:132 msgid "Logout" msgstr "Déconnexion" -#: core/templates/core/base.jinja:161 +#: core/templates/core/base.jinja:167 msgid "Main" msgstr "Accueil" -#: core/templates/core/base.jinja:163 +#: core/templates/core/base.jinja:169 msgid "Associations & Clubs" msgstr "Associations & Clubs" -#: core/templates/core/base.jinja:167 +#: core/templates/core/base.jinja:173 msgid "AE" msgstr "L'AE" -#: core/templates/core/base.jinja:168 +#: core/templates/core/base.jinja:174 msgid "AE's clubs" msgstr "Les clubs de L'AE" -#: core/templates/core/base.jinja:169 +#: core/templates/core/base.jinja:175 msgid "BdF" msgstr "Le BdF" -#: core/templates/core/base.jinja:170 +#: core/templates/core/base.jinja:176 msgid "BDS" msgstr "Le BDS" -#: core/templates/core/base.jinja:171 +#: core/templates/core/base.jinja:177 msgid "CETU" msgstr "Le CETU" -#: core/templates/core/base.jinja:172 +#: core/templates/core/base.jinja:178 msgid "Doceo" msgstr "Doceo" -#: core/templates/core/base.jinja:173 +#: core/templates/core/base.jinja:179 msgid "Positions" msgstr "Postes à pourvoir" -#: core/templates/core/base.jinja:181 core/templates/core/user_tools.jinja:118 +#: core/templates/core/base.jinja:187 core/templates/core/user_tools.jinja:118 msgid "Elections" msgstr "Élections" -#: core/templates/core/base.jinja:182 +#: core/templates/core/base.jinja:188 msgid "Big event" msgstr "Grandes Activités" -#: core/templates/core/base.jinja:185 +#: core/templates/core/base.jinja:191 #: forum/templates/forum/favorite_topics.jinja:14 #: forum/templates/forum/last_unread.jinja:14 #: forum/templates/forum/macros.jinja:90 forum/templates/forum/main.jinja:6 @@ -2480,89 +2477,89 @@ msgstr "Grandes Activités" msgid "Forum" msgstr "Forum" -#: core/templates/core/base.jinja:186 +#: core/templates/core/base.jinja:192 msgid "Gallery" msgstr "Photos" -#: core/templates/core/base.jinja:187 counter/models.py:327 +#: core/templates/core/base.jinja:193 counter/models.py:379 #: counter/templates/counter/counter_list.jinja:11 #: eboutic/templates/eboutic/eboutic_main.jinja:4 #: eboutic/templates/eboutic/eboutic_main.jinja:23 #: eboutic/templates/eboutic/eboutic_makecommand.jinja:17 #: eboutic/templates/eboutic/eboutic_payment_result.jinja:4 -#: sith/settings.py:390 sith/settings.py:398 +#: sith/settings.py:392 sith/settings.py:400 msgid "Eboutic" msgstr "Eboutic" -#: core/templates/core/base.jinja:189 +#: core/templates/core/base.jinja:195 msgid "Services" msgstr "Services" -#: core/templates/core/base.jinja:193 +#: core/templates/core/base.jinja:199 msgid "Matmatronch" msgstr "Matmatronch" -#: core/templates/core/base.jinja:194 launderette/models.py:47 +#: core/templates/core/base.jinja:200 launderette/models.py:47 #: launderette/templates/launderette/launderette_book.jinja:5 #: launderette/templates/launderette/launderette_book_choose.jinja:4 #: launderette/templates/launderette/launderette_main.jinja:4 msgid "Launderette" msgstr "Laverie" -#: core/templates/core/base.jinja:195 core/templates/core/file.jinja:20 +#: core/templates/core/base.jinja:201 core/templates/core/file.jinja:20 #: core/views/files.py:86 msgid "Files" msgstr "Fichiers" -#: core/templates/core/base.jinja:196 core/templates/core/user_tools.jinja:109 +#: core/templates/core/base.jinja:202 core/templates/core/user_tools.jinja:109 msgid "Pedagogy" msgstr "Pédagogie" -#: core/templates/core/base.jinja:200 +#: core/templates/core/base.jinja:206 msgid "My Benefits" msgstr "Mes Avantages" -#: core/templates/core/base.jinja:204 +#: core/templates/core/base.jinja:210 msgid "Sponsors" msgstr "Partenaires" -#: core/templates/core/base.jinja:205 +#: core/templates/core/base.jinja:211 msgid "Subscriber benefits" msgstr "Les avantages cotisants" -#: core/templates/core/base.jinja:209 +#: core/templates/core/base.jinja:215 msgid "Help" msgstr "Aide" -#: core/templates/core/base.jinja:213 +#: core/templates/core/base.jinja:219 msgid "FAQ" msgstr "FAQ" -#: core/templates/core/base.jinja:214 core/templates/core/base.jinja:256 +#: core/templates/core/base.jinja:220 core/templates/core/base.jinja:262 msgid "Contacts" msgstr "Contacts" -#: core/templates/core/base.jinja:215 +#: core/templates/core/base.jinja:221 msgid "Wiki" msgstr "Wiki" -#: core/templates/core/base.jinja:257 +#: core/templates/core/base.jinja:263 msgid "Legal notices" msgstr "Mentions légales" -#: core/templates/core/base.jinja:258 +#: core/templates/core/base.jinja:264 msgid "Intellectual property" msgstr "Propriété intellectuelle" -#: core/templates/core/base.jinja:259 +#: core/templates/core/base.jinja:265 msgid "Help & Documentation" msgstr "Aide & Documentation" -#: core/templates/core/base.jinja:260 +#: core/templates/core/base.jinja:266 msgid "R&D" msgstr "R&D" -#: core/templates/core/base.jinja:262 +#: core/templates/core/base.jinja:268 msgid "Site made by good people" msgstr "Site réalisé par des gens bons" @@ -2591,7 +2588,7 @@ msgstr "Confirmation" #: core/templates/core/delete_confirm.jinja:20 #: core/templates/core/file_delete_confirm.jinja:14 -#: counter/templates/counter/counter_click.jinja:107 +#: counter/templates/counter/counter_click.jinja:121 msgid "Cancel" msgstr "Annuler" @@ -3012,8 +3009,7 @@ msgstr "Résultat de la recherche" msgid "Users" msgstr "Utilisateurs" -#: core/templates/core/search.jinja:18 core/views/user.py:244 -#: counter/templates/counter/stats.jinja:17 +#: core/templates/core/search.jinja:18 core/views/user.py:242 msgid "Clubs" msgstr "Clubs" @@ -3070,7 +3066,7 @@ msgid "Eboutic invoices" msgstr "Facture eboutic" #: core/templates/core/user_account.jinja:57 -#: core/templates/core/user_tools.jinja:37 counter/views.py:787 +#: core/templates/core/user_tools.jinja:37 counter/views.py:807 msgid "Etickets" msgstr "Etickets" @@ -3259,7 +3255,7 @@ msgstr "Voir l'arbre des ancêtres" msgid "No godfathers / godmothers" msgstr "Pas de famille" -#: core/templates/core/user_godfathers.jinja:25 core/views/user.py:464 +#: core/templates/core/user_godfathers.jinja:25 core/views/user.py:462 msgid "Godchildren" msgstr "Fillots / Fillotes" @@ -3328,7 +3324,7 @@ msgid "Error downloading your pictures" msgstr "Erreur de téléchargement de vos photos" #: core/templates/core/user_preferences.jinja:4 -#: core/templates/core/user_preferences.jinja:8 core/views/user.py:236 +#: core/templates/core/user_preferences.jinja:8 core/views/user.py:234 msgid "Preferences" msgstr "Préférences" @@ -3381,7 +3377,7 @@ msgstr "Achats" msgid "Product top 10" msgstr "Top 10 produits" -#: core/templates/core/user_stats.jinja:27 counter/forms.py:168 +#: core/templates/core/user_stats.jinja:27 counter/forms.py:176 msgid "Product" msgstr "Produit" @@ -3398,7 +3394,7 @@ msgstr "Outils utilisateurs" msgid "Sith management" msgstr "Gestion de Sith" -#: core/templates/core/user_tools.jinja:14 core/views/user.py:252 +#: core/templates/core/user_tools.jinja:14 core/views/user.py:250 msgid "Groups" msgstr "Groupes" @@ -3427,7 +3423,7 @@ msgid "Subscription stats" msgstr "Statistiques de cotisation" #: core/templates/core/user_tools.jinja:29 counter/forms.py:139 -#: counter/views.py:765 +#: counter/views.py:777 msgid "Counters" msgstr "Comptoirs" @@ -3444,16 +3440,16 @@ msgid "Product types management" msgstr "Gestion des types de produit" #: core/templates/core/user_tools.jinja:35 -#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:785 +#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:797 msgid "Cash register summaries" msgstr "Relevés de caisse" #: core/templates/core/user_tools.jinja:36 -#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:790 +#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:802 msgid "Invoices call" msgstr "Appels à facture" -#: core/templates/core/user_tools.jinja:44 core/views/user.py:270 +#: core/templates/core/user_tools.jinja:44 core/views/user.py:268 #: counter/templates/counter/counter_list.jinja:18 #: counter/templates/counter/counter_list.jinja:34 #: counter/templates/counter/counter_list.jinja:56 @@ -3547,6 +3543,13 @@ msgstr "Convertir de la syntaxe dokuwiki/BBcode vers Markdown" msgid "Trombi tools" msgstr "Outils Trombi" +#: core/templatetags/renderer.py:77 +#, python-format +msgid "%(nb_days)d day, %(remainder)s" +msgid_plural "%(nb_days)d days, %(remainder)s" +msgstr[0] "" +msgstr[1] "" + #: core/views/files.py:82 msgid "Add a new folder" msgstr "Ajouter un nouveau dossier" @@ -3700,7 +3703,7 @@ msgstr "Utilisateurs à retirer du groupe" msgid "Users to add to group" msgstr "Utilisateurs à ajouter au groupe" -#: core/views/user.py:202 core/views/user.py:466 core/views/user.py:468 +#: core/views/user.py:202 core/views/user.py:464 core/views/user.py:466 msgid "Family" msgstr "Famille" @@ -3709,32 +3712,32 @@ msgstr "Famille" msgid "Pictures" msgstr "Photos" -#: core/views/user.py:217 +#: core/views/user.py:215 msgid "Galaxy" msgstr "Galaxie" -#: core/views/user.py:610 +#: core/views/user.py:608 msgid "User already has a profile picture" msgstr "L'utilisateur a déjà une photo de profil" -#: counter/app.py:31 counter/models.py:340 counter/models.py:749 -#: counter/models.py:779 launderette/models.py:41 stock/models.py:43 +#: counter/app.py:31 counter/models.py:395 counter/models.py:875 +#: counter/models.py:911 launderette/models.py:41 stock/models.py:43 msgid "counter" msgstr "comptoir" -#: counter/forms.py:30 +#: counter/forms.py:38 msgid "This UID is invalid" msgstr "Cet UID est invalide" -#: counter/forms.py:69 +#: counter/forms.py:77 msgid "User not found" msgstr "Utilisateur non trouvé" -#: counter/forms.py:117 +#: counter/forms.py:125 msgid "Parent product" msgstr "Produit parent" -#: counter/forms.py:123 +#: counter/forms.py:131 msgid "Buying groups" msgstr "Groupes d'achat" @@ -3742,165 +3745,165 @@ msgstr "Groupes d'achat" msgid "Ecocup regularization" msgstr "Régularization des ecocups" -#: counter/models.py:59 +#: counter/models.py:63 msgid "account id" msgstr "numéro de compte" -#: counter/models.py:61 +#: counter/models.py:65 msgid "recorded product" msgstr "produits consignés" -#: counter/models.py:64 +#: counter/models.py:68 msgid "customer" msgstr "client" -#: counter/models.py:65 +#: counter/models.py:69 msgid "customers" msgstr "clients" -#: counter/models.py:126 counter/views.py:309 +#: counter/models.py:148 counter/views.py:319 msgid "Not enough money" msgstr "Solde insuffisant" -#: counter/models.py:157 +#: counter/models.py:183 msgid "First name" msgstr "Prénom" -#: counter/models.py:158 +#: counter/models.py:184 msgid "Last name" msgstr "Nom de famille" -#: counter/models.py:159 +#: counter/models.py:185 msgid "Address 1" msgstr "Adresse 1" -#: counter/models.py:161 +#: counter/models.py:186 msgid "Address 2" msgstr "Adresse 2" -#: counter/models.py:163 +#: counter/models.py:187 msgid "Zip code" msgstr "Code postal" -#: counter/models.py:164 +#: counter/models.py:188 msgid "City" msgstr "Ville" -#: counter/models.py:165 +#: counter/models.py:189 msgid "Country" msgstr "Pays" -#: counter/models.py:209 counter/models.py:237 +#: counter/models.py:232 counter/models.py:260 msgid "product type" msgstr "type du produit" -#: counter/models.py:243 +#: counter/models.py:266 msgid "purchase price" msgstr "prix d'achat" -#: counter/models.py:244 +#: counter/models.py:267 msgid "selling price" msgstr "prix de vente" -#: counter/models.py:245 +#: counter/models.py:268 msgid "special selling price" msgstr "prix de vente spécial" -#: counter/models.py:247 +#: counter/models.py:270 msgid "icon" msgstr "icône" -#: counter/models.py:252 +#: counter/models.py:275 msgid "limit age" msgstr "âge limite" -#: counter/models.py:253 +#: counter/models.py:276 msgid "tray price" msgstr "prix plateau" -#: counter/models.py:257 +#: counter/models.py:280 msgid "parent product" msgstr "produit parent" -#: counter/models.py:263 +#: counter/models.py:286 msgid "buying groups" msgstr "groupe d'achat" -#: counter/models.py:265 election/models.py:52 +#: counter/models.py:288 election/models.py:52 msgid "archived" msgstr "archivé" -#: counter/models.py:268 counter/models.py:874 +#: counter/models.py:291 counter/models.py:1006 msgid "product" msgstr "produit" -#: counter/models.py:321 +#: counter/models.py:374 msgid "products" msgstr "produits" -#: counter/models.py:324 +#: counter/models.py:377 msgid "counter type" msgstr "type de comptoir" -#: counter/models.py:326 +#: counter/models.py:379 msgid "Bar" msgstr "Bar" -#: counter/models.py:326 +#: counter/models.py:379 msgid "Office" msgstr "Bureau" -#: counter/models.py:329 +#: counter/models.py:382 msgid "sellers" msgstr "vendeurs" -#: counter/models.py:337 launderette/models.py:207 +#: counter/models.py:390 launderette/models.py:207 msgid "token" msgstr "jeton" -#: counter/models.py:492 +#: counter/models.py:618 msgid "bank" msgstr "banque" -#: counter/models.py:494 counter/models.py:584 +#: counter/models.py:620 counter/models.py:710 msgid "is validated" msgstr "est validé" -#: counter/models.py:497 +#: counter/models.py:623 msgid "refilling" msgstr "rechargement" -#: counter/models.py:561 eboutic/models.py:292 +#: counter/models.py:687 eboutic/models.py:289 msgid "unit price" msgstr "prix unitaire" -#: counter/models.py:562 counter/models.py:859 eboutic/models.py:293 +#: counter/models.py:688 counter/models.py:991 eboutic/models.py:290 msgid "quantity" msgstr "quantité" -#: counter/models.py:581 +#: counter/models.py:707 msgid "Sith account" msgstr "Compte utilisateur" -#: counter/models.py:581 sith/settings.py:359 sith/settings.py:364 -#: sith/settings.py:384 +#: counter/models.py:707 sith/settings.py:385 sith/settings.py:390 +#: sith/settings.py:410 msgid "Credit card" msgstr "Carte bancaire" -#: counter/models.py:587 +#: counter/models.py:713 msgid "selling" msgstr "vente" -#: counter/models.py:614 +#: counter/models.py:740 msgid "Unknown event" msgstr "Événement inconnu" -#: counter/models.py:615 +#: counter/models.py:741 #, python-format msgid "Eticket bought for the event %(event)s" msgstr "Eticket acheté pour l'événement %(event)s" -#: counter/models.py:617 counter/models.py:640 +#: counter/models.py:743 counter/models.py:766 #, python-format msgid "" "You bought an eticket for the event %(event)s.\n" @@ -3912,59 +3915,59 @@ msgstr "" "Vous pouvez également retrouver tous vos e-tickets sur votre page de compte " "%(url)s." -#: counter/models.py:754 +#: counter/models.py:880 msgid "last activity date" msgstr "dernière activité" -#: counter/models.py:757 +#: counter/models.py:883 msgid "permanency" msgstr "permanence" -#: counter/models.py:784 +#: counter/models.py:916 msgid "emptied" msgstr "coffre vidée" -#: counter/models.py:787 +#: counter/models.py:919 msgid "cash register summary" msgstr "relevé de caisse" -#: counter/models.py:855 +#: counter/models.py:987 msgid "cash summary" msgstr "relevé" -#: counter/models.py:858 +#: counter/models.py:990 msgid "value" msgstr "valeur" -#: counter/models.py:860 +#: counter/models.py:992 msgid "check" msgstr "chèque" -#: counter/models.py:863 +#: counter/models.py:995 msgid "cash register summary item" msgstr "élément de relevé de caisse" -#: counter/models.py:878 +#: counter/models.py:1010 msgid "banner" msgstr "bannière" -#: counter/models.py:880 +#: counter/models.py:1012 msgid "event date" msgstr "date de l'événement" -#: counter/models.py:882 +#: counter/models.py:1014 msgid "event title" msgstr "titre de l'événement" -#: counter/models.py:884 +#: counter/models.py:1016 msgid "secret" msgstr "secret" -#: counter/models.py:940 +#: counter/models.py:1072 msgid "uid" msgstr "uid" -#: counter/models.py:945 +#: counter/models.py:1077 msgid "student cards" msgstr "cartes étudiante" @@ -4016,7 +4019,7 @@ msgstr "Liste des relevés de caisse" msgid "Theoric sums" msgstr "Sommes théoriques" -#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:1065 +#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:1085 msgid "Emptied" msgstr "Coffre vidé" @@ -4028,17 +4031,17 @@ msgstr "oui" msgid "There is no cash register summary in this website." msgstr "Il n'y a pas de relevé de caisse dans ce site web." -#: counter/templates/counter/counter_click.jinja:30 +#: counter/templates/counter/counter_click.jinja:36 msgid "Add a student card" msgstr "Ajouter une carte étudiante" -#: counter/templates/counter/counter_click.jinja:33 +#: counter/templates/counter/counter_click.jinja:39 msgid "This is not a valid student card UID" msgstr "Ce n'est pas un UID de carte étudiante valide" -#: counter/templates/counter/counter_click.jinja:35 -#: counter/templates/counter/counter_click.jinja:63 -#: counter/templates/counter/counter_click.jinja:117 +#: counter/templates/counter/counter_click.jinja:41 +#: counter/templates/counter/counter_click.jinja:67 +#: counter/templates/counter/counter_click.jinja:132 #: counter/templates/counter/invoices_call.jinja:16 #: launderette/templates/launderette/launderette_admin.jinja:35 #: launderette/templates/launderette/launderette_click.jinja:13 @@ -4047,41 +4050,33 @@ msgstr "Ce n'est pas un UID de carte étudiante valide" msgid "Go" msgstr "Valider" -#: counter/templates/counter/counter_click.jinja:37 +#: counter/templates/counter/counter_click.jinja:43 msgid "Registered cards" msgstr "Cartes enregistrées" -#: counter/templates/counter/counter_click.jinja:45 +#: counter/templates/counter/counter_click.jinja:51 msgid "No card registered" msgstr "Aucune carte enregistrée" -#: counter/templates/counter/counter_click.jinja:50 +#: counter/templates/counter/counter_click.jinja:56 #: launderette/templates/launderette/launderette_admin.jinja:8 msgid "Selling" msgstr "Vente" -#: counter/templates/counter/counter_click.jinja:65 +#: counter/templates/counter/counter_click.jinja:74 #: eboutic/templates/eboutic/eboutic_makecommand.jinja:20 msgid "Basket: " msgstr "Panier : " -#: counter/templates/counter/counter_click.jinja:102 +#: counter/templates/counter/counter_click.jinja:115 msgid "Finish" msgstr "Terminer" -#: counter/templates/counter/counter_click.jinja:111 +#: counter/templates/counter/counter_click.jinja:125 #: counter/templates/counter/refilling_list.jinja:9 msgid "Refilling" msgstr "Rechargement" -#: counter/templates/counter/counter_click.jinja:193 counter/views.py:578 -msgid "END" -msgstr "FIN" - -#: counter/templates/counter/counter_click.jinja:193 counter/views.py:580 -msgid "CAN" -msgstr "ANN" - #: counter/templates/counter/counter_list.jinja:4 #: counter/templates/counter/counter_list.jinja:10 msgid "Counter admin list" @@ -4211,142 +4206,144 @@ msgid "Seller" msgstr "Vendeur" #: counter/templates/counter/stats.jinja:5 -#: counter/templates/counter/stats.jinja:9 +#: counter/templates/counter/stats.jinja:13 #, python-format msgid "%(counter_name)s stats" msgstr "Stats sur %(counter_name)s" -#: counter/templates/counter/stats.jinja:10 +#: counter/templates/counter/stats.jinja:14 #, python-format msgid "Top 100 %(counter_name)s" msgstr "Top 100 %(counter_name)s" -#: counter/templates/counter/stats.jinja:16 +#: counter/templates/counter/stats.jinja:20 +#: counter/templates/counter/stats.jinja:44 +#: counter/templates/counter/stats.jinja:66 msgid "Promo" msgstr "Promo" -#: counter/templates/counter/stats.jinja:19 +#: counter/templates/counter/stats.jinja:22 msgid "Percentage" msgstr "Pourcentage" -#: counter/templates/counter/stats.jinja:47 +#: counter/templates/counter/stats.jinja:38 #, python-format msgid "Top 100 barman %(counter_name)s" msgstr "Top 100 barman %(counter_name)s" -#: counter/templates/counter/stats.jinja:53 -#: counter/templates/counter/stats.jinja:78 +#: counter/templates/counter/stats.jinja:45 +#: counter/templates/counter/stats.jinja:67 msgid "Time" msgstr "Temps" -#: counter/templates/counter/stats.jinja:72 +#: counter/templates/counter/stats.jinja:60 #, python-format msgid "Top 100 barman %(counter_name)s (all semesters)" msgstr "Top 100 barman %(counter_name)s (tous les semestres)" -#: counter/views.py:167 +#: counter/views.py:177 msgid "Cash summary" msgstr "Relevé de caisse" -#: counter/views.py:181 +#: counter/views.py:191 msgid "Last operations" msgstr "Dernières opérations" -#: counter/views.py:196 +#: counter/views.py:206 msgid "Take items from stock" msgstr "Prendre des éléments du stock" -#: counter/views.py:249 +#: counter/views.py:259 msgid "Bad credentials" msgstr "Mauvais identifiants" -#: counter/views.py:251 +#: counter/views.py:261 msgid "User is not barman" msgstr "L'utilisateur n'est pas barman." -#: counter/views.py:256 +#: counter/views.py:266 msgid "Bad location, someone is already logged in somewhere else" msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs" -#: counter/views.py:300 +#: counter/views.py:310 msgid "Too young for that product" msgstr "Trop jeune pour ce produit" -#: counter/views.py:303 +#: counter/views.py:313 msgid "Not allowed for that product" msgstr "Non autorisé pour ce produit" -#: counter/views.py:306 +#: counter/views.py:316 msgid "No date of birth provided" msgstr "Pas de date de naissance renseignée" -#: counter/views.py:603 +#: counter/views.py:619 msgid "You have not enough money to buy all the basket" msgstr "Vous n'avez pas assez d'argent pour acheter le panier" -#: counter/views.py:751 +#: counter/views.py:771 msgid "Counter administration" msgstr "Administration des comptoirs" -#: counter/views.py:753 +#: counter/views.py:773 msgid "Stocks" msgstr "Stocks" -#: counter/views.py:772 +#: counter/views.py:792 msgid "Product types" msgstr "Types de produit" -#: counter/views.py:1022 +#: counter/views.py:1042 msgid "10 cents" msgstr "10 centimes" -#: counter/views.py:1023 +#: counter/views.py:1043 msgid "20 cents" msgstr "20 centimes" -#: counter/views.py:1024 +#: counter/views.py:1044 msgid "50 cents" msgstr "50 centimes" -#: counter/views.py:1025 +#: counter/views.py:1045 msgid "1 euro" msgstr "1 €" -#: counter/views.py:1026 +#: counter/views.py:1046 msgid "2 euros" msgstr "2 €" -#: counter/views.py:1027 +#: counter/views.py:1047 msgid "5 euros" msgstr "5 €" -#: counter/views.py:1028 +#: counter/views.py:1048 msgid "10 euros" msgstr "10 €" -#: counter/views.py:1029 +#: counter/views.py:1049 msgid "20 euros" msgstr "20 €" -#: counter/views.py:1030 +#: counter/views.py:1050 msgid "50 euros" msgstr "50 €" -#: counter/views.py:1032 +#: counter/views.py:1052 msgid "100 euros" msgstr "100 €" -#: counter/views.py:1035 counter/views.py:1041 counter/views.py:1047 -#: counter/views.py:1053 counter/views.py:1059 +#: counter/views.py:1055 counter/views.py:1061 counter/views.py:1067 +#: counter/views.py:1073 counter/views.py:1079 msgid "Check amount" msgstr "Montant du chèque" -#: counter/views.py:1038 counter/views.py:1044 counter/views.py:1050 -#: counter/views.py:1056 counter/views.py:1062 +#: counter/views.py:1058 counter/views.py:1064 counter/views.py:1070 +#: counter/views.py:1076 counter/views.py:1082 msgid "Check quantity" msgstr "Nombre de chèque" -#: counter/views.py:1676 +#: counter/views.py:1637 msgid "people(s)" msgstr "personne(s)" @@ -4371,37 +4368,37 @@ msgstr "Votre panier est vide" msgid "%(name)s : this product does not exist." msgstr "%(name)s : ce produit n'existe pas." -#: eboutic/forms.py:134 +#: eboutic/forms.py:150 #, python-format msgid "%(name)s : this product does not exist or may no longer be available." msgstr "%(name)s : ce produit n'existe pas ou n'est peut-être plus disponible." -#: eboutic/forms.py:141 +#: eboutic/forms.py:157 #, python-format msgid "You cannot buy %(nbr)d %(name)s." msgstr "Vous ne pouvez pas acheter %(nbr)d %(name)s." -#: eboutic/models.py:241 +#: eboutic/models.py:237 msgid "validated" msgstr "validé" -#: eboutic/models.py:251 +#: eboutic/models.py:247 msgid "Invoice already validated" msgstr "Facture déjà validée" -#: eboutic/models.py:289 +#: eboutic/models.py:286 msgid "product id" msgstr "ID du produit" -#: eboutic/models.py:290 +#: eboutic/models.py:287 msgid "product name" msgstr "nom du produit" -#: eboutic/models.py:291 +#: eboutic/models.py:288 msgid "product type id" msgstr "id du type du produit" -#: eboutic/models.py:308 +#: eboutic/models.py:305 msgid "basket" msgstr "panier" @@ -4467,18 +4464,18 @@ msgstr "" "Vous devez renseigner vos coordonnées de facturation si vous voulez payer " "par carte bancaire" -#: eboutic/templates/eboutic/eboutic_makecommand.jinja:112 +#: eboutic/templates/eboutic/eboutic_makecommand.jinja:111 msgid "Pay with credit card" msgstr "Payer avec une carte bancaire" -#: eboutic/templates/eboutic/eboutic_makecommand.jinja:116 +#: eboutic/templates/eboutic/eboutic_makecommand.jinja:115 msgid "" "AE account payment disabled because your basket contains refilling items." msgstr "" "Paiement par compte AE désactivé parce que votre panier contient des bons de " "rechargement." -#: eboutic/templates/eboutic/eboutic_makecommand.jinja:121 +#: eboutic/templates/eboutic/eboutic_makecommand.jinja:120 msgid "Pay with Sith account" msgstr "Payer avec un compte AE" @@ -4815,43 +4812,44 @@ msgstr "Appliquer les droits et le club propriétaire récursivement" msgid "%(author)s said" msgstr "Citation de %(author)s" -#: galaxy/models.py:52 +#: galaxy/models.py:51 msgid "star owner" msgstr "propriétaire de l'étoile" -#: galaxy/models.py:57 +#: galaxy/models.py:56 msgid "star mass" msgstr "masse de l'étoile" -#: galaxy/models.py:74 +#: galaxy/models.py:73 msgid "galaxy star 1" msgstr "étoile 1" -#: galaxy/models.py:80 +#: galaxy/models.py:79 msgid "galaxy star 2" msgstr "étoile 2" -#: galaxy/models.py:85 +#: galaxy/models.py:84 msgid "distance" msgstr "distance" -#: galaxy/models.py:87 +#: galaxy/models.py:86 msgid "Distance separating star1 and star2" msgstr "Distance séparant étoile 1 et étoile 2" -#: galaxy/models.py:90 +#: galaxy/models.py:89 msgid "family score" msgstr "score de famille" -#: galaxy/models.py:94 +#: galaxy/models.py:93 msgid "pictures score" msgstr "score de photos" -#: galaxy/models.py:98 +#: galaxy/models.py:97 msgid "clubs score" msgstr "score de club" #: galaxy/templates/galaxy/user.jinja:4 +#, python-format msgid "%(user_name)s's Galaxy" msgstr "Galaxie de %(user_name)s" @@ -4912,12 +4910,12 @@ msgid "Washing and drying" msgstr "Lavage et séchage" #: launderette/templates/launderette/launderette_book.jinja:27 -#: sith/settings.py:620 +#: sith/settings.py:622 msgid "Washing" msgstr "Lavage" #: launderette/templates/launderette/launderette_book.jinja:31 -#: sith/settings.py:620 +#: sith/settings.py:622 msgid "Drying" msgstr "Séchage" @@ -5309,15 +5307,15 @@ msgstr "Fusionner deux utilisateurs" msgid "Merge" msgstr "Fusion" -#: rootplace/views.py:113 +#: rootplace/views.py:155 msgid "User that will be kept" msgstr "Utilisateur qui sera conservé" -#: rootplace/views.py:116 +#: rootplace/views.py:158 msgid "User that will be deleted" msgstr "Utilisateur qui sera supprimé" -#: rootplace/views.py:122 +#: rootplace/views.py:164 msgid "User to be selected" msgstr "Utilisateur à sélectionner" @@ -5401,380 +5399,380 @@ msgstr "Erreur de création de l'album %(album)s : %(msg)s" msgid "Add user" msgstr "Ajouter une personne" -#: sith/settings.py:242 sith/settings.py:445 +#: sith/settings.py:244 sith/settings.py:447 msgid "English" msgstr "Anglais" -#: sith/settings.py:242 sith/settings.py:444 +#: sith/settings.py:244 sith/settings.py:446 msgid "French" msgstr "Français" -#: sith/settings.py:364 +#: sith/settings.py:366 msgid "TC" msgstr "TC" -#: sith/settings.py:365 +#: sith/settings.py:367 msgid "IMSI" msgstr "IMSI" -#: sith/settings.py:366 +#: sith/settings.py:368 msgid "IMAP" msgstr "IMAP" -#: sith/settings.py:367 +#: sith/settings.py:369 msgid "INFO" msgstr "INFO" -#: sith/settings.py:368 +#: sith/settings.py:370 msgid "GI" msgstr "GI" -#: sith/settings.py:369 sith/settings.py:455 +#: sith/settings.py:371 sith/settings.py:457 msgid "E" msgstr "E" -#: sith/settings.py:370 +#: sith/settings.py:372 msgid "EE" msgstr "EE" -#: sith/settings.py:371 +#: sith/settings.py:373 msgid "GESC" msgstr "GESC" -#: sith/settings.py:372 +#: sith/settings.py:374 msgid "GMC" msgstr "GMC" -#: sith/settings.py:373 +#: sith/settings.py:375 msgid "MC" msgstr "MC" -#: sith/settings.py:374 +#: sith/settings.py:376 msgid "EDIM" msgstr "EDIM" -#: sith/settings.py:375 +#: sith/settings.py:377 msgid "Humanities" msgstr "Humanités" -#: sith/settings.py:376 +#: sith/settings.py:378 msgid "N/A" msgstr "N/A" -#: sith/settings.py:380 sith/settings.py:387 sith/settings.py:406 +#: sith/settings.py:382 sith/settings.py:389 sith/settings.py:408 msgid "Check" msgstr "Chèque" -#: sith/settings.py:381 sith/settings.py:389 sith/settings.py:407 +#: sith/settings.py:383 sith/settings.py:391 sith/settings.py:409 msgid "Cash" msgstr "Espèces" -#: sith/settings.py:382 +#: sith/settings.py:384 msgid "Transfert" msgstr "Virement" -#: sith/settings.py:395 +#: sith/settings.py:397 msgid "Belfort" msgstr "Belfort" -#: sith/settings.py:396 +#: sith/settings.py:398 msgid "Sevenans" msgstr "Sevenans" -#: sith/settings.py:397 +#: sith/settings.py:399 msgid "Montbéliard" msgstr "Montbéliard" -#: sith/settings.py:425 +#: sith/settings.py:427 msgid "Free" msgstr "Libre" -#: sith/settings.py:426 +#: sith/settings.py:428 msgid "CS" msgstr "CS" -#: sith/settings.py:427 +#: sith/settings.py:429 msgid "TM" msgstr "TM" -#: sith/settings.py:428 +#: sith/settings.py:430 msgid "OM" msgstr "OM" -#: sith/settings.py:429 +#: sith/settings.py:431 msgid "QC" msgstr "QC" -#: sith/settings.py:430 +#: sith/settings.py:432 msgid "EC" msgstr "EC" -#: sith/settings.py:431 +#: sith/settings.py:433 msgid "RN" msgstr "RN" -#: sith/settings.py:432 +#: sith/settings.py:434 msgid "ST" msgstr "ST" -#: sith/settings.py:433 +#: sith/settings.py:435 msgid "EXT" msgstr "EXT" -#: sith/settings.py:438 +#: sith/settings.py:440 msgid "Autumn" msgstr "Automne" -#: sith/settings.py:439 +#: sith/settings.py:441 msgid "Spring" msgstr "Printemps" -#: sith/settings.py:440 +#: sith/settings.py:442 msgid "Autumn and spring" msgstr "Automne et printemps" -#: sith/settings.py:446 +#: sith/settings.py:448 msgid "German" msgstr "Allemant" -#: sith/settings.py:447 +#: sith/settings.py:449 msgid "Spanich" msgstr "Espagnol" -#: sith/settings.py:451 +#: sith/settings.py:453 msgid "A" msgstr "A" -#: sith/settings.py:452 +#: sith/settings.py:454 msgid "B" msgstr "B" -#: sith/settings.py:453 +#: sith/settings.py:455 msgid "C" msgstr "C" -#: sith/settings.py:454 +#: sith/settings.py:456 msgid "D" msgstr "D" -#: sith/settings.py:456 +#: sith/settings.py:458 msgid "FX" msgstr "FX" -#: sith/settings.py:457 +#: sith/settings.py:459 msgid "F" msgstr "F" -#: sith/settings.py:458 +#: sith/settings.py:460 msgid "Abs" msgstr "Abs" -#: sith/settings.py:462 +#: sith/settings.py:464 msgid "Selling deletion" msgstr "Suppression de vente" -#: sith/settings.py:463 +#: sith/settings.py:465 msgid "Refilling deletion" msgstr "Suppression de rechargement" -#: sith/settings.py:500 +#: sith/settings.py:502 msgid "One semester" msgstr "Un semestre, 20 €" -#: sith/settings.py:501 +#: sith/settings.py:503 msgid "Two semesters" msgstr "Deux semestres, 35 €" -#: sith/settings.py:503 +#: sith/settings.py:505 msgid "Common core cursus" msgstr "Cursus tronc commun, 60 €" -#: sith/settings.py:507 +#: sith/settings.py:509 msgid "Branch cursus" msgstr "Cursus branche, 60 €" -#: sith/settings.py:508 +#: sith/settings.py:510 msgid "Alternating cursus" msgstr "Cursus alternant, 30 €" -#: sith/settings.py:509 +#: sith/settings.py:511 msgid "Honorary member" msgstr "Membre honoraire, 0 €" -#: sith/settings.py:510 +#: sith/settings.py:512 msgid "Assidu member" msgstr "Membre d'Assidu, 0 €" -#: sith/settings.py:511 +#: sith/settings.py:513 msgid "Amicale/DOCEO member" msgstr "Membre de l'Amicale/DOCEO, 0 €" -#: sith/settings.py:512 +#: sith/settings.py:514 msgid "UT network member" msgstr "Cotisant du réseau UT, 0 €" -#: sith/settings.py:513 +#: sith/settings.py:515 msgid "CROUS member" msgstr "Membres du CROUS, 0 €" -#: sith/settings.py:514 +#: sith/settings.py:516 msgid "Sbarro/ESTA member" msgstr "Membre de Sbarro ou de l'ESTA, 20 €" -#: sith/settings.py:516 +#: sith/settings.py:518 msgid "One semester Welcome Week" msgstr "Un semestre Welcome Week" -#: sith/settings.py:520 +#: sith/settings.py:522 msgid "One month for free" msgstr "Un mois gratuit" -#: sith/settings.py:521 +#: sith/settings.py:523 msgid "Two months for free" msgstr "Deux mois gratuits" -#: sith/settings.py:522 +#: sith/settings.py:524 msgid "Eurok's volunteer" msgstr "Bénévole Eurockéennes" -#: sith/settings.py:524 +#: sith/settings.py:526 msgid "Six weeks for free" msgstr "6 semaines gratuites" -#: sith/settings.py:528 +#: sith/settings.py:530 msgid "One day" msgstr "Un jour" -#: sith/settings.py:529 +#: sith/settings.py:531 msgid "GA staff member" msgstr "Membre staff GA (2 semaines), 1 €" -#: sith/settings.py:532 +#: sith/settings.py:534 msgid "One semester (-20%)" msgstr "Un semestre (-20%), 12 €" -#: sith/settings.py:537 +#: sith/settings.py:539 msgid "Two semesters (-20%)" msgstr "Deux semestres (-20%), 22 €" -#: sith/settings.py:542 +#: sith/settings.py:544 msgid "Common core cursus (-20%)" msgstr "Cursus tronc commun (-20%), 36 €" -#: sith/settings.py:547 +#: sith/settings.py:549 msgid "Branch cursus (-20%)" msgstr "Cursus branche (-20%), 36 €" -#: sith/settings.py:552 +#: sith/settings.py:554 msgid "Alternating cursus (-20%)" msgstr "Cursus alternant (-20%), 24 €" -#: sith/settings.py:558 +#: sith/settings.py:560 msgid "One year for free(CA offer)" msgstr "Une année offerte (Offre CA)" -#: sith/settings.py:580 +#: sith/settings.py:582 msgid "President" msgstr "Président⸱e" -#: sith/settings.py:581 +#: sith/settings.py:583 msgid "Vice-President" msgstr "Vice-Président⸱e" -#: sith/settings.py:582 +#: sith/settings.py:584 msgid "Treasurer" msgstr "Trésorier⸱e" -#: sith/settings.py:583 +#: sith/settings.py:585 msgid "Communication supervisor" msgstr "Responsable communication" -#: sith/settings.py:584 +#: sith/settings.py:586 msgid "Secretary" msgstr "Secrétaire" -#: sith/settings.py:585 +#: sith/settings.py:587 msgid "IT supervisor" msgstr "Responsable info" -#: sith/settings.py:586 +#: sith/settings.py:588 msgid "Board member" msgstr "Membre du bureau" -#: sith/settings.py:587 +#: sith/settings.py:589 msgid "Active member" msgstr "Membre actif⸱ve" -#: sith/settings.py:588 +#: sith/settings.py:590 msgid "Curious" msgstr "Curieux⸱euse" -#: sith/settings.py:624 +#: sith/settings.py:626 msgid "A new poster needs to be moderated" msgstr "Une nouvelle affiche a besoin d'être modérée" -#: sith/settings.py:625 +#: sith/settings.py:627 msgid "A new mailing list needs to be moderated" msgstr "Une nouvelle mailing list a besoin d'être modérée" -#: sith/settings.py:628 +#: sith/settings.py:630 msgid "A new pedagogy comment has been signaled for moderation" msgstr "" "Un nouveau commentaire de la pédagogie a été signalé pour la modération" -#: sith/settings.py:630 +#: sith/settings.py:632 #, python-format msgid "There are %s fresh news to be moderated" msgstr "Il y a %s nouvelles toutes fraîches à modérer" -#: sith/settings.py:631 +#: sith/settings.py:633 msgid "New files to be moderated" msgstr "Nouveaux fichiers à modérer" -#: sith/settings.py:632 +#: sith/settings.py:634 #, python-format msgid "There are %s pictures to be moderated in the SAS" msgstr "Il y a %s photos à modérer dans le SAS" -#: sith/settings.py:633 +#: sith/settings.py:635 msgid "You've been identified on some pictures" msgstr "Vous avez été identifié sur des photos" -#: sith/settings.py:634 +#: sith/settings.py:636 #, python-format msgid "You just refilled of %s €" msgstr "Vous avez rechargé votre compte de %s€" -#: sith/settings.py:635 +#: sith/settings.py:637 #, python-format msgid "You just bought %s" msgstr "Vous avez acheté %s" -#: sith/settings.py:636 +#: sith/settings.py:638 msgid "You have a notification" msgstr "Vous avez une notification" -#: sith/settings.py:648 +#: sith/settings.py:650 msgid "Success!" msgstr "Succès !" -#: sith/settings.py:649 +#: sith/settings.py:651 msgid "Fail!" msgstr "Échec !" -#: sith/settings.py:650 +#: sith/settings.py:652 msgid "You successfully posted an article in the Weekmail" msgstr "Article posté avec succès dans le Weekmail" -#: sith/settings.py:651 +#: sith/settings.py:653 msgid "You successfully edited an article in the Weekmail" msgstr "Article édité avec succès dans le Weekmail" -#: sith/settings.py:652 +#: sith/settings.py:654 msgid "You successfully sent the Weekmail" msgstr "Weekmail envoyé avec succès" -#: sith/settings.py:660 +#: sith/settings.py:662 msgid "AE tee-shirt" msgstr "Tee-shirt AE" @@ -5972,35 +5970,35 @@ msgstr " demandé" msgid "%(effective_quantity)s left" msgstr "%(effective_quantity)s restant" -#: subscription/models.py:44 +#: subscription/models.py:43 msgid "Bad subscription type" msgstr "Mauvais type de cotisation" -#: subscription/models.py:49 +#: subscription/models.py:48 msgid "Bad payment method" msgstr "Mauvais type de paiement" -#: subscription/models.py:57 +#: subscription/models.py:56 msgid "subscription type" msgstr "type d'inscription" -#: subscription/models.py:63 +#: subscription/models.py:62 msgid "subscription start" msgstr "début de la cotisation" -#: subscription/models.py:64 +#: subscription/models.py:63 msgid "subscription end" msgstr "fin de la cotisation" -#: subscription/models.py:73 +#: subscription/models.py:72 msgid "location" msgstr "lieu" -#: subscription/models.py:93 +#: subscription/models.py:92 msgid "You can not subscribe many time for the same period" msgstr "Vous ne pouvez pas cotiser plusieurs fois pour la même période" -#: subscription/models.py:98 +#: subscription/models.py:97 msgid "Subscription error" msgstr "Erreur de cotisation" diff --git a/sith/settings.py b/sith/settings.py index 69ecddc4..d650f8ba 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -147,6 +147,8 @@ TEMPLATES = [ "filters": { "markdown": "core.templatetags.renderer.markdown", "phonenumber": "core.templatetags.renderer.phonenumber", + "truncate_time": "core.templatetags.renderer.truncate_time", + "format_timedelta": "core.templatetags.renderer.format_timedelta", }, "globals": { "can_edit_prop": "core.views.can_edit_prop",
{% trans %}Nb{% endtrans %}{% trans %}User{% endtrans %}{% trans %}Time{% endtrans %}
{% trans %}User{% endtrans %}{% trans %}Promo{% endtrans %}{% trans %}Time{% endtrans %}
{{ loop.index }}{{ u.get_display_name() }}{{ r.perm_sum }}{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}{{ barman.promo or '' }}{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}