Compare commits
5 commits
master
...
kerl/authe
Author | SHA1 | Date | |
---|---|---|---|
|
3967831e92 | ||
|
0815f79739 | ||
|
7b8d1575c0 | ||
|
a98e56d316 | ||
|
26455ae5c6 |
359 changed files with 2342 additions and 6122 deletions
|
@ -1 +0,0 @@
|
|||
0x0000000000000000000000000000000000000000
|
|
@ -1 +0,0 @@
|
|||
10000000-ffff-ffff-ffff-000000000001
|
|
@ -1 +0,0 @@
|
|||
k-feste_token
|
|
@ -1 +0,0 @@
|
|||
insecure-key
|
|
@ -1 +0,0 @@
|
|||
toto
|
|
@ -1 +0,0 @@
|
|||
sympa
|
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
|||
use nix
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,7 +5,6 @@ cof/settings.py
|
|||
settings.py
|
||||
*~
|
||||
venv/
|
||||
.venv/
|
||||
.vagrant
|
||||
/src
|
||||
media/
|
||||
|
@ -20,5 +19,3 @@ media/
|
|||
|
||||
# VSCode
|
||||
.vscode/
|
||||
.direnv
|
||||
.static
|
||||
|
|
|
@ -17,15 +17,12 @@ variables:
|
|||
# psql password authentication
|
||||
PGPASSWORD: $POSTGRES_PASSWORD
|
||||
|
||||
# apps to check migrations for
|
||||
MIGRATION_APPS: "bda bds cofcms clubs events gestioncof kfet kfetauth kfetcms open petitscours shared"
|
||||
|
||||
.test_template:
|
||||
before_script:
|
||||
- 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
|
||||
- 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 = "";' gestioasso/settings/secret.py
|
||||
- sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py
|
||||
- sed -i.bak -E 's;^REDIS_PASSWD = .*$;REDIS_PASSWD = "";' cof/settings/secret.py
|
||||
# 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"
|
||||
- pip install --upgrade -r requirements-prod.txt coverage tblib
|
||||
|
@ -43,27 +40,19 @@ variables:
|
|||
# Keep this disabled for now, as it may kill GitLab...
|
||||
# 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:
|
||||
stage: test
|
||||
extends: .test_template
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod"
|
||||
DJANGO_SETTINGS_MODULE: "cof.settings.cof_prod"
|
||||
script:
|
||||
- coverage run manage.py test gestioncof bda petitscours shared --parallel
|
||||
- coverage run manage.py test gestioncof bda kfet petitscours shared --parallel
|
||||
|
||||
bdstest:
|
||||
stage: test
|
||||
extends: .test_template
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: "gestioasso.settings.bds_prod"
|
||||
DJANGO_SETTINGS_MODULE: "cof.settings.bds_prod"
|
||||
script:
|
||||
- coverage run manage.py test bds clubs events --parallel
|
||||
|
||||
|
@ -76,7 +65,7 @@ linters:
|
|||
- black --check .
|
||||
- isort --check --diff .
|
||||
# Print errors only
|
||||
- flake8 --exit-zero bda bds clubs gestioasso events gestioncof kfet petitscours provisioning shared
|
||||
- flake8 --exit-zero bda bds clubs cof events gestioncof kfet petitscours provisioning shared
|
||||
cache:
|
||||
key: linters
|
||||
paths:
|
||||
|
@ -86,14 +75,14 @@ linters:
|
|||
migration_checks:
|
||||
stage: test
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: "gestioasso.settings.local"
|
||||
DJANGO_SETTINGS_MODULE: "cof.settings.local"
|
||||
before_script:
|
||||
- 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
|
||||
- cp gestioasso/settings/secret_example.py gestioasso/settings/secret.py
|
||||
- cp cof/settings/secret_example.py cof/settings/secret.py
|
||||
- pip install --upgrade -r requirements-devel.txt
|
||||
- python --version
|
||||
script: python manage.py makemigrations --dry-run --check $MIGRATION_APPS
|
||||
script: python manage.py makemigrations --dry-run --check
|
||||
services:
|
||||
# this should not be necessary…
|
||||
- postgres:11.7
|
||||
|
|
133
CHANGELOG.md
133
CHANGELOG.md
|
@ -21,141 +21,10 @@ Liste des changements notables dans GestioCOF depuis la version 0.1 (septembre
|
|||
Uniquement un modèle simple de clubs avec des respos. Aucune gestion des
|
||||
adhérents ni des cotisations.
|
||||
|
||||
## TODO Prod
|
||||
|
||||
- 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
|
||||
## Version ??? - Bientôt
|
||||
|
||||
### 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 à
|
||||
|
|
20
README.md
20
README.md
|
@ -1,4 +1,4 @@
|
|||
# GestioCOF / GestioBDS
|
||||
# GestioCOF
|
||||
|
||||
[](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master)
|
||||
[](https://git.eleves.ens.fr/cof-geek/gestioCOF/commits/master)
|
||||
|
@ -18,7 +18,7 @@ Il vous faudra installer pip, les librairies de développement de python ainsi
|
|||
que sqlite3, un moteur de base de données léger et simple d'utilisation. Sous
|
||||
Debian et dérivées (Ubuntu, ...) :
|
||||
|
||||
sudo apt-get install python3-pip python3-dev python3-venv sqlite3 libsasl2-dev python-dev-is-python3 libldap2-dev libssl-dev
|
||||
sudo apt-get install python3-pip python3-dev python3-venv sqlite3
|
||||
|
||||
Si vous décidez d'utiliser un environnement virtuel Python (virtualenv;
|
||||
fortement conseillé), déplacez-vous dans le dossier où est installé GestioCOF
|
||||
|
@ -30,15 +30,7 @@ Pour l'activer, il faut taper
|
|||
|
||||
. venv/bin/activate
|
||||
|
||||
depuis le même dossier. Pour préparer l'environnement à l'utilisation de `./manage.py`
|
||||
(qui permet de faire des tests en local), il faut également taper
|
||||
|
||||
export CREDENTIALS_DIRECTORY=$(realpath .credentials)
|
||||
export DJANGO_SETTINGS_MODULE=gestioasso.settings.local
|
||||
export GESTIOCOF_DEBUG=true
|
||||
export GESTIOCOF_STATIC_ROOT=$(realpath .static)
|
||||
export GESTIOBDS_DEBUG=true
|
||||
export GESTIOBDS_STATIC_ROOT=$(realpath .static)
|
||||
depuis le même dossier.
|
||||
|
||||
Vous pouvez maintenant installer les dépendances Python depuis le fichier
|
||||
`requirements-devel.txt` :
|
||||
|
@ -46,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 -r requirements-devel.txt
|
||||
|
||||
Pour terminer, copier le fichier `gestioasso/settings/secret_example.py` vers
|
||||
`gestioasso/settings/secret.py`. Sous Linux ou Mac, préférez plutôt un lien symbolique
|
||||
Pour terminer, copier le fichier `cof/settings/secret_example.py` vers
|
||||
`cof/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:
|
||||
|
||||
ln -s secret_example.py gestioasso/settings/secret.py
|
||||
ln -s secret_example.py cof/settings/secret.py
|
||||
|
||||
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
|
||||
|
|
42
Vagrantfile
vendored
42
Vagrantfile
vendored
|
@ -1,19 +1,47 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Configuration de base pour GestioCOF.
|
||||
# Voir https://docs.vagrantup.com pour plus d'informations.
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure(2) do |config|
|
||||
# On se base sur Debian 10 (Buster) pour avoir le même environnement qu'en
|
||||
# production.
|
||||
config.vm.box = "debian/contrib-buster64"
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
|
||||
# On associe le port 80 dans la machine virtuelle avec le port 8080 de notre
|
||||
# ordinateur, et le port 8000 avec le port 8000.
|
||||
config.vm.network :forwarded_port, guest: 80, host: 8080
|
||||
config.vm.network :forwarded_port, guest: 8000, host: 8000
|
||||
|
||||
# Le restes de la configuration (installation de paquets, etc) est géré un
|
||||
# script shell.
|
||||
# Create a private network, which allows host-only access to the machine
|
||||
# using a specific IP.
|
||||
# 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"
|
||||
end
|
||||
|
|
60
bda/admin.py
60
bda/admin.py
|
@ -33,6 +33,20 @@ class ReadOnlyMixin(object):
|
|||
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):
|
||||
listing = None
|
||||
|
||||
|
@ -80,12 +94,9 @@ class WithoutListingAttributionInline(AttributionInline):
|
|||
class ParticipantAdminForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
queryset = Spectacle.objects.select_related("location")
|
||||
|
||||
if self.instance.pk is not None:
|
||||
queryset = queryset.filter(tirage=self.instance.tirage)
|
||||
|
||||
self.fields["choicesrevente"].queryset = queryset
|
||||
self.fields["choicesrevente"].queryset = Spectacle.objects.select_related(
|
||||
"location"
|
||||
)
|
||||
|
||||
|
||||
class ParticipantPaidFilter(admin.SimpleListFilter):
|
||||
|
@ -191,6 +202,17 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
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):
|
||||
cleaned_data = super().clean()
|
||||
participant = cleaned_data.get("participant")
|
||||
|
@ -203,14 +225,9 @@ class AttributionAdminForm(forms.ModelForm):
|
|||
)
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
widgets = {
|
||||
"participant": ModelSelect2(url="bda-participant-autocomplete"),
|
||||
"spectacle": ModelSelect2(url="bda-spectacle-autocomplete"),
|
||||
}
|
||||
|
||||
|
||||
class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
||||
|
||||
list_display = ("id", "spectacle", "participant", "given", "paid")
|
||||
search_fields = (
|
||||
"spectacle__title",
|
||||
|
@ -223,7 +240,7 @@ class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
||||
autocomplete_fields = ["participant", "spectacle"]
|
||||
form = ChoixSpectacleAdminForm
|
||||
|
||||
def tirage(self, obj):
|
||||
return obj.participant.tirage
|
||||
|
@ -267,14 +284,15 @@ class SalleAdmin(admin.ModelAdmin):
|
|||
class SpectacleReventeAdminForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
qset = Participant.objects.select_related("user", "tirage")
|
||||
|
||||
if self.instance.pk is not None:
|
||||
qset = qset.filter(tirage=self.instance.seller.tirage)
|
||||
|
||||
self.fields["confirmed_entry"].queryset = qset
|
||||
self.fields["seller"].queryset = qset
|
||||
self.fields["soldTo"].queryset = qset
|
||||
self.fields["confirmed_entry"].queryset = Participant.objects.select_related(
|
||||
"user", "tirage"
|
||||
)
|
||||
self.fields["seller"].queryset = Participant.objects.select_related(
|
||||
"user", "tirage"
|
||||
)
|
||||
self.fields["soldTo"].queryset = Participant.objects.select_related(
|
||||
"user", "tirage"
|
||||
)
|
||||
|
||||
|
||||
class SpectacleReventeAdmin(admin.ModelAdmin):
|
||||
|
|
|
@ -2,6 +2,7 @@ import random
|
|||
|
||||
|
||||
class Algorithm(object):
|
||||
|
||||
shows = None
|
||||
ranks = None
|
||||
origranks = None
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.forms.models import BaseInlineFormSet
|
|||
from django.template import loader
|
||||
from django.utils import timezone
|
||||
|
||||
from bda.models import SpectacleRevente
|
||||
from bda.models import Attribution, Spectacle, SpectacleRevente
|
||||
|
||||
|
||||
class InscriptionInlineFormSet(BaseInlineFormSet):
|
||||
|
@ -77,7 +77,7 @@ class ResellForm(forms.Form):
|
|||
super().__init__(*args, **kwargs)
|
||||
self.fields["attributions"] = TemplateLabelField(
|
||||
queryset=participant.attribution_set.filter(
|
||||
spectacle__date__gte=timezone.now(), paid=True
|
||||
spectacle__date__gte=timezone.now()
|
||||
)
|
||||
.exclude(revente__seller=participant)
|
||||
.select_related("spectacle", "spectacle__location", "participant__user"),
|
||||
|
|
|
@ -81,7 +81,7 @@ class Command(MyBaseCommand):
|
|||
shows = random.sample(
|
||||
list(tirage.spectacle_set.all()), tirage.spectacle_set.count() // 2
|
||||
)
|
||||
for rank, show in enumerate(shows):
|
||||
for (rank, show) in enumerate(shows):
|
||||
choices.append(
|
||||
ChoixSpectacle(
|
||||
participant=part,
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -35,6 +35,7 @@ def fill_tirage_fields(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0002_add_tirage")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0003_update_tirage_and_spectacle")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0004_mails-rappel")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -18,6 +18,7 @@ def forwards_func(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0005_encoding")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0006_add_tirage_switch")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0007_extends_spectacle")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0008_py3")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -21,6 +21,7 @@ def forwards_func(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0009_revente")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0010_spectaclerevente_shotgun")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0011_tirage_appear_catalogue")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -13,6 +13,7 @@ def swap_double_choice(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0011_tirage_appear_catalogue")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0012_notif_time"), ("bda", "0012_swap_double_choice")]
|
||||
|
||||
operations = []
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0013_merge_20180524_2123")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -29,6 +29,7 @@ def set_participant_payment(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0014_attribution_paid_field")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0015_move_bda_payment")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bda", "0016_delete_participant_paid")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# 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"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# 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"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 2.2.28 on 2024-07-07 11:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bda', '0018_auto_20201021_1818'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='attribution',
|
||||
name='paymenttype',
|
||||
field=models.CharField(blank=True, choices=[('cash', 'Cash'), ('cb', 'CB'), ('cheque', 'Chèque'), ('virement', 'Virement'), ('autre', 'Autre')], max_length=8, verbose_name='Moyen de paiement'),
|
||||
),
|
||||
]
|
|
@ -1,13 +0,0 @@
|
|||
# Generated by Django 4.2.16 on 2025-02-26 08:23
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bda", "0019_auto_20220630_1245"),
|
||||
("bda", "0019_auto_20240707_1359"),
|
||||
]
|
||||
|
||||
operations = []
|
|
@ -31,7 +31,6 @@ class Tirage(models.Model):
|
|||
"Tirage à afficher dans le catalogue", default=False
|
||||
)
|
||||
enable_do_tirage = models.BooleanField("Le tirage peut être lancé", default=False)
|
||||
archived = models.BooleanField("Archivé", default=False)
|
||||
|
||||
def __str__(self):
|
||||
return "%s - %s" % (
|
||||
|
@ -151,7 +150,6 @@ PAYMENT_TYPES = (
|
|||
("cash", "Cash"),
|
||||
("cb", "CB"),
|
||||
("cheque", "Chèque"),
|
||||
("virement", "Virement"),
|
||||
("autre", "Autre"),
|
||||
)
|
||||
|
||||
|
@ -164,7 +162,7 @@ class Attribution(models.Model):
|
|||
given = models.BooleanField("Donnée", default=False)
|
||||
paid = models.BooleanField("Payée", default=False)
|
||||
paymenttype = models.CharField(
|
||||
"Moyen de paiement", max_length=8, choices=PAYMENT_TYPES, blank=True
|
||||
"Moyen de paiement", max_length=6, choices=PAYMENT_TYPES, blank=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -199,9 +197,7 @@ class Participant(models.Model):
|
|||
attributions = models.ManyToManyField(
|
||||
Spectacle, through="Attribution", related_name="attributed_to"
|
||||
)
|
||||
tirage = models.ForeignKey(
|
||||
Tirage, on_delete=models.CASCADE, limit_choices_to={"archived": False}
|
||||
)
|
||||
tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE)
|
||||
accepte_charte = models.BooleanField("A accepté la charte BdA", default=False)
|
||||
choicesrevente = models.ManyToManyField(
|
||||
Spectacle, related_name="subscribed", blank=True
|
||||
|
@ -212,12 +208,6 @@ class Participant(models.Model):
|
|||
def __str__(self):
|
||||
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 = (
|
||||
("1", "1 place"),
|
||||
|
@ -254,11 +244,7 @@ class ChoixSpectacle(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ("priority",)
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["participant", "spectacle"], name="unique_participation"
|
||||
)
|
||||
]
|
||||
unique_together = (("participant", "spectacle"),)
|
||||
verbose_name = "voeu"
|
||||
verbose_name_plural = "voeux"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>État des inscriptions BdA</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{% static 'vendor/jquery/jquery-ui.min.js' %}" ></script>
|
||||
|
|
|
@ -6,23 +6,25 @@ pour les spectacles suivants :
|
|||
- 1 place pour {{ place }}{% endfor %}
|
||||
|
||||
*Paiement*
|
||||
Au burô :
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant, au bureau du COF pendant les heures de permanences (lundi, mardi, jeudi entre 12h et 14h et entre 18h30 et 19h30, mercredi entre 18h30 et 19h30, vendredi entre 12h et 14h). Les places sont à régler AVANT les représentations. Si vous êtes en vacances, vous pourrez venir les régler dès votre retour. Il est demandé à chacun·e de prendre garde à honorer l’ensemble des places qui lui sont attribuées et de s'engager de fait à payer la ou les place(s) qui lui sont attribuées.
|
||||
|
||||
Par virements :
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant par virements. Il vous sera demandé d'envoyer une confirmation de l'envoi de virement à bda@ens.fr.
|
||||
IBAN AEENS : FR76 4255 9100 0008 0263 8331 927
|
||||
Motif de virements : AVR25(ou MAI25)-tirageprintemps-NOM-prénom
|
||||
|
||||
Les places sont à régler AVANT les représentations. Il est demandé à chacun·e de prendre garde à honorer l’ensemble des places qui lui sont attribuées et de s'engager de fait à payer la ou les place(s) qui lui sont attribuées.
|
||||
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.
|
||||
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/
|
||||
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.
|
||||
|
||||
Un système de revente des places via les mails BdA-revente est disponible directement sur votre compte GestioCOF. Pour pouvoir l'utiliser, il faut que vous ayez payé vos places en amont.
|
||||
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,
|
||||
--
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>{{ spectacle }}</h2>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<tbody>
|
||||
{% for participant in participants %}
|
||||
<tr>
|
||||
<td data-sort-value="{{ participant.name}}">{{participant.name}}</td>
|
||||
<td data-sort-value="{{ participan.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.email}}">{{participant.email}}</td>
|
||||
<td data-sort-value="{{ participant.paid}}" class={%if participant.paid %}"greenratio"{%else%}"redratio"{%endif%}>
|
||||
|
|
|
@ -10,20 +10,9 @@
|
|||
<td>{{place.spectacle.location}}</td>
|
||||
<td>{{place.spectacle.date}}</td>
|
||||
<td>{% if place.double %}deux places{%else%}une place{% endif %}</td>
|
||||
<td>{% if place.spectacle.listing %}sur listing{% else %}place physique{% endif %}</td>
|
||||
<td>
|
||||
{% if place.unpaid == 0 %}
|
||||
Payé
|
||||
{% elif place.unpaid == 1 %}
|
||||
Une place à payer ({{place.unpaid_price|floatformat}}€)
|
||||
{% else %}
|
||||
Deux places à payer ({{place.unpaid_price|floatformat}}€)
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<h4 class="bda-prix">Reste à payer : {{ unpaid|floatformat }}€</h4>
|
||||
<h4 class="bda-prix">Total à payer : {{ total|floatformat }}€</h4>
|
||||
<br/>
|
||||
<p>Ne manque pas un spectacle avec le
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
|
||||
{%block realcontent %}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscription à une revente</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
6
bda/templates/bda/revente/notpaid.html
Normal file
6
bda/templates/bda/revente/notpaid.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2><strong>Nope</strong></h2>
|
||||
<p>Avant de revendre des places, il faut aller les payer !</p>
|
||||
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles%}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscriptions pour BdA-Revente</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />
|
||||
|
|
|
@ -356,9 +356,7 @@ class TestReventeManageTest(TestCase):
|
|||
|
||||
def test_can_get(self):
|
||||
client = Client()
|
||||
client.force_login(
|
||||
self.user, backend="django.contrib.auth.backends.ModelBackend"
|
||||
)
|
||||
client.force_login(self.user)
|
||||
r = client.get(self.url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
|
42
bda/urls.py
42
bda/urls.py
|
@ -1,80 +1,74 @@
|
|||
from django.urls import re_path
|
||||
from django.conf.urls import url
|
||||
|
||||
from bda import views
|
||||
from bda.views import SpectacleListView
|
||||
from gestioncof.decorators import buro_required
|
||||
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
url(
|
||||
r"^inscription/(?P<tirage_id>\d+)$",
|
||||
views.inscription,
|
||||
name="bda-tirage-inscription",
|
||||
),
|
||||
re_path(r"^places/(?P<tirage_id>\d+)$", views.places, name="bda-places-attribuees"),
|
||||
re_path(
|
||||
r"^etat-places/(?P<tirage_id>\d+)$", views.etat_places, name="bda-etat-places"
|
||||
),
|
||||
re_path(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"),
|
||||
re_path(
|
||||
url(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"),
|
||||
url(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"),
|
||||
url(
|
||||
r"^spectacles/(?P<tirage_id>\d+)$",
|
||||
buro_required(SpectacleListView.as_view()),
|
||||
name="bda-liste-spectacles",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$",
|
||||
views.spectacle,
|
||||
name="bda-spectacle",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^spectacles/unpaid/(?P<tirage_id>\d+)$",
|
||||
views.UnpaidParticipants.as_view(),
|
||||
name="bda-unpaid",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^spectacles/autocomplete$",
|
||||
views.spectacle_autocomplete,
|
||||
name="bda-spectacle-autocomplete",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^participants/autocomplete$",
|
||||
views.participant_autocomplete,
|
||||
name="bda-participant-autocomplete",
|
||||
),
|
||||
# Urls BdA-Revente
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<tirage_id>\d+)/manage$",
|
||||
views.revente_manage,
|
||||
name="bda-revente-manage",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<tirage_id>\d+)/subscribe$",
|
||||
views.revente_subscribe,
|
||||
name="bda-revente-subscribe",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<tirage_id>\d+)/tirages$",
|
||||
views.revente_tirages,
|
||||
name="bda-revente-tirages",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<spectacle_id>\d+)/buy$",
|
||||
views.revente_buy,
|
||||
name="bda-revente-buy",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<revente_id>\d+)/confirm$",
|
||||
views.revente_confirm,
|
||||
name="bda-revente-confirm",
|
||||
),
|
||||
re_path(
|
||||
url(
|
||||
r"^revente/(?P<tirage_id>\d+)/shotgun$",
|
||||
views.revente_shotgun,
|
||||
name="bda-revente-shotgun",
|
||||
),
|
||||
re_path(
|
||||
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"
|
||||
),
|
||||
url(r"^mails-rappel/(?P<spectacle_id>\d+)$", views.send_rappel, name="bda-rappels"),
|
||||
url(r"^catalogue/(?P<request_type>[a-z]+)$", views.catalogue, name="bda-catalogue"),
|
||||
]
|
||||
|
|
26
bda/views.py
26
bda/views.py
|
@ -114,7 +114,6 @@ def places(request, tirage_id):
|
|||
"spectacle__date", "spectacle"
|
||||
).select_related("spectacle", "spectacle__location")
|
||||
total = sum(place.spectacle.price for place in places)
|
||||
unpaid = 0
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
spectacles = []
|
||||
|
@ -125,8 +124,6 @@ def places(request, tirage_id):
|
|||
places_dict[place.spectacle].double = True
|
||||
else:
|
||||
place.double = False
|
||||
place.unpaid = 0
|
||||
place.unpaid_price = 0
|
||||
places_dict[place.spectacle] = place
|
||||
spectacles.append(place.spectacle)
|
||||
filtered_places.append(place)
|
||||
|
@ -135,12 +132,6 @@ def places(request, tirage_id):
|
|||
warning = True
|
||||
else:
|
||||
dates.append(date)
|
||||
|
||||
if not place.paid:
|
||||
unpaid += place.spectacle.price
|
||||
places_dict[place.spectacle].unpaid += 1
|
||||
places_dict[place.spectacle].unpaid_price += place.spectacle.price
|
||||
|
||||
# On prévient l'utilisateur s'il a deux places à la même date
|
||||
if warning:
|
||||
messages.warning(
|
||||
|
@ -156,7 +147,6 @@ def places(request, tirage_id):
|
|||
"places": filtered_places,
|
||||
"tirage": tirage,
|
||||
"total": total,
|
||||
"unpaid": unpaid,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -284,13 +274,13 @@ def do_tirage(tirage_elt, token):
|
|||
results = Algorithm(data["shows"], data["members"], choices)(token)
|
||||
|
||||
# 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_losers"] += len(losers)
|
||||
|
||||
# On calcule le déficit et les bénéfices pour le BdA
|
||||
# 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
|
||||
data["total_sold"] += show.slots * show.price
|
||||
if deficit >= 0:
|
||||
|
@ -303,8 +293,8 @@ def do_tirage(tirage_elt, token):
|
|||
# so assign a single object for each Participant id
|
||||
members_uniq = {}
|
||||
members2 = {}
|
||||
for show, members, _ in results:
|
||||
for member, _, _, _ in members:
|
||||
for (show, members, _) in results:
|
||||
for (member, _, _, _) in members:
|
||||
if member.id not in members_uniq:
|
||||
members_uniq[member.id] = member
|
||||
members2[member] = []
|
||||
|
@ -395,6 +385,12 @@ def revente_manage(request, tirage_id):
|
|||
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")
|
||||
annulform = AnnulForm(participant, prefix="annul")
|
||||
soldform = SoldForm(participant, prefix="sold")
|
||||
|
@ -710,7 +706,7 @@ def spectacle(request, tirage_id, spectacle_id):
|
|||
"username": participant.user.username,
|
||||
"email": participant.user.email,
|
||||
"given": int(attrib.given),
|
||||
"paid": attrib.paid,
|
||||
"paid": True,
|
||||
"nb_places": 1,
|
||||
}
|
||||
if participant.id in participants:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
default_app_config = "bds.apps.BdsConfig"
|
|
@ -1,4 +1,5 @@
|
|||
from django.apps import AppConfig, apps as global_apps
|
||||
from django import apps as global_apps
|
||||
from django.apps import AppConfig
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bds.models import BDSProfile
|
||||
|
||||
|
@ -9,8 +8,6 @@ User = get_user_model()
|
|||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
is_buro = forms.BooleanField(label=_("Membre du Burô"), required=False)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["email", "first_name", "last_name"]
|
||||
|
@ -36,6 +33,4 @@ class ProfileForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = BDSProfile
|
||||
exclude = ["user"]
|
||||
widgets = {
|
||||
"birthdate": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d")
|
||||
}
|
||||
widgets = {"birthdate": forms.DateInput(attrs={"type": "date"})}
|
||||
|
|
|
@ -8,6 +8,7 @@ import bds.models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||
|
|
|
@ -9,6 +9,7 @@ def create_bds_buro_group(apps, schema_editor):
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("bds", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bds", "0002_bds_group"),
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bds", "0003_staff_permission"),
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bds", "0004_is_member_cotiz_type"),
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bds", "0005_remove_bdsprofile_certificate_file"),
|
||||
]
|
||||
|
|
|
@ -3,7 +3,6 @@ from os.path import splitext
|
|||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from shared.utils import choices_length
|
||||
|
@ -94,16 +93,6 @@ class BDSProfile(models.Model):
|
|||
),
|
||||
)
|
||||
|
||||
@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:
|
||||
verbose_name = _("Profil BDS")
|
||||
verbose_name_plural = _("Profils BDS")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
{% load bulma_utils %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
{% 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 %}
|
|
@ -34,13 +34,6 @@
|
|||
<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.
|
||||
<br>
|
||||
Suivez notre avancement sur
|
||||
|
|
|
@ -22,18 +22,6 @@ def login_url(next=None):
|
|||
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):
|
||||
@mock.patch("gestioncof.signals.messages")
|
||||
def test_get_autocomplete(self, mock_messages):
|
||||
|
@ -46,14 +34,14 @@ class TestRegistrationView(TestCase):
|
|||
self.assertRedirects(resp, login_url(next=url))
|
||||
|
||||
# Logged-in but unprivileged GET
|
||||
client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
|
||||
client.force_login(user)
|
||||
resp = client.get(url)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
self.assertEquals(resp.status_code, 403)
|
||||
|
||||
# Burô user GET
|
||||
give_bds_buro_permissions(user)
|
||||
resp = client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
|
||||
@mock.patch("gestioncof.signals.messages")
|
||||
def test_get(self, mock_messages):
|
||||
|
@ -66,11 +54,11 @@ class TestRegistrationView(TestCase):
|
|||
self.assertRedirects(resp, login_url(next=url))
|
||||
|
||||
# Logged-in but unprivileged GET
|
||||
client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
|
||||
client.force_login(user)
|
||||
resp = client.get(url)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
self.assertEquals(resp.status_code, 403)
|
||||
|
||||
# Burô user GET
|
||||
give_bds_buro_permissions(user)
|
||||
resp = client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
|
|
14
bds/urls.py
14
bds/urls.py
|
@ -1,7 +1,6 @@
|
|||
from django.urls import path
|
||||
|
||||
from bds import views
|
||||
from shared.views import SympaListView
|
||||
|
||||
app_name = "bds"
|
||||
urlpatterns = [
|
||||
|
@ -15,17 +14,4 @@ urlpatterns = [
|
|||
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"),
|
||||
# Sympa export view
|
||||
path(
|
||||
"sympa/members/",
|
||||
SympaListView.as_view(filters={"bds__is_member": True}),
|
||||
name="export.sympa",
|
||||
),
|
||||
]
|
||||
|
|
56
bds/views.py
56
bds/views.py
|
@ -1,14 +1,9 @@
|
|||
import csv
|
||||
|
||||
from django.contrib import messages
|
||||
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.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DeleteView, ListView, RedirectView, TemplateView
|
||||
from django.views.generic import DeleteView, TemplateView
|
||||
|
||||
from bds.autocomplete import bds_search
|
||||
from bds.forms import ProfileForm, UserForm, UserFromClipperForm, UserFromScratchForm
|
||||
|
@ -30,7 +25,6 @@ class Home(StaffRequiredMixin, TemplateView):
|
|||
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
|
||||
|
||||
|
||||
|
@ -42,9 +36,6 @@ class UserUpdateView(StaffRequiredMixin, MultipleFormView):
|
|||
"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):
|
||||
self.user = get_object_or_404(User, pk=self.kwargs["pk"])
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
@ -61,11 +52,6 @@ class UserUpdateView(StaffRequiredMixin, MultipleFormView):
|
|||
def form_valid(self, forms):
|
||||
user = forms["user"].save()
|
||||
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.save()
|
||||
messages.success(self.request, _("Profil mis à jour avec succès !"))
|
||||
|
@ -142,43 +128,3 @@ class UserDeleteView(StaffRequiredMixin, DeleteView):
|
|||
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
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||
|
|
8
cof/asgi.py
Normal file
8
cof/asgi.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
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()
|
3
cof/routing.py
Normal file
3
cof/routing.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from channels.routing import include
|
||||
|
||||
routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")]
|
|
@ -1,9 +1,7 @@
|
|||
"""
|
||||
Settings de production de GestioBDS.
|
||||
|
||||
Surcharge les settings définis dans common.py
|
||||
Django development settings for the cof project.
|
||||
The settings that are not listed here are imported from .common
|
||||
"""
|
||||
|
||||
from .common import * # NOQA
|
||||
from .common import INSTALLED_APPS
|
||||
|
||||
|
@ -13,26 +11,12 @@ from .common import INSTALLED_APPS
|
|||
|
||||
ALLOWED_HOSTS = ["bds.ens.fr", "www.bds.ens.fr", "dev.cof.ens.fr"]
|
||||
|
||||
INSTALLED_APPS += ["bds", "events", "clubs", "authens"]
|
||||
INSTALLED_APPS += ["bds", "events", "clubs"]
|
||||
|
||||
STATIC_ROOT = "/srv/bds.ens.fr/public/gestion2/static"
|
||||
STATIC_URL = "/gestion2/static/"
|
||||
MEDIA_ROOT = "/srv/bds.ens.fr/gestion2/media"
|
||||
MEDIA_URL = "/gestion2/media/"
|
||||
|
||||
|
||||
# ---
|
||||
# Auth-related stuff
|
||||
# ---
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"authens.backends.ENSCASBackend",
|
||||
"authens.backends.OldCASBackend",
|
||||
]
|
||||
|
||||
AUTHENS_USE_OLDCAS = False
|
||||
|
||||
LOGIN_URL = "authens:login"
|
||||
LOGIN_REDIRECT_URL = "bds:home"
|
||||
LOGOUT_REDIRECT_URL = "bds:home"
|
|
@ -1,15 +1,9 @@
|
|||
"""
|
||||
Settings de production de GestioCOF.
|
||||
|
||||
Surcharge les settings définis dans common.py
|
||||
Django development settings for the cof project.
|
||||
The settings that are not listed here are imported from .common
|
||||
"""
|
||||
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from .common import * # NOQA
|
||||
from .common import (
|
||||
AUTHENTICATION_BACKENDS,
|
||||
BASE_DIR,
|
||||
|
@ -19,6 +13,8 @@ from .common import (
|
|||
import_secret,
|
||||
)
|
||||
|
||||
from .common import * # NOQA
|
||||
|
||||
# ---
|
||||
# COF-specific secrets
|
||||
# ---
|
||||
|
@ -28,8 +24,8 @@ REDIS_DB = import_secret("REDIS_DB")
|
|||
REDIS_HOST = import_secret("REDIS_HOST")
|
||||
REDIS_PORT = import_secret("REDIS_PORT")
|
||||
|
||||
HCAPTCHA_SITEKEY = import_secret("HCAPTCHA_SITEKEY")
|
||||
HCAPTCHA_SECRET = import_secret("HCAPTCHA_SECRET")
|
||||
RECAPTCHA_PUBLIC_KEY = import_secret("RECAPTCHA_PUBLIC_KEY")
|
||||
RECAPTCHA_PRIVATE_KEY = import_secret("RECAPTCHA_PRIVATE_KEY")
|
||||
KFETOPEN_TOKEN = import_secret("KFETOPEN_TOKEN")
|
||||
|
||||
# ---
|
||||
|
@ -52,7 +48,7 @@ INSTALLED_APPS = (
|
|||
+ [
|
||||
"bda",
|
||||
"petitscours",
|
||||
"hcaptcha",
|
||||
"captcha",
|
||||
"kfet",
|
||||
"kfet.open",
|
||||
"channels",
|
||||
|
@ -67,8 +63,8 @@ INSTALLED_APPS = (
|
|||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail",
|
||||
# "wagtail.contrib.modeladmin",
|
||||
"wagtail.core",
|
||||
"wagtail.contrib.modeladmin",
|
||||
"wagtail.contrib.routable_page",
|
||||
"wagtailmenus",
|
||||
"modelcluster",
|
||||
|
@ -85,6 +81,7 @@ MIDDLEWARE = (
|
|||
+ MIDDLEWARE
|
||||
+ [
|
||||
"djconfig.middleware.DjConfigMiddleware",
|
||||
"wagtail.core.middleware.SiteMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
)
|
||||
|
@ -108,25 +105,14 @@ MEDIA_URL = "/gestion/media/"
|
|||
CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.ens.fr")
|
||||
|
||||
|
||||
ASGI_APPLICATION = "gestioasso.routing.application"
|
||||
|
||||
# ---
|
||||
# Auth-related stuff
|
||||
# ---
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
[
|
||||
# Must be in first
|
||||
"kfet.auth.backends.BlockFrozenAccountBackend"
|
||||
]
|
||||
+ AUTHENTICATION_BACKENDS
|
||||
+ [
|
||||
"gestioncof.shared.COFCASBackend",
|
||||
"kfet.auth.backends.GenericBackend",
|
||||
]
|
||||
)
|
||||
LOGIN_URL = "cof-login"
|
||||
AUTHENTICATION_BACKENDS.append("kfet.auth.backends.GenericBackend")
|
||||
|
||||
LOGIN_REDIRECT_URL = "home"
|
||||
LOGOUT_REDIRECT_URL = "home"
|
||||
|
||||
# ---
|
||||
# Cache settings
|
||||
|
@ -148,7 +134,7 @@ CACHES = {
|
|||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "shared.channels.ChannelLayer",
|
||||
"BACKEND": "asgi_redis.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [
|
||||
(
|
||||
|
@ -161,9 +147,11 @@ CHANNEL_LAYERS = {
|
|||
)
|
||||
]
|
||||
},
|
||||
"ROUTING": "cof.routing.routing",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# ---
|
||||
# reCAPTCHA settings
|
||||
# https://github.com/praekelt/django-recaptcha
|
||||
|
@ -205,29 +193,8 @@ MAIL_DATA = {
|
|||
"REPLYTO": "cof@ens.fr",
|
||||
},
|
||||
"rappels": {"FROM": "Le BdA <bda@ens.fr>", "REPLYTO": "Le BdA <bda@ens.fr>"},
|
||||
"kfet": {
|
||||
"FROM": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
"REPLYTO": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
},
|
||||
"revente": {
|
||||
"FROM": "BdA-Revente <bda-revente@ens.fr>",
|
||||
"REPLYTO": "BdA-Revente <bda-revente@ens.fr>",
|
||||
},
|
||||
}
|
||||
|
||||
# ---
|
||||
# kfet history limits
|
||||
# ---
|
||||
|
||||
# L'historique n'est accesible que d'aujourd'hui
|
||||
# à aujourd'hui - KFET_HISTORY_DATE_LIMIT
|
||||
KFET_HISTORY_DATE_LIMIT = timedelta(days=7)
|
||||
|
||||
# Limite plus longue pour les chefs/trez
|
||||
# (qui ont la permission kfet.access_old_history)
|
||||
KFET_HISTORY_LONG_DATE_LIMIT = timedelta(days=30)
|
||||
|
||||
# These accounts don't represent actual people and can be freely accessed
|
||||
# Identification based on trigrammes
|
||||
KFET_HISTORY_NO_DATE_LIMIT_TRIGRAMMES = ["LIQ", "#13"]
|
||||
KFET_HISTORY_NO_DATE_LIMIT = timezone.datetime(1794, 10, 30) # AKA the distant past
|
|
@ -1,5 +1,8 @@
|
|||
"""
|
||||
Settings par défaut et settings communs à GestioCOF et GestioBDS.
|
||||
Django common settings for cof project.
|
||||
|
||||
Everything which is supposed to be identical between the production server and
|
||||
the local development server should be here.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -62,10 +65,11 @@ INSTALLED_APPS = [
|
|||
"django.contrib.messages",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.admindocs",
|
||||
"gestioasso.apps.IgnoreSrcStaticFilesConfig",
|
||||
"cof.apps.IgnoreSrcStaticFilesConfig",
|
||||
"django_cas_ng",
|
||||
"bootstrapform",
|
||||
"widget_tweaks",
|
||||
"authens",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -79,7 +83,7 @@ MIDDLEWARE = [
|
|||
"django.middleware.locale.LocaleMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "gestioasso.urls"
|
||||
ROOT_URLCONF = "cof.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
|
@ -101,7 +105,7 @@ TEMPLATES = [
|
|||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
"NAME": DBNAME,
|
||||
"USER": DBUSER,
|
||||
"PASSWORD": DBPASSWD,
|
||||
|
@ -111,7 +115,6 @@ DATABASES = {
|
|||
|
||||
SITE_ID = 1
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
# ---
|
||||
# Internationalization
|
||||
|
@ -124,18 +127,18 @@ USE_I18N = True
|
|||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
LANGUAGES = (("fr", "Français"), ("en", "English"))
|
||||
FORMAT_MODULE_PATH = "gestioasso.locale"
|
||||
FORMAT_MODULE_PATH = "cof.locale"
|
||||
|
||||
|
||||
# ---
|
||||
# Auth-related stuff
|
||||
# ---
|
||||
|
||||
AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"]
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"authens.backends.ENSCASBackend",
|
||||
]
|
||||
|
||||
CAS_SERVER_URL = "https://cas.eleves.ens.fr/"
|
||||
CAS_VERSION = "2"
|
||||
CAS_LOGIN_MSG = None
|
||||
CAS_IGNORE_REFERER = True
|
||||
CAS_REDIRECT_URL = "/"
|
||||
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
|
||||
AUTHENS_USE_OLDCAS = False
|
||||
|
||||
LOGIN_URL = "authens:login"
|
|
@ -1,9 +1,4 @@
|
|||
"""
|
||||
Settings utilisés lors d'un développement en local (dans un virtualenv).
|
||||
Active toutes les applications (de GestioCOF et de GestioBDS).
|
||||
|
||||
Surcharge les settings définis dans common.py
|
||||
"""
|
||||
"""Django local development settings."""
|
||||
import os
|
||||
|
||||
from . import bds_prod
|
||||
|
@ -27,9 +22,6 @@ ALLOWED_HOSTS = []
|
|||
DEBUG = True
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
SYMPA_PASSWORD = b"sympa"
|
||||
SYMPA_USERNAME = b"sympa"
|
||||
|
||||
if TESTING:
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
|
@ -50,7 +42,8 @@ CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"
|
|||
# Use the default in memory asgi backend for local development
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels.layers.InMemoryChannelLayer",
|
||||
"BACKEND": "asgiref.inmemory.ChannelLayer",
|
||||
"ROUTING": "cof.routing.routing",
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Secrets à re-définir en production.
|
||||
"""
|
||||
|
||||
SECRET_KEY = "q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah"
|
||||
ADMINS = None
|
||||
SERVER_EMAIL = "root@vagrant"
|
||||
|
@ -16,8 +12,8 @@ REDIS_PORT = 6379
|
|||
REDIS_DB = 0
|
||||
REDIS_HOST = "127.0.0.1"
|
||||
|
||||
HCAPTCHA_SITEKEY = "10000000-ffff-ffff-ffff-000000000001"
|
||||
HCAPTCHA_SECRET = "0x0000000000000000000000000000000000000000"
|
||||
RECAPTCHA_PUBLIC_KEY = "DUMMY"
|
||||
RECAPTCHA_PRIVATE_KEY = "DUMMY"
|
||||
|
||||
EMAIL_HOST = None
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
"""
|
||||
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
|
||||
|
@ -9,23 +8,21 @@ from django.contrib import admin
|
|||
from django.urls import include, path
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
bds_is_alone = (
|
||||
"bds" in settings.INSTALLED_APPS and "gestioncof" not in settings.INSTALLED_APPS
|
||||
)
|
||||
|
||||
admin.autodiscover()
|
||||
urlpatterns = [
|
||||
# Redirection / → /gestion, only useful for developpers.
|
||||
path("", RedirectView.as_view(url="gestion/")),
|
||||
# Website administration (independent from installed apps)
|
||||
path("admin/doc/", include("django.contrib.admindocs.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
]
|
||||
|
||||
if not bds_is_alone:
|
||||
# Redirection / → /gestion, only useful for developpers.
|
||||
urlpatterns.append(path("", RedirectView.as_view(url="gestion/")))
|
||||
|
||||
# App-specific urls
|
||||
|
||||
bds_is_alone = (
|
||||
"bds" in settings.INSTALLED_APPS and "gestioncof" not in settings.INSTALLED_APPS
|
||||
)
|
||||
|
||||
app_dict = {
|
||||
"bds": "" if bds_is_alone else "bds/",
|
||||
"kfet": "k-fet/",
|
||||
|
@ -34,9 +31,9 @@ app_dict = {
|
|||
"bda": "gestion/bda/",
|
||||
"petitscours": "gestion/petitcours/",
|
||||
"events": "gestion/event_v2/", # the events module is still experimental !
|
||||
"authens": "gestion/authens/",
|
||||
"authens": "gestion/auth/",
|
||||
}
|
||||
for app_name, url_prefix in app_dict.items():
|
||||
for (app_name, url_prefix) in app_dict.items():
|
||||
if app_name in settings.INSTALLED_APPS:
|
||||
urlpatterns += [path(url_prefix, include("{}.urls".format(app_name)))]
|
||||
|
||||
|
@ -57,13 +54,12 @@ 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)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
|
||||
# Wagtail URLs (wagtail urls must be last, as catch-all)
|
||||
if "wagtail" in settings.INSTALLED_APPS:
|
||||
from wagtail import urls as wagtail_urls
|
||||
# Wagtail URLs (wagtail.core urls must be last, as catch-all)
|
||||
if "wagtail.core" in settings.INSTALLED_APPS:
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.core import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
urlpatterns += [
|
|
@ -2,5 +2,5 @@ import os
|
|||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.bds_prod")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings.bds_prod")
|
||||
application = get_wsgi_application()
|
|
@ -4,6 +4,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("events", "0001_event"),
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("events", "0002_event_subscribers"),
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("events", "0003_options_and_extra_fields"),
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
# 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"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -72,13 +72,9 @@ class Option(models.Model):
|
|||
multi_choices = models.BooleanField(_("choix multiples"), default=False)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["event", "name"], name="unique_event_option"
|
||||
)
|
||||
]
|
||||
verbose_name = _("option d'événement")
|
||||
verbose_name_plural = _("options d'événement")
|
||||
unique_together = [["event", "name"]]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -91,13 +87,9 @@ class OptionChoice(models.Model):
|
|||
choice = models.CharField(_("choix"), max_length=200)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["option", "choice"], name="unique_option_choice"
|
||||
)
|
||||
]
|
||||
verbose_name = _("choix d'option d'événement")
|
||||
verbose_name_plural = _("choix d'option d'événement")
|
||||
unique_together = [["option", "choice"]]
|
||||
|
||||
def __str__(self):
|
||||
return self.choice
|
||||
|
@ -126,9 +118,7 @@ class ExtraField(models.Model):
|
|||
field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["event", "name"], name="unique_extra_field")
|
||||
]
|
||||
unique_together = [["event", "name"]]
|
||||
|
||||
|
||||
class ExtraFieldContent(models.Model):
|
||||
|
@ -147,13 +137,9 @@ class ExtraFieldContent(models.Model):
|
|||
)
|
||||
|
||||
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_plural = _("contenus d'un champ événement supplémentaire")
|
||||
unique_together = [["field", "registration"]]
|
||||
|
||||
def __str__(self):
|
||||
max_length = 50
|
||||
|
@ -177,13 +163,9 @@ class Registration(models.Model):
|
|||
)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["event", "user"], name="unique_registration"
|
||||
)
|
||||
]
|
||||
verbose_name = _("inscription à un événement")
|
||||
verbose_name_plural = _("inscriptions à un événement")
|
||||
unique_together = [["event", "user"]]
|
||||
|
||||
def __str__(self):
|
||||
return "inscription de {} à {}".format(self.user, self.event)
|
||||
|
|
|
@ -54,9 +54,7 @@ class CSVExportAccessTest(MessagePatch, TestCase):
|
|||
|
||||
def test_get(self):
|
||||
client = Client()
|
||||
client.force_login(
|
||||
self.staff, backend="django.contrib.auth.backends.ModelBackend"
|
||||
)
|
||||
client.force_login(self.staff)
|
||||
r = client.get(self.url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
@ -68,7 +66,7 @@ class CSVExportAccessTest(MessagePatch, TestCase):
|
|||
|
||||
def test_unauthorised(self):
|
||||
client = Client()
|
||||
client.force_login(self.u1, backend="django.contrib.auth.backends.ModelBackend")
|
||||
client.force_login(self.u1)
|
||||
r = client.get(self.url)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
||||
|
@ -88,9 +86,7 @@ class CSVExportContentTest(MessagePatch, CSVResponseMixin, TestCase):
|
|||
)
|
||||
self.staff = make_staff_user("staff")
|
||||
self.client = Client()
|
||||
self.client.force_login(
|
||||
self.staff, backend="django.contrib.auth.backends.ModelBackend"
|
||||
)
|
||||
self.client.force_login(self.staff)
|
||||
|
||||
def test_simple_event(self):
|
||||
self.event.subscribers.set([self.u1, self.u2])
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
"""
|
||||
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()
|
|
@ -1,20 +0,0 @@
|
|||
from channels.auth import AuthMiddlewareStack
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from django.core.asgi import get_asgi_application
|
||||
from django.urls import path
|
||||
|
||||
from kfet.routing import KFRouter
|
||||
|
||||
application = ProtocolTypeRouter(
|
||||
{
|
||||
# WebSocket chat handler
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
[
|
||||
path("ws/k-fet", KFRouter),
|
||||
]
|
||||
)
|
||||
),
|
||||
"http": get_asgi_application(),
|
||||
}
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue