Compare commits

..

404 commits
v0.6 ... master

Author SHA1 Message Date
2126224e15 Merge pull request 'Thubrecht/daphne' (#815) from thubrecht/daphne into master
Reviewed-on: #815
2024-07-30 18:40:56 +02:00
d6109a9312 dev: update requirements and directly read the files 2024-07-12 14:54:14 +02:00
a69bd0426f kfet/open: Add comment to test 2024-07-12 14:54:14 +02:00
81a4dbef7c dev: set explicitely DJANGO_SETTINGS_MODULE 2024-07-12 14:54:14 +02:00
4fedf3453d kfet: remove redundant static tag 2024-07-12 14:54:14 +02:00
8607d77c84 dev: update requirements 2024-07-12 14:54:14 +02:00
bc55a3067e Remove useless migrations 2024-07-12 14:54:14 +02:00
dd68ad91cd Update django-hCaptcha 2024-07-12 14:54:14 +02:00
2f71246509 assertDictContainsSubset is deprecated 2024-07-12 14:54:14 +02:00
a20a1c11d6 Explicitely set transform=repr in assertQuerysetEquals 2024-07-12 14:54:14 +02:00
0e1ff1765a assertEquals is deprecated 2024-07-12 14:54:14 +02:00
177d413f4c Use AutoField instead of BigAutoField 2024-07-12 14:54:14 +02:00
01dd16c795 url -> re_path 2024-07-12 14:54:14 +02:00
84c87c1b4b Replace unique_together by UniqueConstraint 2024-07-12 14:54:14 +02:00
1b143b322f Update changelog 2024-07-12 14:54:14 +02:00
f9456e3c29 cof has been renamed to gestioasso 2024-07-12 14:54:14 +02:00
dd1350f1c2 Fix typo 2024-07-12 14:54:14 +02:00
8f4cb68d31 Only run kf tests in sequential mode (to fix issues with channels) 2024-07-12 14:54:14 +02:00
3a3f96a8df Try not running tests in parallel 2024-07-12 14:54:14 +02:00
7d8926e459 Try to flush old messages in tests 2024-07-12 14:54:14 +02:00
3fee014384 Update kfet.open tests 2024-07-12 14:54:14 +02:00
cd351786bb Remove default_app_config 2024-07-12 14:54:14 +02:00
7362c2fa2a Update django-djconfig 2024-07-12 14:54:14 +02:00
efbc947145 Change backend name as the old one is deprecated 2024-07-12 14:54:14 +02:00
0c45262fbc ugettext -> gettext 2024-07-12 14:54:14 +02:00
cabd277b4a Update redis, and implement a custom channel layer to send datetime/decimal objects 2024-07-12 14:54:14 +02:00
4108efe8c9 Fix kfet.ope tests 2024-07-12 14:54:14 +02:00
693e4252d5 Fix kpsul tests 2024-07-12 14:54:14 +02:00
3aa928e8f0 Simplify group_send for kpsul 2024-07-12 14:54:14 +02:00
5e2e68960b Channels 3 2024-07-12 14:54:14 +02:00
91e9beee11 Add default django asgi handler 2024-07-12 14:54:14 +02:00
ac286209ed Fix app import 2024-07-12 14:54:14 +02:00
b03cda5962 keyOrder is deprecated, using a list in Meta gives the correct order 2024-07-12 14:54:14 +02:00
4feb567af7 Update django-redis-cache and fix vagrant setup 2024-07-12 14:54:14 +02:00
1ac47885d0 Update django version 2024-07-12 14:54:14 +02:00
071c810605 Update base consumers 2024-07-12 14:54:14 +02:00
7f00ce0ff1 Add default http router 2024-07-12 14:54:14 +02:00
95136cb4eb Update daphne version 2024-07-12 14:54:14 +02:00
e299997aa8 Update to django channels 2 2024-07-12 14:54:14 +02:00
40f34926bb Fix vagrant setup w/ daphne 2024-07-12 14:54:14 +02:00
69976a878a Merge pull request 'Envoi de mail lors de la création d'un trigramme' (#833) from agroudiev/mail-creation-trigramme into master
Reviewed-on: #833
2024-07-06 16:54:25 +02:00
6621ae3950 style(kfet): suppression de return 2024-07-06 16:49:35 +02:00
9288daaf9e feat(kfet): envoi de mail lors de la création d'un trigramme 2024-07-06 12:14:49 +02:00
e92c500940 feat(shell.nix): Switch to python 3.9 2024-02-11 19:59:29 +01:00
Tom Hubrecht
d75eaf583f Merge branch 'master' into 'master'
Modification du délai pour l'indicateur K-Fêt ouverte

See merge request klub-dev-ens/gestioCOF!529
2023-12-10 10:11:33 +01:00
55bd3ab51d
Modification du délai du websocket 2023-12-08 21:17:53 +01:00
Tom Hubrecht
f640a25f59 Merge branch 'petitcours-template-tweak' into 'master'
[petitcours] Tweak `eleve.txt` template

See merge request klub-dev-ens/gestioCOF!528
2023-10-03 15:20:40 +02:00
Leo Lanteri--Thauvin
f881c7cd8b [petitcours] Tweak eleve.txt template 2023-09-11 16:44:45 +02:00
b548b87c25 Version 0.15.1 et Changelog 2023-06-15 13:52:53 +02:00
Tom Hubrecht
a72302291f Merge branch 'send_neg' into 'master'
feat(kfet): Change l'adresse utilisée pour envoyer les mails de négatif

See merge request klub-dev-ens/gestioCOF!526
2023-06-15 13:33:44 +02:00
a0bde75f50 feat(kfet): Change l'adresse utilisée pour envoyer les mails de négatif 2023-06-15 13:03:54 +02:00
Tom Hubrecht
44b19c12e5 Merge branch 'send_neg' into 'master'
fix(kfet): Récupère lors d'une erreur due à smtplib

See merge request klub-dev-ens/gestioCOF!525
2023-06-15 10:48:23 +02:00
f97d339a1c fix(kfet): Récupère lors d'une erreur due à smtplib 2023-06-14 20:56:25 +02:00
Tom Hubrecht
094116e88d Merge branch 'thubrecht/date-adhesion' into 'master'
Rajout de la date d'adhésion sur les profils COF

Closes #303

See merge request klub-dev-ens/gestioCOF!521
2023-05-26 09:31:45 +02:00
b32a07fc22 Version 0.15 et mise à jour du Changelog 2023-05-22 20:42:23 +02:00
Tom Hubrecht
4fc9902cf6 Merge branch 'thubrecht/contact-soiree' into 'master'
feat(kfet): Ajout d'un formulaire de demande de soirée

See merge request klub-dev-ens/gestioCOF!523
2023-05-22 20:37:38 +02:00
7164cfa37a feat(kfet): Ajout d'un formulaire de demande de soirée 2023-05-22 20:30:05 +02:00
Tom Hubrecht
90f96fb5c9 Merge branch 'thubrecht/contact' into 'master'
feat(kfet): Ajoute un formulaire de contact

Closes #302

See merge request klub-dev-ens/gestioCOF!520
2023-05-22 19:06:26 +02:00
e50249355d feat(kfet): Ajoute un formulaire de contact 2023-05-22 18:59:46 +02:00
Tom Hubrecht
c304d734d9 Merge branch 'thubrecht/comptes-inactifs' into 'master'
feat(kfet): Désactive l'envoi des mails pour les comptes gelés

See merge request klub-dev-ens/gestioCOF!522
2023-05-22 18:34:51 +02:00
c36dd30bce fix(kfet): Affiche la bonne information 2023-05-22 18:26:24 +02:00
2571cc955e feat(kfet): Désactive l'envoi des mails pour les comptes gelés
On utilise la fonctionnalité `is_frozen` pour marquer les comptes qui n'ont plus d'adresse valide, et on répare le formulaire de màj de compte.
2023-05-22 18:23:50 +02:00
3eaac5c68f feat(cof): Rajoute la date d'adhésion dans les profils 2023-05-22 11:28:23 +02:00
af4c8e0744 Update shell.nix and use django-types 2023-05-22 10:57:11 +02:00
14e0a3ef0a Version 0.14 et mise à jour du changelog 2023-05-19 20:18:11 +02:00
Tom Hubrecht
83078d4726 Merge branch 'thubrecht/date-js' into 'master'
Thubrecht/date js

See merge request klub-dev-ens/gestioCOF!518
2023-05-19 17:34:27 +02:00
cb262ad479 fix(kfet): Update timezone data for moment.js 2023-05-19 16:45:15 +02:00
5c47118834 Update gitignore and shell.nix 2023-05-19 15:14:55 +02:00
30e842ce80 shell.nix: Update to use virtualenv 2023-05-19 14:59:19 +02:00
892bf51163 Run black on all files 2023-05-19 14:57:48 +02:00
e20d7ca6c2 requirements: Fix required versions 2023-05-19 14:43:25 +02:00
Tom Hubrecht
1b09293206 Version 0.13 && Update changelog 2023-02-19 10:32:47 +01:00
Tom Hubrecht
e7da476697 Merge branch 'thubrecht/surnom' into 'master'
Rend le surnom lisible par la personne

Closes #297

See merge request klub-dev-ens/gestioCOF!506
2023-01-31 17:06:28 +01:00
Tom Hubrecht
761ab6df90 Merge branch '_aandres/inventory_sum_amounts' into 'master'
Affiche les montants des valeurs des stocks sur l'affichage d'un inventaire

See merge request klub-dev-ens/gestioCOF!515
2023-01-28 15:34:56 +01:00
Tom Hubrecht
a8d4035d33 Merge branch 'thubrecht/rappels_negatifs' into 'master'
On n'envoie des mails de rappel que lorsque le négatif est toujours d'actualité

Closes #298

See merge request klub-dev-ens/gestioCOF!509
2023-01-28 15:33:23 +01:00
b80426c56f feat(kfet): message info about prices 2023-01-28 15:28:54 +01:00
429e611daa feat(kfet): more values and formatting 2023-01-28 15:28:54 +01:00
Tom Hubrecht
5160da7862 Merge branch '292_fix_cancel_js' into 'master'
fix(kfet): fix js error when cancelling already canceled operation

Closes #292

See merge request klub-dev-ens/gestioCOF!516
2023-01-28 15:20:53 +01:00
aad3775222 fix(kfet): fix js error when cancelling already canceled operation 2023-01-28 15:13:55 +01:00
4b92716092 feat: poc inventory amount value
and lint
2023-01-23 21:53:46 +01:00
dfa5b4bf69 changelog: add current date 2022-10-03 18:37:41 +02:00
Tom Hubrecht
1be5dcb6af Merge branch 'thubrecht/kfetcms-css' into 'master'
kfetcms: Update the fixtures and fix the navbar behaviour due to a longer menu

See merge request klub-dev-ens/gestioCOF!513
2022-10-03 10:54:37 +02:00
a891ec56a6 dev: add nixos setup 2022-10-03 10:34:06 +02:00
7a52690a63 kfet: fix pipeline 2022-10-03 10:33:29 +02:00
a2f396ce7a Changelog: Update 2022-10-03 10:33:04 +02:00
85e30056a6 kfetcms: Update the fixtures and fix the navbar behaviour due to a longer menu 2022-10-03 10:22:32 +02:00
Tom Hubrecht
69de48f285 Version 0.12 2022-06-17 21:45:34 +02:00
Tom Hubrecht
eba36f2712 Merge branch 'dodo/kfet-history-limit-exceptions' into 'master'
Dodo/kfet history limit exceptions

See merge request klub-dev-ens/gestioCOF!508
2022-05-20 12:08:59 +02:00
Dorian Lesbre
bfdb34aae7 Dodo/kfet history limit exceptions 2022-05-20 12:08:59 +02:00
fcf2002cd7 On n'affiche le négatif que s'il existe vraiment 2022-01-11 18:10:00 +01:00
b236d6a950 Si last_rappel vaut None il n'est pas inclus dans le __lt 2022-01-11 18:10:00 +01:00
4b29097f02 On sauvegarde la date de fin du négatif 2022-01-11 18:10:00 +01:00
87f383bef1 On n'envoie des mails de rappel que lorsque le négatif est toujours d'actualité 2022-01-11 18:10:00 +01:00
Tom Hubrecht
1ad025e046 Merge branch 'dodo/fix_stat_labels' into 'master'
Dodo/fix stat labels

Closes #296

See merge request klub-dev-ens/gestioCOF!510
2022-01-07 11:07:51 +01:00
Dorian Lesbre
b3c047738a Ajout accent K-Fêt 2022-01-06 16:00:26 +01:00
Dorian Lesbre
17a9ae3302 Update CHANGELOG 2022-01-05 10:50:45 +01:00
Dorian Lesbre
e41bcbb6d7 Removed duplicate import to please flake8 2022-01-05 10:48:04 +01:00
Dorian Lesbre
e384bfb0f3 Fix issue #296 2022-01-05 10:45:32 +01:00
373ff1f62c Rend le surnom lisible par la personne 2021-11-25 14:44:40 +01:00
Martin Pepin
65eb95a3c9 Merge branch 'thubrecht/bds-membres' into 'master'
Réinitialisation des adhésions

Closes #294

See merge request klub-dev-ens/gestioCOF!503
2021-10-27 14:33:18 +02:00
Martin Pépin
1c880b265e
Version 1.11 2021-10-26 19:49:05 +02:00
Martin Pépin
75fbdc7efb
CHANGELOG: todo prod: faire un compte hcaptcha 2021-10-26 19:47:38 +02:00
1b8dd971b0 Ajoute un mécanisme de réinitialisation des adhésions 2021-10-26 10:26:22 +02:00
713d686047 Corrige l'affichage de la date dans le formulaire 2021-10-26 09:24:44 +02:00
Martin Pepin
77aa269c90 Merge branch 'Mails_rappel_kfet' into 'master'
Reminder mails for negative K-Psul accounts

See merge request klub-dev-ens/gestioCOF!492
2021-10-22 22:44:55 +02:00
Martin Pépin
9a143521d5
Update changelog 2021-10-22 22:37:56 +02:00
Martin Pépin
a77cf59b18
Rappels négatifs K-Fêt: ajustements cosmétiques 2021-10-22 22:36:30 +02:00
Alseidon
d8cabda678
First draft of reminder mail for negative K-Psul accounts 2021-10-22 21:29:34 +02:00
Martin Pépin
f086140dad
Update changelog 2021-10-22 21:12:16 +02:00
Martin Pepin
4d1ae8f540 Merge branch 'thubrecht/embed' into 'master'
On utilise |richtext pour les champs RichText, ce qui permet de bien faire les rendus

Closes #274

See merge request klub-dev-ens/gestioCOF!500
2021-10-22 21:04:20 +02:00
Ludovic Stephan
6d824a58be Merge branch 'thubrecht/ordre-consos' into 'master'
Corrige le tri des articles dans K-Psul

See merge request klub-dev-ens/gestioCOF!502
2021-10-12 16:05:26 +02:00
20880114aa Corrige le tri des articles dans K-Psul 2021-10-12 15:57:09 +02:00
Ludovic Stephan
df180d7446 Merge branch 'thubrecht/promo' into 'master'
Déplace le choix de la promo dans le formulaire

Closes #215

See merge request klub-dev-ens/gestioCOF!501
2021-07-01 09:03:47 +00:00
ef1793a348 Avec une seule majuscule 2021-07-01 10:29:54 +02:00
2d677b2093 Utilise des callables pour les choix 2021-07-01 10:29:14 +02:00
f70eacfc37 Déplace le choix de la promo dans le formulaire 2021-06-27 00:23:49 +02:00
264a0a852f On utilise |richtext pour les champs RichText, ce qui permet de bien faire les rendus 2021-06-26 22:52:23 +02:00
7ca7f7298a Update CHANGELOG 2021-06-17 21:28:08 +02:00
Tom Hubrecht
a5c822e7f7 Merge branch 'Aufinal/remove_negative' into 'master'
Fonctionnement du négatif + erreurs de K-Psul

Closes #279

See merge request klub-dev-ens/gestioCOF!494
2021-06-17 19:22:14 +00:00
Ludovic Stephan
6b316c482b Remove obsolete section 2021-06-17 17:22:17 +02:00
Ludovic Stephan
4060730ec5 Remove logging 2021-06-17 10:49:35 +02:00
Ludovic Stephan
c6cfc311e0 CHANGELOG 2021-06-17 10:45:53 +02:00
Ludovic Stephan
4326ba9016 Oublis de renaming 2021-06-17 10:42:15 +02:00
Ludovic Stephan
4205e0ad0e Tests 2021-06-17 10:42:15 +02:00
Ludovic Stephan
964eec6ab1 Adapte le JS aux nouvelles erreurs 2021-06-17 10:42:13 +02:00
Ludovic Stephan
29236e0b0e Nouvelle gestion des erreurs JSON 2021-06-17 10:40:51 +02:00
Ludovic Stephan
1939a54fef Tests du nouveau comportement 2021-06-17 10:40:51 +02:00
Ludovic Stephan
348881d207 Migration 2021-06-17 10:40:51 +02:00
Ludovic Stephan
ef8c1b8bf2 Nouveau fonctionnement des négatifs 2021-06-17 10:40:51 +02:00
Tom Hubrecht
8743301105 Merge branch 'Aufinal/let_it_go' into 'master'
Change le fonctionnement du gel de compte

Closes #280

See merge request klub-dev-ens/gestioCOF!493
2021-06-15 15:24:44 +00:00
Ludovic Stephan
6a11139588 Fix tests 2021-06-15 16:52:56 +02:00
Ludovic Stephan
a34b83c236 Use backend to enforce frozen accounts 2021-06-15 16:52:50 +02:00
Ludovic Stephan
02584982f6 gnagnagna 2021-06-15 14:48:35 +02:00
Ludovic Stephan
7bf0c5f09e Fix frozen forms 2021-06-15 14:07:43 +02:00
Ludovic Stephan
b9aaf6a19c Fix test 2021-06-15 14:07:43 +02:00
Ludovic Stephan
16dee0c143 Remove print 2021-06-15 14:07:43 +02:00
Ludovic Stephan
a947b9d3f2 Fix decorator 2021-06-15 14:07:43 +02:00
Ludovic Stephan
93d283fecb Remove unused permission 2021-06-15 14:07:43 +02:00
Ludovic Stephan
63738e8e02 Frozen error display 2021-06-15 14:07:40 +02:00
Ludovic Stephan
1e44550e12 New frozen function 2021-06-15 14:05:39 +02:00
Ludovic Stephan
4136cb6868 Unfreeze every account 2021-06-15 14:05:39 +02:00
Ludovic Stephan
99809209e0 Change les permissions pour geler/dégeler un compte 2021-06-15 14:05:39 +02:00
Ludovic Stephan
0351f6728b CHANGELOG 2021-05-05 02:10:44 +02:00
Ludovic Stephan
7efc7e6b94 Merge branch 'thubrecht/autocomplete-css' into 'master'
On modifie le curseur quand on survole un compte dans l'autocomplete

See merge request klub-dev-ens/gestioCOF!499
2021-05-05 00:06:15 +00:00
7d21a5a1fc On supprime des sélecteurs inutiles 2021-05-05 01:57:46 +02:00
dba785bf13 Pareil, mais dans gestiocof 2021-05-05 00:59:47 +02:00
71878caf2c On modifie le curseur quand on survole un compte dans l'autocomplete 2021-05-05 00:03:52 +02:00
Tom Hubrecht
db42028228 Merge branch 'Aufinal/backbone' into 'master'
Refactor le JS de K-Psul via Backbone : 2e étape

Closes #267 and #290

See merge request klub-dev-ens/gestioCOF!400
2021-05-04 21:17:09 +00:00
Ludovic Stephan
7171a7567c Remove double negative 2021-05-04 21:43:48 +02:00
Ludovic Stephan
339223bec0 Black 2021-05-04 18:12:47 +02:00
Ludovic Stephan
d62a8d61de Search fix and CSS update 2021-05-04 17:52:13 +02:00
Ludovic Stephan
f6c83dc692 FINALLY fix this f***ing whitespace mess 2021-05-04 17:52:13 +02:00
Ludovic Stephan
a984d1fd6f Clarity 2021-05-04 17:52:13 +02:00
Ludovic Stephan
f901ea9396 Remove useless kpsul.html code 2021-05-04 17:52:13 +02:00
Ludovic Stephan
17d96f1775 New account manager logic 2021-05-04 17:52:13 +02:00
Ludovic Stephan
c10e5fe45c Refactor Account model a bit 2021-05-04 17:52:13 +02:00
Martin Pépin
9bbe3f50cb
Update CHANGELOG.md 2021-04-18 18:17:38 +02:00
Martin Pépin
1f4a4ec76f
Update CHANGELOG.md 2021-04-18 17:46:54 +02:00
Martin Pepin
2befa584aa Merge branch 'Aufinal/remove_24' into 'master'
Remove limit for purchases

Closes #289

See merge request klub-dev-ens/gestioCOF!498
2021-04-18 08:35:24 +00:00
Ludovic Stephan
b48d32f4bc Remove limit for purchases 2021-04-16 16:42:12 +02:00
Tom Hubrecht
e36e88e77a Merge branch 'Aufinal/no_warnings' into 'master'
Fix : plus de warnings chelous pendant les tests

See merge request klub-dev-ens/gestioCOF!495
2021-03-17 22:12:01 +00:00
Tom Hubrecht
8e9fc341ca Merge branch 'Aufinal/forbidden_kfet' into 'master'
Test plus général pour l'erreur de permissions K-Fêt

See merge request klub-dev-ens/gestioCOF!491
2021-03-16 23:22:18 +01:00
Ludovic Stephan
c14c2d54a5 More general forbidden test 2021-03-16 23:04:03 +01:00
Tom Hubrecht
6adfaba8e9 Merge branch 'Aufinal/account_update_forms' into 'master'
Refactor la vue `account_update`

Closes #232 and #119

See merge request klub-dev-ens/gestioCOF!490
2021-03-16 23:02:12 +01:00
Tom Hubrecht
06005014f9 Merge branch 'Aufinal/delete_balance_offset' into 'master'
Supprime le champ `balance_offset` et harmonise la gestion des négatifs

Closes #281

See merge request klub-dev-ens/gestioCOF!489
2021-03-16 22:42:13 +01:00
Ludovic Stephan
4268a30d51 CHANGELOG 2021-03-16 22:10:33 +01:00
Tom Hubrecht
c71e6d22bf Merge branch 'Aufinal/hcaptcha' into 'master'
Remplace recaptcha par hcaptcha

Closes #262

See merge request klub-dev-ens/gestioCOF!497
2021-03-16 22:00:48 +01:00
Ludovic Stephan
4df3ef4dd9 Fix secret import 2021-03-04 23:28:55 +01:00
Ludovic Stephan
af95e64344 TODO de prod 2021-03-04 23:14:10 +01:00
Ludovic Stephan
ac8ad15ad1 Fix tests: mock captcha clean method 2021-03-04 18:30:51 +01:00
Ludovic Stephan
47dd078b6a Remplace recaptcha par hcaptcha 2021-03-04 17:56:42 +01:00
Ludovic Stephan
472a44c30f Remove useless buttons 2021-03-03 23:11:39 +01:00
Ludovic Stephan
b72ea9ebf9 Forgot a warning 2021-02-28 02:56:12 +01:00
Ludovic Stephan
f9958e4da0 Fix : plus de warnings chelous pendant les tests 2021-02-28 02:35:40 +01:00
Ludovic Stephan
47f406e09e Fix tests 2021-02-23 22:52:39 +01:00
Ludovic Stephan
1450b65dcd Rework complet de account_update 2021-02-23 22:52:39 +01:00
Ludovic Stephan
aac94afcd0 Améliore le formulaire de mdp K-Fêt 2021-02-23 22:52:39 +01:00
Ludovic Stephan
209360f535 Delete self-update form 2021-02-23 22:52:39 +01:00
Ludovic Stephan
b224fedf28 Fix frozen account display 2021-02-23 22:52:39 +01:00
Ludovic Stephan
1ab071d16e LINT 2021-02-23 22:52:27 +01:00
Ludovic Stephan
1cf6f6f3e7 Fix migration conflict 2021-02-23 22:41:04 +01:00
Ludovic Stephan
a421bec625 Fix templates 2021-02-23 22:33:00 +01:00
Ludovic Stephan
4e758fbba0 Delete balance_offset field 2021-02-23 22:33:00 +01:00
Martin Pepin
2350109a33 Merge branch 'Aufinal/migration_checks' into 'master'
CI: ne lance `migration_checks` que sur nos propres apps

See merge request klub-dev-ens/gestioCOF!488
2021-02-23 21:18:36 +01:00
Ludovic Stephan
778637d60e Merge branch 'dodo/limit-history-acces' into 'master'
Limit kfet history access

See merge request klub-dev-ens/gestioCOF!487
2021-02-20 22:58:59 +01:00
Dorian Lesbre
23f7865140 Switch back from config to settings 2021-02-20 20:59:54 +01:00
Dorian Lesbre
cc7c4306f4 Added change description to CHANGELOG 2021-02-20 19:10:49 +01:00
Dorian Lesbre
1183e50f60 Fixed tests 2021-02-19 13:48:12 +01:00
Dorian Lesbre
a8de7e0ae0 makemigrations 2021-02-19 13:38:36 +01:00
Dorian Lesbre
30a39ef2f6 Switch from account test to user test 2021-02-19 12:16:43 +01:00
Dorian Lesbre
9a635148bb Switched from datetime.today() to timezone.now() 2021-02-19 12:13:23 +01:00
Dorian Lesbre
4b95b65be2 Removed unused import 2021-02-19 11:55:18 +01:00
Dorian Lesbre
884ec2535b Fixed stupid errors 2021-02-19 11:51:48 +01:00
Dorian Lesbre
beba3052dd Switched from hardcoded settings to config 2021-02-19 11:46:42 +01:00
Dorian Lesbre
46242ad2c0 Added separate permission for chef/trez 2021-02-19 10:48:24 +01:00
Dorian Lesbre
fa8c57269c Added help_text to history form 2021-02-19 10:32:12 +01:00
Dorian Lesbre
b97bc8bfa8 Changed accoutn comparaison from id to equality 2021-02-19 10:26:05 +01:00
Dorian Lesbre
89fc309c01 Returned 403 on dubious history request 2021-02-19 10:18:47 +01:00
Ludovic Stephan
d7367476bc Fix app names 2021-02-18 17:41:52 +01:00
Ludovic Stephan
7297baaf7e Only check migrations for custom apps 2021-02-18 17:30:28 +01:00
Ludovic Stephan
8bf7914728 Merge branch 'kerl/fix_bds_production_urls' into 'master'
Hotfixes appliqués en production pour GestioBDS

See merge request klub-dev-ens/gestioCOF!484
2021-02-18 17:02:21 +01:00
Ludovic Stephan
71fbbcff8a Merge branch 'kerl/rm_login_clipper' into 'master'
Admin : supprime la colonne login_clipper dans la liste des Users

See merge request klub-dev-ens/gestioCOF!486
2021-02-18 17:01:05 +01:00
Dorian Lesbre
9303772f9a Renamed week_ago => history_limit and removed print 2021-02-10 22:19:52 +01:00
559b36b6f0 Limite le datepicker pour ne pas demander plus de temps que possible dans l'historique 2021-02-10 22:13:50 +01:00
Dorian Lesbre
fbafdb7134 Added kfet history date limit when not accessing own account 2021-02-10 21:32:44 +01:00
Martin Pépin
a53bd94737
admin: rm the login_clipper column in the user list 2021-02-09 22:42:49 +01:00
Ludovic Stephan
46ef12309a Merge branch 'kerl/rename_cof_gestioAsso' into 'master'
Renomme le dossier cof/ en gestioasso/

See merge request klub-dev-ens/gestioCOF!485
2021-02-08 19:29:07 +01:00
Martin Pépin
4f60ba35eb
Update the settings' docstrings 2021-02-08 19:19:54 +01:00
Martin Pépin
f29b3f0187
Make "GestioBDS" appear in the README 2021-02-07 18:11:17 +01:00
Martin Pépin
aa3462aaee
Update the CI config wrt the new project name 2021-02-07 18:11:06 +01:00
Martin Pépin
7c35357060
Fix a reverse url resolution on the BDS home page 2021-02-07 17:39:28 +01:00
Martin Pépin
726b3f55a0
Rename the cof/ folder to gestioasso/
This is a much more sensible name since it contains configuration
applicable to both GestioCOF and GestioBDS.

The next logical step would be to rename the `gestioncof/` folder to
`cof/`.
2021-02-07 17:17:15 +01:00
Martin Pepin
63eeb5b7a9 Merge branch 'Aufinal/single_checkout' into 'master'
Fix "Checkout is not iterable" error

See merge request klub-dev-ens/gestioCOF!483
2021-02-07 16:40:48 +01:00
Martin Pépin
7081380058
Only redirect / → /gestion in development 2021-02-07 16:29:47 +01:00
Ludovic Stephan
288de95c49 Checkout form is single-option now 2021-02-06 18:58:25 +01:00
Martin Pépin
9a01d1e877
CHANGELOG: add missing items in the v0.9 release 2021-02-06 17:17:47 +01:00
Martin Pépin
10746c0469
Version 0.9 2021-02-06 17:01:22 +01:00
Basile Clement
5c8eca15b6 Merge branch 'kerl/admin_autocomplete' into 'master'
Admin: on utilise la recherche builtin de Django

See merge request klub-dev-ens/gestioCOF!476
2021-02-04 22:16:27 +01:00
Martin Pepin
ef64f9ce5c Merge branch 'Buro_rights_by_BDS' into 'master'
Added basic buro right handling while updating member

See merge request klub-dev-ens/gestioCOF!482
2021-01-30 15:06:52 +01:00
Alseidon
9762838921
Basic Buro right handling - minor corrections 2021-01-30 14:59:04 +01:00
Alseidon
bf6d6d6430
Added basic buro right handling while updating member 2021-01-30 14:57:24 +01:00
Martin Pepin
ba9aa06b4f Merge branch 'dodo/bds_export_csv' into 'master'
Dodo/bds export csv

Closes #285

See merge request klub-dev-ens/gestioCOF!481
2021-01-29 19:38:55 +01:00
Dorian Lesbre
880dc31353 Update CHANGELOG.md 2021-01-29 09:37:37 +01:00
Dorian Lesbre
9a78fca507 Switched to named url 2021-01-29 09:34:56 +01:00
Ludovic Stephan
4bc56d34e0 Fix tests 2021-01-21 21:08:57 +01:00
Ludovic Stephan
79f0757e9f Fix kfet stats 2021-01-21 20:55:23 +01:00
Dorian Lesbre
a2eed13717 Added download button to home template 2021-01-21 20:38:15 +01:00
Dorian Lesbre
830aba984e Added bds/members to export members list as CSV 2021-01-21 20:32:36 +01:00
Ludovic Stephan
33319cfe76 Merge branch 'newbie' into 'master'
Reset comptes COF

See merge request klub-dev-ens/gestioCOF!479
2021-01-07 09:32:33 +01:00
Alseidon
44b001bd3c Satisfy Lord Black 2021-01-07 09:19:56 +01:00
Martin Pepin
ab9d95055e Merge branch 'Tragicus/kfetTriArticles' into 'master'
kfet articles vendus en premier dans inventaire et commandes

Closes #219

See merge request klub-dev-ens/gestioCOF!477
2021-01-06 21:41:39 +01:00
Martin Pépin
40391d8814
Update CHANGELOG.md 2021-01-06 21:32:41 +01:00
Martin Pépin
681507f211
Happy new year! 2021-01-06 21:31:47 +01:00
Ludovic Stephan
7f133316a4 Merge branch 'Aufinal/black_required' into 'master'
Add black to requirements-devel

See merge request klub-dev-ens/gestioCOF!480
2020-12-17 18:58:59 +01:00
Ludovic Stephan
0bdbcf59fa Add black to requirements-devel 2020-12-10 16:46:53 +01:00
Ludovic Stephan
0bad404b71 Merge branch 'Dodo/date_fermeture_bda' into 'master'
Ajout date de fermeture de tirage BDA sur la page d'acceuil

Closes #172

See merge request klub-dev-ens/gestioCOF!478
2020-12-10 14:20:56 +01:00
Alseidon
c100f2fc8d Version 1.1 remise à zéro comptes COF 2020-12-09 23:00:00 +01:00
Alseidon
ba74779f95 Version 1.0 remise à zéro comptes COF 2020-12-09 22:40:32 +01:00
Quentin VERMANDE
035bbe68a5 make black happy 2020-12-09 22:22:12 +01:00
Alseidon
319db68655 Ra0 effective 2020-12-09 22:11:21 +01:00
Quentin VERMANDE
9d2c13e67c kfetTriArticles 2020-12-09 22:03:54 +01:00
Alseidon
73c068055b Remise à zéro basique comptes COF 2020-12-09 21:57:40 +01:00
Dorian Lesbre
30ce8d13af Ajout date de fermeture de tirage BDA sur la page d'acceuil 2020-12-09 21:16:49 +01:00
Ludovic Stephan
340f8f16a7 Merge branch 'kerl/fix_vagrant' into 'master'
Mise à jour du setup vagrant

See merge request klub-dev-ens/gestioCOF!475
2020-12-07 20:56:09 +01:00
Martin Pépin
0ce1e62586
Fichier bootstrap.sh mieux commenté 2020-12-07 20:09:25 +01:00
Martin Pépin
783fe1de32
Liste des paquets dans un fichier séparé 2020-12-07 20:09:24 +01:00
Martin Pépin
49fde85187
Admin: on utilise la recherche builtin de Django 2020-12-04 19:33:17 +01:00
Martin Pepin
479e751b7c Merge branch 'Aufinal/can_resell_paid' into 'master'
On peut revendre une place dès qu'on l'a payée

Closes #277

See merge request klub-dev-ens/gestioCOF!473
2020-12-04 18:01:22 +01:00
Martin Pépin
f2c1ff2abd
Update CHANGELOG 2020-12-04 17:53:56 +01:00
Ludovic Stephan
8b73460165
Make flake8 happy 2020-12-04 17:52:33 +01:00
Ludovic Stephan
411d7e7dce
On peut revendre une place qu'on a payée 2020-12-04 17:52:21 +01:00
Martin Pepin
f952d50b12 Merge branch 'Aufinal/email_validation' into 'master'
Utilise un EmailField pour valider des emails

See merge request klub-dev-ens/gestioCOF!471
2020-12-04 17:29:07 +01:00
Ludovic Stephan
7324a72e6e Merge branch 'kerl/make_kfetloaddevdata_idempotent' into 'master'
Rend kfetloaddevdata idempotent

See merge request klub-dev-ens/gestioCOF!474
2020-12-04 17:17:38 +01:00
Martin Pépin
ad73cc987d
CHANGELOG 2020-12-04 17:16:35 +01:00
Ludovic Stephan
e9e0c79b40
Migration 2020-12-04 17:15:15 +01:00
Ludovic Stephan
badee498a3
Use EmailField for email field 2020-12-04 17:15:15 +01:00
Martin Pepin
72cd55716b Merge branch 'Aufinal/inventory_delete' into 'master'
Permet l'annulation d'un inventaire

Closes #251

See merge request klub-dev-ens/gestioCOF!457
2020-12-04 17:13:32 +01:00
Martin Pépin
a7cbd2d451
CHANGELOG 2020-12-04 17:02:36 +01:00
Ludovic Stephan
b9699637aa
Message de confirmation plus clair 2020-12-04 17:01:25 +01:00
Ludovic Stephan
521be6db85
Tests 2020-12-04 17:01:25 +01:00
Ludovic Stephan
f3701d91fc
Url and template for InventoryDeleteView 2020-12-04 17:01:25 +01:00
Ludovic Stephan
59dacda37d
Inventory deletion view 2020-12-04 17:01:25 +01:00
Martin Pépin
7f58b5fa00
Vagrant: toutes les units systemd sont là 2020-12-04 16:58:25 +01:00
Martin Pépin
df222f18a3
Update the vagrant config → should work now 2020-12-04 16:10:27 +01:00
Martin Pépin
5d22a4cac4
Rend kfetloaddevdata idempotent
Problème :

Le script assigne des trigrammes 001, 002, 003, etc aux comptes COF des
Gaulois et des Romains en utilisant l'ordre du queryset
CofProfile.objects.all().
L'ordre des comptes dans le queryset n'est pas spécifié et peut varier
d'une exécution à l'autre, ça pose problème dans la suite :

Account.objects.get_or_create(trigramme=trigramme, cofprofile=profile)

Cette command essaie de créer un nouveau trigramme pour certains comptes
quand l'ordre change.

Solution :

Ordonner le queryset.
2020-12-04 12:44:09 +01:00
Martin Pépin
cc3a436750
Version 0.8 2020-12-03 20:22:31 +01:00
Ludovic Stephan
404d3f4f4c Merge branch 'Aufinal/spectacle_paid' into 'master'
Fix `paid` field in `bda/spectacles`

See merge request klub-dev-ens/gestioCOF!472
2020-10-29 11:15:41 +01:00
Ludovic Stephan
43fcdc8526 Fix paid field in bda/spectacles 2020-10-28 14:35:45 +01:00
Ludovic Stephan
35f896b40f CHANGELOG 2020-10-23 10:25:23 +02:00
Ludovic Stephan
fb1a38cff3 Merge branch 'Aufinal/bda_admin_misc' into 'master'
Ergonomie de l'admin du BdA

Closes #276

See merge request klub-dev-ens/gestioCOF!469
2020-10-23 10:11:15 +02:00
Ludovic Stephan
f88795a60e Use same qset for every field 2020-10-22 19:34:59 +02:00
Ludovic Stephan
0ffebdf82f Merge branch 'Aufinal/meta' into 'master'
Fix : autocomplétion sans classe `Meta`

See merge request klub-dev-ens/gestioCOF!468
2020-10-22 18:54:17 +02:00
Ludovic Stephan
147b8514ef Limite les select au tirage concerné 2020-10-21 18:22:48 +02:00
Ludovic Stephan
d535cf24a3 Migration 2020-10-21 18:22:19 +02:00
Ludovic Stephan
84dab59c72 Ordre des participants + unicité 2020-10-21 18:22:05 +02:00
Ludovic Stephan
22cf0d403e Permet d'archiver un tirage 2020-10-21 18:21:40 +02:00
Ludovic Stephan
a525cffaff Fix participant autocomplete 2020-10-21 16:02:01 +02:00
Martin Pepin
1af602c9f7 Merge branch 'Aufinal/listing' into 'master'
Indique si les places sont sur listing

Closes #213

See merge request klub-dev-ens/gestioCOF!467
2020-09-22 21:48:40 +02:00
Martin Pépin
8a17aa2caa
Update CHANGELOG 2020-09-22 21:35:50 +02:00
Ludovic Stephan
7a9d96d83a
Indique si les places sont sur listing 2020-09-22 21:34:53 +02:00
Martin Pépin
3869c02dfa
Merge branch 'Aufinal/fix-fa-again' into master 2020-09-22 21:23:49 +02:00
Martin Pépin
848eb2274a
Update CHANGELOG 2020-09-22 21:23:20 +02:00
Ludovic Stephan
ebd8b7ccdb
Fix fa path in petitscours 2020-09-22 21:22:16 +02:00
Martin Pépin
3c6ab35390
Update CHANGELOG 2020-09-22 21:20:14 +02:00
Martin Pepin
57901c0013 Merge branch 'Aufinal/stat_2' into 'master'
Repassage sur les stats

Closes #246 and #255

See merge request klub-dev-ens/gestioCOF!462
2020-09-22 21:06:46 +02:00
Ludovic Stephan
d172dad0ab Merge branch 'thubrecht/creation-compte' into 'master'
Empêche la modification des informations COF lors de la création d'un compte K-Psul

Closes #230

See merge request klub-dev-ens/gestioCOF!464
2020-09-21 16:43:17 +02:00
Tom Hubrecht
d0b7000747 Empêche la modification des informations COF lors de la création d'un compte K-Psul 2020-09-19 19:14:44 +02:00
Martin Pepin
84ff0d7182 Merge branch 'Aufinal/history_form' into 'master'
On utilise un vrai formulaire pour l'historique

Closes #242

See merge request klub-dev-ens/gestioCOF!461
2020-09-17 21:04:42 +02:00
Martin Pepin
600927b21c Merge branch 'Aufinal/rip-custommail' into 'master'
Supprime `custommail` de gestioCOF

Closes #227

See merge request klub-dev-ens/gestioCOF!459
2020-09-17 20:22:11 +02:00
Ludovic Stephan
d965050563 Fix tests again 2020-09-16 19:31:10 +02:00
Ludovic Stephan
a14c9d9574 Fix tests 2020-09-16 19:19:29 +02:00
Ludovic Stephan
8f9c94fe10 Plein de nettoyage partout 2020-09-16 17:16:49 +02:00
Ludovic Stephan
46f447ec5d Formulaires pour nettoyage 2020-09-16 17:16:14 +02:00
Ludovic Stephan
4dbf11f91e Template tweaks 2020-09-15 21:10:36 +02:00
Martin Pepin
569ce0ba25 Merge branch 'Aufinal/open_password' into 'master'
Fix : fermeture manuelle de la K-Fêt avec mot de passe

Closes #183

See merge request klub-dev-ens/gestioCOF!460
2020-09-15 20:22:05 +02:00
Ludovic Stephan
aa955a06ef Fin des adaptations 2020-09-15 20:05:54 +02:00
Ludovic Stephan
a9eb32217f Adapte history.js pour serialize() 2020-09-15 20:05:32 +02:00
Ludovic Stephan
c7998f56f0 Datetimepicker tweaks 2020-09-15 20:05:06 +02:00
Ludovic Stephan
a6e58dcd68 On utilise le render par défaut 2020-09-15 20:04:35 +02:00
Ludovic Stephan
49591fa67e Use form to clean data 2020-09-15 20:03:37 +02:00
Ludovic Stephan
9f9724b1d1 Arrow function works now 2020-09-15 19:57:27 +02:00
Ludovic Stephan
205dc93f4b FilterHistoryForm est un formulaire décent 2020-09-15 19:40:45 +02:00
Martin Pepin
7f6d4527ed Merge branch 'Aufinal/petitscours_uniqueness' into 'master'
Fix : KeyError sur les petits cours

See merge request klub-dev-ens/gestioCOF!458
2020-09-15 19:38:15 +02:00
Ludovic Stephan
43a2f8db53 Use arrow functions everywhere for consistency 2020-09-15 16:44:32 +02:00
Ludovic Stephan
ba4cc01ed4 Fix formatting 2020-09-15 16:37:41 +02:00
Ludovic Stephan
11d94ecba8 Fix this shenanigans 2020-09-15 16:34:19 +02:00
Ludovic Stephan
82d58d23c9 Remove all traces of custommail 2020-09-15 11:49:32 +02:00
Ludovic Stephan
6377dd5c95 BdA : tests 2020-09-15 11:49:18 +02:00
Ludovic Stephan
f364928004 Remove custommail in bda 2020-09-15 11:49:05 +02:00
Ludovic Stephan
dc070278f7 Gestioncof : tests 2020-09-15 11:48:36 +02:00
Ludovic Stephan
561a121e04 Remove custommail in gestioncof 2020-09-15 11:48:21 +02:00
Ludovic Stephan
b03cf05ef7 Petits cours : tests 2020-09-15 11:47:53 +02:00
Ludovic Stephan
edf6a03bc4 Phase out custommail in petitscours 2020-09-15 11:47:28 +02:00
Ludovic Stephan
eb3cba31a7 Emails as text files 2020-09-15 11:15:12 +02:00
Ludovic Stephan
1ffda1a5c4 Better uniqueness checks 2020-09-14 11:40:21 +02:00
Martin Pépin
2bc97a115c
Version 0.7.2 2020-09-08 20:06:26 +02:00
Ludovic Stephan
b8072f4346 Merge branch 'kerl/404' into 'master'
Nouvelle page 404

See merge request klub-dev-ens/gestioCOF!454
2020-09-07 20:33:43 +02:00
Martin Pépin
cedd3cf816
404: english text looks better in italic 2020-09-07 20:16:16 +02:00
Martin Pépin
2c833daa7f
404.html: English version + tighter header 2020-09-07 20:10:34 +02:00
Martin Pepin
2a05c2247c
Apply suggestion to gestioncof/templates/404.html 2020-09-07 20:10:34 +02:00
Martin Pépin
c957ab2b72
Add K-Psul on the 404 page 2020-09-07 20:10:34 +02:00
Martin Pépin
98cce25f4c
cofsite-looking 404 ? 2020-09-07 20:10:26 +02:00
Martin Pepin
ba6ddfc516 Merge branch 'Aufinal/kfet-auth' into 'master'
Groupes et perms K-Fêt

See merge request klub-dev-ens/gestioCOF!438
2020-09-07 20:09:19 +02:00
Ludovic Stephan
c5d7eb9d30 Move permission handling to loadkfetdevdata 2020-09-07 14:57:41 +02:00
Ludovic Stephan
d3185f25c3 Black 2020-09-07 14:57:41 +02:00
Ludovic Stephan
007b5006d4 Use convenience imports 2020-09-07 14:57:41 +02:00
Ludovic Stephan
2d36c85085 Fix dev data 2020-09-07 14:57:41 +02:00
Ludovic Stephan
d6fa738a25 Fix tests 2020-09-07 14:57:41 +02:00
Ludovic Stephan
c145191e55 Use new models and mixins 2020-09-07 14:57:41 +02:00
Ludovic Stephan
91852bd4a0 Template fixes 2020-09-07 14:57:41 +02:00
Ludovic Stephan
6f5fa19fc3 M2M form mixin 2020-09-07 14:57:41 +02:00
Ludovic Stephan
e92d50593c New models 2020-09-07 14:57:41 +02:00
Ludovic Stephan
0590bc3aab Merge branch 'kerl/kfet_autocomplete_fix' into 'master'
Remets le lien pour inscrire des nouveaux comptes en K-Fêt sur la page d'autocomplétion

See merge request klub-dev-ens/gestioCOF!456
2020-09-07 14:43:04 +02:00
Martin Pépin
3286ad09df
Update CHANGELOG 2020-09-07 11:35:25 +02:00
Martin Pépin
3da0a613f7
K-Fêt autocompletion shows the 'new user' link 2020-09-07 11:32:28 +02:00
Ludovic Stephan
34be9e2393 Merge branch 'kerl/fix_exte_login_bug' into 'master'
Meilleure gestion des erreurs dans le formulaire de login Exté → pas de crash

See merge request klub-dev-ens/gestioCOF!455
2020-09-06 20:59:48 +02:00
Martin Pépin
97bdeed97a
Prevent a crash in exte login form error handling 2020-09-05 23:53:31 +02:00
Martin Pépin
8016b16904
Version 0.7.1 2020-09-05 00:02:28 +02:00
Ludovic Stephan
6e8926595d Merge branch 'kerl/gestion_prefix' into 'master'
Ajoute le préfixe /gestion dans toutes les urls sauf celles de la K-Fêt et de Wagtail

Closes #256

See merge request klub-dev-ens/gestioCOF!450
2020-09-02 23:45:34 +02:00
Ludovic Stephan
a9b6bc65a2 Merge branch 'kerl/discard_weird_ldap_users' into 'master'
Ignore les comptes LDAP bizarres (e.g. root)

See merge request klub-dev-ens/gestioCOF!453
2020-09-02 23:44:33 +02:00
Martin Pépin
b1fd6e6021
Discard the (weird) ldap accounts that have no uid 2020-09-02 21:34:21 +02:00
Martin Pepin
576d43f44d Merge branch 'sakarah/club-email-bug' into 'master'
Replace all "pont" by "." in COF clubs emails

See merge request klub-dev-ens/gestioCOF!452
2020-09-02 20:42:36 +02:00
Martin Pépin
c55a2c8c8e
Update changelog 2020-09-02 20:35:17 +02:00
Guillaume Bertholon
65c979ea59
Replace all "pont" by "." in COF clubs emails
This patches wrongly displayed "Contact : fromages@lists.enspontfr" on
https://cof.ens.fr/gestion/sitecof/annuaires-des-clubs/
2020-09-02 20:34:25 +02:00
Martin Pépin
dcd592ed11
Fix 100 tests wrt. 754a0b70e (big url changes) 2020-09-02 20:28:19 +02:00
Martin Pépin
e401303a08
User-friendly redirect : / → /gestion 2020-09-02 20:25:46 +02:00
Martin Pépin
2b72f3b40b
All pages go under /gestion except wagtail & k-fet 2020-09-02 20:23:11 +02:00
Martin Pepin
2aae281120 Merge branch 'Aufinal/urls' into 'master'
Améliore les URLs de gestiocof

Closes #265

See merge request klub-dev-ens/gestioCOF!439
2020-09-02 20:15:12 +02:00
Ludovic Stephan
1387da3b54
black 20 2020-09-02 20:06:28 +02:00
Ludovic Stephan
e868e6eb18
No bds prefix for prod 2020-09-02 20:06:28 +02:00
Ludovic Stephan
fc988e3fad
Fix isort 2020-09-02 20:06:28 +02:00
Ludovic Stephan
205b5c206b
Fix tests 2020-09-02 20:06:28 +02:00
Ludovic Stephan
ab9b4d14ef
Some changes to mega urls 2020-09-02 20:06:28 +02:00
Ludovic Stephan
5f8b8661bf
Better URL management 2020-09-02 20:06:28 +02:00
Ludovic Stephan
858a0c61e2
Rend à gestiocof ce qui est à gestiocof 2020-09-02 20:06:27 +02:00
Martin Pepin
12d3ef0c02 Merge branch 'Aufinal/wakemeup' into 'master'
On est en septembre !

See merge request klub-dev-ens/gestioCOF!451
2020-09-02 19:57:04 +02:00
Ludovic Stephan
da40ed1d8c Migration 2020-09-01 15:27:28 +02:00
Martin Pépin
f10bd1eea2
Update changelog 2020-08-30 12:48:51 +02:00
Martin Pepin
8576023b42 Merge branch 'Evarin/css-sitecof' into 'master'
Améliorations CSS + directoryentrypage sitecof encore

See merge request klub-dev-ens/gestioCOF!447
2020-08-30 12:47:11 +02:00
Evarin
24eaaa277f Sitecof : Affiche titre des pages dans <title> 2020-08-30 12:11:13 +02:00
Evarin
8f4e3bb048 isort linting migration 2020-08-29 23:39:31 +02:00
Evarin
7775e45b60 Black linting migration 2020-08-29 23:23:52 +02:00
Evarin
b3ada0eb89 Sitecof css modifs mineures 2020-08-29 23:21:20 +02:00
Evarin
74b9721fbd Sitecof : Champ libre dans directoryentry 2020-08-29 23:15:28 +02:00
Evarin
7db75c0060 Sitecof CSS ++ 2020-08-29 23:14:19 +02:00
Martin Pepin
d96b3d26b6 Merge branch 'Evarin/css-sitecof' into 'master'
Améliorations mineures CSS sitecof

See merge request klub-dev-ens/gestioCOF!446
2020-08-29 22:54:29 +02:00
Evarin
72210e1980 Sitecof : améliogrations CSS 2020-08-29 22:18:32 +02:00
Evarin
359f85a42d Sitecof : CSS fixes 2020-08-29 21:34:28 +02:00
Martin Pépin
95cac47f4e
Version 0.7 2020-08-29 19:28:38 +02:00
Martin Pepin
7dc32add35 Merge branch 'Aufinal/kfet_autocomplete' into 'master'
Rajoute un nombre min de caractères pour la création d'un compte

See merge request klub-dev-ens/gestioCOF!440
2020-08-29 12:25:43 +02:00
Martin Pepin
0011cfe8f7 Merge branch 'Aufinal/bds_perms' into 'master'
Le groupe BDS a des permissions normales

Closes #263

See merge request klub-dev-ens/gestioCOF!444
2020-08-29 12:24:35 +02:00
Ludovic Stephan
5a7c4f64d5 changelog 2020-08-28 18:46:13 +02:00
Ludovic Stephan
46893a8df5 Min chars for autocompletion 2020-08-28 18:45:07 +02:00
Ludovic Stephan
81b45f74e3 changelog 2020-08-28 18:42:37 +02:00
Ludovic Stephan
c6dfcea5e2 Remove signals import 2020-08-28 18:41:40 +02:00
Ludovic Stephan
198e456c22 Fix BDS group perms 2020-08-28 18:41:40 +02:00
Martin Pepin
1518f4c703 Merge branch 'Aufinal/misc_bds' into 'master'
Quelques améliorations pour le BDS

Closes #270

See merge request klub-dev-ens/gestioCOF!443
2020-08-28 18:38:49 +02:00
Martin Pépin
2d59565f61
Update changelog 2020-08-28 18:23:20 +02:00
Ludovic Stephan
d3384dc5fc
Remove context processor 2020-08-28 18:23:20 +02:00
Ludovic Stephan
e7cc705350
Add member count to home 2020-08-28 18:23:20 +02:00
Ludovic Stephan
8fa635773c
Notifications are closable 2020-08-28 18:23:20 +02:00
Ludovic Stephan
1ac3e0f976
Plug logout link 2020-08-28 18:23:19 +02:00
Ludovic Stephan
f811230c25
Add comment field 2020-08-28 18:23:19 +02:00
Martin Pepin
74c3afe9ca Merge branch 'Aufinal/black20' into 'master'
Fix la CI pour black

See merge request klub-dev-ens/gestioCOF!445
2020-08-28 18:18:15 +02:00
Ludovic Stephan
0875ef1278 Black v20 2020-08-28 18:00:54 +02:00
Martin Pepin
1d707aad41 Merge branch 'Aufinal/delete_bds_user' into 'master'
Possibilité de supprimer un utilisateur sur gestioBDS

Closes #271

See merge request klub-dev-ens/gestioCOF!442
2020-08-25 20:17:51 +02:00
Martin Pépin
fef19024d8
Update changelog 2020-08-25 20:04:26 +02:00
Ludovic Stephan
566e968849
Fix cance button 2020-08-25 20:04:26 +02:00
Ludovic Stephan
55c69ae42b
Styling 2020-08-25 20:04:26 +02:00
Ludovic Stephan
40839458a5
Form logic for user deletion 2020-08-25 20:04:26 +02:00
Ludovic Stephan
a259dd524f
UserDelete view 2020-08-25 20:04:26 +02:00
Ludovic Stephan
85c750d380
Delete unused template 2020-08-25 20:04:26 +02:00
Martin Pépin
826e45f619
Move CaptchaFrom from views.py to forms.py 2020-08-25 19:24:54 +02:00
Martin Pépin
5989f65154
Fix linting issues 2020-08-25 19:24:02 +02:00
Martin Pepin
2cf0ccbb6b Merge branch 'Aufinal/ldap_mail' into 'master'
Utilise le mail LDAP lors de l'inscription d'utilisateurs

Closes #268

See merge request klub-dev-ens/gestioCOF!437
2020-08-25 19:08:25 +02:00
Ludovic Stephan
1677768177 Merge branch 'kerl/bds_authens' into 'master'
Authens pour le BDS

See merge request klub-dev-ens/gestioCOF!441
2020-08-24 15:38:24 +02:00
Martin Pépin
7a7e02adab
Bump authens to 0.1b0 2020-08-24 15:30:50 +02:00
Martin Pépin
62d26560d9
Fix BDS {MEDIA,STATIC}_{URL,ROOT} 2020-08-24 14:56:26 +02:00
Martin Pépin
df9639715b
Move COF-specific settings (channels) to common.py 2020-08-24 14:55:27 +02:00
Evarin
72237fef60 Sitecof : captcha pour les listes mail 2020-08-22 12:34:08 +02:00
Ludovic Stephan
8fa07bb845 Fix tests 2020-08-03 14:54:58 +02:00
Ludovic Stephan
7931f50611 Use ldap email 2020-08-03 14:30:21 +02:00
Ludovic Stephan
910536c6d3 Add email to ldap autocomplete 2020-08-03 14:30:12 +02:00
Martin Pépin
3d830884b1
Use authens in GestioBDS 2020-07-30 12:04:04 +02:00
379 changed files with 6521 additions and 3673 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

2
.gitignore vendored
View file

@ -5,6 +5,7 @@ cof/settings.py
settings.py settings.py
*~ *~
venv/ venv/
.venv/
.vagrant .vagrant
/src /src
media/ media/
@ -19,3 +20,4 @@ media/
# VSCode # VSCode
.vscode/ .vscode/
.direnv

View file

@ -17,12 +17,15 @@ variables:
# psql password authentication # psql password authentication
PGPASSWORD: $POSTGRES_PASSWORD PGPASSWORD: $POSTGRES_PASSWORD
# apps to check migrations for
MIGRATION_APPS: "bda bds cofcms clubs events gestioncof kfet kfetauth kfetcms open petitscours shared"
.test_template: .test_template:
before_script: before_script:
- mkdir -p vendor/{pip,apt} - mkdir -p vendor/{pip,apt}
- apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client libldap2-dev libsasl2-dev - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client libldap2-dev libsasl2-dev
- sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py - sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' gestioasso/settings/secret_example.py > gestioasso/settings/secret.py
- sed -i.bak -E 's;^REDIS_PASSWD = .*$;REDIS_PASSWD = "";' cof/settings/secret.py - sed -i.bak -E 's;^REDIS_PASSWD = .*$;REDIS_PASSWD = "";' gestioasso/settings/secret.py
# Remove the old test database if it has not been done yet # Remove the old test database if it has not been done yet
- psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB" - psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB"
- pip install --upgrade -r requirements-prod.txt coverage tblib - pip install --upgrade -r requirements-prod.txt coverage tblib
@ -40,19 +43,27 @@ variables:
# Keep this disabled for now, as it may kill GitLab... # Keep this disabled for now, as it may kill GitLab...
# coverage: '/TOTAL.*\s(\d+\.\d+)\%$/' # coverage: '/TOTAL.*\s(\d+\.\d+)\%$/'
kfettest:
stage: test
extends: .test_template
variables:
DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod"
script:
- coverage run manage.py test kfet
coftest: coftest:
stage: test stage: test
extends: .test_template extends: .test_template
variables: variables:
DJANGO_SETTINGS_MODULE: "cof.settings.cof_prod" DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod"
script: script:
- coverage run manage.py test gestioncof bda kfet petitscours shared --parallel - coverage run manage.py test gestioncof bda petitscours shared --parallel
bdstest: bdstest:
stage: test stage: test
extends: .test_template extends: .test_template
variables: variables:
DJANGO_SETTINGS_MODULE: "cof.settings.bds_prod" DJANGO_SETTINGS_MODULE: "gestioasso.settings.bds_prod"
script: script:
- coverage run manage.py test bds clubs events --parallel - coverage run manage.py test bds clubs events --parallel
@ -65,7 +76,7 @@ linters:
- black --check . - black --check .
- isort --check --diff . - isort --check --diff .
# Print errors only # Print errors only
- flake8 --exit-zero bda bds clubs cof events gestioncof kfet petitscours provisioning shared - flake8 --exit-zero bda bds clubs gestioasso events gestioncof kfet petitscours provisioning shared
cache: cache:
key: linters key: linters
paths: paths:
@ -75,14 +86,14 @@ linters:
migration_checks: migration_checks:
stage: test stage: test
variables: variables:
DJANGO_SETTINGS_MODULE: "cof.settings.local" DJANGO_SETTINGS_MODULE: "gestioasso.settings.local"
before_script: before_script:
- mkdir -p vendor/{pip,apt} - mkdir -p vendor/{pip,apt}
- apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client libldap2-dev libsasl2-dev - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client libldap2-dev libsasl2-dev
- cp cof/settings/secret_example.py cof/settings/secret.py - cp gestioasso/settings/secret_example.py gestioasso/settings/secret.py
- pip install --upgrade -r requirements-devel.txt - pip install --upgrade -r requirements-devel.txt
- python --version - python --version
script: python manage.py makemigrations --dry-run --check script: python manage.py makemigrations --dry-run --check $MIGRATION_APPS
services: services:
# this should not be necessary… # this should not be necessary…
- postgres:11.7 - postgres:11.7

View file

@ -16,18 +16,194 @@ Liste des changements notables dans GestioCOF depuis la version 0.1 (septembre
- Vue d'inscription à un événement **ou** intégration propre dans la vue - Vue d'inscription à un événement **ou** intégration propre dans la vue
"inscription d'un nouveau membre" "inscription d'un nouveau membre"
### Nouveau module BDS
Uniquement un modèle BDSProfile pour le moment…
### Nouveau module de gestion des clubs ### Nouveau module de gestion des clubs
Uniquement un modèle simple de clubs avec des respos. Aucune gestion des Uniquement un modèle simple de clubs avec des respos. Aucune gestion des
adhérents ni des cotisations. adhérents ni des cotisations.
## Upcoming ## TODO Prod
## Version 0.6 - 27/0702020 - Lancer `python manage.py update_translation_fields` après la migration
- Mettre à jour les units systemd `daphne.service` et `worker.service`
- Créer un compte hCaptcha (https://www.hcaptcha.com/), au COF, et remplacer les secrets associés
## Version ??? - ??/??/????
## Version 0.15.1 - 15/06/2023
### K-Fêt
- Rattrape les erreurs d'envoi de mail de négatif
- Utilise l'adresse chefs pour les envois de négatifs
## Version 0.15 - 22/05/2023
### K-Fêt
- Rajoute un formulaire de contact
- Rajoute un formulaire de demande de soirée
- Désactive les mails d'envoi de négatifs sur les comptes gelés
## Version 0.14 - 19/05/2023
- Répare les dépendances en spécifiant toutes les versions
### K-Fêt
- Répare la gestion des changement d'heure via moment.js
## Version 0.13 - 19/02/2023
### K-Fêt
- Rajoute la valeur des inventaires
- Résout les problèmes de négatif ne disparaissant pas
- Affiche son surnom s'il y en a un
- Bugfixes
## Version 0.12.1 - 03/10/2022
### K-Fêt
- Fixe un problème de rendu causé par l'agrandissement du menu
- Mise à jour vers Channels 3.x et Django 3.2
## Version 0.12 - 17/06/2022
### K-Fêt
- Ajoute une exception à la limite d'historique pour les comptes `LIQ` et `#13`
- Répare le problème des étiquettes LIQ/Comptes K-Fêt inversées dans les stats des articles K-Fêt
## Version 0.11 - 26/10/2021
### COF
- Répare un problème de rendu sur le wagtail du COF
### K-Fêt
- Ajoute de mails de rappels pour les comptes en négatif
- La recherche de comptes sur K-Psul remarche normalement
- Le pointeur de la souris change de forme quand on survole un item d'autocomplétion
- Modification du gel de compte:
- on ne peut plus geler/dégeler son compte soi-même (il faut la permission "Gérer les permissions K-Fêt")
- on ne peut rien compter sur un compte gelé (aucune override possible), et les K-Fêteux·ses dont le compte est gelé perdent tout accès à K-Psul
- les comptes actuellement gelés (sur l'ancien système) sont dégelés automatiquement
- Modification du fonctionnement des négatifs
- impossible d'avoir des négatifs inférieurs à `kfet_config.overdraft_amount`
- il n'y a plus de limite de temps sur les négatifs
- supression des autorisations de négatif
- il n'est plus possible de réinitialiser la durée d'un négatif en faisant puis en annulant une charge
- La gestion des erreurs passe du client au serveur, ce qui permet d'avoir des messages plus explicites
- La supression d'opérations anciennes est réparée
## Version 0.10 - 18/04/2021
### K-Fêt
- On fait sauter la limite qui empêchait de vendre plus de 24 unités d'un item à
la fois.
- L'interface indique plus clairement quand on fait une erreur en modifiant un
compte.
- On supprime la fonction "décalage de balance".
- L'accès à l'historique est maintenant limité à 7 jours pour raison de
confidentialité. Les chefs/trez peuvent disposer d'une permission
supplémentaire pour accéder à jusqu'à 30 jours en cas de problème de compta.
L'accès à son historique personnel n'est pas limité. Les durées sont
configurables dans `settings/cof_prod.py`.
### COF
- Le Captcha sur la page de demande de petits cours utilise maintenant hCaptcha
au lieu de ReCaptcha, pour mieux respecter la vie privée des utilisateur·ices
## Version 0.9 - 06/02/2020
### COF / BdA
- Le COF peut remettre à zéro la liste de ses adhérents en août (sans passer par
KDE).
- La page d'accueil affiche la date de fermeture des tirages BdA.
- On peut revendre une place dès qu'on l'a payée, plus besoin de payer toutes
ses places pour pouvoir revendre.
- On s'assure que l'email fourni lors d'une demande de petit cours est valide.
### BDS
- Le burô peut maintenant accorder ou révoquer le statut de membre du Burô
en modifiant le profil d'un membre du BDS.
- Le burô peut exporter la liste de ses membres avec email au format CSV depuis
la page d'accueil.
### K-Fêt
- On affiche les articles actuellement en vente en premier lors des inventaires
et des commandes.
- On peut supprimer un inventaire. Seuls les articles dont c'est le dernier
inventaire sont affectés.
## Version 0.8 - 03/12/2020
### COF
- La page "Mes places" dans la section BdA indique quelles places sont sur
listing.
- ergonomie de l'interface admin du BdA : moins d'options inutiles lors de
la sélection de participants.
- les tirages sont maintenant archivables pour éviter d'avoir encore d'autres
options inutiles.
- l'autocomplétion dans l'admin BdA est réparée.
- Les icones de la page de gestion des petits cours sont (à nouveau) réparées.
- On a supprimé la possibilité de modifier les mails automatiques depuis
l'interface admin car trop problématique. Faute de mieux, envoyer un mail à
KDE pour modifier ces mails.
- corrige un crash sporadique sur la page d'inscription au système de petits
cours
### K-Fêt
- (fix partiel) Empêche la K-Fêt de modifier des données COF (e.g. nom, prénom,
username) lors de la création d'un nouveau compte.
- Les statistiques de conso globales montrent deux courbes COF / non-COF au
lieu de LIQ / sur compte.
- Un bug empêchait de fermer manuellement la K-Fêt depuis un compte non
privilégié en tapant un mot de passe. C'est corrigé.
## Version 0.7.2 - 08/09/2020
- Nouvelle page 404
- Correction de bug en K-Fêt : le lien pour créer un nouveau compte exté apparaît
à nouveau dans l'autocomplétion
## Version 0.7.1 - 05/09/2020
Petits ajustements sur le site du COF :
- Possibilité d'ajouter des champs d'infos supplémentaires en plus de l'email et
de la page web dans les annuaires (clubs et partenaires).
- Corrige un bug d'affichage des adresses emails de clubs
## Version 0.7 - 29/08/2020
### GestioBDS
- Ajout d'un bouton pour supprimer un compte
- Le nombre d'adhérent⋅es est affiché sur la page d'accueil
- le groupe BDS a les bonnes permissions
### Site du COF
- Captcha fonctionnel pour les mailing-listes
### K-Fêt
- L'autocomplétion pour la création de compte K-Fêt se lance à 3 caractères seulement,
donc est plus rapide.
## Version 0.6 - 27/07/2020
Arrivée du BDS ! Arrivée du BDS !
GestioCOF et GestioBDS ont du code en commun mais tournent de façon séparée, les GestioCOF et GestioBDS ont du code en commun mais tournent de façon séparée, les

View file

@ -1,4 +1,4 @@
# GestioCOF # GestioCOF / GestioBDS
[![pipeline status](https://git.eleves.ens.fr/cof-geek/gestioCOF/badges/master/pipeline.svg)](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master) [![pipeline status](https://git.eleves.ens.fr/cof-geek/gestioCOF/badges/master/pipeline.svg)](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master)
[![coverage report](https://git.eleves.ens.fr/cof-geek/gestioCOF/badges/master/coverage.svg)](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master) [![coverage report](https://git.eleves.ens.fr/cof-geek/gestioCOF/badges/master/coverage.svg)](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master)
@ -38,11 +38,11 @@ Vous pouvez maintenant installer les dépendances Python depuis le fichier
pip install -U pip # parfois nécessaire la première fois pip install -U pip # parfois nécessaire la première fois
pip install -r requirements-devel.txt pip install -r requirements-devel.txt
Pour terminer, copier le fichier `cof/settings/secret_example.py` vers Pour terminer, copier le fichier `gestioasso/settings/secret_example.py` vers
`cof/settings/secret.py`. Sous Linux ou Mac, préférez plutôt un lien symbolique `gestioasso/settings/secret.py`. Sous Linux ou Mac, préférez plutôt un lien symbolique
pour profiter de façon transparente des mises à jour du fichier: pour profiter de façon transparente des mises à jour du fichier:
ln -s secret_example.py cof/settings/secret.py ln -s secret_example.py gestioasso/settings/secret.py
Nous avons un git hook de pre-commit pour formatter et vérifier que votre code Nous avons un git hook de pre-commit pour formatter et vérifier que votre code
vérifie nos conventions. Pour bénéficier des mises à jour du hook, préférez vérifie nos conventions. Pour bénéficier des mises à jour du hook, préférez

42
Vagrantfile vendored
View file

@ -1,47 +1,19 @@
# -*- mode: ruby -*- # -*- mode: ruby -*-
# vi: set ft=ruby : # vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure # Configuration de base pour GestioCOF.
# configures the configuration version (we support older styles for # Voir https://docs.vagrantup.com pour plus d'informations.
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config| Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below. # On se base sur Debian 10 (Buster) pour avoir le même environnement qu'en
# For a complete reference, please see the online documentation at # production.
# https://docs.vagrantup.com. config.vm.box = "debian/contrib-buster64"
config.vm.box = "ubuntu/xenial64"
# On associe le port 80 dans la machine virtuelle avec le port 8080 de notre # On associe le port 80 dans la machine virtuelle avec le port 8080 de notre
# ordinateur, et le port 8000 avec le port 8000. # ordinateur, et le port 8000 avec le port 8000.
config.vm.network :forwarded_port, guest: 80, host: 8080 config.vm.network :forwarded_port, guest: 80, host: 8080
config.vm.network :forwarded_port, guest: 8000, host: 8000 config.vm.network :forwarded_port, guest: 8000, host: 8000
# Create a private network, which allows host-only access to the machine # Le restes de la configuration (installation de paquets, etc) est géré un
# using a specific IP. # script shell.
# config.vm.network "private_network", ip: "192.168.33.10"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# sudo apt-get update
# sudo apt-get install -y apache2
# SHELL
config.vm.provision :shell, path: "provisioning/bootstrap.sh" config.vm.provision :shell, path: "provisioning/bootstrap.sh"
end end

View file

@ -1,10 +1,11 @@
from datetime import timedelta from datetime import timedelta
from custommail.shortcuts import send_mass_custom_mail
from dal.autocomplete import ModelSelect2 from dal.autocomplete import ModelSelect2
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.core.mail import send_mass_mail
from django.db.models import Count, Q, Sum from django.db.models import Count, Q, Sum
from django.template import loader
from django.template.defaultfilters import pluralize from django.template.defaultfilters import pluralize
from django.utils import timezone from django.utils import timezone
@ -32,20 +33,6 @@ class ReadOnlyMixin(object):
return readonly_fields + self.readonly_fields_update return readonly_fields + self.readonly_fields_update
class ChoixSpectacleAdminForm(forms.ModelForm):
class Meta:
widgets = {
"participant": ModelSelect2(url="bda-participant-autocomplete"),
"spectacle": ModelSelect2(url="bda-spectacle-autocomplete"),
}
class ChoixSpectacleInline(admin.TabularInline):
model = ChoixSpectacle
form = ChoixSpectacleAdminForm
sortable_field_name = "priority"
class AttributionTabularAdminForm(forms.ModelForm): class AttributionTabularAdminForm(forms.ModelForm):
listing = None listing = None
@ -93,9 +80,12 @@ class WithoutListingAttributionInline(AttributionInline):
class ParticipantAdminForm(forms.ModelForm): class ParticipantAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["choicesrevente"].queryset = Spectacle.objects.select_related( queryset = Spectacle.objects.select_related("location")
"location"
) if self.instance.pk is not None:
queryset = queryset.filter(tirage=self.instance.tirage)
self.fields["choicesrevente"].queryset = queryset
class ParticipantPaidFilter(admin.SimpleListFilter): class ParticipantPaidFilter(admin.SimpleListFilter):
@ -169,19 +159,23 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin):
form = ParticipantAdminForm form = ParticipantAdminForm
def send_attribs(self, request, queryset): def send_attribs(self, request, queryset):
datatuple = [] emails = []
for member in queryset.all(): for member in queryset.all():
subject = "Résultats du tirage au sort"
attribs = member.attributions.all() attribs = member.attributions.all()
context = {"member": member.user} context = {"member": member.user}
shortname = ""
template_name = ""
if len(attribs) == 0: if len(attribs) == 0:
shortname = "bda-attributions-decus" template_name = "bda/mails/attributions-decus.txt"
else: else:
shortname = "bda-attributions" template_name = "bda/mails/attributions.txt"
context["places"] = attribs context["places"] = attribs
print(context)
datatuple.append((shortname, context, "bda@ens.fr", [member.user.email])) message = loader.render_to_string(template_name, context)
send_mass_custom_mail(datatuple) emails.append((subject, message, "bda@ens.fr", [member.user.email]))
send_mass_mail(emails)
count = len(queryset.all()) count = len(queryset.all())
if count == 1: if count == 1:
message_bit = "1 membre a" message_bit = "1 membre a"
@ -197,17 +191,6 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin):
class AttributionAdminForm(forms.ModelForm): class AttributionAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if "spectacle" in self.fields:
self.fields["spectacle"].queryset = Spectacle.objects.select_related(
"location"
)
if "participant" in self.fields:
self.fields["participant"].queryset = Participant.objects.select_related(
"user", "tirage"
)
def clean(self): def clean(self):
cleaned_data = super().clean() cleaned_data = super().clean()
participant = cleaned_data.get("participant") participant = cleaned_data.get("participant")
@ -220,9 +203,14 @@ class AttributionAdminForm(forms.ModelForm):
) )
return cleaned_data return cleaned_data
class Meta:
widgets = {
"participant": ModelSelect2(url="bda-participant-autocomplete"),
"spectacle": ModelSelect2(url="bda-spectacle-autocomplete"),
}
class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin): class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
list_display = ("id", "spectacle", "participant", "given", "paid") list_display = ("id", "spectacle", "participant", "given", "paid")
search_fields = ( search_fields = (
"spectacle__title", "spectacle__title",
@ -235,7 +223,7 @@ class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
class ChoixSpectacleAdmin(admin.ModelAdmin): class ChoixSpectacleAdmin(admin.ModelAdmin):
form = ChoixSpectacleAdminForm autocomplete_fields = ["participant", "spectacle"]
def tirage(self, obj): def tirage(self, obj):
return obj.participant.tirage return obj.participant.tirage
@ -279,15 +267,14 @@ class SalleAdmin(admin.ModelAdmin):
class SpectacleReventeAdminForm(forms.ModelForm): class SpectacleReventeAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["confirmed_entry"].queryset = Participant.objects.select_related( qset = Participant.objects.select_related("user", "tirage")
"user", "tirage"
) if self.instance.pk is not None:
self.fields["seller"].queryset = Participant.objects.select_related( qset = qset.filter(tirage=self.instance.seller.tirage)
"user", "tirage"
) self.fields["confirmed_entry"].queryset = qset
self.fields["soldTo"].queryset = Participant.objects.select_related( self.fields["seller"].queryset = qset
"user", "tirage" self.fields["soldTo"].queryset = qset
)
class SpectacleReventeAdmin(admin.ModelAdmin): class SpectacleReventeAdmin(admin.ModelAdmin):

View file

@ -2,7 +2,6 @@ import random
class Algorithm(object): class Algorithm(object):
shows = None shows = None
ranks = None ranks = None
origranks = None origranks = None

View file

@ -3,7 +3,7 @@ from django.forms.models import BaseInlineFormSet
from django.template import loader from django.template import loader
from django.utils import timezone from django.utils import timezone
from bda.models import Attribution, Spectacle, SpectacleRevente from bda.models import SpectacleRevente
class InscriptionInlineFormSet(BaseInlineFormSet): class InscriptionInlineFormSet(BaseInlineFormSet):
@ -77,7 +77,7 @@ class ResellForm(forms.Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["attributions"] = TemplateLabelField( self.fields["attributions"] = TemplateLabelField(
queryset=participant.attribution_set.filter( queryset=participant.attribution_set.filter(
spectacle__date__gte=timezone.now() spectacle__date__gte=timezone.now(), paid=True
) )
.exclude(revente__seller=participant) .exclude(revente__seller=participant)
.select_related("spectacle", "spectacle__location", "participant__user"), .select_related("spectacle", "spectacle__location", "participant__user"),

View file

@ -81,7 +81,7 @@ class Command(MyBaseCommand):
shows = random.sample( shows = random.sample(
list(tirage.spectacle_set.all()), tirage.spectacle_set.count() // 2 list(tirage.spectacle_set.all()), tirage.spectacle_set.count() // 2
) )
for (rank, show) in enumerate(shows): for rank, show in enumerate(shows):
choices.append( choices.append(
ChoixSpectacle( ChoixSpectacle(
participant=part, participant=part,

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
operations = [ operations = [

View file

@ -35,7 +35,6 @@ def fill_tirage_fields(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0001_initial")] dependencies = [("bda", "0001_initial")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0002_add_tirage")] dependencies = [("bda", "0002_add_tirage")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0003_update_tirage_and_spectacle")] dependencies = [("bda", "0003_update_tirage_and_spectacle")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0004_mails-rappel")] dependencies = [("bda", "0004_mails-rappel")]
operations = [ operations = [

View file

@ -18,7 +18,6 @@ def forwards_func(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0005_encoding")] dependencies = [("bda", "0005_encoding")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0006_add_tirage_switch")] dependencies = [("bda", "0006_add_tirage_switch")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0007_extends_spectacle")] dependencies = [("bda", "0007_extends_spectacle")]
operations = [ operations = [

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0008_py3")] dependencies = [("bda", "0008_py3")]
operations = [ operations = [

View file

@ -12,15 +12,15 @@ def forwards_func(apps, schema_editor):
for revente in SpectacleRevente.objects.all(): for revente in SpectacleRevente.objects.all():
is_expired = timezone.now() > revente.date_tirage() is_expired = timezone.now() > revente.date_tirage()
is_direct = revente.attribution.spectacle.date >= revente.date and timezone.now() > revente.date + timedelta( is_direct = (
minutes=15 revente.attribution.spectacle.date >= revente.date
and timezone.now() > revente.date + timedelta(minutes=15)
) )
revente.shotgun = is_expired or is_direct revente.shotgun = is_expired or is_direct
revente.save() revente.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0009_revente")] dependencies = [("bda", "0009_revente")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0010_spectaclerevente_shotgun")] dependencies = [("bda", "0010_spectaclerevente_shotgun")]
operations = [ operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0011_tirage_appear_catalogue")] dependencies = [("bda", "0011_tirage_appear_catalogue")]
operations = [ operations = [

View file

@ -13,7 +13,6 @@ def swap_double_choice(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0011_tirage_appear_catalogue")] dependencies = [("bda", "0011_tirage_appear_catalogue")]
operations = [ operations = [

View file

@ -6,7 +6,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0012_notif_time"), ("bda", "0012_swap_double_choice")] dependencies = [("bda", "0012_notif_time"), ("bda", "0012_swap_double_choice")]
operations = [] operations = []

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0013_merge_20180524_2123")] dependencies = [("bda", "0013_merge_20180524_2123")]
operations = [ operations = [

View file

@ -29,7 +29,6 @@ def set_participant_payment(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0014_attribution_paid_field")] dependencies = [("bda", "0014_attribution_paid_field")]
operations = [ operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0015_move_bda_payment")] dependencies = [("bda", "0015_move_bda_payment")]
operations = [ operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bda", "0016_delete_participant_paid")] dependencies = [("bda", "0016_delete_participant_paid")]
operations = [ operations = [

View file

@ -0,0 +1,37 @@
# Generated by Django 2.2.12 on 2020-10-21 16:18
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bda", "0017_participant_accepte_charte"),
]
operations = [
migrations.AlterModelOptions(
name="participant",
options={"ordering": ("-tirage", "user__last_name", "user__first_name")},
),
migrations.AddField(
model_name="tirage",
name="archived",
field=models.BooleanField(default=False, verbose_name="Archivé"),
),
migrations.AlterField(
model_name="participant",
name="tirage",
field=models.ForeignKey(
limit_choices_to={"archived": False},
on_delete=django.db.models.deletion.CASCADE,
to="bda.Tirage",
),
),
migrations.AddConstraint(
model_name="participant",
constraint=models.UniqueConstraint(
fields=("tirage", "user"), name="unique_tirage"
),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.13 on 2022-06-30 10:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bda", "0018_auto_20201021_1818"),
]
operations = [
migrations.AlterUniqueTogether(
name="choixspectacle",
unique_together=set(),
),
migrations.AddConstraint(
model_name="choixspectacle",
constraint=models.UniqueConstraint(
fields=("participant", "spectacle"), name="unique_participation"
),
),
]

View file

@ -2,14 +2,14 @@ import calendar
import random import random
from datetime import timedelta from datetime import timedelta
from custommail.models import CustomMail
from custommail.shortcuts import send_mass_custom_mail
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core import mail from django.core import mail
from django.core.mail import EmailMessage, send_mass_mail
from django.db import models from django.db import models
from django.db.models import Count, Exists from django.db.models import Count, Exists
from django.template import loader
from django.utils import formats, timezone from django.utils import formats, timezone
@ -31,6 +31,7 @@ class Tirage(models.Model):
"Tirage à afficher dans le catalogue", default=False "Tirage à afficher dans le catalogue", default=False
) )
enable_do_tirage = models.BooleanField("Le tirage peut être lancé", default=False) enable_do_tirage = models.BooleanField("Le tirage peut être lancé", default=False)
archived = models.BooleanField("Archivé", default=False)
def __str__(self): def __str__(self):
return "%s - %s" % ( return "%s - %s" % (
@ -116,16 +117,19 @@ class Spectacle(models.Model):
bda_generic.nb_attr = 1 bda_generic.nb_attr = 1
members.append(bda_generic) members.append(bda_generic)
# On écrit un mail personnalisé à chaque participant # On écrit un mail personnalisé à chaque participant
datatuple = [ mails = [
( (
"bda-rappel", str(self),
{"member": member, "nb_attr": member.nb_attr, "show": self}, loader.render_to_string(
"bda/mails/rappel.txt",
context={"member": member, "nb_attr": member.nb_attr, "show": self},
),
settings.MAIL_DATA["rappels"]["FROM"], settings.MAIL_DATA["rappels"]["FROM"],
[member.email], [member.email],
) )
for member in members for member in members
] ]
send_mass_custom_mail(datatuple) send_mass_mail(mails)
# On enregistre le fait que l'envoi a bien eu lieu # On enregistre le fait que l'envoi a bien eu lieu
self.rappel_sent = timezone.now() self.rappel_sent = timezone.now()
self.save() self.save()
@ -194,7 +198,9 @@ class Participant(models.Model):
attributions = models.ManyToManyField( attributions = models.ManyToManyField(
Spectacle, through="Attribution", related_name="attributed_to" Spectacle, through="Attribution", related_name="attributed_to"
) )
tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE) tirage = models.ForeignKey(
Tirage, on_delete=models.CASCADE, limit_choices_to={"archived": False}
)
accepte_charte = models.BooleanField("A accepté la charte BdA", default=False) accepte_charte = models.BooleanField("A accepté la charte BdA", default=False)
choicesrevente = models.ManyToManyField( choicesrevente = models.ManyToManyField(
Spectacle, related_name="subscribed", blank=True Spectacle, related_name="subscribed", blank=True
@ -205,6 +211,12 @@ class Participant(models.Model):
def __str__(self): def __str__(self):
return "%s - %s" % (self.user, self.tirage.title) return "%s - %s" % (self.user, self.tirage.title)
class Meta:
ordering = ("-tirage", "user__last_name", "user__first_name")
constraints = [
models.UniqueConstraint(fields=("tirage", "user"), name="unique_tirage"),
]
DOUBLE_CHOICES = ( DOUBLE_CHOICES = (
("1", "1 place"), ("1", "1 place"),
@ -241,7 +253,11 @@ class ChoixSpectacle(models.Model):
class Meta: class Meta:
ordering = ("priority",) ordering = ("priority",)
unique_together = (("participant", "spectacle"),) constraints = [
models.UniqueConstraint(
fields=["participant", "spectacle"], name="unique_participation"
)
]
verbose_name = "voeu" verbose_name = "voeu"
verbose_name_plural = "voeux" verbose_name_plural = "voeux"
@ -348,21 +364,24 @@ class SpectacleRevente(models.Model):
BdA-Revente à tous les intéressés. BdA-Revente à tous les intéressés.
""" """
inscrits = self.attribution.spectacle.subscribed.select_related("user") inscrits = self.attribution.spectacle.subscribed.select_related("user")
datatuple = [ mails = [
( (
"bda-revente", "BdA-Revente : {}".format(self.attribution.spectacle.title),
{ loader.render_to_string(
"bda/mails/revente-new.txt",
context={
"member": participant.user, "member": participant.user,
"show": self.attribution.spectacle, "show": self.attribution.spectacle,
"revente": self, "revente": self,
"site": Site.objects.get_current(), "site": Site.objects.get_current(),
}, },
),
settings.MAIL_DATA["revente"]["FROM"], settings.MAIL_DATA["revente"]["FROM"],
[participant.user.email], [participant.user.email],
) )
for participant in inscrits for participant in inscrits
] ]
send_mass_custom_mail(datatuple) send_mass_mail(mails)
self.notif_sent = True self.notif_sent = True
self.notif_time = timezone.now() self.notif_time = timezone.now()
self.save() self.save()
@ -373,20 +392,23 @@ class SpectacleRevente(models.Model):
leur indiquer qu'il est désormais disponible au shotgun. leur indiquer qu'il est désormais disponible au shotgun.
""" """
inscrits = self.attribution.spectacle.subscribed.select_related("user") inscrits = self.attribution.spectacle.subscribed.select_related("user")
datatuple = [ mails = [
( (
"bda-shotgun", "BdA-Revente : {}".format(self.attribution.spectacle.title),
{ loader.render_to_string(
"bda/mails/revente-shotgun.txt",
context={
"member": participant.user, "member": participant.user,
"show": self.attribution.spectacle, "show": self.attribution.spectacle,
"site": Site.objects.get_current(), "site": Site.objects.get_current(),
}, },
),
settings.MAIL_DATA["revente"]["FROM"], settings.MAIL_DATA["revente"]["FROM"],
[participant.user.email], [participant.user.email],
) )
for participant in inscrits for participant in inscrits
] ]
send_mass_custom_mail(datatuple) send_mass_mail(mails)
self.notif_sent = True self.notif_sent = True
self.notif_time = timezone.now() self.notif_time = timezone.now()
# Flag inutile, sauf si l'horloge interne merde # Flag inutile, sauf si l'horloge interne merde
@ -418,31 +440,30 @@ class SpectacleRevente(models.Model):
"show": spectacle, "show": spectacle,
} }
c_mails_qs = CustomMail.objects.filter( subject = "BdA-Revente : {}".format(spectacle.title)
shortname__in=[
"bda-revente-winner",
"bda-revente-loser",
"bda-revente-seller",
]
)
c_mails = {cm.shortname: cm for cm in c_mails_qs}
mails.append( mails.append(
c_mails["bda-revente-winner"].get_message( EmailMessage(
context, subject=subject,
body=loader.render_to_string(
"bda/mails/revente-tirage-winner.txt",
context=context,
),
from_email=settings.MAIL_DATA["revente"]["FROM"], from_email=settings.MAIL_DATA["revente"]["FROM"],
to=[winner.user.email], to=[winner.user.email],
) )
) )
mails.append( mails.append(
c_mails["bda-revente-seller"].get_message( EmailMessage(
context, subject=subject,
body=loader.render_to_string(
"bda/mails/revente-tirage-seller.txt",
context=context,
),
from_email=settings.MAIL_DATA["revente"]["FROM"], from_email=settings.MAIL_DATA["revente"]["FROM"],
to=[seller.user.email], to=[seller.user.email],
reply_to=[winner.user.email], reply_to=[winner.user.email],
) ),
) )
# Envoie un mail aux perdants # Envoie un mail aux perdants
@ -452,11 +473,15 @@ class SpectacleRevente(models.Model):
new_context["acheteur"] = inscrit.user new_context["acheteur"] = inscrit.user
mails.append( mails.append(
c_mails["bda-revente-loser"].get_message( EmailMessage(
new_context, subject=subject,
body=loader.render_to_string(
"bda/mails/revente-tirage-loser.txt",
context=new_context,
),
from_email=settings.MAIL_DATA["revente"]["FROM"], from_email=settings.MAIL_DATA["revente"]["FROM"],
to=[inscrit.user.email], to=[inscrit.user.email],
) ),
) )
mail_conn = mail.get_connection() mail_conn = mail.get_connection()

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block extra_head %} {% block extra_head %}
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" /> <link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}
<h2>État des inscriptions BdA</h2> <h2>État des inscriptions BdA</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block extra_head %} {% block extra_head %}
<script type="text/javascript" src="{% static 'vendor/jquery/jquery-ui.min.js' %}" ></script> <script type="text/javascript" src="{% static 'vendor/jquery/jquery-ui.min.js' %}" ></script>

View file

@ -26,13 +26,6 @@
<hr \> <hr \>
<p>
<em>Note :</em> le template de ce mail peut être modifié à
<a href="{% url 'admin:custommail_custommail_change' custommail.pk %}">cette adresse</a>
</p>
<hr \>
<h3>Forme des mails</h3> <h3>Forme des mails</h3>
<h4>Une seule place</h4> <h4>Une seule place</h4>

View file

@ -0,0 +1,10 @@
Cher-e {{ member.first_name }},
Tu t'es inscrit-e pour le tirage au sort du BdA. Malheureusement, tu n'as
obtenu aucune place.
Nous proposons cependant de nombreuses offres hors-tirage tout au long de
l'année, et nous t'invitons à nous contacter si l'une d'entre elles
t'intéresse !
--
Le Bureau des Arts

View file

@ -0,0 +1,31 @@
Cher-e {{ member.first_name }},
Tu t'es inscrit-e pour le tirage au sort du BdA. Tu as été sélectionné-e
pour les spectacles suivants :
{% for place in places %}
- 1 place pour {{ place }}{% endfor %}
*Paiement*
L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT
vendredi prochain, au bureau du COF pendant les heures de permanences (du lundi au vendredi
entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien
évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement,
ou bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas
venir payer au bureau, merci de nous contacter par mail.
*Mode de retrait des places*
Au moment du paiement, certaines places vous seront remises directement,
d'autres seront à récupérer au cours de l'année, d'autres encore seront
nominatives et à retirer le soir même dans les théâtres correspondants.
Pour chaque spectacle, vous recevrez un mail quelques jours avant la
représentation vous indiquant le mode de retrait.
Nous vous rappelons que l'obtention de places du BdA vous engage à
respecter les règles de fonctionnement :
https://bda.ens.fr/lequipe/charte-bda/
Un système de revente des places via les mails BdA-revente est disponible
directement sur votre compte GestioCOF.
En vous souhaitant de très beaux spectacles tout au long de l'année,
--
Le Bureau des Arts

View file

@ -0,0 +1,23 @@
Bonjour {{ member.first_name }},
Nous te rappellons que tu as eu la chance d'obtenir {{ nb_attr|pluralize:"une place,deux places" }}
pour {{ show.title }}, le {{ show.date }} au {{ show.location }}. N'oublie pas de t'y rendre !
{% if nb_attr == 2 %}
Tu as obtenu deux places pour ce spectacle. Nous te rappelons que
ces places sont strictement réservées aux personnes de moins de 28 ans.
{% endif %}
{% if show.listing %}Pour ce spectacle, tu as reçu {{ nb_attr|pluralize:"une place,des places" }} sur
listing. Il te faudra donc te rendre 15 minutes en avance sur les lieux de la représentation
pour {{ nb_attr|pluralize:"la,les" }} retirer.
{% else %}Pour assister à ce spectacle, tu dois présenter les billets qui ont
été distribués au burô.
{% endif %}
Si tu ne peux plus assister à cette représentation, tu peux
revendre ta place via BdA-revente, accessible directement sur
GestioCOF (lien "revendre une place du premier tirage" sur la page
d'accueil https://www.cof.ens.fr/gestion/).
En te souhaitant un excellent spectacle,
--
Le Bureau des Arts

View file

@ -0,0 +1,12 @@
Bonjour {{ member.first_name }}
Une place pour le spectacle {{ show.title }} ({{ show.date }})
a été postée sur BdA-Revente.
Si ce spectacle t'intéresse toujours, merci de nous le signaler en cliquant
sur ce lien : https://{{ site }}{% url "bda-revente-confirm" revente.id %}.
Dans le cas où plusieurs personnes seraient intéressées, nous procèderons à
un tirage au sort le {{ revente.date_tirage|date:"DATE_FORMAT" }}.
Chaleureusement,
Le BdA

View file

@ -0,0 +1,13 @@
Bonjour {{ vendeur.first_name }},
Tu tes bien inscrit·e pour revendre une place pour {{ show.title }}.
{% with revente.date_tirage as time %}
Le tirage au sort entre tout·e·s les racheteuse·eur·s potentiel·le·s aura lieu
le {{ time|date:"DATE_FORMAT" }} à {{ time|time:"TIME_FORMAT" }} (dans {{time|timeuntil }}).
Si personne ne sest inscrit pour racheter la place, celle-ci apparaîtra parmi
les « Places disponibles immédiatement à la revente » sur GestioCOF.
{% endwith %}
Bonne revente !
Le Bureau des Arts

View file

@ -0,0 +1,6 @@
Bonjour {{ vendeur.first_name }} !
Je souhaiterais racheter ta place pour {{ show.title }} le {{ show.date }} ({{ show.location }}) à {{ show.price|floatformat:2 }}€.
Contacte-moi si tu es toujours intéressé·e !
{{ acheteur.get_full_name }} ({{ acheteur.email }})

View file

@ -0,0 +1,11 @@
Bonjour {{ member.first_name }}
Une place pour le spectacle {{ show.title }} ({{ show.date }})
a été postée sur BdA-Revente.
Puisque ce spectacle a lieu dans moins de 24h, il n'y a pas de tirage au sort pour
cette place : elle est disponible immédiatement à l'adresse
https://{{ site }}{% url "bda-revente-buy" show.id %}, à la disposition de tous.
Chaleureusement,
Le BdA

View file

@ -0,0 +1,9 @@
Bonjour {{ acheteur.first_name }},
Tu t'étais inscrit·e pour la revente de la place de {{ vendeur.get_full_name }}
pour {{ show.title }}.
Malheureusement, une autre personne a été tirée au sort pour racheter la place.
Tu pourras certainement retenter ta chance pour une autre revente !
À très bientôt,
Le Bureau des Arts

View file

@ -0,0 +1,7 @@
Bonjour {{ vendeur.first_name }},
La personne tirée au sort pour racheter ta place pour {{ show.title }} est {{ acheteur.get_full_name }}.
Tu peux le/la contacter à l'adresse {{ acheteur.email }}, ou en répondant à ce mail.
Chaleureusement,
Le BdA

View file

@ -0,0 +1,7 @@
Bonjour {{ acheteur.first_name }},
Tu as été tiré·e au sort pour racheter une place pour {{ show.title }} le {{ show.date }} ({{ show.location }}) à {{ show.price|floatformat:2 }}€.
Tu peux contacter le/la vendeur·se à l'adresse {{ vendeur.email }}.
Chaleureusement,
Le BdA

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}
<h2>{{ spectacle }}</h2> <h2>{{ spectacle }}</h2>
@ -16,7 +16,7 @@
<tbody> <tbody>
{% for participant in participants %} {% for participant in participants %}
<tr> <tr>
<td data-sort-value="{{ participan.name}}">{{participant.name}}</td> <td data-sort-value="{{ participant.name}}">{{participant.name}}</td>
<td data-sort-value="{{participant.nb_places}}">{{participant.nb_places}} place{{participant.nb_places|pluralize}}</td> <td data-sort-value="{{participant.nb_places}}">{{participant.nb_places}} place{{participant.nb_places|pluralize}}</td>
<td data-sort-value="{{participant.email}}">{{participant.email}}</td> <td data-sort-value="{{participant.email}}">{{participant.email}}</td>
<td data-sort-value="{{ participant.paid}}" class={%if participant.paid %}"greenratio"{%else%}"redratio"{%endif%}> <td data-sort-value="{{ participant.paid}}" class={%if participant.paid %}"greenratio"{%else%}"redratio"{%endif%}>

View file

@ -10,6 +10,7 @@
<td>{{place.spectacle.location}}</td> <td>{{place.spectacle.location}}</td>
<td>{{place.spectacle.date}}</td> <td>{{place.spectacle.date}}</td>
<td>{% if place.double %}deux places{%else%}une place{% endif %}</td> <td>{% if place.double %}deux places{%else%}une place{% endif %}</td>
<td>{% if place.spectacle.listing %}sur listing{% else %}place physique{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{%block realcontent %} {%block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}
<h2>Inscription à une revente</h2> <h2>Inscription à une revente</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}

View file

@ -1,6 +0,0 @@
{% extends "base_title.html" %}
{% block realcontent %}
<h2><strong>Nope</strong></h2>
<p>Avant de revendre des places, il faut aller les payer !</p>
{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles%} {% load static %}
{% block realcontent %} {% block realcontent %}
<h2>Inscriptions pour BdA-Revente</h2> <h2>Inscriptions pour BdA-Revente</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block realcontent %} {% block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %} {% extends "base_title.html" %}
{% load staticfiles %} {% load static %}
{% block extra_head %} {% block extra_head %}
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" /> <link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />

View file

@ -1,7 +1,3 @@
import os
from django.conf import settings
from django.core.management import call_command
from django.utils import timezone from django.utils import timezone
from shared.tests.mixins import ViewTestCaseMixin from shared.tests.mixins import ViewTestCaseMixin
@ -28,12 +24,6 @@ class BdATestHelpers:
if self.bda_testdata: if self.bda_testdata:
self.load_bda_testdata() self.load_bda_testdata()
def require_custommails(self):
data_file = os.path.join(
settings.BASE_DIR, "gestioncof", "management", "data", "custommail.json"
)
call_command("syncmails", data_file, verbosity=0)
def load_bda_testdata(self): def load_bda_testdata(self):
self.tirage = Tirage.objects.create( self.tirage = Tirage.objects.create(
title="Test tirage", title="Test tirage",

View file

@ -19,8 +19,6 @@ User = get_user_model()
class SpectacleReventeTests(TestCase): class SpectacleReventeTests(TestCase):
fixtures = ["gestioncof/management/data/custommail.json"]
def setUp(self): def setUp(self):
now = timezone.now() now = timezone.now()

View file

@ -29,7 +29,7 @@ class InscriptionViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/inscription/{}".format(self.tirage.id) return "/gestion/bda/inscription/{}".format(self.tirage.id)
def test_get_opened(self): def test_get_opened(self):
self.tirage.ouverture = timezone.now() - timedelta(days=1) self.tirage.ouverture = timezone.now() - timedelta(days=1)
@ -149,7 +149,7 @@ class PlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/places/{}".format(self.tirage.id) return "/gestion/bda/places/{}".format(self.tirage.id)
class EtatPlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): class EtatPlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@ -166,7 +166,7 @@ class EtatPlacesViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/etat-places/{}".format(self.tirage.id) return "/gestion/bda/etat-places/{}".format(self.tirage.id)
class TirageViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): class TirageViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@ -185,7 +185,7 @@ class TirageViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/tirage/{}".format(self.tirage.id) return "/gestion/bda/tirage/{}".format(self.tirage.id)
def test_perform_tirage_disabled(self): def test_perform_tirage_disabled(self):
# Cannot be performed if disabled # Cannot be performed if disabled
@ -225,7 +225,7 @@ class SpectacleListViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/spectacles/{}".format(self.tirage.id) return "/gestion/bda/spectacles/{}".format(self.tirage.id)
class SpectacleViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): class SpectacleViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@ -242,7 +242,7 @@ class SpectacleViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/spectacles/{}/{}".format(self.tirage.id, self.show1.id) return "/gestion/bda/spectacles/{}/{}".format(self.tirage.id, self.show1.id)
class UnpaidViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): class UnpaidViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@ -259,7 +259,7 @@ class UnpaidViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/spectacles/unpaid/{}".format(self.tirage.id) return "/gestion/bda/spectacles/unpaid/{}".format(self.tirage.id)
class SendRemindersViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase): class SendRemindersViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@ -276,10 +276,9 @@ class SendRemindersViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
@property @property
def url_expected(self): def url_expected(self):
return "/bda/mails-rappel/{}".format(self.show1.id) return "/gestion/bda/mails-rappel/{}".format(self.show1.id)
def test_post(self): def test_post(self):
self.require_custommails()
resp = self.client.post(self.url) resp = self.client.post(self.url)
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
# TODO: check that emails are sent # TODO: check that emails are sent
@ -292,7 +291,7 @@ class CatalogueViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
bda_testdata = True bda_testdata = True
def test_api_list(self): def test_api_list(self):
url_list = "/bda/catalogue/list" url_list = "/gestion/bda/catalogue/list"
resp = self.client.get(url_list) resp = self.client.get(url_list)
self.assertJSONEqual( self.assertJSONEqual(
resp.content.decode("utf-8"), resp.content.decode("utf-8"),
@ -300,7 +299,7 @@ class CatalogueViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
) )
def test_api_details(self): def test_api_details(self):
url_details = "/bda/catalogue/details?id={}".format(self.tirage.id) url_details = "/gestion/bda/catalogue/details?id={}".format(self.tirage.id)
resp = self.client.get(url_details) resp = self.client.get(url_details)
self.assertJSONEqual( self.assertJSONEqual(
resp.content.decode("utf-8"), resp.content.decode("utf-8"),
@ -311,7 +310,9 @@ class CatalogueViewTestCase(BdATestHelpers, BdAViewTestCaseMixin, TestCase):
) )
def test_api_descriptions(self): def test_api_descriptions(self):
url_descriptions = "/bda/catalogue/descriptions?id={}".format(self.tirage.id) url_descriptions = "/gestion/bda/catalogue/descriptions?id={}".format(
self.tirage.id
)
resp = self.client.get(url_descriptions) resp = self.client.get(url_descriptions)
raw = resp.content.decode("utf-8") raw = resp.content.decode("utf-8")
try: try:
@ -355,7 +356,9 @@ class TestReventeManageTest(TestCase):
def test_can_get(self): def test_can_get(self):
client = Client() client = Client()
client.force_login(self.user) client.force_login(
self.user, backend="django.contrib.auth.backends.ModelBackend"
)
r = client.get(self.url) r = client.get(self.url)
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)

View file

@ -1,74 +1,80 @@
from django.conf.urls import url from django.urls import re_path
from bda import views from bda import views
from bda.views import SpectacleListView from bda.views import SpectacleListView
from gestioncof.decorators import buro_required from gestioncof.decorators import buro_required
urlpatterns = [ urlpatterns = [
url( re_path(
r"^inscription/(?P<tirage_id>\d+)$", r"^inscription/(?P<tirage_id>\d+)$",
views.inscription, views.inscription,
name="bda-tirage-inscription", name="bda-tirage-inscription",
), ),
url(r"^places/(?P<tirage_id>\d+)$", views.places, name="bda-places-attribuees"), re_path(r"^places/(?P<tirage_id>\d+)$", views.places, name="bda-places-attribuees"),
url(r"^etat-places/(?P<tirage_id>\d+)$", views.etat_places, name="bda-etat-places"), re_path(
url(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"), r"^etat-places/(?P<tirage_id>\d+)$", views.etat_places, name="bda-etat-places"
url( ),
re_path(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"),
re_path(
r"^spectacles/(?P<tirage_id>\d+)$", r"^spectacles/(?P<tirage_id>\d+)$",
buro_required(SpectacleListView.as_view()), buro_required(SpectacleListView.as_view()),
name="bda-liste-spectacles", name="bda-liste-spectacles",
), ),
url( re_path(
r"^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$", r"^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$",
views.spectacle, views.spectacle,
name="bda-spectacle", name="bda-spectacle",
), ),
url( re_path(
r"^spectacles/unpaid/(?P<tirage_id>\d+)$", r"^spectacles/unpaid/(?P<tirage_id>\d+)$",
views.UnpaidParticipants.as_view(), views.UnpaidParticipants.as_view(),
name="bda-unpaid", name="bda-unpaid",
), ),
url( re_path(
r"^spectacles/autocomplete$", r"^spectacles/autocomplete$",
views.spectacle_autocomplete, views.spectacle_autocomplete,
name="bda-spectacle-autocomplete", name="bda-spectacle-autocomplete",
), ),
url( re_path(
r"^participants/autocomplete$", r"^participants/autocomplete$",
views.participant_autocomplete, views.participant_autocomplete,
name="bda-participant-autocomplete", name="bda-participant-autocomplete",
), ),
# Urls BdA-Revente # Urls BdA-Revente
url( re_path(
r"^revente/(?P<tirage_id>\d+)/manage$", r"^revente/(?P<tirage_id>\d+)/manage$",
views.revente_manage, views.revente_manage,
name="bda-revente-manage", name="bda-revente-manage",
), ),
url( re_path(
r"^revente/(?P<tirage_id>\d+)/subscribe$", r"^revente/(?P<tirage_id>\d+)/subscribe$",
views.revente_subscribe, views.revente_subscribe,
name="bda-revente-subscribe", name="bda-revente-subscribe",
), ),
url( re_path(
r"^revente/(?P<tirage_id>\d+)/tirages$", r"^revente/(?P<tirage_id>\d+)/tirages$",
views.revente_tirages, views.revente_tirages,
name="bda-revente-tirages", name="bda-revente-tirages",
), ),
url( re_path(
r"^revente/(?P<spectacle_id>\d+)/buy$", r"^revente/(?P<spectacle_id>\d+)/buy$",
views.revente_buy, views.revente_buy,
name="bda-revente-buy", name="bda-revente-buy",
), ),
url( re_path(
r"^revente/(?P<revente_id>\d+)/confirm$", r"^revente/(?P<revente_id>\d+)/confirm$",
views.revente_confirm, views.revente_confirm,
name="bda-revente-confirm", name="bda-revente-confirm",
), ),
url( re_path(
r"^revente/(?P<tirage_id>\d+)/shotgun$", r"^revente/(?P<tirage_id>\d+)/shotgun$",
views.revente_shotgun, views.revente_shotgun,
name="bda-revente-shotgun", name="bda-revente-shotgun",
), ),
url(r"^mails-rappel/(?P<spectacle_id>\d+)$", views.send_rappel, name="bda-rappels"), re_path(
url(r"^catalogue/(?P<request_type>[a-z]+)$", views.catalogue, name="bda-catalogue"), r"^mails-rappel/(?P<spectacle_id>\d+)$", views.send_rappel, name="bda-rappels"
),
re_path(
r"^catalogue/(?P<request_type>[a-z]+)$", views.catalogue, name="bda-catalogue"
),
] ]

View file

@ -4,17 +4,17 @@ import random
import time import time
from collections import defaultdict from collections import defaultdict
from custommail.models import CustomMail
from custommail.shortcuts import send_custom_mail, send_mass_custom_mail
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.core import serializers from django.core import serializers
from django.core.exceptions import NON_FIELD_ERRORS from django.core.exceptions import NON_FIELD_ERRORS
from django.core.mail import send_mail, send_mass_mail
from django.db import transaction from django.db import transaction
from django.db.models import Count, Prefetch from django.db.models import Count, Prefetch
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.template import loader
from django.template.defaultfilters import pluralize from django.template.defaultfilters import pluralize
from django.urls import reverse from django.urls import reverse
from django.utils import formats, timezone from django.utils import formats, timezone
@ -274,13 +274,13 @@ def do_tirage(tirage_elt, token):
results = Algorithm(data["shows"], data["members"], choices)(token) results = Algorithm(data["shows"], data["members"], choices)(token)
# On compte les places attribuées et les déçus # On compte les places attribuées et les déçus
for (_, members, losers) in results: for _, members, losers in results:
data["total_slots"] += len(members) data["total_slots"] += len(members)
data["total_losers"] += len(losers) data["total_losers"] += len(losers)
# On calcule le déficit et les bénéfices pour le BdA # On calcule le déficit et les bénéfices pour le BdA
# FIXME: le traitement de l'opéra est sale # FIXME: le traitement de l'opéra est sale
for (show, members, _) in results: for show, members, _ in results:
deficit = (show.slots - len(members)) * show.price deficit = (show.slots - len(members)) * show.price
data["total_sold"] += show.slots * show.price data["total_sold"] += show.slots * show.price
if deficit >= 0: if deficit >= 0:
@ -293,8 +293,8 @@ def do_tirage(tirage_elt, token):
# so assign a single object for each Participant id # so assign a single object for each Participant id
members_uniq = {} members_uniq = {}
members2 = {} members2 = {}
for (show, members, _) in results: for show, members, _ in results:
for (member, _, _, _) in members: for member, _, _, _ in members:
if member.id not in members_uniq: if member.id not in members_uniq:
members_uniq[member.id] = member members_uniq[member.id] = member
members2[member] = [] members2[member] = []
@ -385,12 +385,6 @@ def revente_manage(request, tirage_id):
user=request.user, tirage=tirage user=request.user, tirage=tirage
) )
# If the participant has just been created, the `paid` field is not
# automatically added by our custom ObjectManager. Skip the check in this
# scenario.
if not created and not participant.paid:
return render(request, "bda/revente/notpaid.html", {})
resellform = ResellForm(participant, prefix="resell") resellform = ResellForm(participant, prefix="resell")
annulform = AnnulForm(participant, prefix="annul") annulform = AnnulForm(participant, prefix="annul")
soldform = SoldForm(participant, prefix="sold") soldform = SoldForm(participant, prefix="sold")
@ -400,7 +394,7 @@ def revente_manage(request, tirage_id):
if "resell" in request.POST: if "resell" in request.POST:
resellform = ResellForm(participant, request.POST, prefix="resell") resellform = ResellForm(participant, request.POST, prefix="resell")
if resellform.is_valid(): if resellform.is_valid():
datatuple = [] mails = []
attributions = resellform.cleaned_data["attributions"] attributions = resellform.cleaned_data["attributions"]
with transaction.atomic(): with transaction.atomic():
for attribution in attributions: for attribution in attributions:
@ -415,16 +409,17 @@ def revente_manage(request, tirage_id):
"show": attribution.spectacle, "show": attribution.spectacle,
"revente": revente, "revente": revente,
} }
datatuple.append( mails.append(
( (
"bda-revente-new", "BdA-Revente : {}".format(attribution.spectacle),
context, loader.render_to_string(
"bda/mails/revente-seller.txt", context=context
),
settings.MAIL_DATA["revente"]["FROM"], settings.MAIL_DATA["revente"]["FROM"],
[participant.user.email], [participant.user.email],
) )
) )
revente.save() send_mass_mail(mails)
send_mass_custom_mail(datatuple)
# On annule une revente # On annule une revente
elif "annul" in request.POST: elif "annul" in request.POST:
annulform = AnnulForm(participant, request.POST, prefix="annul") annulform = AnnulForm(participant, request.POST, prefix="annul")
@ -643,12 +638,16 @@ def revente_buy(request, spectacle_id):
"acheteur": request.user, "acheteur": request.user,
"vendeur": revente.seller.user, "vendeur": revente.seller.user,
} }
send_custom_mail(
"bda-buy-shotgun", send_mail(
"bda@ens.fr", "BdA-Revente : {}".format(spectacle.title),
loader.render_to_string(
"bda/mails/revente-shotgun-seller.txt", context=context
),
request.user.email,
[revente.seller.user.email], [revente.seller.user.email],
context=context,
) )
return render( return render(
request, request,
"bda/revente/mail-success.html", "bda/revente/mail-success.html",
@ -701,7 +700,7 @@ def spectacle(request, tirage_id, spectacle_id):
"username": participant.user.username, "username": participant.user.username,
"email": participant.user.email, "email": participant.user.email,
"given": int(attrib.given), "given": int(attrib.given),
"paid": True, "paid": attrib.paid,
"nb_places": 1, "nb_places": 1,
} }
if participant.id in participants: if participant.id in participants:
@ -751,19 +750,21 @@ class UnpaidParticipants(BuroRequiredMixin, ListView):
def send_rappel(request, spectacle_id): def send_rappel(request, spectacle_id):
show = get_object_or_404(Spectacle, id=spectacle_id) show = get_object_or_404(Spectacle, id=spectacle_id)
# Mails d'exemples # Mails d'exemples
custommail = CustomMail.objects.get(shortname="bda-rappel") subject = show.title
exemple_mail_1place = custommail.render( body_mail_1place = loader.render_to_string(
{"member": request.user, "show": show, "nb_attr": 1} "bda/mails/rappel.txt",
context={"member": request.user, "show": show, "nb_attr": 1},
) )
exemple_mail_2places = custommail.render( body_mail_2places = loader.render_to_string(
{"member": request.user, "show": show, "nb_attr": 2} "bda/mails/rappel.txt",
context={"member": request.user, "show": show, "nb_attr": 2},
) )
# Contexte # Contexte
ctxt = { ctxt = {
"show": show, "show": show,
"exemple_mail_1place": exemple_mail_1place, "exemple_mail_1place": (subject, body_mail_1place),
"exemple_mail_2places": exemple_mail_2places, "exemple_mail_2places": (subject, body_mail_2places),
"custommail": custommail,
} }
# Envoi confirmé # Envoi confirmé
if request.method == "POST": if request.method == "POST":

View file

@ -1,5 +1,28 @@
from django.apps import AppConfig from django.apps import AppConfig, apps as global_apps
from django.db.models import Q
from django.db.models.signals import post_migrate
def bds_group_perms(app_config, apps=global_apps, **kwargs):
try:
Permission = apps.get_model("auth", "Permission")
Group = apps.get_model("auth", "Group")
group = Group.objects.get(name="Burô du BDS")
perms = Permission.objects.filter(
Q(content_type__app_label="bds")
| Q(content_type__app_label="auth") & Q(content_type__model="user")
)
group.permissions.set(perms)
group.save()
except (LookupError, Group.DoesNotExist):
return
class BdsConfig(AppConfig): class BdsConfig(AppConfig):
name = "bds" name = "bds"
verbose_name = "Gestion des adhérent·e·s du BDS"
def ready(self):
post_migrate.connect(bds_group_perms, sender=self)

View file

@ -47,7 +47,7 @@ class BDSOthersSearch(autocomplete.ModelSearch):
class BDSLDAPSearch(autocomplete.LDAPSearch): class BDSLDAPSearch(autocomplete.LDAPSearch):
def result_link(self, clipper): def result_link(self, clipper):
url = reverse("bds:user.create.fromclipper", args=(clipper.clipper,)) url = reverse("bds:user.create.fromclipper", args=(clipper.clipper,))
get = {"fullname": clipper.fullname} get = {"fullname": clipper.fullname, "mail": clipper.mail}
return "{}?{}".format(url, urlencode(get)) return "{}?{}".format(url, urlencode(get))

View file

@ -1,6 +1,7 @@
from django import forms from django import forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _
from bds.models import BDSProfile from bds.models import BDSProfile
@ -8,6 +9,8 @@ User = get_user_model()
class UserForm(forms.ModelForm): class UserForm(forms.ModelForm):
is_buro = forms.BooleanField(label=_("Membre du Burô"), required=False)
class Meta: class Meta:
model = User model = User
fields = ["email", "first_name", "last_name"] fields = ["email", "first_name", "last_name"]
@ -33,4 +36,6 @@ class ProfileForm(forms.ModelForm):
class Meta: class Meta:
model = BDSProfile model = BDSProfile
exclude = ["user"] exclude = ["user"]
widgets = {"birthdate": forms.DateInput(attrs={"type": "date"})} widgets = {
"birthdate": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d")
}

View file

@ -8,7 +8,6 @@ import bds.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]

View file

@ -1,28 +1,14 @@
# Generated by Django 2.2 on 2019-07-17 14:56 # Generated by Django 2.2 on 2019-07-17 14:56
from django.contrib.auth.management import create_permissions
from django.db import migrations from django.db import migrations
from django.db.models import Q
def create_bds_buro_group(apps, schema_editor): def create_bds_buro_group(apps, schema_editor):
for app_config in apps.get_app_configs():
create_permissions(app_config, apps=apps, verbosity=0)
Group = apps.get_model("auth", "Group") Group = apps.get_model("auth", "Group")
Permission = apps.get_model("auth", "Permission") Group.objects.get_or_create(name="Burô du BDS")
group, created = Group.objects.get_or_create(name="Burô du BDS")
if created:
perms = Permission.objects.filter(
Q(content_type__app_label="bds")
| Q(content_type__app_label="auth") & Q(content_type__model="user")
)
group.permissions.set(perms)
group.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [("bds", "0001_initial")] dependencies = [("bds", "0001_initial")]
operations = [ operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("bds", "0002_bds_group"), ("bds", "0002_bds_group"),
] ]
@ -18,5 +17,8 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Profils BDS", "verbose_name_plural": "Profils BDS",
}, },
), ),
migrations.RemoveField(model_name="bdsprofile", name="is_buro",), migrations.RemoveField(
model_name="bdsprofile",
name="is_buro",
),
] ]

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("bds", "0003_staff_permission"), ("bds", "0003_staff_permission"),
] ]

View file

@ -4,11 +4,13 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("bds", "0004_is_member_cotiz_type"), ("bds", "0004_is_member_cotiz_type"),
] ]
operations = [ operations = [
migrations.RemoveField(model_name="bdsprofile", name="certificate_file",), migrations.RemoveField(
model_name="bdsprofile",
name="certificate_file",
),
] ]

View file

@ -0,0 +1,22 @@
# Generated by Django 2.2.12 on 2020-08-28 12:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bds", "0005_remove_bdsprofile_certificate_file"),
]
operations = [
migrations.AddField(
model_name="bdsprofile",
name="comments",
field=models.TextField(
blank=True,
help_text="Attention : l'utilisateur·ice dispose d'un droit d'accès"
" aux données le/la concernant, dont le contenu de ce champ !",
verbose_name="commentaires",
),
),
]

View file

@ -9,7 +9,7 @@ class StaffRequiredMixin(PermissionRequiredMixin):
class MultipleFormMixin(ContextMixin): class MultipleFormMixin(ContextMixin):
""" Mixin pour gérer plusieurs formulaires dans la même vue. """Mixin pour gérer plusieurs formulaires dans la même vue.
Le fonctionnement est relativement identique à celui de Le fonctionnement est relativement identique à celui de
FormMixin, dont la documentation est disponible ici : FormMixin, dont la documentation est disponible ici :
https://docs.djangoproject.com/en/3.0/ref/class-based-views/mixins-editing/ https://docs.djangoproject.com/en/3.0/ref/class-based-views/mixins-editing/
@ -97,7 +97,7 @@ class MultipleFormMixin(ContextMixin):
class ProcessMultipleFormView(View): class ProcessMultipleFormView(View):
""" Équivalent de `ProcessFormView` pour plusieurs forms. """Équivalent de `ProcessFormView` pour plusieurs forms.
Note : il faut que *tous* les formulaires soient valides pour Note : il faut que *tous* les formulaires soient valides pour
qu'ils soient sauvegardés ! qu'ils soient sauvegardés !
""" """

View file

@ -3,6 +3,7 @@ from os.path import splitext
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from shared.utils import choices_length from shared.utils import choices_length
@ -84,6 +85,25 @@ class BDSProfile(models.Model):
_("type de cotisation"), choices=TYPE_COTIZ_CHOICES, max_length=9 _("type de cotisation"), choices=TYPE_COTIZ_CHOICES, max_length=9
) )
comments = models.TextField(
_("commentaires"),
blank=True,
help_text=_(
"Attention : l'utilisateur·ice dispose d'un droit d'accès aux données "
"le/la concernant, dont le contenu de ce champ !"
),
)
@classmethod
def expired_members(cls):
now = timezone.now()
qs = cls.objects.filter(is_member=True)
if now.month > 1 and now.month < 7:
return qs.filter(cotisation_period="SE1")
elif now.month < 2 or now.month > 8:
return qs.none()
return qs
class Meta: class Meta:
verbose_name = _("Profil BDS") verbose_name = _("Profil BDS")
verbose_name_plural = _("Profils BDS") verbose_name_plural = _("Profils BDS")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
bds/static/bds/js/bds.js Normal file
View file

@ -0,0 +1,7 @@
$(function () {
// Close notifications when delete button is pressed
$(".notification .delete").on("click", function () {
$(this).parent().remove();
});
});

View file

@ -123,6 +123,16 @@ input[type="submit"] {
} }
} }
.button.is-primary {
background-color: $button_color;
color: findColorInvert($button_color);
&:hover {
background-color: $primary_color;
color: findColorInvert($primary_color);
}
}
/* --- Message styling --- */ /* --- Message styling --- */
.notification { .notification {
@ -131,3 +141,12 @@ input[type="submit"] {
text-align: center; text-align: center;
} }
/* --- Modals --- */
.modal-card-head {
background-color: $primary_color;
.modal-card-title {
color: findColorInvert($primary_color);
}
}

View file

@ -1,4 +1,4 @@
{% load staticfiles %} {% load static %}
{% load bulma_utils %} {% load bulma_utils %}
<!DOCTYPE html> <!DOCTYPE html>
@ -16,15 +16,17 @@
{# Javascript #} {# Javascript #}
<script src="{% static 'vendor/jquery/jquery-3.3.1.min.js' %}"></script> <script src="{% static 'vendor/jquery/jquery-3.3.1.min.js' %}"></script>
<script src="{% static "vendor/jquery/jquery.autocomplete-light.min.js" %}"></script> <script src="{% static "vendor/jquery/jquery.autocomplete-light.min.js" %}"></script>
<script src="{% static 'bds/js/bds.js' %}"></script>
{% block extra_head %}{% endblock extra_head %} {% block extra_head %}{% endblock extra_head %}
</head> </head>
<body> <body>
{% include "bds/nav.html" %} {% include "bds/nav.html" %}
{% block layout %}
<section class="section">
<div class="columns"> <div class="columns">
<div class="column is-two-thirds is-offset-2"> <div class="column is-two-thirds is-offset-2">
<section class="section">
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="notification is-{{ message.level_tag|bulma_message_tag }}"> <div class="notification is-{{ message.level_tag|bulma_message_tag }}">
@ -33,14 +35,16 @@
{% else %} {% else %}
{{ message }} {{ message }}
{% endif %} {% endif %}
<button class="delete"></button>
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% block content %} {% block content %}
{% endblock content %} {% endblock content %}
</div>
</div>
</section> </section>
</div>
</div>
{% endblock layout %}
</body> </body>
</html> </html>

View file

@ -1,28 +0,0 @@
{% extends "bds/base.html" %}
{% load bulma_utils %}
{% block body %}
{% include "bds/nav.html" %}
<section class="section">
<div class="columns">
<div class="column is-two-thirds is-offset-2">
{% if messages %}
{% for message in messages %}
<div class="notification is-{{ message.level_tag|bulma_message_tag }}">
{% if 'safe' in message.tags %}
{{ message|safe }}
{% else %}
{{ message }}
{% endif %}
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock content %}
</div>
</div>
</section>
{% endblock body %}

View file

@ -0,0 +1,22 @@
{% extends "bds/base.html" %}
{% block content %}
<h1 class="title">Liste des adhésions expirées</h1>
{% if object_list %}
<div class="content">
<ul>
{% for p in object_list %}
<li>{{ p.user.first_name }} {{ p.user.last_name }} ({{ p.user.username }}), {{ p.get_cotisation_period_display }}</li>
{% endfor %}
</ul>
</div>
<div class="buttons is-centered">
<a class="button is-danger" href="{% url 'bds:members.reset' %}">Réinitialiser les adhésions expirées</a>
</div>
{% endif %}
{% endblock %}

View file

@ -1,3 +1,5 @@
{% load bulma_utils %}
<label class="label {% if field.field.required %}{{ form.required_css_class }}{% endif %}"> <label class="label {% if field.field.required %}{{ form.required_css_class }}{% endif %}">
{{ field.label }} {{ field.label }}
</label> </label>

View file

@ -1,12 +1,46 @@
{% extends "bds/base.html" %} {% extends "bds/base.html" %}
{% load bulma_utils %}
{% block content %} {% block layout %}
<div class="container"> <div class="columns">
<div class="column is-2">
<section class="section">
<div class="box">
<div class="content has-text-centered">
<h1>{{ member_count }}</h1>
adhérent·e·s
</div>
</div>
</section>
</div>
<div class="column is-two-thirds">
<section class="section">
{% if messages %}
{% for message in messages %}
<div class="notification is-{{ message.level_tag|bulma_message_tag }}">
{% if 'safe' in message.tags %}
{{ message|safe }}
{% else %}
{{ message }}
{% endif %}
<button class="delete"></button>
</div>
{% endfor %}
{% endif %}
<div class="container">
Bienvenue sur GestioBDS&#8239;! Bienvenue sur GestioBDS&#8239;!
<br> <br>
<br> <br>
<a class=button href="{% url 'bds:export.members' %}">Télécharger la liste des membres (CSV)</a>
<a class=button href="{% url 'bds:members.expired' %}">Liste des adhésions expirées ({{ nb_expired }})</a>
<br>
<br>
Le site est encore en développement. Le site est encore en développement.
<br> <br>
Suivez notre avancement sur Suivez notre avancement sur
@ -18,5 +52,11 @@
ou en ouvrant une ou en ouvrant une
<a href="#https://git.eleves.ens.fr/klub-dev-ens/gestioCOF/issues?milestone_title=Int%C3%A9gration+du+BDS"> <a href="#https://git.eleves.ens.fr/klub-dev-ens/gestioCOF/issues?milestone_title=Int%C3%A9gration+du+BDS">
issue</a>. issue</a>.
</div>
</section>
</div>
</div> </div>
{% endblock %} {% endblock layout %}

View file

@ -12,7 +12,7 @@
<div class="level-item" id="logout-mobile"> <div class="level-item" id="logout-mobile">
<figure class="image is-64x64"> <figure class="image is-64x64">
<a href="#TODO"> <a href="{% url 'authens:logout' %}">
<img src="{% static "bds/images/logout.svg" %}" alt="logout"> <img src="{% static "bds/images/logout.svg" %}" alt="logout">
</a> </a>
</figure> </figure>
@ -31,7 +31,7 @@
<div class="level-item" id="logout"> <div class="level-item" id="logout">
<figure class="image is-64x64"> <figure class="image is-64x64">
<a href="#TODO"> <a href="{% url 'authens:logout' %}">
<img src="{% static "bds/images/logout.svg" %}" alt="logout"> <img src="{% static "bds/images/logout.svg" %}" alt="logout">
</a> </a>
</figure> </figure>

View file

@ -5,29 +5,100 @@
{% block content %} {% block content %}
{% for form in forms.values %} {% for form in forms.values %}
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
<div class="notification is-danger"> <div class="notification is-danger">
{{ error }} {{ error }}
</div> </div>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
<h1 class="title">{% trans "Modification du profil " %}{{ view.user.username }}</h1> <h1 class="title">{% trans "Modification du profil " %}{{ view.user.username }}</h1>
<div class="container"> <div class="container">
<form action="" method="post"> <form method="post" action="" id="user-update-form">
{% csrf_token %} {% csrf_token %}
{% for form in forms.values %} {% for form in forms.values %}
{% include "bds/forms/form.html" with form=form errors=False %} {% include "bds/forms/form.html" with form=form errors=False %}
{% endfor %} {% endfor %}
<div class="field">
<p class="control">
<input class="button is-fullwidth" type="submit" value="Enregistrer">
</p>
</div>
</form> </form>
<form method="post" action="{% url 'bds:user.delete' view.user.pk %}" id="user-delete-form">
{% csrf_token %}
</form>
<div class="columns is-gapless mt-5">
<div class="column is-5">
<button id="user-update-button" class="button is-fullwidth is-primary">Enregistrer</button>
</div>
<div class="column is-2">
</div>
<div class="column is-5">
<button id="user-delete-button" class="button is-fullwidth is-danger">Supprimer</button>
</div>
</div>
</div> </div>
<!-- Hidden by default -->
<div id="confirm-delete-modal" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Confirmer la suppression</p>
<button id="modal-close" class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
Voulez-vous réellement supprimer ce compte ? Attention, cette opération est irréversible !
</section>
<footer class="modal-card-foot">
<button id="confirm-delete-button" class="button is-primary">Supprimer</button>
<button id="cancel-delete-button" class="button">Annuler</button>
</footer>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
"use strict";
// Adapted from https://bulma.io/lib/main.js
function openModal(target) {
$(document).addClass('is-clipped');
$(`#${target}`).addClass('is-active');
}
function closeModals() {
$(document).removeClass('is-clipped');
$(".modal").removeClass('is-active');
}
// Si on clique sur enregistrer, ça marche
$("#user-update-button").on("click", function() {
$("#user-update-form").submit();
})
// Si on clique sur supprimer, confirmation demandée
$("#user-delete-button").on("click", function() {
openModal("confirm-delete-modal");
});
$(".modal-background, #modal-close, #cancel-delete-button").on("click", closeModals);
$("#confirm-delete-button").on("click", function() {
$("#user-delete-form").submit();
});
$(document).on("keydown", function(e) {
if (e.key == "Escape") {
closeModals();
}
if (e.key == "Enter") {
$("#user-update-form").submit();
}
});
});
</script>
{% endblock %} {% endblock %}

