forked from DGNum/gestioCOF
Merge branch 'master' into Kerl/drop_py2_compat
This commit is contained in:
commit
5a5b60ec4d
48 changed files with 483 additions and 290 deletions
15
bda/admin.py
15
bda/admin.py
|
@ -1,4 +1,3 @@
|
||||||
import autocomplete_light
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from custommail.shortcuts import send_mass_custom_mail
|
from custommail.shortcuts import send_mass_custom_mail
|
||||||
|
|
||||||
|
@ -7,6 +6,9 @@ from django.db.models import Sum, Count
|
||||||
from django.template.defaultfilters import pluralize
|
from django.template.defaultfilters import pluralize
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
from dal.autocomplete import ModelSelect2
|
||||||
|
|
||||||
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\
|
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\
|
||||||
Attribution, Tirage, Quote, CategorieSpectacle, SpectacleRevente
|
Attribution, Tirage, Quote, CategorieSpectacle, SpectacleRevente
|
||||||
|
|
||||||
|
@ -22,8 +24,17 @@ 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):
|
class ChoixSpectacleInline(admin.TabularInline):
|
||||||
model = ChoixSpectacle
|
model = ChoixSpectacle
|
||||||
|
form = ChoixSpectacleAdminForm
|
||||||
sortable_field_name = "priority"
|
sortable_field_name = "priority"
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,7 +189,7 @@ class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
||||||
|
|
||||||
|
|
||||||
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
||||||
form = autocomplete_light.modelform_factory(ChoixSpectacle, exclude=[])
|
form = ChoixSpectacleAdminForm
|
||||||
|
|
||||||
def tirage(self, obj):
|
def tirage(self, obj):
|
||||||
return obj.participant.tirage
|
return obj.participant.tirage
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import autocomplete_light
|
|
||||||
|
|
||||||
from bda.models import Participant, Spectacle
|
|
||||||
|
|
||||||
autocomplete_light.register(
|
|
||||||
Participant, search_fields=('user__username', 'user__first_name',
|
|
||||||
'user__last_name'),
|
|
||||||
autocomplete_js_attributes={'placeholder': 'participant...'})
|
|
||||||
|
|
||||||
autocomplete_light.register(
|
|
||||||
Spectacle, search_fields=('title', ),
|
|
||||||
autocomplete_js_attributes={'placeholder': 'spectacle...'})
|
|
|
@ -59,7 +59,7 @@ class Migration(migrations.Migration):
|
||||||
('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)),
|
('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)),
|
||||||
('slots', models.IntegerField(verbose_name=b'Places')),
|
('slots', models.IntegerField(verbose_name=b'Places')),
|
||||||
('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')),
|
('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')),
|
||||||
('location', models.ForeignKey(to='bda.Salle')),
|
('location', models.ForeignKey(to='bda.Salle', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('priority', 'date', 'title'),
|
'ordering': ('priority', 'date', 'title'),
|
||||||
|
@ -79,27 +79,27 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.OneToOneField(to=settings.AUTH_USER_MODEL),
|
field=models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='choixspectacle',
|
model_name='choixspectacle',
|
||||||
name='participant',
|
name='participant',
|
||||||
field=models.ForeignKey(to='bda.Participant'),
|
field=models.ForeignKey(to='bda.Participant', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='choixspectacle',
|
model_name='choixspectacle',
|
||||||
name='spectacle',
|
name='spectacle',
|
||||||
field=models.ForeignKey(related_name='participants', to='bda.Spectacle'),
|
field=models.ForeignKey(related_name='participants', to='bda.Spectacle', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='attribution',
|
model_name='attribution',
|
||||||
name='participant',
|
name='participant',
|
||||||
field=models.ForeignKey(to='bda.Participant'),
|
field=models.ForeignKey(to='bda.Participant', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='attribution',
|
model_name='attribution',
|
||||||
name='spectacle',
|
name='spectacle',
|
||||||
field=models.ForeignKey(related_name='attribues', to='bda.Spectacle'),
|
field=models.ForeignKey(related_name='attribues', to='bda.Spectacle', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='choixspectacle',
|
name='choixspectacle',
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
# Create fields `spectacle` for `Participant` and `Spectacle` models.
|
# Create fields `spectacle` for `Participant` and `Spectacle` models.
|
||||||
# These fields are not nullable, but we first create them as nullable
|
# These fields are not nullable, but we first create them as nullable
|
||||||
|
@ -63,22 +63,22 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(to='bda.Tirage', null=True),
|
field=models.ForeignKey(to='bda.Tirage', null=True, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='spectacle',
|
model_name='spectacle',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(to='bda.Tirage', null=True),
|
field=models.ForeignKey(to='bda.Tirage', null=True, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.RunPython(fill_tirage_fields, migrations.RunPython.noop),
|
migrations.RunPython(fill_tirage_fields, migrations.RunPython.noop),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(to='bda.Tirage'),
|
field=models.ForeignKey(to='bda.Tirage', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='spectacle',
|
model_name='spectacle',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(to='bda.Tirage'),
|
field=models.ForeignKey(to='bda.Tirage', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -73,6 +73,7 @@ class Migration(migrations.Migration):
|
||||||
model_name='spectacle',
|
model_name='spectacle',
|
||||||
name='category',
|
name='category',
|
||||||
field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle',
|
field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
null=True),
|
null=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
|
@ -84,6 +85,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='quote',
|
model_name='quote',
|
||||||
name='spectacle',
|
name='spectacle',
|
||||||
field=models.ForeignKey(to='bda.Spectacle'),
|
field=models.ForeignKey(to='bda.Spectacle',
|
||||||
|
on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -47,12 +47,14 @@ class Migration(migrations.Migration):
|
||||||
model_name='spectaclerevente',
|
model_name='spectaclerevente',
|
||||||
name='attribution',
|
name='attribution',
|
||||||
field=models.OneToOneField(to='bda.Attribution',
|
field=models.OneToOneField(to='bda.Attribution',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
related_name='revente'),
|
related_name='revente'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='spectaclerevente',
|
model_name='spectaclerevente',
|
||||||
name='seller',
|
name='seller',
|
||||||
field=models.ForeignKey(to='bda.Participant',
|
field=models.ForeignKey(to='bda.Participant',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
verbose_name='Vendeur',
|
verbose_name='Vendeur',
|
||||||
related_name='original_shows'),
|
related_name='original_shows'),
|
||||||
),
|
),
|
||||||
|
@ -60,6 +62,7 @@ class Migration(migrations.Migration):
|
||||||
model_name='spectaclerevente',
|
model_name='spectaclerevente',
|
||||||
name='soldTo',
|
name='soldTo',
|
||||||
field=models.ForeignKey(to='bda.Participant',
|
field=models.ForeignKey(to='bda.Participant',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
verbose_name='Vendue à', null=True,
|
verbose_name='Vendue à', null=True,
|
||||||
blank=True),
|
blank=True),
|
||||||
),
|
),
|
||||||
|
|
109
bda/models.py
109
bda/models.py
|
@ -4,12 +4,15 @@ from datetime import timedelta
|
||||||
from custommail.shortcuts import send_mass_custom_mail
|
from custommail.shortcuts import send_mass_custom_mail
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
from django.core import mail
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone, formats
|
from django.utils import timezone, formats
|
||||||
|
|
||||||
|
from custommail.models import CustomMail
|
||||||
|
|
||||||
|
|
||||||
def get_generic_user():
|
def get_generic_user():
|
||||||
generic, _ = User.objects.get_or_create(
|
generic, _ = User.objects.get_or_create(
|
||||||
|
@ -57,9 +60,12 @@ class CategorieSpectacle(models.Model):
|
||||||
|
|
||||||
class Spectacle(models.Model):
|
class Spectacle(models.Model):
|
||||||
title = models.CharField("Titre", max_length=300)
|
title = models.CharField("Titre", max_length=300)
|
||||||
category = models.ForeignKey(CategorieSpectacle, blank=True, null=True)
|
category = models.ForeignKey(
|
||||||
|
CategorieSpectacle, on_delete=models.CASCADE,
|
||||||
|
blank=True, null=True,
|
||||||
|
)
|
||||||
date = models.DateTimeField("Date & heure")
|
date = models.DateTimeField("Date & heure")
|
||||||
location = models.ForeignKey(Salle)
|
location = models.ForeignKey(Salle, on_delete=models.CASCADE)
|
||||||
vips = models.TextField('Personnalités', blank=True)
|
vips = models.TextField('Personnalités', blank=True)
|
||||||
description = models.TextField("Description", blank=True)
|
description = models.TextField("Description", blank=True)
|
||||||
slots_description = models.TextField("Description des places", blank=True)
|
slots_description = models.TextField("Description des places", blank=True)
|
||||||
|
@ -69,7 +75,7 @@ class Spectacle(models.Model):
|
||||||
max_length=500)
|
max_length=500)
|
||||||
price = models.FloatField("Prix d'une place")
|
price = models.FloatField("Prix d'une place")
|
||||||
slots = models.IntegerField("Places")
|
slots = models.IntegerField("Places")
|
||||||
tirage = models.ForeignKey(Tirage)
|
tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE)
|
||||||
listing = models.BooleanField("Les places sont sur listing")
|
listing = models.BooleanField("Les places sont sur listing")
|
||||||
rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True,
|
rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True,
|
||||||
null=True)
|
null=True)
|
||||||
|
@ -133,7 +139,7 @@ class Spectacle(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Quote(models.Model):
|
class Quote(models.Model):
|
||||||
spectacle = models.ForeignKey(Spectacle)
|
spectacle = models.ForeignKey(Spectacle, on_delete=models.CASCADE)
|
||||||
text = models.TextField('Citation')
|
text = models.TextField('Citation')
|
||||||
author = models.CharField('Auteur', max_length=200)
|
author = models.CharField('Auteur', max_length=200)
|
||||||
|
|
||||||
|
@ -147,7 +153,7 @@ PAYMENT_TYPES = (
|
||||||
|
|
||||||
|
|
||||||
class Participant(models.Model):
|
class Participant(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
choices = models.ManyToManyField(Spectacle,
|
choices = models.ManyToManyField(Spectacle,
|
||||||
through="ChoixSpectacle",
|
through="ChoixSpectacle",
|
||||||
related_name="chosen_by")
|
related_name="chosen_by")
|
||||||
|
@ -158,7 +164,7 @@ class Participant(models.Model):
|
||||||
paymenttype = models.CharField("Moyen de paiement",
|
paymenttype = models.CharField("Moyen de paiement",
|
||||||
max_length=6, choices=PAYMENT_TYPES,
|
max_length=6, choices=PAYMENT_TYPES,
|
||||||
blank=True)
|
blank=True)
|
||||||
tirage = models.ForeignKey(Tirage)
|
tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE)
|
||||||
choicesrevente = models.ManyToManyField(Spectacle,
|
choicesrevente = models.ManyToManyField(Spectacle,
|
||||||
related_name="subscribed",
|
related_name="subscribed",
|
||||||
blank=True)
|
blank=True)
|
||||||
|
@ -174,8 +180,11 @@ DOUBLE_CHOICES = (
|
||||||
|
|
||||||
|
|
||||||
class ChoixSpectacle(models.Model):
|
class ChoixSpectacle(models.Model):
|
||||||
participant = models.ForeignKey(Participant)
|
participant = models.ForeignKey(Participant, on_delete=models.CASCADE)
|
||||||
spectacle = models.ForeignKey(Spectacle, related_name="participants")
|
spectacle = models.ForeignKey(
|
||||||
|
Spectacle, on_delete=models.CASCADE,
|
||||||
|
related_name="participants",
|
||||||
|
)
|
||||||
priority = models.PositiveIntegerField("Priorité")
|
priority = models.PositiveIntegerField("Priorité")
|
||||||
double_choice = models.CharField("Nombre de places",
|
double_choice = models.CharField("Nombre de places",
|
||||||
default="1", choices=DOUBLE_CHOICES,
|
default="1", choices=DOUBLE_CHOICES,
|
||||||
|
@ -202,8 +211,11 @@ class ChoixSpectacle(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Attribution(models.Model):
|
class Attribution(models.Model):
|
||||||
participant = models.ForeignKey(Participant)
|
participant = models.ForeignKey(Participant, on_delete=models.CASCADE)
|
||||||
spectacle = models.ForeignKey(Spectacle, related_name="attribues")
|
spectacle = models.ForeignKey(
|
||||||
|
Spectacle, on_delete=models.CASCADE,
|
||||||
|
related_name="attribues",
|
||||||
|
)
|
||||||
given = models.BooleanField("Donnée", default=False)
|
given = models.BooleanField("Donnée", default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -212,18 +224,25 @@ class Attribution(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class SpectacleRevente(models.Model):
|
class SpectacleRevente(models.Model):
|
||||||
attribution = models.OneToOneField(Attribution,
|
attribution = models.OneToOneField(
|
||||||
related_name="revente")
|
Attribution, on_delete=models.CASCADE,
|
||||||
|
related_name="revente",
|
||||||
|
)
|
||||||
date = models.DateTimeField("Date de mise en vente",
|
date = models.DateTimeField("Date de mise en vente",
|
||||||
default=timezone.now)
|
default=timezone.now)
|
||||||
answered_mail = models.ManyToManyField(Participant,
|
answered_mail = models.ManyToManyField(Participant,
|
||||||
related_name="wanted",
|
related_name="wanted",
|
||||||
blank=True)
|
blank=True)
|
||||||
seller = models.ForeignKey(Participant,
|
seller = models.ForeignKey(
|
||||||
related_name="original_shows",
|
Participant, on_delete=models.CASCADE,
|
||||||
verbose_name="Vendeur")
|
verbose_name="Vendeur",
|
||||||
soldTo = models.ForeignKey(Participant, blank=True, null=True,
|
related_name="original_shows",
|
||||||
verbose_name="Vendue à")
|
)
|
||||||
|
soldTo = models.ForeignKey(
|
||||||
|
Participant, on_delete=models.CASCADE,
|
||||||
|
verbose_name="Vendue à",
|
||||||
|
blank=True, null=True,
|
||||||
|
)
|
||||||
|
|
||||||
notif_sent = models.BooleanField("Notification envoyée",
|
notif_sent = models.BooleanField("Notification envoyée",
|
||||||
default=False)
|
default=False)
|
||||||
|
@ -310,37 +329,55 @@ class SpectacleRevente(models.Model):
|
||||||
# Envoie un mail au gagnant et au vendeur
|
# Envoie un mail au gagnant et au vendeur
|
||||||
winner = random.choice(inscrits)
|
winner = random.choice(inscrits)
|
||||||
self.soldTo = winner
|
self.soldTo = winner
|
||||||
datatuple = []
|
|
||||||
|
mails = []
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'acheteur': winner.user,
|
'acheteur': winner.user,
|
||||||
'vendeur': seller.user,
|
'vendeur': seller.user,
|
||||||
'show': spectacle,
|
'show': spectacle,
|
||||||
}
|
}
|
||||||
datatuple.append((
|
|
||||||
'bda-revente-winner',
|
c_mails_qs = CustomMail.objects.filter(shortname__in=[
|
||||||
context,
|
'bda-revente-winner', 'bda-revente-loser',
|
||||||
settings.MAIL_DATA['revente']['FROM'],
|
|
||||||
[winner.user.email],
|
|
||||||
))
|
|
||||||
datatuple.append((
|
|
||||||
'bda-revente-seller',
|
'bda-revente-seller',
|
||||||
context,
|
])
|
||||||
settings.MAIL_DATA['revente']['FROM'],
|
|
||||||
[seller.user.email]
|
c_mails = {cm.shortname: cm for cm in c_mails_qs}
|
||||||
))
|
|
||||||
|
mails.append(
|
||||||
|
c_mails['bda-revente-winner'].get_message(
|
||||||
|
context,
|
||||||
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
||||||
|
to=[winner.user.email],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mails.append(
|
||||||
|
c_mails['bda-revente-seller'].get_message(
|
||||||
|
context,
|
||||||
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
||||||
|
to=[seller.user.email],
|
||||||
|
reply_to=[winner.user.email],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Envoie un mail aux perdants
|
# Envoie un mail aux perdants
|
||||||
for inscrit in inscrits:
|
for inscrit in inscrits:
|
||||||
if inscrit != winner:
|
if inscrit != winner:
|
||||||
new_context = dict(context)
|
new_context = dict(context)
|
||||||
new_context['acheteur'] = inscrit.user
|
new_context['acheteur'] = inscrit.user
|
||||||
datatuple.append((
|
|
||||||
'bda-revente-loser',
|
mails.append(
|
||||||
new_context,
|
c_mails['bda-revente-loser'].get_message(
|
||||||
settings.MAIL_DATA['revente']['FROM'],
|
new_context,
|
||||||
[inscrit.user.email]
|
from_email=settings.MAIL_DATA['revente']['FROM'],
|
||||||
))
|
to=[inscrit.user.email],
|
||||||
send_mass_custom_mail(datatuple)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mail_conn = mail.get_connection()
|
||||||
|
mail_conn.send_messages(mails)
|
||||||
# Si personne ne veut de la place, elle part au shotgun
|
# Si personne ne veut de la place, elle part au shotgun
|
||||||
else:
|
else:
|
||||||
self.shotgun = True
|
self.shotgun = True
|
||||||
|
|
0
bda/tests/__init__.py
Normal file
0
bda/tests/__init__.py
Normal file
100
bda/tests/test_models.py
Normal file
100
bda/tests/test_models.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from datetime import timedelta
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core import mail
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from bda.models import (
|
||||||
|
Attribution, Participant, Salle, Spectacle, SpectacleRevente, Tirage,
|
||||||
|
)
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class SpectacleReventeTests(TestCase):
|
||||||
|
fixtures = ['gestioncof/management/data/custommail.json']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
self.t = Tirage.objects.create(
|
||||||
|
title='Tirage',
|
||||||
|
ouverture=now - timedelta(days=7),
|
||||||
|
fermeture=now - timedelta(days=3),
|
||||||
|
active=True,
|
||||||
|
)
|
||||||
|
self.s = Spectacle.objects.create(
|
||||||
|
title='Spectacle',
|
||||||
|
date=now + timedelta(days=20),
|
||||||
|
location=Salle.objects.create(name='Salle', address='Address'),
|
||||||
|
price=10.5,
|
||||||
|
slots=5,
|
||||||
|
tirage=self.t,
|
||||||
|
listing=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.seller = Participant.objects.create(
|
||||||
|
user=User.objects.create(
|
||||||
|
username='seller', email='seller@mail.net'),
|
||||||
|
tirage=self.t,
|
||||||
|
)
|
||||||
|
self.p1 = Participant.objects.create(
|
||||||
|
user=User.objects.create(username='part1', email='part1@mail.net'),
|
||||||
|
tirage=self.t,
|
||||||
|
)
|
||||||
|
self.p2 = Participant.objects.create(
|
||||||
|
user=User.objects.create(username='part2', email='part2@mail.net'),
|
||||||
|
tirage=self.t,
|
||||||
|
)
|
||||||
|
self.p3 = Participant.objects.create(
|
||||||
|
user=User.objects.create(username='part3', email='part3@mail.net'),
|
||||||
|
tirage=self.t,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.attr = Attribution.objects.create(
|
||||||
|
participant=self.seller,
|
||||||
|
spectacle=self.s,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rev = SpectacleRevente.objects.create(
|
||||||
|
attribution=self.attr,
|
||||||
|
seller=self.seller,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_tirage(self):
|
||||||
|
revente = self.rev
|
||||||
|
|
||||||
|
wanted_by = [self.p1, self.p2, self.p3]
|
||||||
|
revente.answered_mail = wanted_by
|
||||||
|
|
||||||
|
with mock.patch('bda.models.random.choice') as mc:
|
||||||
|
# Set winner to self.p1.
|
||||||
|
mc.return_value = self.p1
|
||||||
|
|
||||||
|
revente.tirage()
|
||||||
|
|
||||||
|
# Call to random.choice used participants in wanted_by.
|
||||||
|
mc_args, _ = mc.call_args
|
||||||
|
|
||||||
|
self.assertEqual(set(mc_args[0]), set(wanted_by))
|
||||||
|
|
||||||
|
self.assertEqual(revente.soldTo, self.p1)
|
||||||
|
self.assertTrue(revente.tirage_done)
|
||||||
|
|
||||||
|
mails = {m.to[0]: m for m in mail.outbox}
|
||||||
|
|
||||||
|
self.assertEqual(len(mails), 4)
|
||||||
|
|
||||||
|
m_seller = mails['seller@mail.net']
|
||||||
|
self.assertListEqual(m_seller.to, ['seller@mail.net'])
|
||||||
|
self.assertListEqual(m_seller.reply_to, ['part1@mail.net'])
|
||||||
|
|
||||||
|
m_winner = mails['part1@mail.net']
|
||||||
|
self.assertListEqual(m_winner.to, ['part1@mail.net'])
|
||||||
|
|
||||||
|
self.assertCountEqual(
|
||||||
|
[mails['part2@mail.net'].to, mails['part3@mail.net'].to],
|
||||||
|
[['part2@mail.net'], ['part3@mail.net']],
|
||||||
|
)
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from .models import Tirage, Spectacle, Salle, CategorieSpectacle
|
from bda.models import Tirage, Spectacle, Salle, CategorieSpectacle
|
||||||
|
|
||||||
|
|
||||||
class TestBdAViews(TestCase):
|
class TestBdAViews(TestCase):
|
|
@ -26,6 +26,12 @@ urlpatterns = [
|
||||||
url(r'^spectacles/unpaid/(?P<tirage_id>\d+)$',
|
url(r'^spectacles/unpaid/(?P<tirage_id>\d+)$',
|
||||||
views.unpaid,
|
views.unpaid,
|
||||||
name="bda-unpaid"),
|
name="bda-unpaid"),
|
||||||
|
url(r'^spectacles/autocomplete$',
|
||||||
|
views.spectacle_autocomplete,
|
||||||
|
name="bda-spectacle-autocomplete"),
|
||||||
|
url(r'^participants/autocomplete$',
|
||||||
|
views.participant_autocomplete,
|
||||||
|
name="bda-participant-autocomplete"),
|
||||||
url(r'^liste-revente/(?P<tirage_id>\d+)$',
|
url(r'^liste-revente/(?P<tirage_id>\d+)$',
|
||||||
views.list_revente,
|
views.list_revente,
|
||||||
name="bda-liste-revente"),
|
name="bda-liste-revente"),
|
||||||
|
|
25
bda/views.py
25
bda/views.py
|
@ -31,6 +31,8 @@ from bda.forms import (
|
||||||
InscriptionInlineFormSet,
|
InscriptionInlineFormSet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from utils.views.autocomplete import Select2QuerySetView
|
||||||
|
|
||||||
|
|
||||||
@cof_required
|
@cof_required
|
||||||
def etat_places(request, tirage_id):
|
def etat_places(request, tirage_id):
|
||||||
|
@ -811,3 +813,26 @@ def catalogue(request, request_type):
|
||||||
return JsonResponse(data_return, safe=False)
|
return JsonResponse(data_return, safe=False)
|
||||||
# Si la requête n'est pas de la forme attendue, on quitte avec une erreur
|
# Si la requête n'est pas de la forme attendue, on quitte avec une erreur
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Autocomplete views
|
||||||
|
#
|
||||||
|
# https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#create-an-autocomplete-view
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
class ParticipantAutocomplete(Select2QuerySetView):
|
||||||
|
model = Participant
|
||||||
|
search_fields = ('user__username', 'user__first_name', 'user__last_name')
|
||||||
|
|
||||||
|
|
||||||
|
participant_autocomplete = buro_required(ParticipantAutocomplete.as_view())
|
||||||
|
|
||||||
|
|
||||||
|
class SpectacleAutocomplete(Select2QuerySetView):
|
||||||
|
model = Spectacle
|
||||||
|
search_fields = ('title',)
|
||||||
|
|
||||||
|
|
||||||
|
spectacle_autocomplete = buro_required(SpectacleAutocomplete.as_view())
|
||||||
|
|
|
@ -56,17 +56,22 @@ BASE_DIR = os.path.dirname(
|
||||||
# Application definition
|
# Application definition
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'gestioncof',
|
'gestioncof',
|
||||||
|
|
||||||
|
# Must be before 'django.contrib.admin'.
|
||||||
|
# https://django-autocomplete-light.readthedocs.io/en/master/install.html
|
||||||
|
'dal',
|
||||||
|
'dal_select2',
|
||||||
|
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'grappelli',
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.admindocs',
|
'django.contrib.admindocs',
|
||||||
|
|
||||||
'bda',
|
'bda',
|
||||||
'autocomplete_light',
|
|
||||||
'captcha',
|
'captcha',
|
||||||
'django_cas_ng',
|
'django_cas_ng',
|
||||||
'bootstrapform',
|
'bootstrapform',
|
||||||
|
@ -95,7 +100,7 @@ INSTALLED_APPS = [
|
||||||
'kfet.cms',
|
'kfet.cms',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = [
|
MIDDLEWARE = [
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
@ -123,9 +128,9 @@ TEMPLATES = [
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.core.context_processors.i18n',
|
'django.template.context_processors.i18n',
|
||||||
'django.core.context_processors.media',
|
'django.template.context_processors.media',
|
||||||
'django.core.context_processors.static',
|
'django.template.context_processors.static',
|
||||||
'wagtailmenus.context_processors.wagtailmenus',
|
'wagtailmenus.context_processors.wagtailmenus',
|
||||||
'djconfig.context_processors.config',
|
'djconfig.context_processors.config',
|
||||||
'gestioncof.shared.context_processor',
|
'gestioncof.shared.context_processor',
|
||||||
|
|
|
@ -4,7 +4,7 @@ The settings that are not listed here are imported from .common
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .common import * # NOQA
|
from .common import * # NOQA
|
||||||
from .common import INSTALLED_APPS, MIDDLEWARE_CLASSES
|
from .common import INSTALLED_APPS, MIDDLEWARE
|
||||||
|
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
@ -37,10 +37,11 @@ def show_toolbar(request):
|
||||||
return DEBUG
|
return DEBUG
|
||||||
|
|
||||||
INSTALLED_APPS += ["debug_toolbar", "debug_panel"]
|
INSTALLED_APPS += ["debug_toolbar", "debug_panel"]
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
["debug_panel.middleware.DebugPanelMiddleware"]
|
MIDDLEWARE = [
|
||||||
+ MIDDLEWARE_CLASSES
|
"debug_panel.middleware.DebugPanelMiddleware"
|
||||||
)
|
] + MIDDLEWARE
|
||||||
|
|
||||||
DEBUG_TOOLBAR_CONFIG = {
|
DEBUG_TOOLBAR_CONFIG = {
|
||||||
'SHOW_TOOLBAR_CALLBACK': show_toolbar,
|
'SHOW_TOOLBAR_CALLBACK': show_toolbar,
|
||||||
}
|
}
|
||||||
|
|
25
cof/urls.py
25
cof/urls.py
|
@ -2,8 +2,6 @@
|
||||||
Fichier principal de configuration des urls du projet GestioCOF
|
Fichier principal de configuration des urls du projet GestioCOF
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import autocomplete_light
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
@ -22,7 +20,6 @@ from gestioncof.urls import export_patterns, petitcours_patterns, \
|
||||||
clubs_patterns
|
clubs_patterns
|
||||||
from gestioncof.autocomplete import autocomplete
|
from gestioncof.autocomplete import autocomplete
|
||||||
|
|
||||||
autocomplete_light.autodiscover()
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -47,18 +44,22 @@ urlpatterns = [
|
||||||
name="cof-denied"),
|
name="cof-denied"),
|
||||||
url(r'^cas/login$', django_cas_views.login, name="cas_login_view"),
|
url(r'^cas/login$', django_cas_views.login, name="cas_login_view"),
|
||||||
url(r'^cas/logout$', django_cas_views.logout),
|
url(r'^cas/logout$', django_cas_views.logout),
|
||||||
url(r'^outsider/login$', gestioncof_views.login_ext),
|
url(r'^outsider/login$', gestioncof_views.login_ext,
|
||||||
|
name="ext_login_view"),
|
||||||
url(r'^outsider/logout$', django_views.logout, {'next_page': 'home'}),
|
url(r'^outsider/logout$', django_views.logout, {'next_page': 'home'}),
|
||||||
url(r'^login$', gestioncof_views.login, name="cof-login"),
|
url(r'^login$', gestioncof_views.login, name="cof-login"),
|
||||||
url(r'^logout$', gestioncof_views.logout, name="cof-logout"),
|
url(r'^logout$', gestioncof_views.logout, name="cof-logout"),
|
||||||
# Infos persos
|
# Infos persos
|
||||||
url(r'^profile$', gestioncof_views.profile),
|
url(r'^profile$', gestioncof_views.profile,
|
||||||
url(r'^outsider/password-change$', django_views.password_change),
|
name='profile'),
|
||||||
|
url(r'^outsider/password-change$', django_views.password_change,
|
||||||
|
name='password_change'),
|
||||||
url(r'^outsider/password-change-done$',
|
url(r'^outsider/password-change-done$',
|
||||||
django_views.password_change_done,
|
django_views.password_change_done,
|
||||||
name='password_change_done'),
|
name='password_change_done'),
|
||||||
# Inscription d'un nouveau membre
|
# Inscription d'un nouveau membre
|
||||||
url(r'^registration$', gestioncof_views.registration),
|
url(r'^registration$', gestioncof_views.registration,
|
||||||
|
name='registration'),
|
||||||
url(r'^registration/clipper/(?P<login_clipper>[\w-]+)/'
|
url(r'^registration/clipper/(?P<login_clipper>[\w-]+)/'
|
||||||
r'(?P<fullname>.*)$',
|
r'(?P<fullname>.*)$',
|
||||||
gestioncof_views.registration_form2, name="clipper-registration"),
|
gestioncof_views.registration_form2, name="clipper-registration"),
|
||||||
|
@ -68,7 +69,8 @@ urlpatterns = [
|
||||||
name="empty-registration"),
|
name="empty-registration"),
|
||||||
# Autocompletion
|
# Autocompletion
|
||||||
url(r'^autocomplete/registration$', autocomplete),
|
url(r'^autocomplete/registration$', autocomplete),
|
||||||
url(r'^autocomplete/', include('autocomplete_light.urls')),
|
url(r'^user/autocomplete$', gestioncof_views.user_autocomplete,
|
||||||
|
name='cof-user-autocomplete'),
|
||||||
# Interface admin
|
# Interface admin
|
||||||
url(r'^admin/logout/', gestioncof_views.logout),
|
url(r'^admin/logout/', gestioncof_views.logout),
|
||||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
@ -76,10 +78,11 @@ urlpatterns = [
|
||||||
csv_views.admin_list_export,
|
csv_views.admin_list_export,
|
||||||
{'fields': ['username', ]}),
|
{'fields': ['username', ]}),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
url(r'^grappelli/', include('grappelli.urls')),
|
|
||||||
# Liens utiles du COF et du BdA
|
# Liens utiles du COF et du BdA
|
||||||
url(r'^utile_cof$', gestioncof_views.utile_cof),
|
url(r'^utile_cof$', gestioncof_views.utile_cof,
|
||||||
url(r'^utile_bda$', gestioncof_views.utile_bda),
|
name='utile_cof'),
|
||||||
|
url(r'^utile_bda$', gestioncof_views.utile_bda,
|
||||||
|
name='utile_bda'),
|
||||||
url(r'^utile_bda/bda_diff$', gestioncof_views.liste_bdadiff),
|
url(r'^utile_bda/bda_diff$', gestioncof_views.liste_bdadiff),
|
||||||
url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof),
|
url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof),
|
||||||
url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente),
|
url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente),
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.core.urlresolvers import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
import autocomplete_light
|
from dal.autocomplete import ModelSelect2
|
||||||
|
|
||||||
|
|
||||||
def add_link_field(target_model='', field='', link_text=str,
|
def add_link_field(target_model='', field='', link_text=str,
|
||||||
|
@ -217,8 +217,16 @@ def user_str(self):
|
||||||
User.__str__ = user_str
|
User.__str__ = user_str
|
||||||
|
|
||||||
|
|
||||||
|
class EventRegistrationAdminForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
widgets = {
|
||||||
|
'user': ModelSelect2(url='cof-user-autocomplete'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class EventRegistrationAdmin(admin.ModelAdmin):
|
class EventRegistrationAdmin(admin.ModelAdmin):
|
||||||
form = autocomplete_light.modelform_factory(EventRegistration, exclude=[])
|
form = EventRegistrationAdminForm
|
||||||
|
|
||||||
list_display = ('__str__', 'event', 'user', 'paid')
|
list_display = ('__str__', 'event', 'user', 'paid')
|
||||||
list_filter = ('paid',)
|
list_filter = ('paid',)
|
||||||
search_fields = ('user__username', 'user__first_name', 'user__last_name',
|
search_fields = ('user__username', 'user__first_name', 'user__last_name',
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import autocomplete_light
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
autocomplete_light.register(
|
|
||||||
User, search_fields=('username', 'first_name', 'last_name'),
|
|
||||||
attrs={'placeholder': 'membre...'}
|
|
||||||
)
|
|
|
@ -48,7 +48,7 @@ class Migration(migrations.Migration):
|
||||||
('is_buro', models.BooleanField(default=False, verbose_name=b'Membre du Bur\xc3\xb4')),
|
('is_buro', models.BooleanField(default=False, verbose_name=b'Membre du Bur\xc3\xb4')),
|
||||||
('petits_cours_accept', models.BooleanField(default=False, verbose_name=b'Recevoir des petits cours')),
|
('petits_cours_accept', models.BooleanField(default=False, verbose_name=b'Recevoir des petits cours')),
|
||||||
('petits_cours_remarques', models.TextField(default=b'', verbose_name='Remarques et pr\xe9cisions pour les petits cours', blank=True)),
|
('petits_cours_remarques', models.TextField(default=b'', verbose_name='Remarques et pr\xe9cisions pour les petits cours', blank=True)),
|
||||||
('user', models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL)),
|
('user', models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Profil COF',
|
'verbose_name': 'Profil COF',
|
||||||
|
@ -91,7 +91,7 @@ class Migration(migrations.Migration):
|
||||||
('name', models.CharField(max_length=200, verbose_name=b'Champ')),
|
('name', models.CharField(max_length=200, verbose_name=b'Champ')),
|
||||||
('fieldtype', models.CharField(default=b'text', max_length=10, verbose_name=b'Type', choices=[(b'text', 'Texte long'), (b'char', 'Texte court')])),
|
('fieldtype', models.CharField(default=b'text', max_length=10, verbose_name=b'Type', choices=[(b'text', 'Texte long'), (b'char', 'Texte court')])),
|
||||||
('default', models.TextField(verbose_name=b'Valeur par d\xc3\xa9faut', blank=True)),
|
('default', models.TextField(verbose_name=b'Valeur par d\xc3\xa9faut', blank=True)),
|
||||||
('event', models.ForeignKey(related_name='commentfields', to='gestioncof.Event')),
|
('event', models.ForeignKey(related_name='commentfields', to='gestioncof.Event', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Champ',
|
'verbose_name': 'Champ',
|
||||||
|
@ -102,7 +102,7 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('content', models.TextField(null=True, verbose_name=b'Contenu', blank=True)),
|
('content', models.TextField(null=True, verbose_name=b'Contenu', blank=True)),
|
||||||
('commentfield', models.ForeignKey(related_name='values', to='gestioncof.EventCommentField')),
|
('commentfield', models.ForeignKey(related_name='values', to='gestioncof.EventCommentField', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -111,7 +111,7 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('name', models.CharField(max_length=200, verbose_name=b'Option')),
|
('name', models.CharField(max_length=200, verbose_name=b'Option')),
|
||||||
('multi_choices', models.BooleanField(default=False, verbose_name=b'Choix multiples')),
|
('multi_choices', models.BooleanField(default=False, verbose_name=b'Choix multiples')),
|
||||||
('event', models.ForeignKey(related_name='options', to='gestioncof.Event')),
|
('event', models.ForeignKey(related_name='options', to='gestioncof.Event', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Option',
|
'verbose_name': 'Option',
|
||||||
|
@ -122,7 +122,7 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('value', models.CharField(max_length=200, verbose_name=b'Valeur')),
|
('value', models.CharField(max_length=200, verbose_name=b'Valeur')),
|
||||||
('event_option', models.ForeignKey(related_name='choices', to='gestioncof.EventOption')),
|
('event_option', models.ForeignKey(related_name='choices', to='gestioncof.EventOption', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Choix',
|
'verbose_name': 'Choix',
|
||||||
|
@ -133,10 +133,10 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('paid', models.BooleanField(default=False, verbose_name=b'A pay\xc3\xa9')),
|
('paid', models.BooleanField(default=False, verbose_name=b'A pay\xc3\xa9')),
|
||||||
('event', models.ForeignKey(to='gestioncof.Event')),
|
('event', models.ForeignKey(to='gestioncof.Event', on_delete=models.CASCADE)),
|
||||||
('filledcomments', models.ManyToManyField(to='gestioncof.EventCommentField', through='gestioncof.EventCommentValue')),
|
('filledcomments', models.ManyToManyField(to='gestioncof.EventCommentField', through='gestioncof.EventCommentValue')),
|
||||||
('options', models.ManyToManyField(to='gestioncof.EventOptionChoice')),
|
('options', models.ManyToManyField(to='gestioncof.EventOptionChoice')),
|
||||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Inscription',
|
'verbose_name': 'Inscription',
|
||||||
|
@ -240,7 +240,7 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('question', models.CharField(max_length=200, verbose_name=b'Question')),
|
('question', models.CharField(max_length=200, verbose_name=b'Question')),
|
||||||
('multi_answers', models.BooleanField(default=False, verbose_name=b'Choix multiples')),
|
('multi_answers', models.BooleanField(default=False, verbose_name=b'Choix multiples')),
|
||||||
('survey', models.ForeignKey(related_name='questions', to='gestioncof.Survey')),
|
('survey', models.ForeignKey(related_name='questions', to='gestioncof.Survey', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Question',
|
'verbose_name': 'Question',
|
||||||
|
@ -251,7 +251,7 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('answer', models.CharField(max_length=200, verbose_name=b'R\xc3\xa9ponse')),
|
('answer', models.CharField(max_length=200, verbose_name=b'R\xc3\xa9ponse')),
|
||||||
('survey_question', models.ForeignKey(related_name='answers', to='gestioncof.SurveyQuestion')),
|
('survey_question', models.ForeignKey(related_name='answers', to='gestioncof.SurveyQuestion', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'R\xe9ponse',
|
'verbose_name': 'R\xe9ponse',
|
||||||
|
@ -265,12 +265,12 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='surveyanswer',
|
model_name='surveyanswer',
|
||||||
name='survey',
|
name='survey',
|
||||||
field=models.ForeignKey(to='gestioncof.Survey'),
|
field=models.ForeignKey(to='gestioncof.Survey', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='surveyanswer',
|
model_name='surveyanswer',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursdemande',
|
model_name='petitcoursdemande',
|
||||||
|
@ -280,47 +280,47 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursdemande',
|
model_name='petitcoursdemande',
|
||||||
name='traitee_par',
|
name='traitee_par',
|
||||||
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True),
|
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursattributioncounter',
|
model_name='petitcoursattributioncounter',
|
||||||
name='matiere',
|
name='matiere',
|
||||||
field=models.ForeignKey(verbose_name='Matiere', to='gestioncof.PetitCoursSubject'),
|
field=models.ForeignKey(verbose_name='Matiere', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursattributioncounter',
|
model_name='petitcoursattributioncounter',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursattribution',
|
model_name='petitcoursattribution',
|
||||||
name='demande',
|
name='demande',
|
||||||
field=models.ForeignKey(verbose_name='Demande', to='gestioncof.PetitCoursDemande'),
|
field=models.ForeignKey(verbose_name='Demande', to='gestioncof.PetitCoursDemande', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursattribution',
|
model_name='petitcoursattribution',
|
||||||
name='matiere',
|
name='matiere',
|
||||||
field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject'),
|
field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursattribution',
|
model_name='petitcoursattribution',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursability',
|
model_name='petitcoursability',
|
||||||
name='matiere',
|
name='matiere',
|
||||||
field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject'),
|
field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='petitcoursability',
|
model_name='petitcoursability',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='eventcommentvalue',
|
model_name='eventcommentvalue',
|
||||||
name='registration',
|
name='registration',
|
||||||
field=models.ForeignKey(related_name='comments', to='gestioncof.EventRegistration'),
|
field=models.ForeignKey(related_name='comments', to='gestioncof.EventRegistration', on_delete=models.CASCADE),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='surveyanswer',
|
name='surveyanswer',
|
||||||
|
|
|
@ -23,7 +23,8 @@ class Migration(migrations.Migration):
|
||||||
('subscribe_to_events', models.BooleanField(default=True)),
|
('subscribe_to_events', models.BooleanField(default=True)),
|
||||||
('subscribe_to_my_shows', models.BooleanField(default=True)),
|
('subscribe_to_my_shows', models.BooleanField(default=True)),
|
||||||
('other_shows', models.ManyToManyField(to='bda.Spectacle')),
|
('other_shows', models.ManyToManyField(to='bda.Spectacle')),
|
||||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
|
|
|
@ -49,7 +49,10 @@ class CofProfile(models.Model):
|
||||||
(COTIZ_GRATIS, _("Gratuit")),
|
(COTIZ_GRATIS, _("Gratuit")),
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.OneToOneField(User, related_name="profile")
|
user = models.OneToOneField(
|
||||||
|
User, on_delete=models.CASCADE,
|
||||||
|
related_name="profile",
|
||||||
|
)
|
||||||
login_clipper = models.CharField(
|
login_clipper = models.CharField(
|
||||||
"Login clipper", max_length=32, blank=True
|
"Login clipper", max_length=32, blank=True
|
||||||
)
|
)
|
||||||
|
@ -130,7 +133,10 @@ class Event(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class EventCommentField(models.Model):
|
class EventCommentField(models.Model):
|
||||||
event = models.ForeignKey(Event, related_name="commentfields")
|
event = models.ForeignKey(
|
||||||
|
Event, on_delete=models.CASCADE,
|
||||||
|
related_name="commentfields",
|
||||||
|
)
|
||||||
name = models.CharField("Champ", max_length=200)
|
name = models.CharField("Champ", max_length=200)
|
||||||
fieldtype = models.CharField("Type", max_length=10,
|
fieldtype = models.CharField("Type", max_length=10,
|
||||||
choices=TYPE_COMMENT_FIELD, default="text")
|
choices=TYPE_COMMENT_FIELD, default="text")
|
||||||
|
@ -144,9 +150,14 @@ class EventCommentField(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class EventCommentValue(models.Model):
|
class EventCommentValue(models.Model):
|
||||||
commentfield = models.ForeignKey(EventCommentField, related_name="values")
|
commentfield = models.ForeignKey(
|
||||||
registration = models.ForeignKey("EventRegistration",
|
EventCommentField, on_delete=models.CASCADE,
|
||||||
related_name="comments")
|
related_name="values",
|
||||||
|
)
|
||||||
|
registration = models.ForeignKey(
|
||||||
|
"EventRegistration", on_delete=models.CASCADE,
|
||||||
|
related_name="comments",
|
||||||
|
)
|
||||||
content = models.TextField("Contenu", blank=True, null=True)
|
content = models.TextField("Contenu", blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -154,7 +165,10 @@ class EventCommentValue(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class EventOption(models.Model):
|
class EventOption(models.Model):
|
||||||
event = models.ForeignKey(Event, related_name="options")
|
event = models.ForeignKey(
|
||||||
|
Event, on_delete=models.CASCADE,
|
||||||
|
related_name="options",
|
||||||
|
)
|
||||||
name = models.CharField("Option", max_length=200)
|
name = models.CharField("Option", max_length=200)
|
||||||
multi_choices = models.BooleanField("Choix multiples", default=False)
|
multi_choices = models.BooleanField("Choix multiples", default=False)
|
||||||
|
|
||||||
|
@ -166,7 +180,10 @@ class EventOption(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class EventOptionChoice(models.Model):
|
class EventOptionChoice(models.Model):
|
||||||
event_option = models.ForeignKey(EventOption, related_name="choices")
|
event_option = models.ForeignKey(
|
||||||
|
EventOption, on_delete=models.CASCADE,
|
||||||
|
related_name="choices",
|
||||||
|
)
|
||||||
value = models.CharField("Valeur", max_length=200)
|
value = models.CharField("Valeur", max_length=200)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -178,8 +195,8 @@ class EventOptionChoice(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class EventRegistration(models.Model):
|
class EventRegistration(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
event = models.ForeignKey(Event)
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||||
options = models.ManyToManyField(EventOptionChoice)
|
options = models.ManyToManyField(EventOptionChoice)
|
||||||
filledcomments = models.ManyToManyField(EventCommentField,
|
filledcomments = models.ManyToManyField(EventCommentField,
|
||||||
through=EventCommentValue)
|
through=EventCommentValue)
|
||||||
|
@ -207,7 +224,10 @@ class Survey(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class SurveyQuestion(models.Model):
|
class SurveyQuestion(models.Model):
|
||||||
survey = models.ForeignKey(Survey, related_name="questions")
|
survey = models.ForeignKey(
|
||||||
|
Survey, on_delete=models.CASCADE,
|
||||||
|
related_name="questions",
|
||||||
|
)
|
||||||
question = models.CharField("Question", max_length=200)
|
question = models.CharField("Question", max_length=200)
|
||||||
multi_answers = models.BooleanField("Choix multiples", default=False)
|
multi_answers = models.BooleanField("Choix multiples", default=False)
|
||||||
|
|
||||||
|
@ -219,7 +239,10 @@ class SurveyQuestion(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class SurveyQuestionAnswer(models.Model):
|
class SurveyQuestionAnswer(models.Model):
|
||||||
survey_question = models.ForeignKey(SurveyQuestion, related_name="answers")
|
survey_question = models.ForeignKey(
|
||||||
|
SurveyQuestion, on_delete=models.CASCADE,
|
||||||
|
related_name="answers",
|
||||||
|
)
|
||||||
answer = models.CharField("Réponse", max_length=200)
|
answer = models.CharField("Réponse", max_length=200)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -230,8 +253,8 @@ class SurveyQuestionAnswer(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class SurveyAnswer(models.Model):
|
class SurveyAnswer(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
survey = models.ForeignKey(Survey)
|
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
|
||||||
answers = models.ManyToManyField(SurveyQuestionAnswer,
|
answers = models.ManyToManyField(SurveyQuestionAnswer,
|
||||||
related_name="selected_by")
|
related_name="selected_by")
|
||||||
|
|
||||||
|
@ -247,7 +270,7 @@ class SurveyAnswer(models.Model):
|
||||||
|
|
||||||
class CalendarSubscription(models.Model):
|
class CalendarSubscription(models.Model):
|
||||||
token = models.UUIDField()
|
token = models.UUIDField()
|
||||||
user = models.OneToOneField(User)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
other_shows = models.ManyToManyField(Spectacle)
|
other_shows = models.ManyToManyField(Spectacle)
|
||||||
subscribe_to_events = models.BooleanField(default=True)
|
subscribe_to_events = models.BooleanField(default=True)
|
||||||
subscribe_to_my_shows = models.BooleanField(default=True)
|
subscribe_to_my_shows = models.BooleanField(default=True)
|
||||||
|
|
|
@ -33,8 +33,11 @@ class PetitCoursSubject(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class PetitCoursAbility(models.Model):
|
class PetitCoursAbility(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matière"))
|
matiere = models.ForeignKey(
|
||||||
|
PetitCoursSubject, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("Matière"),
|
||||||
|
)
|
||||||
niveau = models.CharField(_("Niveau"),
|
niveau = models.CharField(_("Niveau"),
|
||||||
choices=LEVELS_CHOICES,
|
choices=LEVELS_CHOICES,
|
||||||
max_length=choices_length(LEVELS_CHOICES))
|
max_length=choices_length(LEVELS_CHOICES))
|
||||||
|
@ -82,7 +85,10 @@ class PetitCoursDemande(models.Model):
|
||||||
remarques = models.TextField(_("Remarques et précisions"), blank=True)
|
remarques = models.TextField(_("Remarques et précisions"), blank=True)
|
||||||
|
|
||||||
traitee = models.BooleanField(_("Traitée"), default=False)
|
traitee = models.BooleanField(_("Traitée"), default=False)
|
||||||
traitee_par = models.ForeignKey(User, blank=True, null=True)
|
traitee_par = models.ForeignKey(
|
||||||
|
User, on_delete=models.CASCADE,
|
||||||
|
blank=True, null=True,
|
||||||
|
)
|
||||||
processed = models.DateTimeField(_("Date de traitement"),
|
processed = models.DateTimeField(_("Date de traitement"),
|
||||||
blank=True, null=True)
|
blank=True, null=True)
|
||||||
created = models.DateTimeField(_("Date de création"), auto_now_add=True)
|
created = models.DateTimeField(_("Date de création"), auto_now_add=True)
|
||||||
|
@ -124,9 +130,15 @@ class PetitCoursDemande(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class PetitCoursAttribution(models.Model):
|
class PetitCoursAttribution(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
demande = models.ForeignKey(PetitCoursDemande, verbose_name=_("Demande"))
|
demande = models.ForeignKey(
|
||||||
matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matière"))
|
PetitCoursDemande, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("Demande"),
|
||||||
|
)
|
||||||
|
matiere = models.ForeignKey(
|
||||||
|
PetitCoursSubject, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("Matière"),
|
||||||
|
)
|
||||||
date = models.DateTimeField(_("Date d'attribution"), auto_now_add=True)
|
date = models.DateTimeField(_("Date d'attribution"), auto_now_add=True)
|
||||||
rank = models.IntegerField("Rang dans l'email")
|
rank = models.IntegerField("Rang dans l'email")
|
||||||
selected = models.BooleanField(_("Sélectionné par le demandeur"),
|
selected = models.BooleanField(_("Sélectionné par le demandeur"),
|
||||||
|
@ -143,8 +155,11 @@ class PetitCoursAttribution(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class PetitCoursAttributionCounter(models.Model):
|
class PetitCoursAttributionCounter(models.Model):
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matiere"))
|
matiere = models.ForeignKey(
|
||||||
|
PetitCoursSubject, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("Matiere"),
|
||||||
|
)
|
||||||
count = models.IntegerField("Nombre d'envois", default=0)
|
count = models.IntegerField("Nombre d'envois", default=0)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
|
||||||
from custommail.shortcuts import render_custom_mail
|
from custommail.shortcuts import render_custom_mail
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
@ -11,6 +10,7 @@ from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from gestioncof.models import CofProfile
|
from gestioncof.models import CofProfile
|
||||||
from gestioncof.petits_cours_models import (
|
from gestioncof.petits_cours_models import (
|
||||||
|
@ -285,7 +285,7 @@ def _traitement_post(request, demande):
|
||||||
attrib.save()
|
attrib.save()
|
||||||
demande.traitee = True
|
demande.traitee = True
|
||||||
demande.traitee_par = request.user
|
demande.traitee_par = request.user
|
||||||
demande.processed = datetime.now()
|
demande.processed = timezone.now()
|
||||||
demande.save()
|
demande.save()
|
||||||
return render(request,
|
return render(request,
|
||||||
"gestioncof/traitement_demande_petit_cours_success.html",
|
"gestioncof/traitement_demande_petit_cours_success.html",
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends "admin/base.html" %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
|
|
||||||
{% include 'autocomplete_light/static.html' %}
|
|
||||||
{% endblock %}
|
|
|
@ -1,78 +0,0 @@
|
||||||
{% extends "admin/base_site.html" %}
|
|
||||||
|
|
||||||
<!-- LOADING -->
|
|
||||||
{% load i18n grp_tags log %}
|
|
||||||
|
|
||||||
<!-- JAVASCRIPTS -->
|
|
||||||
{% block javascripts %}
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!-- COLTYPE/BODYCLASS-- >
|
|
||||||
{% block bodyclass %}dashboard{% endblock %}
|
|
||||||
{% block content-class %}content-grid{% endblock %}
|
|
||||||
|
|
||||||
<!-- BREADCRUMBS -->
|
|
||||||
{% block breadcrumbs %}
|
|
||||||
<ul class="grp-horizontal-list">
|
|
||||||
<li>{% trans "Home" %}</li>
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
||||||
{% block content_title %}
|
|
||||||
{% if title %}
|
|
||||||
<header><h1>{{ title }}</h1></header>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!-- CONTENT -->
|
|
||||||
{% block content %}
|
|
||||||
<div class="g-d-c">
|
|
||||||
<div class="g-d-12 g-d-f">
|
|
||||||
|
|
||||||
{% for app in app_list %}
|
|
||||||
<div class="grp-module" id="app_{{ app.name|lower }}">
|
|
||||||
<h2><a href="{{ app.app_url }}" class="grp-section">{% trans app.name %}</a></h2>
|
|
||||||
{% for model in app.models %}
|
|
||||||
<div class="grp-row">
|
|
||||||
{% if model.perms.change %}<a href="{{ model.admin_url }}"><strong>{{ model.name }}</strong></a>{% else %}<span><strong>{{ model.name }}</strong></span>{% endif %}
|
|
||||||
{% if model.perms.add or model.perms.change %}
|
|
||||||
<ul class="grp-actions">
|
|
||||||
{% if model.perms.add %}<li class="grp-add-link"><a href="{{ model.admin_url }}add/">{% trans 'Add' %}</a></li>{% endif %}
|
|
||||||
{% if model.perms.change %}<li class="grp-change-link"><a href="{{ model.admin_url }}">{% trans 'Change' %}</a></li>{% endif %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% empty %}
|
|
||||||
<p>{% trans "You don´t have permission to edit anything." %}</p>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="g-d-6 g-d-l">
|
|
||||||
<div class="grp-module" id="grp-recent-actions-module">
|
|
||||||
<h2>{% trans 'Recent Actions' %}</h2>
|
|
||||||
<div class="grp-module">
|
|
||||||
<h3>{% trans 'My Actions' %}</h3>
|
|
||||||
{% get_admin_log 20 as admin_log for_user user %}
|
|
||||||
{% if not admin_log %}
|
|
||||||
<p>{% trans 'None available' %}</p>
|
|
||||||
{% else %}
|
|
||||||
<ul class="grp-listing-small">
|
|
||||||
{% for entry in admin_log %}
|
|
||||||
<li class="grp-row{% if entry.is_addition %} grp-add-link{% endif %}{% if entry.is_change %} grp-change-link{% endif %}{% if entry.is_deletion %} grp-delete-link{% endif %}">
|
|
||||||
{% if entry.is_deletion %}
|
|
||||||
<span>{{ entry.object_repr }}</span>
|
|
||||||
{% else %}
|
|
||||||
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
|
|
||||||
{% endif %}
|
|
||||||
<span class="grp-font-color-quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<header>
|
<header>
|
||||||
<div class="container banner">
|
<div class="container banner">
|
||||||
<a href="{% url "gestioncof.views.home" %}">
|
<a href="{% url "home" %}">
|
||||||
<h1>GestioCOF</h1>
|
<h1>GestioCOF</h1>
|
||||||
{% block homelink %}
|
{% block homelink %}
|
||||||
<span class="glyphicon glyphicon-home" aria-hidden=true></span>
|
<span class="glyphicon glyphicon-home" aria-hidden=true></span>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
</a>
|
</a>
|
||||||
<div class="secondary">
|
<div class="secondary">
|
||||||
<span class="hidden-xxs"> | </span>
|
<span class="hidden-xxs"> | </span>
|
||||||
<span><a href="{% url "gestioncof.views.logout" %}">Se déconnecter <span class="glyphicon glyphicon-log-out"></span></a></span>
|
<span><a href="{% url "cof-logout" %}">Se déconnecter <span class="glyphicon glyphicon-log-out"></span></a></span>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="member-status">{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
<h2 class="member-status">{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
||||||
</div><!-- /.container -->
|
</div><!-- /.container -->
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% if event.details %}
|
{% if event.details %}
|
||||||
<p>{{ event.details }}</p>
|
<p>{{ event.details }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post" action="{% url 'gestioncof.views.event' event.id %}">
|
<form method="post" action="{% url 'event.details' event.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input type="submit" class="btn-submit" value="Enregistrer" />
|
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div class="hm-block">
|
<div class="hm-block">
|
||||||
<ul>
|
<ul>
|
||||||
{% for event in open_events %}
|
{% for event in open_events %}
|
||||||
<li><a href="{% url "gestioncof.views.event" event.id %}">{{ event.title }}</a></li>
|
<li><a href="{% url "event.details" event.id %}">{{ event.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<div class="hm-block">
|
<div class="hm-block">
|
||||||
<ul>
|
<ul>
|
||||||
{% for survey in open_surveys %}
|
{% for survey in open_surveys %}
|
||||||
<li><a href="{% url "gestioncof.views.survey" survey.id %}">{{ survey.title }}</a></li>
|
<li><a href="{% url "survey.details" survey.id %}">{{ survey.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,11 +69,11 @@
|
||||||
<h3 class="block-title">Divers<span class="pull-right glyphicon glyphicon-question-sign"></span></h3>
|
<h3 class="block-title">Divers<span class="pull-right glyphicon glyphicon-question-sign"></span></h3>
|
||||||
<div class="hm-block">
|
<div class="hm-block">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url "gestioncof.views.calendar" %}">Calendrier dynamique</a></li>
|
<li><a href="{% url "calendar" %}">Calendrier dynamique</a></li>
|
||||||
{% if user.profile.is_cof %}<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>{% endif %}
|
{% if user.profile.is_cof %}<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>{% endif %}
|
||||||
|
|
||||||
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
|
<li><a href="{% url "profile" %}">Éditer mon profil</a></li>
|
||||||
{% if not user.profile.login_clipper %}<li><a href="{% url "django.contrib.auth.views.password_change" %}">Changer mon mot de passe</a></li>{% endif %}
|
{% if not user.profile.login_clipper %}<li><a href="{% url "password_change" %}">Changer mon mot de passe</a></li>{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -86,16 +86,16 @@
|
||||||
<h4>Général</h4>
|
<h4>Général</h4>
|
||||||
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
|
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
|
||||||
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
|
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
|
||||||
<li><a href="{% url "gestioncof.views.registration" %}">Inscription d'un nouveau membre</a></li>
|
<li><a href="{% url "registration" %}">Inscription d'un nouveau membre</a></li>
|
||||||
<li><a href="{% url "liste-clubs" %}">Gestion des clubs</a></li>
|
<li><a href="{% url "liste-clubs" %}">Gestion des clubs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<h4>Évènements & Sondages</h4>
|
<h4>Évènements & Sondages</h4>
|
||||||
{% for event in events %}
|
{% for event in events %}
|
||||||
<li><a href="{% url "gestioncof.views.event_status" event.id %}">Événement : {{ event.title }}</a></li>
|
<li><a href="{% url "event.details.status" event.id %}">Événement : {{ event.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for survey in surveys %}
|
{% for survey in surveys %}
|
||||||
<li><a href="{% url "gestioncof.views.survey_status" survey.id %}">Sondage : {{ survey.title }}</a></li>
|
<li><a href="{% url "survey.details.status" survey.id %}">Sondage : {{ survey.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,8 +120,8 @@
|
||||||
<h3 class="block-title">Liens utiles<span class="pull-right glyphicon glyphicon-link"></span></h3>
|
<h3 class="block-title">Liens utiles<span class="pull-right glyphicon glyphicon-link"></span></h3>
|
||||||
<div class="hm-block">
|
<div class="hm-block">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url "gestioncof.views.utile_cof" %}">Liens utiles du COF</a></li>
|
<li><a href="{% url "utile_cof" %}">Liens utiles du COF</a></li>
|
||||||
<li><a href="{% url "gestioncof.views.utile_bda" %}">Liens utiles BdA</a></li>
|
<li><a href="{% url "utile_bda" %}">Liens utiles BdA</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<p class="error">Identifiants incorrects.</p>
|
<p class="error">Identifiants incorrects.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form class="form-horizontal" method="post"
|
<form class="form-horizontal" method="post"
|
||||||
action="{% url 'gestioncof.views.login_ext' %}?next={{ next|urlencode }}">
|
action="{% url 'ext_login_view' %}?next={{ next|urlencode }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="id_username" maxlength="254" name="username" type="text" placeholder="Nom d'utilisateur">
|
<input class="form-control" id="id_username" maxlength="254" name="username" type="text" placeholder="Nom d'utilisateur">
|
||||||
|
|
|
@ -12,13 +12,13 @@
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row" style="margin:0;">
|
<div class="row" style="margin:0;">
|
||||||
<a aria-label="Compte clipper"
|
<a aria-label="Compte clipper"
|
||||||
href="{% url 'django_cas_ng.views.login' %}?next={{ next|urlencode }}">
|
href="{% url 'cas_login_view' %}?next={{ next|urlencode }}">
|
||||||
<div class="col-xs-12 col-sm-6" id="login_clipper">
|
<div class="col-xs-12 col-sm-6" id="login_clipper">
|
||||||
Compte clipper
|
Compte clipper
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a aria-label="Extérieur"
|
<a aria-label="Extérieur"
|
||||||
href="{% url 'gestioncof.views.login_ext' %}?next={{ next|urlencode }}">
|
href="{% url 'ext_login_view' %}?next={{ next|urlencode }}">
|
||||||
<div class="col-xs-12 col-sm-6" id="login_outsider">
|
<div class="col-xs-12 col-sm-6" id="login_outsider">
|
||||||
Extérieur
|
Extérieur
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
|
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
<h2>Mot de passe modifié avec succès !</h2>
|
<h2>Mot de passe modifié avec succès !</h2>
|
||||||
<h3><a href="{% url "gestioncof.views.home" %}">Retour au menu principal</a></h3>
|
<h3><a href="{% url "home" %}">Retour au menu principal</a></h3>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
<h2>Changement de mot de passe</h2>
|
<h2>Changement de mot de passe</h2>
|
||||||
<form class="form-horizontal" method="post" action="{% url 'django.contrib.auth.views.password_change' %}">
|
<form class="form-horizontal" method="post" action="{% url 'password_change' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form | bootstrap }}
|
{{ form | bootstrap }}
|
||||||
<input type="submit" class="btn btn-primary pull-right" value="Changer" />
|
<input type="submit" class="btn btn-primary pull-right" value="Changer" />
|
||||||
|
|
|
@ -34,19 +34,23 @@ petitcours_patterns = [
|
||||||
]
|
]
|
||||||
|
|
||||||
surveys_patterns = [
|
surveys_patterns = [
|
||||||
url(r'^(?P<survey_id>\d+)/status$', views.survey_status),
|
url(r'^(?P<survey_id>\d+)/status$', views.survey_status,
|
||||||
url(r'^(?P<survey_id>\d+)$', views.survey),
|
name='survey.details.status'),
|
||||||
|
url(r'^(?P<survey_id>\d+)$', views.survey,
|
||||||
|
name='survey.details'),
|
||||||
]
|
]
|
||||||
|
|
||||||
events_patterns = [
|
events_patterns = [
|
||||||
url(r'^(?P<event_id>\d+)$', views.event),
|
url(r'^(?P<event_id>\d+)$', views.event,
|
||||||
url(r'^(?P<event_id>\d+)/status$', views.event_status),
|
name='event.details'),
|
||||||
|
url(r'^(?P<event_id>\d+)/status$', views.event_status,
|
||||||
|
name='event.details.status'),
|
||||||
]
|
]
|
||||||
|
|
||||||
calendar_patterns = [
|
calendar_patterns = [
|
||||||
url(r'^subscription$', 'gestioncof.views.calendar'),
|
url(r'^subscription$', views.calendar,
|
||||||
url(r'^(?P<token>[a-z0-9-]+)/calendar.ics$',
|
name='calendar'),
|
||||||
'gestioncof.views.calendar_ics')
|
url(r'^(?P<token>[a-z0-9-]+)/calendar.ics$', views.calendar_ics)
|
||||||
]
|
]
|
||||||
|
|
||||||
clubs_patterns = [
|
clubs_patterns = [
|
||||||
|
|
|
@ -20,6 +20,8 @@ from django.contrib import messages
|
||||||
|
|
||||||
from django_cas_ng.views import logout as cas_logout_view
|
from django_cas_ng.views import logout as cas_logout_view
|
||||||
|
|
||||||
|
from utils.views.autocomplete import Select2QuerySetView
|
||||||
|
|
||||||
from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
|
from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
|
||||||
SurveyQuestionAnswer
|
SurveyQuestionAnswer
|
||||||
from gestioncof.models import Event, EventRegistration, EventOption, \
|
from gestioncof.models import Event, EventRegistration, EventOption, \
|
||||||
|
@ -54,8 +56,8 @@ def home(request):
|
||||||
|
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated:
|
||||||
return redirect("gestioncof.views.home")
|
return redirect("home")
|
||||||
context = {}
|
context = {}
|
||||||
if request.method == "GET" and 'next' in request.GET:
|
if request.method == "GET" and 'next' in request.GET:
|
||||||
context['next'] = request.GET['next']
|
context['next'] = request.GET['next']
|
||||||
|
@ -786,3 +788,18 @@ class ConfigUpdate(FormView):
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.save()
|
form.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Autocomplete views
|
||||||
|
#
|
||||||
|
# https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#create-an-autocomplete-view
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
class UserAutocomplete(Select2QuerySetView):
|
||||||
|
model = User
|
||||||
|
search_fields = ('username', 'first_name', 'last_name')
|
||||||
|
|
||||||
|
|
||||||
|
user_autocomplete = buro_required(UserAutocomplete.as_view())
|
||||||
|
|
|
@ -12,8 +12,11 @@ class TemporaryAuthMiddleware:
|
||||||
values from CofProfile and Account of this user.
|
values from CofProfile and Account of this user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def process_request(self, request):
|
def __init__(self, get_response):
|
||||||
if request.user.is_authenticated():
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
if request.user.is_authenticated:
|
||||||
# avoid multiple db accesses in views and templates
|
# avoid multiple db accesses in views and templates
|
||||||
request.user = (
|
request.user = (
|
||||||
User.objects
|
User.objects
|
||||||
|
@ -30,6 +33,8 @@ class TemporaryAuthMiddleware:
|
||||||
request.real_user = request.user
|
request.real_user = request.user
|
||||||
request.user = temp_request_user
|
request.user = temp_request_user
|
||||||
|
|
||||||
|
return self.get_response(request)
|
||||||
|
|
||||||
def get_kfet_password(self, request):
|
def get_kfet_password(self, request):
|
||||||
return (
|
return (
|
||||||
request.META.get('HTTP_KFETPASSWORD') or
|
request.META.get('HTTP_KFETPASSWORD') or
|
||||||
|
|
|
@ -285,6 +285,8 @@ class TemporaryAuthTests(TestCase):
|
||||||
|
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
self.middleware = TemporaryAuthMiddleware(mock.Mock())
|
||||||
|
|
||||||
user1_acc = Account(trigramme='000')
|
user1_acc = Account(trigramme='000')
|
||||||
user1_acc.change_pwd('kfet_user1')
|
user1_acc.change_pwd('kfet_user1')
|
||||||
user1_acc.save({'username': 'user1'})
|
user1_acc.save({'username': 'user1'})
|
||||||
|
@ -311,7 +313,7 @@ class TemporaryAuthTests(TestCase):
|
||||||
request = self.factory.get('/', HTTP_KFETPASSWORD='kfet_user2')
|
request = self.factory.get('/', HTTP_KFETPASSWORD='kfet_user2')
|
||||||
request.user = self.user1
|
request.user = self.user1
|
||||||
|
|
||||||
TemporaryAuthMiddleware().process_request(request)
|
self.middleware(request)
|
||||||
|
|
||||||
self.assertEqual(request.user, self.user2)
|
self.assertEqual(request.user, self.user2)
|
||||||
self.assertEqual(request.real_user, self.user1)
|
self.assertEqual(request.real_user, self.user1)
|
||||||
|
@ -324,7 +326,7 @@ class TemporaryAuthTests(TestCase):
|
||||||
request = self.factory.post('/', {'KFETPASSWORD': 'kfet_user2'})
|
request = self.factory.post('/', {'KFETPASSWORD': 'kfet_user2'})
|
||||||
request.user = self.user1
|
request.user = self.user1
|
||||||
|
|
||||||
TemporaryAuthMiddleware().process_request(request)
|
self.middleware(request)
|
||||||
|
|
||||||
self.assertEqual(request.user, self.user2)
|
self.assertEqual(request.user, self.user2)
|
||||||
self.assertEqual(request.real_user, self.user1)
|
self.assertEqual(request.real_user, self.user1)
|
||||||
|
@ -336,7 +338,7 @@ class TemporaryAuthTests(TestCase):
|
||||||
request = self.factory.post('/', {'KFETPASSWORD': 'invalid'})
|
request = self.factory.post('/', {'KFETPASSWORD': 'invalid'})
|
||||||
request.user = self.user1
|
request.user = self.user1
|
||||||
|
|
||||||
TemporaryAuthMiddleware().process_request(request)
|
self.middleware(request)
|
||||||
|
|
||||||
self.assertEqual(request.user, self.user1)
|
self.assertEqual(request.user, self.user1)
|
||||||
self.assertFalse(hasattr(request, 'real_user'))
|
self.assertFalse(hasattr(request, 'real_user'))
|
||||||
|
|
|
@ -20,7 +20,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='KFetPage',
|
name='KFetPage',
|
||||||
fields=[
|
fields=[
|
||||||
('page_ptr', models.OneToOneField(serialize=False, primary_key=True, parent_link=True, auto_created=True, to='wagtailcore.Page')),
|
('page_ptr', models.OneToOneField(serialize=False, primary_key=True, parent_link=True, auto_created=True, to='wagtailcore.Page', on_delete=models.CASCADE)),
|
||||||
('no_header', models.BooleanField(verbose_name='Sans en-tête', help_text="Coché, l'en-tête (avec le titre) de la page n'est pas affiché.", default=False)),
|
('no_header', models.BooleanField(verbose_name='Sans en-tête', help_text="Coché, l'en-tête (avec le titre) de la page n'est pas affiché.", default=False)),
|
||||||
('content', wagtail.wagtailcore.fields.StreamField((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses'))))), ('group', wagtail.wagtailcore.blocks.StreamBlock((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses')))))), label='Contenu groupé'))), verbose_name='Contenu')),
|
('content', wagtail.wagtailcore.fields.StreamField((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses'))))), ('group', wagtail.wagtailcore.blocks.StreamBlock((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses')))))), label='Contenu groupé'))), verbose_name='Contenu')),
|
||||||
('layout', models.CharField(max_length=255, choices=[('kfet/base_col_1.html', 'Une colonne : centrée sur la page'), ('kfet/base_col_2.html', 'Deux colonnes : fixe à gauche, contenu à droite'), ('kfet/base_col_mult.html', 'Contenu scindé sur plusieurs colonnes')], help_text='Comment cette page devrait être affichée ?', verbose_name='Template', default='kfet/base_col_mult.html')),
|
('layout', models.CharField(max_length=255, choices=[('kfet/base_col_1.html', 'Une colonne : centrée sur la page'), ('kfet/base_col_2.html', 'Deux colonnes : fixe à gauche, contenu à droite'), ('kfet/base_col_mult.html', 'Contenu scindé sur plusieurs colonnes')], help_text='Comment cette page devrait être affichée ?', verbose_name='Template', default='kfet/base_col_mult.html')),
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from gestioncof.models import CofProfile
|
from gestioncof.models import CofProfile
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.six.moves import reduce
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
|
@ -85,7 +85,7 @@ class OpenKfet(CachedMixin, object):
|
||||||
'admin_status': self.admin_status(status),
|
'admin_status': self.admin_status(status),
|
||||||
'force_close': self.force_close,
|
'force_close': self.force_close,
|
||||||
}
|
}
|
||||||
return base, {**base, **restrict}
|
return base, dict(base, **restrict)
|
||||||
|
|
||||||
def export(self, user):
|
def export(self, user):
|
||||||
"""Export internal state for a given user.
|
"""Export internal state for a given user.
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
{% load wagtailcore_tags %}
|
{% load wagtailcore_tags %}
|
||||||
|
|
||||||
|
{% slugurl "kfet" as kfet_home_url %}
|
||||||
|
|
||||||
<nav class="navbar navbar-fixed-top">
|
<nav class="navbar navbar-fixed-top">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<a class="navbar-brand" href="{% slugurl "k-fet" %}">
|
<a class="navbar-brand" href="{{ kfet_home_url }}">
|
||||||
<img src="{% static 'kfet/img/logo3.png' %}">
|
<img src="{% static 'kfet/img/logo3.png' %}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,7 +101,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url "cof-logout" %}?next={% slugurl "k-fet" %}">
|
<a href="{% url "cof-logout" %}?next={{ kfet_home_url|urlencode }}">
|
||||||
<span class="glyphicon glyphicon-log-out"></span><span>Déconnexion</span>
|
<span class="glyphicon glyphicon-log-out"></span><span>Déconnexion</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -108,13 +110,13 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_authenticated and not perms.kfet.is_team %}
|
{% if user.is_authenticated and not perms.kfet.is_team %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url "cof-logout" %}?next={% slugurl "k-fet" %}" title="Déconnexion">
|
<a href="{% url "cof-logout" %}?next={{ kfet_home_url|urlencode }}" title="Déconnexion">
|
||||||
<span class="glyphicon glyphicon-log-out"></span>
|
<span class="glyphicon glyphicon-log-out"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% elif not user.is_authenticated %}
|
{% elif not user.is_authenticated %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url "cof-login" %}?next={{ request.path }}" title="Connexion">
|
<a href="{% url "cof-login" %}?next={{ request.path|urlencode }}" title="Connexion">
|
||||||
<span>Connexion</span><!--
|
<span>Connexion</span><!--
|
||||||
--><span class="glyphicon glyphicon-log-in"></span>
|
--><span class="glyphicon glyphicon-log-in"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -452,7 +452,7 @@ class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase):
|
||||||
'kfet.manage_perms',
|
'kfet.manage_perms',
|
||||||
)
|
)
|
||||||
self.group = Group.objects.create(name='K-Fêt - Group')
|
self.group = Group.objects.create(name='K-Fêt - Group')
|
||||||
self.group.permissions = self.perms.values()
|
self.group.permissions.set(self.perms.values())
|
||||||
|
|
||||||
def test_get_ok(self):
|
def test_get_ok(self):
|
||||||
r = self.client.get(self.url)
|
r = self.client.get(self.url)
|
||||||
|
|
|
@ -57,7 +57,7 @@ class TestCaseMixin:
|
||||||
'path': request.get_full_path(),
|
'path': request.get_full_path(),
|
||||||
'username': (
|
'username': (
|
||||||
"'{}'".format(request.user)
|
"'{}'".format(request.user)
|
||||||
if request.user.is_authenticated()
|
if request.user.is_authenticated
|
||||||
else 'anonymous'
|
else 'anonymous'
|
||||||
),
|
),
|
||||||
'code': response.status_code,
|
'code': response.status_code,
|
||||||
|
@ -96,7 +96,7 @@ class TestCaseMixin:
|
||||||
'path': request.get_full_path(),
|
'path': request.get_full_path(),
|
||||||
'username': (
|
'username': (
|
||||||
"'%s'" % request.user
|
"'%s'" % request.user
|
||||||
if request.user.is_authenticated()
|
if request.user.is_authenticated
|
||||||
else 'anonymous'
|
else 'anonymous'
|
||||||
),
|
),
|
||||||
'form_ctx': form_ctx,
|
'form_ctx': form_ctx,
|
||||||
|
|
|
@ -184,5 +184,5 @@ def user_add_perms(user, perms_labels):
|
||||||
|
|
||||||
# If permissions have already been fetched for this user, we need to reload
|
# If permissions have already been fetched for this user, we need to reload
|
||||||
# it to avoid using of the previous permissions cache.
|
# it to avoid using of the previous permissions cache.
|
||||||
# https://docs.djangoproject.com/en/1.11/topics/auth/default/#permission-caching
|
# https://docs.djangoproject.com/en/dev/topics/auth/default/#permission-caching
|
||||||
return User.objects.get(pk=user.pk)
|
return User.objects.get(pk=user.pk)
|
||||||
|
|
|
@ -859,30 +859,34 @@ def account_read_json(request):
|
||||||
'trigramme': account.trigramme }
|
'trigramme': account.trigramme }
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def kpsul_checkout_data(request):
|
def kpsul_checkout_data(request):
|
||||||
pk = request.POST.get('pk', 0)
|
pk = request.POST.get('pk', 0)
|
||||||
if not pk:
|
if not pk:
|
||||||
pk = 0
|
pk = 0
|
||||||
data = (Checkout.objects
|
|
||||||
|
data = (
|
||||||
|
Checkout.objects
|
||||||
.annotate(
|
.annotate(
|
||||||
last_statement_by_first_name=F('statements__by__cofprofile__user__first_name'),
|
last_statement_by_first_name=F('statements__by__cofprofile__user__first_name'),
|
||||||
last_statement_by_last_name=F('statements__by__cofprofile__user__last_name'),
|
last_statement_by_last_name=F('statements__by__cofprofile__user__last_name'),
|
||||||
last_statement_by_trigramme=F('statements__by__trigramme'),
|
last_statement_by_trigramme=F('statements__by__trigramme'),
|
||||||
last_statement_balance=F('statements__balance_new'),
|
last_statement_balance=F('statements__balance_new'),
|
||||||
last_statement_at=F('statements__at'))
|
last_statement_at=F('statements__at'))
|
||||||
.values(
|
|
||||||
'id', 'name', 'balance', 'valid_from', 'valid_to',
|
|
||||||
'last_statement_balance', 'last_statement_at',
|
|
||||||
'last_statement_by_trigramme', 'last_statement_by_last_name',
|
|
||||||
'last_statement_by_first_name')
|
|
||||||
.select_related(
|
.select_related(
|
||||||
'statements'
|
'statements'
|
||||||
'statements__by',
|
'statements__by',
|
||||||
'statements__by__cofprofile__user')
|
'statements__by__cofprofile__user')
|
||||||
.filter(pk=pk)
|
.filter(pk=pk)
|
||||||
.order_by('statements__at')
|
.order_by('statements__at')
|
||||||
.last())
|
.values(
|
||||||
|
'id', 'name', 'balance', 'valid_from', 'valid_to',
|
||||||
|
'last_statement_balance', 'last_statement_at',
|
||||||
|
'last_statement_by_trigramme', 'last_statement_by_last_name',
|
||||||
|
'last_statement_by_first_name')
|
||||||
|
.last()
|
||||||
|
)
|
||||||
if data is None:
|
if data is None:
|
||||||
raise Http404
|
raise Http404
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
@ -1310,13 +1314,13 @@ def history_json(request):
|
||||||
|
|
||||||
# Construction de la requête (sur les opérations) pour le prefetch
|
# Construction de la requête (sur les opérations) pour le prefetch
|
||||||
queryset_prefetch = Operation.objects.select_related(
|
queryset_prefetch = Operation.objects.select_related(
|
||||||
'canceled_by__trigramme', 'addcost_for__trigramme',
|
'article', 'canceled_by', 'addcost_for')
|
||||||
'article__name')
|
|
||||||
|
|
||||||
# Construction de la requête principale
|
# Construction de la requête principale
|
||||||
opegroups = (OperationGroup.objects
|
opegroups = (
|
||||||
.prefetch_related(Prefetch('opes', queryset = queryset_prefetch))
|
OperationGroup.objects
|
||||||
.select_related('on_acc__trigramme', 'valid_by__trigramme')
|
.prefetch_related(Prefetch('opes', queryset=queryset_prefetch))
|
||||||
|
.select_related('on_acc', 'valid_by')
|
||||||
.order_by('at')
|
.order_by('at')
|
||||||
)
|
)
|
||||||
# Application des filtres
|
# Application des filtres
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
-e git://github.com/jazzband/django-debug-toolbar.git@88ddc7bdf39c7ff660eac054eab225ac22926754#egg=django-debug-toolbar
|
django-debug-toolbar
|
||||||
django-debug-panel
|
django-debug-panel
|
||||||
ipython
|
ipython
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
configparser==3.5.0
|
configparser==3.5.0
|
||||||
Django==1.8.*
|
Django==1.11.*
|
||||||
django-autocomplete-light==2.3.3
|
django-autocomplete-light==3.1.3
|
||||||
django-autoslug==1.9.3
|
django-autoslug==1.9.3
|
||||||
django-cas-ng==3.5.7
|
django-cas-ng==3.5.7
|
||||||
django-djconfig==0.5.3
|
django-djconfig==0.5.3
|
||||||
django-grappelli==2.8.1
|
django-recaptcha==1.2.1
|
||||||
django-recaptcha==1.0.5
|
|
||||||
django-redis-cache==1.7.1
|
django-redis-cache==1.7.1
|
||||||
|
icalendar
|
||||||
psycopg2
|
psycopg2
|
||||||
Pillow==3.3.0
|
Pillow
|
||||||
unicodecsv==0.14.1
|
unicodecsv
|
||||||
icalendar==3.10
|
|
||||||
django-bootstrap-form==3.2.1
|
django-bootstrap-form==3.2.1
|
||||||
asgiref==1.1.1
|
asgiref==1.1.1
|
||||||
daphne==1.3.0
|
daphne==1.3.0
|
||||||
|
@ -24,8 +23,5 @@ python-dateutil
|
||||||
wagtail==1.10.*
|
wagtail==1.10.*
|
||||||
wagtailmenus==2.2.*
|
wagtailmenus==2.2.*
|
||||||
|
|
||||||
# Remove this when we switch to Django 1.11
|
|
||||||
djangorestframework==3.6.4
|
|
||||||
|
|
||||||
# Production tools
|
# Production tools
|
||||||
wheel
|
wheel
|
||||||
|
|
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
0
utils/views/__init__.py
Normal file
0
utils/views/__init__.py
Normal file
26
utils/views/autocomplete.py
Normal file
26
utils/views/autocomplete.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from dal import autocomplete
|
||||||
|
|
||||||
|
|
||||||
|
class Select2QuerySetView(autocomplete.Select2QuerySetView):
|
||||||
|
model = None
|
||||||
|
search_fields = []
|
||||||
|
|
||||||
|
def get_queryset_filter(self):
|
||||||
|
q = self.q
|
||||||
|
filter_q = Q()
|
||||||
|
|
||||||
|
if not q:
|
||||||
|
return filter_q
|
||||||
|
|
||||||
|
words = q.split()
|
||||||
|
|
||||||
|
for word in words:
|
||||||
|
for field in self.search_fields:
|
||||||
|
filter_q |= Q(**{'{}__icontains'.format(field): word})
|
||||||
|
|
||||||
|
return filter_q
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.model.objects.filter(self.get_queryset_filter())
|
Loading…
Reference in a new issue