mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-06-30 13:11:09 +02:00
Compare commits
34 Commits
permission
...
11223430fd
Author | SHA1 | Date | |
---|---|---|---|
11223430fd | |||
7aeb977e72 | |||
52fef1df42 | |||
d5819ac562 | |||
a79df8f1f6 | |||
364b18e188 | |||
10a883b2e5 | |||
1410ab6c4f | |||
623dd61be6 | |||
48a0a87e7c | |||
563f525b11 | |||
63c1d74f1a | |||
c42fb380a6 | |||
c636d52a73 | |||
6a9021ec14 | |||
9c9149b53a | |||
cb74311e7b | |||
9d7dd566c9 | |||
9944ebcaad | |||
8537f043f7 | |||
c89a95f8d2 | |||
73640b1dfa | |||
84b16ab603 | |||
6a1b51dbbf | |||
c441a43a8b | |||
87f3b51b04 | |||
0a853fd3e6 | |||
c429734810 | |||
5d759111b6 | |||
70baf7566c | |||
eb355f547c | |||
7068170f18 | |||
45ee9a8941 | |||
454ea19603 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -42,6 +42,7 @@ map.json
|
||||
backups/
|
||||
/static/
|
||||
/media/
|
||||
/tmp/
|
||||
|
||||
# Virtualenv
|
||||
env/
|
||||
|
5
apps/api/pagination.py
Normal file
5
apps/api/pagination.py
Normal file
@ -0,0 +1,5 @@
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
|
||||
class CustomPagination(PageNumberPagination):
|
||||
page_size_query_param = 'page_size'
|
||||
|
18
apps/member/migrations/0009_auto_20220904_2325.py
Normal file
18
apps/member/migrations/0009_auto_20220904_2325.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.26 on 2022-09-04 21:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('member', '0008_auto_20211005_1544'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='promotion',
|
||||
field=models.PositiveSmallIntegerField(default=2022, help_text='Year of entry to the school (None if not ENS student)', null=True, verbose_name='promotion'),
|
||||
),
|
||||
]
|
@ -4,7 +4,7 @@
|
||||
from django.contrib import admin
|
||||
from note_kfet.admin import admin_site
|
||||
|
||||
from .models import Permission, PermissionVar, PermissionMask, Role
|
||||
from .models import Permission, PermissionMask, Role
|
||||
|
||||
|
||||
@admin.register(PermissionMask, site=admin_site)
|
||||
@ -15,14 +15,6 @@ class PermissionMaskAdmin(admin.ModelAdmin):
|
||||
list_display = ('description', 'rank', )
|
||||
|
||||
|
||||
@admin.register(PermissionVar, site=admin_site)
|
||||
class PermissionVarAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin customisation for PermissionVar
|
||||
"""
|
||||
list_display = ('name', 'description',)
|
||||
|
||||
|
||||
@admin.register(Permission, site=admin_site)
|
||||
class PermissionAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
|
@ -1967,7 +1967,7 @@
|
||||
"note",
|
||||
"transaction"
|
||||
],
|
||||
"query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]]",
|
||||
"query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]]",
|
||||
"type": "change",
|
||||
"mask": 2,
|
||||
"field": "valid",
|
||||
@ -2607,7 +2607,7 @@
|
||||
"note",
|
||||
"transaction"
|
||||
],
|
||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]",
|
||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]",
|
||||
"type": "change",
|
||||
"mask": 2,
|
||||
"field": "valid",
|
||||
@ -2623,7 +2623,7 @@
|
||||
"note",
|
||||
"transaction"
|
||||
],
|
||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]",
|
||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]",
|
||||
"type": "change",
|
||||
"mask": 2,
|
||||
"field": "invalidity_reason",
|
||||
@ -2928,7 +2928,7 @@
|
||||
"application"
|
||||
],
|
||||
"query": "{\"user\": [\"user\"]}",
|
||||
"type": "add",
|
||||
"type": "create",
|
||||
"mask": 1,
|
||||
"field": "",
|
||||
"permanent": true,
|
||||
@ -3114,10 +3114,10 @@
|
||||
187,
|
||||
188,
|
||||
189,
|
||||
190,
|
||||
191,
|
||||
195,
|
||||
196
|
||||
190,
|
||||
191,
|
||||
195,
|
||||
196
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3159,8 +3159,8 @@
|
||||
159,
|
||||
160,
|
||||
179,
|
||||
189,
|
||||
190
|
||||
189,
|
||||
190
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3310,10 +3310,10 @@
|
||||
176,
|
||||
177,
|
||||
178,
|
||||
188,
|
||||
188,
|
||||
183,
|
||||
186,
|
||||
187
|
||||
186,
|
||||
187
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3508,13 +3508,13 @@
|
||||
187,
|
||||
188,
|
||||
189,
|
||||
190,
|
||||
191,
|
||||
192,
|
||||
193,
|
||||
194,
|
||||
195,
|
||||
196
|
||||
190,
|
||||
191,
|
||||
192,
|
||||
193,
|
||||
194,
|
||||
195,
|
||||
196
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -1,22 +0,0 @@
|
||||
# Generated by Django 2.2.28 on 2022-10-10 17:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('permission', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PermissionVar',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.SlugField(unique=True, verbose_name='name')),
|
||||
('query', models.TextField(verbose_name='query')),
|
||||
('description', models.CharField(blank=True, max_length=255, verbose_name='description')),
|
||||
],
|
||||
),
|
||||
]
|
@ -118,25 +118,6 @@ class PermissionMask(models.Model):
|
||||
verbose_name_plural = _("permission masks")
|
||||
|
||||
|
||||
class PermissionVar(models.Model):
|
||||
|
||||
name = models.SlugField(
|
||||
unique=True,
|
||||
blank=False,
|
||||
verbose_name=_("name"),
|
||||
)
|
||||
|
||||
query = models.TextField(
|
||||
verbose_name=_("query"),
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
verbose_name=_("description"),
|
||||
)
|
||||
|
||||
|
||||
class Permission(models.Model):
|
||||
|
||||
PERMISSION_TYPES = [
|
||||
@ -158,7 +139,6 @@ class Permission(models.Model):
|
||||
# query -> ["AND", query, …] AND multiple queries
|
||||
# | ["OR", query, …] OR multiple queries
|
||||
# | ["NOT", query] Opposite of query
|
||||
# | ["VAR", query] A var name as defined in PermissionVar
|
||||
# query -> {key: value, …} A list of fields and values of a Q object
|
||||
# key -> string A field name
|
||||
# value -> int | string | bool | null Literal values
|
||||
@ -170,7 +150,6 @@ class Permission(models.Model):
|
||||
# | ["MUL", oper, …] Multiply F objects or literals
|
||||
# | int | string | bool | null Literal values
|
||||
# | ["F", string] A field
|
||||
# | ["VAR", string] A var name as defined in PermissionVar
|
||||
#
|
||||
# Examples:
|
||||
# Q(is_superuser=True) := {"is_superuser": true}
|
||||
@ -236,8 +215,6 @@ class Permission(models.Model):
|
||||
return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]])
|
||||
elif oper[0] == 'F':
|
||||
return F(oper[1])
|
||||
elif oper[0] == 'VAR':
|
||||
return compute_f(json.loads(PermissionVar.objects.get(name=oper[1]).query), **kwargs)
|
||||
else:
|
||||
field = kwargs[oper[0]]
|
||||
for i in range(1, len(oper)):
|
||||
@ -312,8 +289,6 @@ class Permission(models.Model):
|
||||
return functools.reduce(operator.or_, [Permission._about(query, **kwargs) for query in query[1:]])
|
||||
elif query[0] == 'NOT':
|
||||
return ~Permission._about(query[1], **kwargs)
|
||||
elif query[0] == 'VAR':
|
||||
return Permission._about(json.loads(PermissionVar.objects.get(name=query[1]).query), **kwargs)
|
||||
else:
|
||||
return Q(pk=F("pk")) if Permission.compute_param(query, **kwargs) else ~Q(pk=F("pk"))
|
||||
elif isinstance(query, dict):
|
||||
|
Submodule apps/scripts updated: 86bc2d2698...4471307b37
18
apps/treasury/migrations/0005_auto_20230129_2348.py
Normal file
18
apps/treasury/migrations/0005_auto_20230129_2348.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.28 on 2023-01-29 22:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0004_auto_20211005_1544'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='bde',
|
||||
field=models.CharField(choices=[('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='TotalistSpies', max_length=32, verbose_name='BDE'),
|
||||
),
|
||||
]
|
18
apps/treasury/migrations/0006_auto_20230414_1651.py
Normal file
18
apps/treasury/migrations/0006_auto_20230414_1651.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.28 on 2023-04-14 14:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0005_auto_20230129_2348'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='bde',
|
||||
field=models.CharField(choices=[('SecretStorlist', 'SecretStor[list]'), ('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='SecretStorlist', max_length=32, verbose_name='BDE'),
|
||||
),
|
||||
]
|
@ -28,8 +28,10 @@ class Invoice(models.Model):
|
||||
|
||||
bde = models.CharField(
|
||||
max_length=32,
|
||||
default='Saperlistpopette',
|
||||
default='SecretStorlist',
|
||||
choices=(
|
||||
('SecretStorlist', 'SecretStor[list]'),
|
||||
('TotalistSpies', 'Tota[list]Spies'),
|
||||
('Saperlistpopette', 'Saper[list]popette'),
|
||||
('Finalist', 'Fina[list]'),
|
||||
('Listorique', '[List]orique'),
|
||||
@ -95,7 +97,7 @@ class Invoice(models.Model):
|
||||
products = self.products.all()
|
||||
|
||||
self.place = "Gif-sur-Yvette"
|
||||
self.my_name = "BDE ENS Cachan"
|
||||
self.my_name = "BDE ENS Paris Saclay"
|
||||
self.my_address_street = "4 avenue des Sciences"
|
||||
self.my_city = "91190 Gif-sur-Yvette"
|
||||
self.bank_code = 30003
|
||||
@ -310,8 +312,8 @@ class SogeCredit(models.Model):
|
||||
amount = sum(transaction.total for transaction in self.transactions.all())
|
||||
if 'wei' in settings.INSTALLED_APPS:
|
||||
from wei.models import WEIMembership
|
||||
if not WEIMembership.objects.filter(club__weiclub__year=datetime.date.today().year, user=self.user)\
|
||||
.exists():
|
||||
if not WEIMembership.objects\
|
||||
.filter(club__weiclub__year=self.credit_transaction.created_at.year, user=self.user).exists():
|
||||
# 80 € for people that don't go to WEI
|
||||
amount += 8000
|
||||
return amount
|
||||
@ -329,17 +331,18 @@ class SogeCredit(models.Model):
|
||||
bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
|
||||
kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
|
||||
|
||||
if bde_qs.exists():
|
||||
m = bde_qs.get()
|
||||
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||
if m.transaction not in self.transactions.all():
|
||||
self.transactions.add(m.transaction)
|
||||
|
||||
if kfet_qs.exists():
|
||||
m = kfet_qs.get()
|
||||
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||
if m.transaction not in self.transactions.all():
|
||||
self.transactions.add(m.transaction)
|
||||
## Soge do not pay BDE and kfet memberships this year (2022-2023)
|
||||
# if bde_qs.exists():
|
||||
# m = bde_qs.get()
|
||||
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||
# if m.transaction not in self.transactions.all():
|
||||
# self.transactions.add(m.transaction)
|
||||
#
|
||||
# if kfet_qs.exists():
|
||||
# m = kfet_qs.get()
|
||||
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||
# if m.transaction not in self.transactions.all():
|
||||
# self.transactions.add(m.transaction)
|
||||
|
||||
if 'wei' in settings.INSTALLED_APPS:
|
||||
from wei.models import WEIClub
|
||||
|
BIN
apps/treasury/static/img/SecretStorlist.png
Normal file
BIN
apps/treasury/static/img/SecretStorlist.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 690 KiB |
BIN
apps/treasury/static/img/SecretStorlist_bg.jpg
Normal file
BIN
apps/treasury/static/img/SecretStorlist_bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
apps/treasury/static/img/TotalistSpies.png
Normal file
BIN
apps/treasury/static/img/TotalistSpies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
BIN
apps/treasury/static/img/TotalistSpies_bg.jpg
Normal file
BIN
apps/treasury/static/img/TotalistSpies_bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -105,8 +105,8 @@
|
||||
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\cfoot{
|
||||
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)6 89 88 56 50\newline
|
||||
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00011
|
||||
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)7 78 17 22 34\newline
|
||||
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00029
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
|
||||
from .wei2022 import WEISurvey2022
|
||||
from .wei2023 import WEISurvey2023
|
||||
|
||||
|
||||
__all__ = [
|
||||
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
|
||||
]
|
||||
|
||||
CurrentSurvey = WEISurvey2022
|
||||
CurrentSurvey = WEISurvey2023
|
||||
|
@ -14,14 +14,17 @@ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInf
|
||||
from ...models import WEIMembership
|
||||
|
||||
WORDS = [
|
||||
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
|
||||
'Cartouche', 'Catacombes', 'Chansons paillardes', 'Chansons populaires', 'Chanteur', 'Chartreuse', 'Chill',
|
||||
'Core', 'DJ', 'Dancefloor', 'Danse', 'David Guetta', 'Disco', 'Eau de vie', 'Électro', 'Escalade', 'Familial',
|
||||
'Fanfare', 'Fracassage', 'Féria', 'Hard rock', 'Hoeggarden', 'House', 'Huit-six', 'IPA', 'Inclusif', 'Inferno',
|
||||
'Introverti', 'Jager bomb', 'Jazz', 'Jeux d\'alcool', 'Jeux de rôles', 'Jeux vidéo', 'Jul', 'Jus de fruit',
|
||||
'Karaoké', 'LGBTQI+', 'Lady Gaga', 'Loup garou', 'Morning beer', 'Métal', 'Nuit blanche', 'Ovalie', 'Psychedelic',
|
||||
'Pétanque', 'Rave', 'Reggae', 'Rhum', 'Ricard', 'Rock', 'Rosé', 'Rétro', 'Séducteur', 'Techno', 'Thérapie taxi',
|
||||
'Théâtre', 'Trap', 'Turn up', 'Underground', 'Volley', 'Wati B', 'Zinédine Zidane',
|
||||
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
|
||||
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
|
||||
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
|
||||
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
|
||||
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
|
||||
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
|
||||
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
|
||||
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
|
||||
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
|
||||
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
|
||||
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
|
||||
]
|
||||
|
||||
|
||||
|
296
apps/wei/forms/surveys/wei2023.py
Normal file
296
apps/wei/forms/surveys/wei2023.py
Normal file
@ -0,0 +1,296 @@
|
||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import time
|
||||
from functools import lru_cache
|
||||
from random import Random
|
||||
|
||||
from django import forms
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
|
||||
from ...models import WEIMembership
|
||||
|
||||
WORDS = [
|
||||
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
|
||||
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
|
||||
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
|
||||
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
|
||||
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
|
||||
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
|
||||
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
|
||||
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
|
||||
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
|
||||
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
|
||||
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
|
||||
]
|
||||
|
||||
|
||||
class WEISurveyForm2023(forms.Form):
|
||||
"""
|
||||
Survey form for the year 2023.
|
||||
Members choose 20 words, from which we calculate the best associated bus.
|
||||
"""
|
||||
|
||||
word = forms.ChoiceField(
|
||||
label=_("Choose a word:"),
|
||||
widget=forms.RadioSelect(),
|
||||
)
|
||||
|
||||
def set_registration(self, registration):
|
||||
"""
|
||||
Filter the bus selector with the buses of the current WEI.
|
||||
"""
|
||||
information = WEISurveyInformation2023(registration)
|
||||
if not information.seed:
|
||||
information.seed = int(1000 * time.time())
|
||||
information.save(registration)
|
||||
registration._force_save = True
|
||||
registration.save()
|
||||
|
||||
if self.data:
|
||||
self.fields["word"].choices = [(w, w) for w in WORDS]
|
||||
if self.is_valid():
|
||||
return
|
||||
|
||||
rng = Random((information.step + 1) * information.seed)
|
||||
|
||||
words = None
|
||||
|
||||
buses = WEISurveyAlgorithm2023.get_buses()
|
||||
informations = {bus: WEIBusInformation2023(bus) for bus in buses}
|
||||
scores = sum((list(informations[bus].scores.values()) for bus in buses), [])
|
||||
average_score = sum(scores) / len(scores)
|
||||
|
||||
preferred_words = {bus: [word for word in WORDS
|
||||
if informations[bus].scores[word] >= average_score]
|
||||
for bus in buses}
|
||||
while words is None or len(set(words)) != len(words):
|
||||
# Ensure that there is no the same word 2 times
|
||||
words = [rng.choice(words) for _ignored2, words in preferred_words.items()]
|
||||
rng.shuffle(words)
|
||||
words = [(w, w) for w in words]
|
||||
self.fields["word"].choices = words
|
||||
|
||||
|
||||
class WEIBusInformation2023(WEIBusInformation):
|
||||
"""
|
||||
For each word, the bus has a score
|
||||
"""
|
||||
scores: dict
|
||||
|
||||
def __init__(self, bus):
|
||||
self.scores = {}
|
||||
for word in WORDS:
|
||||
self.scores[word] = 0.0
|
||||
super().__init__(bus)
|
||||
|
||||
|
||||
class WEISurveyInformation2023(WEISurveyInformation):
|
||||
"""
|
||||
We store the id of the selected bus. We store only the name, but is not used in the selection:
|
||||
that's only for humans that try to read data.
|
||||
"""
|
||||
# Random seed that is stored at the first time to ensure that words are generated only once
|
||||
seed = 0
|
||||
step = 0
|
||||
|
||||
def __init__(self, registration):
|
||||
for i in range(1, 21):
|
||||
setattr(self, "word" + str(i), None)
|
||||
super().__init__(registration)
|
||||
|
||||
|
||||
class WEISurvey2023(WEISurvey):
|
||||
"""
|
||||
Survey for the year 2023.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_year(cls):
|
||||
return 2023
|
||||
|
||||
@classmethod
|
||||
def get_survey_information_class(cls):
|
||||
return WEISurveyInformation2023
|
||||
|
||||
def get_form_class(self):
|
||||
return WEISurveyForm2023
|
||||
|
||||
def update_form(self, form):
|
||||
"""
|
||||
Filter the bus selector with the buses of the WEI.
|
||||
"""
|
||||
form.set_registration(self.registration)
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
word = form.cleaned_data["word"]
|
||||
self.information.step += 1
|
||||
setattr(self.information, "word" + str(self.information.step), word)
|
||||
self.save()
|
||||
|
||||
@classmethod
|
||||
def get_algorithm_class(cls):
|
||||
return WEISurveyAlgorithm2023
|
||||
|
||||
def is_complete(self) -> bool:
|
||||
"""
|
||||
The survey is complete once the bus is chosen.
|
||||
"""
|
||||
return self.information.step == 20
|
||||
|
||||
@classmethod
|
||||
@lru_cache()
|
||||
def word_mean(cls, word):
|
||||
"""
|
||||
Calculate the mid-score given by all buses.
|
||||
"""
|
||||
buses = cls.get_algorithm_class().get_buses()
|
||||
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
||||
|
||||
@lru_cache()
|
||||
def score(self, bus):
|
||||
if not self.is_complete():
|
||||
raise ValueError("Survey is not ended, can't calculate score")
|
||||
|
||||
bus_info = self.get_algorithm_class().get_bus_information(bus)
|
||||
# Score is the given score by the bus subtracted to the mid-score of the buses.
|
||||
s = sum(bus_info.scores[getattr(self.information, 'word' + str(i))]
|
||||
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
||||
return s
|
||||
|
||||
@lru_cache()
|
||||
def scores_per_bus(self):
|
||||
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
||||
|
||||
@lru_cache()
|
||||
def ordered_buses(self):
|
||||
values = list(self.scores_per_bus().items())
|
||||
values.sort(key=lambda item: -item[1])
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def clear_cache(cls):
|
||||
cls.word_mean.cache_clear()
|
||||
return super().clear_cache()
|
||||
|
||||
|
||||
class WEISurveyAlgorithm2023(WEISurveyAlgorithm):
|
||||
"""
|
||||
The algorithm class for the year 2023.
|
||||
We use Gale-Shapley algorithm to attribute 1y students into buses.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_survey_class(cls):
|
||||
return WEISurvey2023
|
||||
|
||||
@classmethod
|
||||
def get_bus_information_class(cls):
|
||||
return WEIBusInformation2023
|
||||
|
||||
def run_algorithm(self, display_tqdm=False):
|
||||
"""
|
||||
Gale-Shapley algorithm implementation.
|
||||
We modify it to allow buses to have multiple "weddings".
|
||||
"""
|
||||
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
|
||||
surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys
|
||||
# Don't manage hardcoded people
|
||||
surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded]
|
||||
|
||||
# Reset previous algorithm run
|
||||
for survey in surveys:
|
||||
survey.free()
|
||||
survey.save()
|
||||
|
||||
non_men = [s for s in surveys if s.registration.gender != 'male']
|
||||
men = [s for s in surveys if s.registration.gender == 'male']
|
||||
|
||||
quotas = {}
|
||||
registrations = self.get_registrations()
|
||||
non_men_total = registrations.filter(~Q(gender='male')).count()
|
||||
for bus in self.get_buses():
|
||||
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
||||
# Remove hardcoded people
|
||||
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
||||
registration__information_json__icontains="hardcoded").count()
|
||||
quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats)
|
||||
|
||||
tqdm_obj = None
|
||||
if display_tqdm:
|
||||
from tqdm import tqdm
|
||||
tqdm_obj = tqdm(total=len(non_men), desc="Non-hommes")
|
||||
|
||||
# Repartition for non men people first
|
||||
self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj)
|
||||
|
||||
quotas = {}
|
||||
for bus in self.get_buses():
|
||||
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
||||
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
|
||||
# Remove hardcoded people
|
||||
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
||||
registration__information_json__icontains="hardcoded").count()
|
||||
quotas[bus] = free_seats
|
||||
|
||||
if display_tqdm:
|
||||
tqdm_obj.close()
|
||||
|
||||
from tqdm import tqdm
|
||||
tqdm_obj = tqdm(total=len(men), desc="Hommes")
|
||||
|
||||
self.make_repartition(men, quotas, tqdm_obj=tqdm_obj)
|
||||
|
||||
if display_tqdm:
|
||||
tqdm_obj.close()
|
||||
|
||||
# Clear cache information after running algorithm
|
||||
WEISurvey2023.clear_cache()
|
||||
|
||||
def make_repartition(self, surveys, quotas=None, tqdm_obj=None):
|
||||
free_surveys = surveys.copy() # Remaining surveys
|
||||
while free_surveys: # Some students are not affected
|
||||
survey = free_surveys[0]
|
||||
buses = survey.ordered_buses() # Preferences of the student
|
||||
for bus, current_score in buses:
|
||||
if self.get_bus_information(bus).has_free_seats(surveys, quotas):
|
||||
# Selected bus has free places. Put student in the bus
|
||||
survey.select_bus(bus)
|
||||
survey.save()
|
||||
free_surveys.remove(survey)
|
||||
break
|
||||
else:
|
||||
# Current bus has not enough places. Remove the least preferred student from the bus if existing
|
||||
least_preferred_survey = None
|
||||
least_score = -1
|
||||
# Find the least student in the bus that has a lower score than the current student
|
||||
for survey2 in surveys:
|
||||
if not survey2.information.valid or survey2.information.get_selected_bus() != bus:
|
||||
continue
|
||||
score2 = survey2.score(bus)
|
||||
if current_score <= score2: # Ignore better students
|
||||
continue
|
||||
if least_preferred_survey is None or score2 < least_score:
|
||||
least_preferred_survey = survey2
|
||||
least_score = score2
|
||||
|
||||
if least_preferred_survey is not None:
|
||||
# Remove the least student from the bus and put the current student in.
|
||||
# If it does not exist, choose the next bus.
|
||||
least_preferred_survey.free()
|
||||
least_preferred_survey.save()
|
||||
free_surveys.append(least_preferred_survey)
|
||||
survey.select_bus(bus)
|
||||
survey.save()
|
||||
free_surveys.remove(survey)
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"User {survey.registration.user} has no free seat")
|
||||
|
||||
if tqdm_obj is not None:
|
||||
tqdm_obj.n = len(surveys) - len(free_surveys)
|
||||
tqdm_obj.refresh()
|
18
apps/wei/migrations/0004_auto_20220904_2325.py
Normal file
18
apps/wei/migrations/0004_auto_20220904_2325.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.26 on 2022-09-04 21:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0003_bus_size'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='weiclub',
|
||||
name='year',
|
||||
field=models.PositiveIntegerField(default=2022, unique=True, verbose_name='year'),
|
||||
),
|
||||
]
|
18
apps/wei/migrations/0005_auto_20230128_1850.py
Normal file
18
apps/wei/migrations/0005_auto_20230128_1850.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.28 on 2023-01-28 17:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0004_auto_20220904_2325'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='weiclub',
|
||||
name='year',
|
||||
field=models.PositiveIntegerField(default=2023, unique=True, verbose_name='year'),
|
||||
),
|
||||
]
|
110
apps/wei/tests/test_wei_algorithm_2023.py
Normal file
110
apps/wei/tests/test_wei_algorithm_2023.py
Normal file
@ -0,0 +1,110 @@
|
||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import random
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
|
||||
from ..forms.surveys.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023
|
||||
from ..models import Bus, WEIClub, WEIRegistration
|
||||
|
||||
|
||||
class TestWEIAlgorithm(TestCase):
|
||||
"""
|
||||
Run some tests to ensure that the WEI algorithm is working well.
|
||||
"""
|
||||
fixtures = ('initial',)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create some test data, with one WEI and 10 buses with random score attributions.
|
||||
"""
|
||||
self.wei = WEIClub.objects.create(
|
||||
name="WEI 2023",
|
||||
email="wei2023@example.com",
|
||||
date_start='2023-09-16',
|
||||
date_end='2023-09-18',
|
||||
year=2023,
|
||||
)
|
||||
|
||||
self.buses = []
|
||||
for i in range(10):
|
||||
bus = Bus.objects.create(wei=self.wei, name=f"Bus {i}", size=10)
|
||||
self.buses.append(bus)
|
||||
information = WEIBusInformation2023(bus)
|
||||
for word in WORDS:
|
||||
information.scores[word] = random.randint(0, 101)
|
||||
information.save()
|
||||
bus.save()
|
||||
|
||||
def test_survey_algorithm_small(self):
|
||||
"""
|
||||
There are only a few people in each bus, ensure that each person has its best bus
|
||||
"""
|
||||
# Add a few users
|
||||
for i in range(10):
|
||||
user = User.objects.create(username=f"user{i}")
|
||||
registration = WEIRegistration.objects.create(
|
||||
user=user,
|
||||
wei=self.wei,
|
||||
first_year=True,
|
||||
birth_date='2000-01-01',
|
||||
)
|
||||
information = WEISurveyInformation2023(registration)
|
||||
for j in range(1, 21):
|
||||
setattr(information, f'word{j}', random.choice(WORDS))
|
||||
information.step = 20
|
||||
information.save(registration)
|
||||
registration.save()
|
||||
|
||||
# Run algorithm
|
||||
WEISurvey2023.get_algorithm_class()().run_algorithm()
|
||||
|
||||
# Ensure that everyone has its first choice
|
||||
for r in WEIRegistration.objects.filter(wei=self.wei).all():
|
||||
survey = WEISurvey2023(r)
|
||||
preferred_bus = survey.ordered_buses()[0][0]
|
||||
chosen_bus = survey.information.get_selected_bus()
|
||||
self.assertEqual(preferred_bus, chosen_bus)
|
||||
|
||||
def test_survey_algorithm_full(self):
|
||||
"""
|
||||
Buses are full of first year people, ensure that they are happy
|
||||
"""
|
||||
# Add a lot of users
|
||||
for i in range(95):
|
||||
user = User.objects.create(username=f"user{i}")
|
||||
registration = WEIRegistration.objects.create(
|
||||
user=user,
|
||||
wei=self.wei,
|
||||
first_year=True,
|
||||
birth_date='2000-01-01',
|
||||
)
|
||||
information = WEISurveyInformation2023(registration)
|
||||
for j in range(1, 21):
|
||||
setattr(information, f'word{j}', random.choice(WORDS))
|
||||
information.step = 20
|
||||
information.save(registration)
|
||||
registration.save()
|
||||
|
||||
# Run algorithm
|
||||
WEISurvey2023.get_algorithm_class()().run_algorithm()
|
||||
|
||||
penalty = 0
|
||||
# Ensure that everyone seems to be happy
|
||||
# We attribute a penalty for each user that didn't have its first choice
|
||||
# The penalty is the square of the distance between the score of the preferred bus
|
||||
# and the score of the attributed bus
|
||||
# We consider it acceptable if the mean of this distance is lower than 5 %
|
||||
for r in WEIRegistration.objects.filter(wei=self.wei).all():
|
||||
survey = WEISurvey2023(r)
|
||||
chosen_bus = survey.information.get_selected_bus()
|
||||
buses = survey.ordered_buses()
|
||||
score = min(v for bus, v in buses if bus == chosen_bus)
|
||||
max_score = buses[0][1]
|
||||
penalty += (max_score - score) ** 2
|
||||
|
||||
self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance
|
||||
|
||||
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import subprocess
|
||||
@ -782,7 +782,7 @@ class TestDefaultWEISurvey(TestCase):
|
||||
WEISurvey.update_form(None, None)
|
||||
|
||||
self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey)
|
||||
self.assertEqual(CurrentSurvey.get_year(), 2022)
|
||||
self.assertEqual(CurrentSurvey.get_year(), 2023)
|
||||
|
||||
|
||||
class TestWeiAPI(TestAPI):
|
||||
|
@ -7,9 +7,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
||||
"POT-Creation-Date: 2023-03-31 17:08+0200\n"
|
||||
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
||||
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
|
||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
||||
"Language-Team: German <http://translate.ynerant.fr/projects/nk20/nk20/de/>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -115,7 +115,7 @@ msgid "type"
|
||||
msgstr "Type"
|
||||
|
||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:286
|
||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||
#: apps/wei/templates/wei/survey.html:15
|
||||
msgid "user"
|
||||
@ -258,7 +258,7 @@ msgstr "Eingetreten um "
|
||||
msgid "remove"
|
||||
msgstr "entfernen"
|
||||
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:200
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
@ -1570,7 +1570,7 @@ msgstr "Sondertranskationen"
|
||||
msgid "membership transaction"
|
||||
msgstr "Mitgliedschafttransaktion"
|
||||
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:293
|
||||
msgid "membership transactions"
|
||||
msgstr "Mitgliedschaftttransaktionen"
|
||||
|
||||
@ -1689,7 +1689,7 @@ msgid "Amount"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: apps/note/templates/note/transaction_form.html:132
|
||||
#: apps/treasury/models.py:54
|
||||
#: apps/treasury/models.py:55
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
@ -2183,7 +2183,7 @@ msgstr "Ungültige Vorregistrierung"
|
||||
msgid "Treasury"
|
||||
msgstr "Quaestor"
|
||||
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:94
|
||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||
msgid "This invoice is locked and can no longer be edited."
|
||||
msgstr "Diese Rechnung ist gesperrt und kann nicht mehr bearbeitet werden."
|
||||
@ -2196,7 +2196,7 @@ msgstr "Überweisung ist bereits geschlossen."
|
||||
msgid "You can't change the type of the remittance."
|
||||
msgstr "Sie können die Art der Überweisung nicht ändern."
|
||||
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:268
|
||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||
@ -2212,116 +2212,116 @@ msgstr "Keine beigefügte Überweisung"
|
||||
msgid "Invoice identifier"
|
||||
msgstr "Rechnungskennung"
|
||||
|
||||
#: apps/treasury/models.py:40
|
||||
#: apps/treasury/models.py:41
|
||||
msgid "BDE"
|
||||
msgstr "BDE"
|
||||
|
||||
#: apps/treasury/models.py:45
|
||||
#: apps/treasury/models.py:46
|
||||
msgid "Object"
|
||||
msgstr "Objekt"
|
||||
|
||||
#: apps/treasury/models.py:49
|
||||
#: apps/treasury/models.py:50
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: apps/treasury/models.py:58
|
||||
#: apps/treasury/models.py:59
|
||||
msgid "Address"
|
||||
msgstr "Adresse"
|
||||
|
||||
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||
#: apps/treasury/models.py:64 apps/treasury/models.py:194
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: apps/treasury/models.py:67
|
||||
#: apps/treasury/models.py:68
|
||||
msgid "Acquitted"
|
||||
msgstr "Bezahlt"
|
||||
|
||||
#: apps/treasury/models.py:72
|
||||
#: apps/treasury/models.py:73
|
||||
msgid "Locked"
|
||||
msgstr "Gesperrt"
|
||||
|
||||
#: apps/treasury/models.py:73
|
||||
#: apps/treasury/models.py:74
|
||||
msgid "An invoice can't be edited when it is locked."
|
||||
msgstr "Eine Rechnung kann nicht bearbeitet werden, wenn sie gesperrt ist."
|
||||
|
||||
#: apps/treasury/models.py:79
|
||||
#: apps/treasury/models.py:80
|
||||
msgid "tex source"
|
||||
msgstr "Tex Quelle"
|
||||
|
||||
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||
#: apps/treasury/models.py:114 apps/treasury/models.py:130
|
||||
msgid "invoice"
|
||||
msgstr "Rechnung"
|
||||
|
||||
#: apps/treasury/models.py:114
|
||||
#: apps/treasury/models.py:115
|
||||
msgid "invoices"
|
||||
msgstr "Rechnungen"
|
||||
|
||||
#: apps/treasury/models.py:117
|
||||
#: apps/treasury/models.py:118
|
||||
#, python-brace-format
|
||||
msgid "Invoice #{id}"
|
||||
msgstr "Rechnung #{id}"
|
||||
|
||||
#: apps/treasury/models.py:134
|
||||
#: apps/treasury/models.py:135
|
||||
msgid "Designation"
|
||||
msgstr "Bezeichnung"
|
||||
|
||||
#: apps/treasury/models.py:140
|
||||
#: apps/treasury/models.py:141
|
||||
msgid "Quantity"
|
||||
msgstr "Qualität"
|
||||
|
||||
#: apps/treasury/models.py:145
|
||||
#: apps/treasury/models.py:146
|
||||
msgid "Unit price"
|
||||
msgstr "Einzelpreis"
|
||||
|
||||
#: apps/treasury/models.py:161
|
||||
#: apps/treasury/models.py:162
|
||||
msgid "product"
|
||||
msgstr "Produkt"
|
||||
|
||||
#: apps/treasury/models.py:162
|
||||
#: apps/treasury/models.py:163
|
||||
msgid "products"
|
||||
msgstr "Produkten"
|
||||
|
||||
#: apps/treasury/models.py:182
|
||||
#: apps/treasury/models.py:183
|
||||
msgid "remittance type"
|
||||
msgstr "Überweisungstyp"
|
||||
|
||||
#: apps/treasury/models.py:183
|
||||
#: apps/treasury/models.py:184
|
||||
msgid "remittance types"
|
||||
msgstr "Überweisungstypen"
|
||||
|
||||
#: apps/treasury/models.py:204
|
||||
#: apps/treasury/models.py:205
|
||||
msgid "Comment"
|
||||
msgstr "Kommentar"
|
||||
|
||||
#: apps/treasury/models.py:209
|
||||
#: apps/treasury/models.py:210
|
||||
msgid "Closed"
|
||||
msgstr "Geschlossen"
|
||||
|
||||
#: apps/treasury/models.py:213
|
||||
#: apps/treasury/models.py:214
|
||||
msgid "remittance"
|
||||
msgstr "Überweisung"
|
||||
|
||||
#: apps/treasury/models.py:214
|
||||
#: apps/treasury/models.py:215
|
||||
msgid "remittances"
|
||||
msgstr "Überweisungen"
|
||||
|
||||
#: apps/treasury/models.py:247
|
||||
#: apps/treasury/models.py:248
|
||||
msgid "Remittance #{:d}: {}"
|
||||
msgstr "Überweisung #{:d}:{}"
|
||||
|
||||
#: apps/treasury/models.py:271
|
||||
#: apps/treasury/models.py:272
|
||||
msgid "special transaction proxy"
|
||||
msgstr "spezielle Transaktion Proxy"
|
||||
|
||||
#: apps/treasury/models.py:272
|
||||
#: apps/treasury/models.py:273
|
||||
msgid "special transaction proxies"
|
||||
msgstr "spezielle Transaktion Proxies"
|
||||
|
||||
#: apps/treasury/models.py:298
|
||||
#: apps/treasury/models.py:299
|
||||
msgid "credit transaction"
|
||||
msgstr "Kredit Transaktion"
|
||||
|
||||
#: apps/treasury/models.py:430
|
||||
#: apps/treasury/models.py:432
|
||||
msgid ""
|
||||
"This user doesn't have enough money to pay the memberships with its note. "
|
||||
"Please ask her/him to credit the note before invalidating this credit."
|
||||
@ -2329,16 +2329,16 @@ msgstr ""
|
||||
"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner "
|
||||
"Note zu bezahlen."
|
||||
|
||||
#: apps/treasury/models.py:451
|
||||
#: apps/treasury/models.py:453
|
||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||
msgid "Credit from the Société générale"
|
||||
msgstr "Kredit von der Société générale"
|
||||
|
||||
#: apps/treasury/models.py:452
|
||||
#: apps/treasury/models.py:454
|
||||
msgid "Credits from the Société générale"
|
||||
msgstr "Krediten von der Société générale"
|
||||
|
||||
#: apps/treasury/models.py:455
|
||||
#: apps/treasury/models.py:457
|
||||
#, python-brace-format
|
||||
msgid "Soge credit for {user}"
|
||||
msgstr "Kredit von der Société générale für {user}"
|
||||
@ -2640,7 +2640,7 @@ msgstr "Wählen Sie die Rollen aus, an denen Sie interessiert sind."
|
||||
msgid "This team doesn't belong to the given bus."
|
||||
msgstr "Dieses Team gehört nicht zum angegebenen Bus."
|
||||
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
||||
msgid "Choose a word:"
|
||||
msgstr "Wählen Sie ein Wort:"
|
||||
|
||||
@ -3361,6 +3361,10 @@ msgstr "Kontakt"
|
||||
msgid "Technical Support"
|
||||
msgstr ""
|
||||
|
||||
#: note_kfet/templates/base.html:198
|
||||
msgid "FAQ (FR)"
|
||||
msgstr "FAQ (FR)"
|
||||
|
||||
#: note_kfet/templates/base_search.html:15
|
||||
msgid "Search by attribute such as name…"
|
||||
msgstr "Suche nach Attributen wie Name…"
|
||||
@ -3611,7 +3615,6 @@ msgstr ""
|
||||
#~ msgid "This user didn't give her/his caution check."
|
||||
#~ msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft."
|
||||
|
||||
#, python-format
|
||||
#~ msgid ""
|
||||
#~ "A new version of the application is available. This instance runs "
|
||||
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
|
||||
|
@ -7,9 +7,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
||||
"POT-Creation-Date: 2023-03-31 17:08+0200\n"
|
||||
"PO-Revision-Date: 2022-04-11 23:12+0200\n"
|
||||
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -114,7 +114,7 @@ msgid "type"
|
||||
msgstr "tipo"
|
||||
|
||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:286
|
||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||
#: apps/wei/templates/wei/survey.html:15
|
||||
msgid "user"
|
||||
@ -257,7 +257,7 @@ msgstr "Entrado el "
|
||||
msgid "remove"
|
||||
msgstr "quitar"
|
||||
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:200
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
@ -1557,7 +1557,7 @@ msgstr "Transacciones especiales"
|
||||
msgid "membership transaction"
|
||||
msgstr "transacción de afiliación"
|
||||
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:293
|
||||
msgid "membership transactions"
|
||||
msgstr "transacciones de afiliación"
|
||||
|
||||
@ -1676,7 +1676,7 @@ msgid "Amount"
|
||||
msgstr "Monto"
|
||||
|
||||
#: apps/note/templates/note/transaction_form.html:132
|
||||
#: apps/treasury/models.py:54
|
||||
#: apps/treasury/models.py:55
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
@ -2163,7 +2163,7 @@ msgstr "Invalidar la afiliación"
|
||||
msgid "Treasury"
|
||||
msgstr "Tesorería"
|
||||
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:94
|
||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||
msgid "This invoice is locked and can no longer be edited."
|
||||
msgstr "Esta factura esta bloqueada y no puede ser modificada."
|
||||
@ -2176,7 +2176,7 @@ msgstr "El descuento ya esta cerrado."
|
||||
msgid "You can't change the type of the remittance."
|
||||
msgstr "No puede cambiar el tipo de descuento."
|
||||
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:268
|
||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||
@ -2192,116 +2192,116 @@ msgstr "No hay descuento relacionado"
|
||||
msgid "Invoice identifier"
|
||||
msgstr "Numero de factura"
|
||||
|
||||
#: apps/treasury/models.py:40
|
||||
#: apps/treasury/models.py:41
|
||||
msgid "BDE"
|
||||
msgstr "BDE"
|
||||
|
||||
#: apps/treasury/models.py:45
|
||||
#: apps/treasury/models.py:46
|
||||
msgid "Object"
|
||||
msgstr "Asunto"
|
||||
|
||||
#: apps/treasury/models.py:49
|
||||
#: apps/treasury/models.py:50
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: apps/treasury/models.py:58
|
||||
#: apps/treasury/models.py:59
|
||||
msgid "Address"
|
||||
msgstr "Dirección"
|
||||
|
||||
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||
#: apps/treasury/models.py:64 apps/treasury/models.py:194
|
||||
msgid "Date"
|
||||
msgstr "Fecha"
|
||||
|
||||
#: apps/treasury/models.py:67
|
||||
#: apps/treasury/models.py:68
|
||||
msgid "Acquitted"
|
||||
msgstr "Pagada"
|
||||
|
||||
#: apps/treasury/models.py:72
|
||||
#: apps/treasury/models.py:73
|
||||
msgid "Locked"
|
||||
msgstr "Bloqueada"
|
||||
|
||||
#: apps/treasury/models.py:73
|
||||
#: apps/treasury/models.py:74
|
||||
msgid "An invoice can't be edited when it is locked."
|
||||
msgstr "Une factura no puede ser modificada cuando esta bloqueada."
|
||||
|
||||
#: apps/treasury/models.py:79
|
||||
#: apps/treasury/models.py:80
|
||||
msgid "tex source"
|
||||
msgstr "código fuente TeX"
|
||||
|
||||
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||
#: apps/treasury/models.py:114 apps/treasury/models.py:130
|
||||
msgid "invoice"
|
||||
msgstr "factura"
|
||||
|
||||
#: apps/treasury/models.py:114
|
||||
#: apps/treasury/models.py:115
|
||||
msgid "invoices"
|
||||
msgstr "facturas"
|
||||
|
||||
#: apps/treasury/models.py:117
|
||||
#: apps/treasury/models.py:118
|
||||
#, python-brace-format
|
||||
msgid "Invoice #{id}"
|
||||
msgstr "Factura n°{id}"
|
||||
|
||||
#: apps/treasury/models.py:134
|
||||
#: apps/treasury/models.py:135
|
||||
msgid "Designation"
|
||||
msgstr "Designación"
|
||||
|
||||
#: apps/treasury/models.py:140
|
||||
#: apps/treasury/models.py:141
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: apps/treasury/models.py:145
|
||||
#: apps/treasury/models.py:146
|
||||
msgid "Unit price"
|
||||
msgstr "Precio unitario"
|
||||
|
||||
#: apps/treasury/models.py:161
|
||||
#: apps/treasury/models.py:162
|
||||
msgid "product"
|
||||
msgstr "producto"
|
||||
|
||||
#: apps/treasury/models.py:162
|
||||
#: apps/treasury/models.py:163
|
||||
msgid "products"
|
||||
msgstr "productos"
|
||||
|
||||
#: apps/treasury/models.py:182
|
||||
#: apps/treasury/models.py:183
|
||||
msgid "remittance type"
|
||||
msgstr "tipo de descuento"
|
||||
|
||||
#: apps/treasury/models.py:183
|
||||
#: apps/treasury/models.py:184
|
||||
msgid "remittance types"
|
||||
msgstr "tipos de descuentos"
|
||||
|
||||
#: apps/treasury/models.py:204
|
||||
#: apps/treasury/models.py:205
|
||||
msgid "Comment"
|
||||
msgstr "Comentario"
|
||||
|
||||
#: apps/treasury/models.py:209
|
||||
#: apps/treasury/models.py:210
|
||||
msgid "Closed"
|
||||
msgstr "Cerrada"
|
||||
|
||||
#: apps/treasury/models.py:213
|
||||
#: apps/treasury/models.py:214
|
||||
msgid "remittance"
|
||||
msgstr "descuento"
|
||||
|
||||
#: apps/treasury/models.py:214
|
||||
#: apps/treasury/models.py:215
|
||||
msgid "remittances"
|
||||
msgstr "descuentos"
|
||||
|
||||
#: apps/treasury/models.py:247
|
||||
#: apps/treasury/models.py:248
|
||||
msgid "Remittance #{:d}: {}"
|
||||
msgstr "Descuento n°{:d} : {}"
|
||||
|
||||
#: apps/treasury/models.py:271
|
||||
#: apps/treasury/models.py:272
|
||||
msgid "special transaction proxy"
|
||||
msgstr "proxy de transacción especial"
|
||||
|
||||
#: apps/treasury/models.py:272
|
||||
#: apps/treasury/models.py:273
|
||||
msgid "special transaction proxies"
|
||||
msgstr "proxys de transacciones especiales"
|
||||
|
||||
#: apps/treasury/models.py:298
|
||||
#: apps/treasury/models.py:299
|
||||
msgid "credit transaction"
|
||||
msgstr "transacción de crédito"
|
||||
|
||||
#: apps/treasury/models.py:430
|
||||
#: apps/treasury/models.py:432
|
||||
msgid ""
|
||||
"This user doesn't have enough money to pay the memberships with its note. "
|
||||
"Please ask her/him to credit the note before invalidating this credit."
|
||||
@ -2310,16 +2310,16 @@ msgstr ""
|
||||
"afiliaciones. Por favor pídelo acreditar su note antes de invalidar este "
|
||||
"crédito."
|
||||
|
||||
#: apps/treasury/models.py:451
|
||||
#: apps/treasury/models.py:453
|
||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||
msgid "Credit from the Société générale"
|
||||
msgstr "Crédito de la Société Générale"
|
||||
|
||||
#: apps/treasury/models.py:452
|
||||
#: apps/treasury/models.py:454
|
||||
msgid "Credits from the Société générale"
|
||||
msgstr "Créditos de la Société Générale"
|
||||
|
||||
#: apps/treasury/models.py:455
|
||||
#: apps/treasury/models.py:457
|
||||
#, python-brace-format
|
||||
msgid "Soge credit for {user}"
|
||||
msgstr "Crédito de la Société Générale para {user}"
|
||||
@ -2612,7 +2612,7 @@ msgstr "Elegir los papeles que le interesa."
|
||||
msgid "This team doesn't belong to the given bus."
|
||||
msgstr "Este equipo no pertenece al bus dado."
|
||||
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
||||
msgid "Choose a word:"
|
||||
msgstr "Elegir una palabra :"
|
||||
|
||||
@ -3316,6 +3316,10 @@ msgstr "Contactarnos"
|
||||
msgid "Technical Support"
|
||||
msgstr "Soporte técnico"
|
||||
|
||||
#: note_kfet/templates/base.html:198
|
||||
msgid "FAQ (FR)"
|
||||
msgstr "FAQ (FR)"
|
||||
|
||||
#: note_kfet/templates/base_search.html:15
|
||||
msgid "Search by attribute such as name…"
|
||||
msgstr "Buscar con atributo, como el nombre…"
|
||||
|
@ -7,9 +7,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
||||
"POT-Creation-Date: 2023-03-31 17:08+0200\n"
|
||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
||||
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -115,7 +115,7 @@ msgid "type"
|
||||
msgstr "type"
|
||||
|
||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:286
|
||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||
#: apps/wei/templates/wei/survey.html:15
|
||||
msgid "user"
|
||||
@ -258,7 +258,7 @@ msgstr "Entré le "
|
||||
msgid "remove"
|
||||
msgstr "supprimer"
|
||||
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:200
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
@ -1563,7 +1563,7 @@ msgstr "Transactions de crédit/retrait"
|
||||
msgid "membership transaction"
|
||||
msgstr "transaction d'adhésion"
|
||||
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:293
|
||||
msgid "membership transactions"
|
||||
msgstr "transactions d'adhésion"
|
||||
|
||||
@ -1682,7 +1682,7 @@ msgid "Amount"
|
||||
msgstr "Montant"
|
||||
|
||||
#: apps/note/templates/note/transaction_form.html:132
|
||||
#: apps/treasury/models.py:54
|
||||
#: apps/treasury/models.py:55
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
@ -2171,7 +2171,7 @@ msgstr "Invalider l'inscription"
|
||||
msgid "Treasury"
|
||||
msgstr "Trésorerie"
|
||||
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:94
|
||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||
msgid "This invoice is locked and can no longer be edited."
|
||||
msgstr "Cette facture est verrouillée et ne peut plus être éditée."
|
||||
@ -2184,7 +2184,7 @@ msgstr "La remise est déjà fermée."
|
||||
msgid "You can't change the type of the remittance."
|
||||
msgstr "Vous ne pouvez pas changer le type de la remise."
|
||||
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:268
|
||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||
@ -2200,116 +2200,116 @@ msgstr "Pas de remise associée"
|
||||
msgid "Invoice identifier"
|
||||
msgstr "Numéro de facture"
|
||||
|
||||
#: apps/treasury/models.py:40
|
||||
#: apps/treasury/models.py:41
|
||||
msgid "BDE"
|
||||
msgstr "BDE"
|
||||
|
||||
#: apps/treasury/models.py:45
|
||||
#: apps/treasury/models.py:46
|
||||
msgid "Object"
|
||||
msgstr "Objet"
|
||||
|
||||
#: apps/treasury/models.py:49
|
||||
#: apps/treasury/models.py:50
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
#: apps/treasury/models.py:58
|
||||
#: apps/treasury/models.py:59
|
||||
msgid "Address"
|
||||
msgstr "Adresse"
|
||||
|
||||
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||
#: apps/treasury/models.py:64 apps/treasury/models.py:194
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#: apps/treasury/models.py:67
|
||||
#: apps/treasury/models.py:68
|
||||
msgid "Acquitted"
|
||||
msgstr "Acquittée"
|
||||
|
||||
#: apps/treasury/models.py:72
|
||||
#: apps/treasury/models.py:73
|
||||
msgid "Locked"
|
||||
msgstr "Verrouillée"
|
||||
|
||||
#: apps/treasury/models.py:73
|
||||
#: apps/treasury/models.py:74
|
||||
msgid "An invoice can't be edited when it is locked."
|
||||
msgstr "Une facture ne peut plus être modifiée si elle est verrouillée."
|
||||
|
||||
#: apps/treasury/models.py:79
|
||||
#: apps/treasury/models.py:80
|
||||
msgid "tex source"
|
||||
msgstr "fichier TeX source"
|
||||
|
||||
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||
#: apps/treasury/models.py:114 apps/treasury/models.py:130
|
||||
msgid "invoice"
|
||||
msgstr "facture"
|
||||
|
||||
#: apps/treasury/models.py:114
|
||||
#: apps/treasury/models.py:115
|
||||
msgid "invoices"
|
||||
msgstr "factures"
|
||||
|
||||
#: apps/treasury/models.py:117
|
||||
#: apps/treasury/models.py:118
|
||||
#, python-brace-format
|
||||
msgid "Invoice #{id}"
|
||||
msgstr "Facture n°{id}"
|
||||
|
||||
#: apps/treasury/models.py:134
|
||||
#: apps/treasury/models.py:135
|
||||
msgid "Designation"
|
||||
msgstr "Désignation"
|
||||
|
||||
#: apps/treasury/models.py:140
|
||||
#: apps/treasury/models.py:141
|
||||
msgid "Quantity"
|
||||
msgstr "Quantité"
|
||||
|
||||
#: apps/treasury/models.py:145
|
||||
#: apps/treasury/models.py:146
|
||||
msgid "Unit price"
|
||||
msgstr "Prix unitaire"
|
||||
|
||||
#: apps/treasury/models.py:161
|
||||
#: apps/treasury/models.py:162
|
||||
msgid "product"
|
||||
msgstr "produit"
|
||||
|
||||
#: apps/treasury/models.py:162
|
||||
#: apps/treasury/models.py:163
|
||||
msgid "products"
|
||||
msgstr "produits"
|
||||
|
||||
#: apps/treasury/models.py:182
|
||||
#: apps/treasury/models.py:183
|
||||
msgid "remittance type"
|
||||
msgstr "type de remise"
|
||||
|
||||
#: apps/treasury/models.py:183
|
||||
#: apps/treasury/models.py:184
|
||||
msgid "remittance types"
|
||||
msgstr "types de remises"
|
||||
|
||||
#: apps/treasury/models.py:204
|
||||
#: apps/treasury/models.py:205
|
||||
msgid "Comment"
|
||||
msgstr "Commentaire"
|
||||
|
||||
#: apps/treasury/models.py:209
|
||||
#: apps/treasury/models.py:210
|
||||
msgid "Closed"
|
||||
msgstr "Fermée"
|
||||
|
||||
#: apps/treasury/models.py:213
|
||||
#: apps/treasury/models.py:214
|
||||
msgid "remittance"
|
||||
msgstr "remise"
|
||||
|
||||
#: apps/treasury/models.py:214
|
||||
#: apps/treasury/models.py:215
|
||||
msgid "remittances"
|
||||
msgstr "remises"
|
||||
|
||||
#: apps/treasury/models.py:247
|
||||
#: apps/treasury/models.py:248
|
||||
msgid "Remittance #{:d}: {}"
|
||||
msgstr "Remise n°{:d} : {}"
|
||||
|
||||
#: apps/treasury/models.py:271
|
||||
#: apps/treasury/models.py:272
|
||||
msgid "special transaction proxy"
|
||||
msgstr "proxy de transaction spéciale"
|
||||
|
||||
#: apps/treasury/models.py:272
|
||||
#: apps/treasury/models.py:273
|
||||
msgid "special transaction proxies"
|
||||
msgstr "proxys de transactions spéciales"
|
||||
|
||||
#: apps/treasury/models.py:298
|
||||
#: apps/treasury/models.py:299
|
||||
msgid "credit transaction"
|
||||
msgstr "transaction de crédit"
|
||||
|
||||
#: apps/treasury/models.py:430
|
||||
#: apps/treasury/models.py:432
|
||||
msgid ""
|
||||
"This user doesn't have enough money to pay the memberships with its note. "
|
||||
"Please ask her/him to credit the note before invalidating this credit."
|
||||
@ -2317,16 +2317,16 @@ msgstr ""
|
||||
"Cet utilisateur n'a pas assez d'argent pour payer les adhésions avec sa "
|
||||
"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit."
|
||||
|
||||
#: apps/treasury/models.py:451
|
||||
#: apps/treasury/models.py:453
|
||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||
msgid "Credit from the Société générale"
|
||||
msgstr "Crédit de la Société générale"
|
||||
|
||||
#: apps/treasury/models.py:452
|
||||
#: apps/treasury/models.py:454
|
||||
msgid "Credits from the Société générale"
|
||||
msgstr "Crédits de la Société générale"
|
||||
|
||||
#: apps/treasury/models.py:455
|
||||
#: apps/treasury/models.py:457
|
||||
#, python-brace-format
|
||||
msgid "Soge credit for {user}"
|
||||
msgstr "Crédit de la société générale pour l'utilisateur {user}"
|
||||
@ -2624,7 +2624,7 @@ msgstr "Sélectionnez les rôles qui vous intéressent."
|
||||
msgid "This team doesn't belong to the given bus."
|
||||
msgstr "Cette équipe n'appartient pas à ce bus."
|
||||
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
||||
msgid "Choose a word:"
|
||||
msgstr "Choisissez un mot :"
|
||||
|
||||
@ -3335,6 +3335,10 @@ msgstr "Nous contacter"
|
||||
msgid "Technical Support"
|
||||
msgstr "Support technique"
|
||||
|
||||
#: note_kfet/templates/base.html:198
|
||||
msgid "FAQ (FR)"
|
||||
msgstr "FAQ (FR)"
|
||||
|
||||
#: note_kfet/templates/base_search.html:15
|
||||
msgid "Search by attribute such as name…"
|
||||
msgstr "Chercher par un attribut tel que le nom …"
|
||||
|
@ -18,7 +18,7 @@ MAILTO=notekfet2020@lists.crans.org
|
||||
# Spammer les gens en négatif
|
||||
00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam --negative-amount 1 -v 0
|
||||
# Envoyer le rapport mensuel aux trésoriers et respos info
|
||||
00 8 6 * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
|
||||
00 8 * * 5 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
|
||||
# Envoyer les rapports aux gens
|
||||
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports -v 0
|
||||
# Mettre à jour les boutons mis en avant
|
||||
|
@ -252,7 +252,7 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
|
||||
],
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||
'DEFAULT_PAGINATION_CLASS': 'apps.api.pagination.CustomPagination',
|
||||
'PAGE_SIZE': 20,
|
||||
}
|
||||
|
||||
|
72
note_kfet/static/css/custom.css
Normal file → Executable file
72
note_kfet/static/css/custom.css
Normal file → Executable file
@ -65,7 +65,10 @@ mark {
|
||||
|
||||
/* Last BDE colors */
|
||||
.bg-primary {
|
||||
background-color: rgb(102, 83, 105) !important;
|
||||
/* background-color: rgb(18, 67, 4) !important; */
|
||||
/* MODE VIEUXCON=ON */
|
||||
/* background-color: rgb(166, 0, 2) !important; */
|
||||
background-color: rgb(0, 0, 0) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
@ -80,15 +83,15 @@ body {
|
||||
.btn-outline-primary:hover,
|
||||
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
||||
color: #fff;
|
||||
background-color: rgb(102, 83, 105);
|
||||
border-color: rgb(102, 83, 105);
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: rgb(102, 83, 105);
|
||||
background-color: rgba(248, 249, 250, 0.9);
|
||||
border-color: rgb(102, 83, 105);
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #464647;
|
||||
}
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
@ -98,36 +101,63 @@ body {
|
||||
.btn-primary:hover,
|
||||
.btn-primary:not(:disabled):not(.disabled).active,
|
||||
.btn-primary:not(:disabled):not(.disabled):active {
|
||||
color: #fff;
|
||||
background-color: rgb(102, 83, 105);
|
||||
border-color: rgb(102, 83, 105);
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: rgba(248, 249, 250, 0.9);
|
||||
background-color: rgb(102, 83, 105);
|
||||
border-color: rgb(102, 83, 105);
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
|
||||
.border-primary {
|
||||
border-color: rgb(115, 15, 115) !important;
|
||||
border-color: rgb(228, 35, 132) !important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
|
||||
.btn-secondary:hover,
|
||||
.btn-secondary:not(:disabled):not(.disabled).active,
|
||||
.btn-secondary:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-dark {
|
||||
color: #343a40;
|
||||
border-color: #343a40;
|
||||
}
|
||||
|
||||
.btn-outline-dark:hover,
|
||||
.btn-outline-dark:not(:disabled):not(.disabled).active,
|
||||
.btn-outline-dark:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: rgb(102, 83, 105);
|
||||
color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: rgb(200, 30, 200);
|
||||
color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.25);
|
||||
border-color: rgb(200, 30, 200);
|
||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 50%);
|
||||
border-color: rgb(228, 35, 132);
|
||||
}
|
||||
|
||||
.btn-outline-primary.focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.5);
|
||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 10%);
|
||||
}
|
||||
|
||||
|
||||
|
@ -194,6 +194,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
class="text-muted">{% trans "Contact us" %}</a> —
|
||||
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
|
||||
class="text-muted">{% trans "Technical Support" %}</a> —
|
||||
<a href="https://note.crans.org/doc/faq/"
|
||||
class="text-muted">{% trans "FAQ (FR)" %}</a> —
|
||||
</span>
|
||||
{% csrf_token %}
|
||||
<select title="language" name="language"
|
||||
|
@ -23,11 +23,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{{ profile_form|crispy }}
|
||||
{{ soge_form|crispy }}
|
||||
{% comment "Soge not for membership (only WEI)" %} {{ soge_form|crispy }} {% endcomment %}
|
||||
<button class="btn btn-success" type="submit">
|
||||
{% trans "Sign up" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user