View file

@ -22,6 +22,18 @@ def login_url(next=None):
return "{}?next={}".format(login_url, next) return "{}?next={}".format(login_url, next)
class TestHomeView(TestCase):
@mock.patch("gestioncof.signals.messages")
def test_get(self, mock_messages):
user = User.objects.create_user(username="random_user")
give_bds_buro_permissions(user)
self.client.force_login(
user, backend="django.contrib.auth.backends.ModelBackend"
)
resp = self.client.get(reverse("bds:home"))
self.assertEqual(resp.status_code, 200)
class TestRegistrationView(TestCase): class TestRegistrationView(TestCase):
@mock.patch("gestioncof.signals.messages") @mock.patch("gestioncof.signals.messages")
def test_get_autocomplete(self, mock_messages): def test_get_autocomplete(self, mock_messages):
@ -34,20 +46,19 @@ class TestRegistrationView(TestCase):
self.assertRedirects(resp, login_url(next=url)) self.assertRedirects(resp, login_url(next=url))
# Logged-in but unprivileged GET # Logged-in but unprivileged GET
client.force_login(user) client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
resp = client.get(url) resp = client.get(url)
self.assertEquals(resp.status_code, 403) self.assertEqual(resp.status_code, 403)
# Burô user GET # Burô user GET
give_bds_buro_permissions(user) give_bds_buro_permissions(user)
resp = client.get(url) resp = client.get(url)
self.assertEquals(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@mock.patch("gestioncof.signals.messages") @mock.patch("gestioncof.signals.messages")
def test_get(self, mock_messages): def test_get(self, mock_messages):
user = User.objects.create_user(username="toto") user = User.objects.create_user(username="toto")
url = reverse("bds:user.update", args=(user.id,)) url = reverse("bds:user.update", args=(user.id,))
print(url)
client = Client() client = Client()
# Anonymous GET # Anonymous GET
@ -55,11 +66,11 @@ class TestRegistrationView(TestCase):
self.assertRedirects(resp, login_url(next=url)) self.assertRedirects(resp, login_url(next=url))
# Logged-in but unprivileged GET # Logged-in but unprivileged GET
client.force_login(user) client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
resp = client.get(url) resp = client.get(url)
self.assertEquals(resp.status_code, 403) self.assertEqual(resp.status_code, 403)
# Burô user GET # Burô user GET
give_bds_buro_permissions(user) give_bds_buro_permissions(user)
resp = client.get(url) resp = client.get(url)
self.assertEquals(resp.status_code, 200) self.assertEqual(resp.status_code, 200)

View file

@ -13,4 +13,12 @@ urlpatterns = [
views.UserCreateView.as_view(), views.UserCreateView.as_view(),
name="user.create.fromclipper", name="user.create.fromclipper",
), ),
path("user/delete/<int:pk>", views.UserDeleteView.as_view(), name="user.delete"),
path("members", views.export_members, name="export.members"),
path(
"members/expired",
views.ResetMembershipListView.as_view(),
name="members.expired",
),
path("members/reset", views.ResetMembershipView.as_view(), name="members.reset"),
] ]

View file

@ -1,13 +1,19 @@
import csv
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import Permission
from django.http import HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView from django.views.generic import DeleteView, ListView, RedirectView, TemplateView
from bds.autocomplete import bds_search from bds.autocomplete import bds_search
from bds.forms import ProfileForm, UserForm, UserFromClipperForm, UserFromScratchForm from bds.forms import ProfileForm, UserForm, UserFromClipperForm, UserFromScratchForm
from bds.mixins import MultipleFormView, StaffRequiredMixin from bds.mixins import MultipleFormView, StaffRequiredMixin
from bds.models import BDSProfile
from shared.views import AutocompleteView from shared.views import AutocompleteView
User = get_user_model() User = get_user_model()
@ -21,6 +27,12 @@ class BDSAutocompleteView(StaffRequiredMixin, AutocompleteView):
class Home(StaffRequiredMixin, TemplateView): class Home(StaffRequiredMixin, TemplateView):
template_name = "bds/home.html" template_name = "bds/home.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["member_count"] = BDSProfile.objects.filter(is_member=True).count()
context["nb_expired"] = BDSProfile.expired_members().count()
return context
class UserUpdateView(StaffRequiredMixin, MultipleFormView): class UserUpdateView(StaffRequiredMixin, MultipleFormView):
template_name = "bds/user_update.html" template_name = "bds/user_update.html"
@ -30,6 +42,9 @@ class UserUpdateView(StaffRequiredMixin, MultipleFormView):
"profile": ProfileForm, "profile": ProfileForm,
} }
def get_user_initial(self):
return {"is_buro": self.get_user_instance().has_perm("bds.is_team")}
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.user = get_object_or_404(User, pk=self.kwargs["pk"]) self.user = get_object_or_404(User, pk=self.kwargs["pk"])
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -46,6 +61,11 @@ class UserUpdateView(StaffRequiredMixin, MultipleFormView):
def form_valid(self, forms): def form_valid(self, forms):
user = forms["user"].save() user = forms["user"].save()
profile = forms["profile"].save(commit=False) profile = forms["profile"].save(commit=False)
perm = Permission.objects.get(content_type__app_label="bds", codename="is_team")
if forms["user"].cleaned_data["is_buro"]:
user.user_permissions.add(perm)
else:
user.user_permissions.remove(perm)
profile.user = user profile.user = user
profile.save() profile.save()
messages.success(self.request, _("Profil mis à jour avec succès !")) messages.success(self.request, _("Profil mis à jour avec succès !"))
@ -73,7 +93,7 @@ class UserCreateView(StaffRequiredMixin, MultipleFormView):
def get_user_initial(self): def get_user_initial(self):
if "clipper" in self.kwargs: if "clipper" in self.kwargs:
clipper = self.kwargs["clipper"] clipper = self.kwargs["clipper"]
email = "{}@clipper.ens.fr".format(clipper) email = self.request.GET.get("mail", "{}@clipper.ens.fr".format(clipper))
fullname = self.request.GET.get("fullname", None) fullname = self.request.GET.get("fullname", None)
if fullname: if fullname:
@ -109,3 +129,56 @@ class UserCreateView(StaffRequiredMixin, MultipleFormView):
def form_invalid(self, forms): def form_invalid(self, forms):
messages.error(self.request, _("Veuillez corriger les erreurs ci-dessous")) messages.error(self.request, _("Veuillez corriger les erreurs ci-dessous"))
return super().form_invalid(forms) return super().form_invalid(forms)
class UserDeleteView(StaffRequiredMixin, DeleteView):
model = User
success_url = reverse_lazy("bds:home")
success_message = "Profil supprimé avec succès !"
def delete(self, request, *args, **kwargs):
# SuccessMessageMixin does not work with DeleteView, see :
# https://code.djangoproject.com/ticket/21926
messages.success(request, self.success_message)
return super().delete(request, *args, **kwargs)
class ResetMembershipListView(StaffRequiredMixin, ListView):
model = BDSProfile
template_name = "bds/expired_members.html"
def get_queryset(self):
return BDSProfile.expired_members()
class ResetMembershipView(StaffRequiredMixin, RedirectView):
url = reverse_lazy("bds:members.expired")
def get(self, request, *args, **kwargs):
qs = BDSProfile.expired_members()
nb = qs.count()
qs.update(cotisation_period="NO", is_member=False, mails_bds=False)
messages.success(request, f"{nb} adhésions réinitialisées")
return super().get(request, *args, **kwargs)
@permission_required("bds.is_team")
def export_members(request):
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=membres_bds.csv"
writer = csv.writer(response)
for profile in BDSProfile.objects.filter(is_member=True).all():
user = profile.user
bits = [
user.username,
user.get_full_name(),
user.email,
]
writer.writerow([str(bit) for bit in bits])
return response

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]

View file

@ -1,8 +0,0 @@
import os
from channels.asgi import get_channel_layer
if "DJANGO_SETTINGS_MODULE" not in os.environ:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings")
channel_layer = get_channel_layer()

View file

@ -1,3 +0,0 @@
from channels.routing import include
routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")]

View file

@ -1,32 +0,0 @@
"""
Django development settings for the cof project.
The settings that are not listed here are imported from .common
"""
import os
from .common import * # NOQA
from .common import BASE_DIR, INSTALLED_APPS
# ---
# BDS-only Django settings
# ---
ALLOWED_HOSTS = ["bds.ens.fr", "www.bds.ens.fr", "dev.cof.ens.fr"]
INSTALLED_APPS += ["bds", "events", "clubs"]
STATIC_ROOT = os.path.join(
os.path.dirname(os.path.dirname(BASE_DIR)), "public", "gestion", "static"
)
STATIC_URL = "/gestion/static/"
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media")
MEDIA_URL = "/gestion/media/"
# ---
# Auth-related stuff
# ---
LOGIN_URL = "admin:login"
LOGIN_REDIRECT_URL = "bds:home"

View file

@ -1,163 +0,0 @@
"""
Fichier principal de configuration des urls du projet GestioCOF
"""
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth import views as django_auth_views
from django.urls import include, path
from django.views.generic.base import TemplateView
from django_cas_ng import views as django_cas_views
admin.autodiscover()
urlpatterns = [
path("admin/doc/", include("django.contrib.admindocs.urls")),
path("admin/", admin.site.urls),
]
if "gestioncof" in settings.INSTALLED_APPS:
from django_js_reverse.views import urls_js
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from gestioncof import csv_views, views as gestioncof_views
from gestioncof.urls import (
calendar_patterns,
clubs_patterns,
events_patterns,
export_patterns,
surveys_patterns,
)
# Also includes BdA, K-Fêt, etc.
urlpatterns += [
path("admin/logout/", gestioncof_views.logout),
path(
"admin/<slug:app_label>/<slug:model_name>/csv/",
csv_views.admin_list_export,
{"fields": ["username"]},
),
# Page d'accueil
path("", gestioncof_views.HomeView.as_view(), name="home"),
# Le BdA
path("bda/", include("bda.urls")),
# Les exports
path("export/", include(export_patterns)),
# Les petits cours
path("petitcours/", include("petitscours.urls")),
# Les sondages
path("survey/", include(surveys_patterns)),
# Evenements
path("event/", include(events_patterns)),
# Calendrier
path("calendar/", include(calendar_patterns)),
# Clubs
path("clubs/", include(clubs_patterns)),
# Authentification
path(
"cof/denied",
TemplateView.as_view(template_name="cof-denied.html"),
name="cof-denied",
),
path("cas/login", django_cas_views.LoginView.as_view(), name="cas_login_view"),
path("cas/logout", django_cas_views.LogoutView.as_view()),
path(
"outsider/login",
gestioncof_views.LoginExtView.as_view(),
name="ext_login_view",
),
path(
"outsider/logout",
django_auth_views.LogoutView.as_view(),
{"next_page": "home"},
),
path("login", gestioncof_views.login, name="cof-login"),
path("logout", gestioncof_views.logout, name="cof-logout"),
# Infos persos
path("profile", gestioncof_views.profile, name="profile"),
path(
"outsider/password-change",
django_auth_views.PasswordChangeView.as_view(),
name="password_change",
),
path(
"outsider/password-change-done",
django_auth_views.PasswordChangeDoneView.as_view(),
name="password_change_done",
),
# Inscription d'un nouveau membre
path("registration", gestioncof_views.registration, name="registration"),
path(
"registration/clipper/<slug:login_clipper>/<fullname>",
gestioncof_views.registration_form2,
name="clipper-registration",
),
path(
"registration/user/<username>",
gestioncof_views.registration_form2,
name="user-registration",
),
path(
"registration/empty",
gestioncof_views.registration_form2,
name="empty-registration",
),
# Autocompletion
path(
"autocomplete/registration",
gestioncof_views.RegistrationAutocompleteView.as_view(),
name="cof.registration.autocomplete",
),
path(
"user/autocomplete",
gestioncof_views.UserAutocompleteView.as_view(),
name="cof-user-autocomplete",
),
# Liens utiles du COF et du BdA
path("utile_cof", gestioncof_views.utile_cof, name="utile_cof"),
path("utile_bda", gestioncof_views.utile_bda, name="utile_bda"),
path("utile_bda/bda_diff", gestioncof_views.liste_bdadiff, name="ml_diffbda"),
path("utile_cof/diff_cof", gestioncof_views.liste_diffcof, name="ml_diffcof"),
path(
"utile_bda/bda_revente",
gestioncof_views.liste_bdarevente,
name="ml_bda_revente",
),
path("k-fet/", include("kfet.urls")),
path("cms/", include(wagtailadmin_urls)),
path("documents/", include(wagtaildocs_urls)),
# djconfig
path("config", gestioncof_views.ConfigUpdate.as_view(), name="config.edit"),
# js-reverse
path("jsreverse/", urls_js, name="js_reverse"),
]
if "bds" in settings.INSTALLED_APPS:
urlpatterns.append(path("bds/", include("bds.urls")))
if "events" in settings.INSTALLED_APPS:
# The new event application is still in development
# → for now it is namespaced below events_v2
# → rename this when the old events system is out
urlpatterns += [path("event_v2/", include("events.urls"))]
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar
urlpatterns += [path("__debug__/", include(debug_toolbar.urls))]
if settings.DEBUG:
# Si on est en production, MEDIA_ROOT est servi par Apache.
# Il faut dire à Django de servir MEDIA_ROOT lui-même en développement.
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# Wagtail for uncatched
if "wagtail.core" in settings.INSTALLED_APPS:
from wagtail.core import urls as wagtail_urls
urlpatterns += i18n_patterns(
path("", include(wagtail_urls)), prefix_default_language=False
)

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = []

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("events", "0001_event"), ("events", "0001_event"),

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("events", "0002_event_subscribers"), ("events", "0002_event_subscribers"),

View file

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("events", "0003_options_and_extra_fields"), ("events", "0003_options_and_extra_fields"),
@ -13,18 +12,23 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="extrafield", unique_together={("event", "name")}, name="extrafield",
unique_together={("event", "name")},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="extrafieldcontent", unique_together={("field", "registration")}, name="extrafieldcontent",
unique_together={("field", "registration")},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="option", unique_together={("event", "name")}, name="option",
unique_together={("event", "name")},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="optionchoice", unique_together={("option", "choice")}, name="optionchoice",
unique_together={("option", "choice")},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="registration", unique_together={("event", "user")}, name="registration",
unique_together={("event", "user")},
), ),
] ]

View file

@ -0,0 +1,63 @@
# Generated by Django 3.2.13 on 2022-06-30 10:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("events", "0004_unique_constraints"),
]
operations = [
migrations.AlterUniqueTogether(
name="extrafield",
unique_together=set(),
),
migrations.AlterUniqueTogether(
name="extrafieldcontent",
unique_together=set(),
),
migrations.AlterUniqueTogether(
name="option",
unique_together=set(),
),
migrations.AlterUniqueTogether(
name="optionchoice",
unique_together=set(),
),
migrations.AlterUniqueTogether(
name="registration",
unique_together=set(),
),
migrations.AddConstraint(
model_name="extrafield",
constraint=models.UniqueConstraint(
fields=("event", "name"), name="unique_extra_field"
),
),
migrations.AddConstraint(
model_name="extrafieldcontent",
constraint=models.UniqueConstraint(
fields=("field", "registration"), name="unique_extra_field_content"
),
),
migrations.AddConstraint(
model_name="option",
constraint=models.UniqueConstraint(
fields=("event", "name"), name="unique_event_option"
),
),
migrations.AddConstraint(
model_name="optionchoice",
constraint=models.UniqueConstraint(
fields=("option", "choice"), name="unique_option_choice"
),
),
migrations.AddConstraint(
model_name="registration",
constraint=models.UniqueConstraint(
fields=("event", "user"), name="unique_registration"
),
),
]

View file

@ -72,9 +72,13 @@ class Option(models.Model):
multi_choices = models.BooleanField(_("choix multiples"), default=False) multi_choices = models.BooleanField(_("choix multiples"), default=False)
class Meta: class Meta:
constraints = [
models.UniqueConstraint(
fields=["event", "name"], name="unique_event_option"
)
]
verbose_name = _("option d'événement") verbose_name = _("option d'événement")
verbose_name_plural = _("options d'événement") verbose_name_plural = _("options d'événement")
unique_together = [["event", "name"]]
def __str__(self): def __str__(self):
return self.name return self.name
@ -87,9 +91,13 @@ class OptionChoice(models.Model):
choice = models.CharField(_("choix"), max_length=200) choice = models.CharField(_("choix"), max_length=200)
class Meta: class Meta:
constraints = [
models.UniqueConstraint(
fields=["option", "choice"], name="unique_option_choice"
)
]
verbose_name = _("choix d'option d'événement") verbose_name = _("choix d'option d'événement")
verbose_name_plural = _("choix d'option d'événement") verbose_name_plural = _("choix d'option d'événement")
unique_together = [["option", "choice"]]
def __str__(self): def __str__(self):
return self.choice return self.choice
@ -118,7 +126,9 @@ class ExtraField(models.Model):
field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE) field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE)
class Meta: class Meta:
unique_together = [["event", "name"]] constraints = [
models.UniqueConstraint(fields=["event", "name"], name="unique_extra_field")
]
class ExtraFieldContent(models.Model): class ExtraFieldContent(models.Model):
@ -137,9 +147,13 @@ class ExtraFieldContent(models.Model):
) )
class Meta: class Meta:
constraints = [
models.UniqueConstraint(
fields=["field", "registration"], name="unique_extra_field_content"
)
]
verbose_name = _("contenu d'un champ événement supplémentaire") verbose_name = _("contenu d'un champ événement supplémentaire")
verbose_name_plural = _("contenus d'un champ événement supplémentaire") verbose_name_plural = _("contenus d'un champ événement supplémentaire")
unique_together = [["field", "registration"]]
def __str__(self): def __str__(self):
max_length = 50 max_length = 50
@ -163,9 +177,13 @@ class Registration(models.Model):
) )
class Meta: class Meta:
constraints = [
models.UniqueConstraint(
fields=["event", "user"], name="unique_registration"
)
]
verbose_name = _("inscription à un événement") verbose_name = _("inscription à un événement")
verbose_name_plural = _("inscriptions à un événement") verbose_name_plural = _("inscriptions à un événement")
unique_together = [["event", "user"]]
def __str__(self): def __str__(self):
return "inscription de {} à {}".format(self.user, self.event) return "inscription de {} à {}".format(self.user, self.event)

View file

@ -25,7 +25,8 @@ def make_user(name):
def make_staff_user(name): def make_staff_user(name):
view_event_perm = Permission.objects.get( view_event_perm = Permission.objects.get(
codename="view_event", content_type__app_label="events", codename="view_event",
content_type__app_label="events",
) )
user = make_user(name) user = make_user(name)
user.user_permissions.add(view_event_perm) user.user_permissions.add(view_event_perm)
@ -53,7 +54,9 @@ class CSVExportAccessTest(MessagePatch, TestCase):
def test_get(self): def test_get(self):
client = Client() client = Client()
client.force_login(self.staff) client.force_login(
self.staff, backend="django.contrib.auth.backends.ModelBackend"
)
r = client.get(self.url) r = client.get(self.url)
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
@ -65,7 +68,7 @@ class CSVExportAccessTest(MessagePatch, TestCase):
def test_unauthorised(self): def test_unauthorised(self):
client = Client() client = Client()
client.force_login(self.u1) client.force_login(self.u1, backend="django.contrib.auth.backends.ModelBackend")
r = client.get(self.url) r = client.get(self.url)
self.assertEqual(r.status_code, 403) self.assertEqual(r.status_code, 403)
@ -85,7 +88,9 @@ class CSVExportContentTest(MessagePatch, CSVResponseMixin, TestCase):
) )
self.staff = make_staff_user("staff") self.staff = make_staff_user("staff")
self.client = Client() self.client = Client()
self.client.force_login(self.staff) self.client.force_login(
self.staff, backend="django.contrib.auth.backends.ModelBackend"
)
def test_simple_event(self): def test_simple_event(self):
self.event.subscribers.set([self.u1, self.u2]) self.event.subscribers.set([self.u1, self.u2])

15
gestioasso/asgi.py Normal file
View file

@ -0,0 +1,15 @@
"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""
import os
import django
from channels.routing import get_default_application
if "DJANGO_SETTINGS_MODULE" not in os.environ:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local")
django.setup()
application = get_default_application()

Some files were not shown because too many files have changed in this diff Show more