forked from DGNum/gestioCOF
Merge branch 'Kerl/supportBDS/events' into supportBDS
Move event-related models from 'cof' app to 'gestion' app. Add 'Association' model to register name, related groups (buro, members), etc. Club is now associated with a single Association instance. Migrations take care of these changes.
This commit is contained in:
commit
3842b5d160
22 changed files with 1061 additions and 466 deletions
30
bds/apps.py
30
bds/apps.py
|
@ -1,6 +1,36 @@
|
|||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
def setup_groups(sender, apps, **kwargs):
|
||||
"""
|
||||
Add the appropriate permissions to the "member" and "buro" groups after the
|
||||
`post_migrate` signal since the permissions will only be inserted in the
|
||||
database at the very end of the migrations.
|
||||
"""
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
|
||||
# Buro members have perms bds.* and gestion.*
|
||||
buro, _ = Group.objects.get_or_create(name="bds_buro")
|
||||
app_perms = Permission.objects.filter(
|
||||
content_type__app_label__in=["cof", "gestion"]
|
||||
)
|
||||
buro.permissions.add(*app_perms)
|
||||
|
||||
# Members have perm bds.member
|
||||
members, _ = Group.objects.get_or_create(name="bds_members")
|
||||
perm = Permission.objects.get(
|
||||
codename="member",
|
||||
content_type__app_label="bds"
|
||||
)
|
||||
members.permissions.add(perm)
|
||||
|
||||
|
||||
class BDSConfig(AppConfig):
|
||||
name = "bds"
|
||||
verbose_name = "Application de gestion du BDS"
|
||||
|
||||
def ready(self):
|
||||
# https://docs.djangoproject.com/en/1.11/ref/signals/#post-migrate
|
||||
post_migrate.connect(setup_groups, sender=self)
|
||||
|
|
44
cof/admin.py
44
cof/admin.py
|
@ -9,9 +9,7 @@ from .petits_cours_models import PetitCoursDemande, \
|
|||
PetitCoursSubject, PetitCoursAbility, PetitCoursAttribution, \
|
||||
PetitCoursAttributionCounter
|
||||
from .models import (
|
||||
SurveyQuestionAnswer, SurveyQuestion, CofProfile, EventOption,
|
||||
EventOptionChoice, Event, EventCommentField, EventRegistration,
|
||||
Survey
|
||||
SurveyQuestionAnswer, SurveyQuestion, CofProfile, Survey
|
||||
)
|
||||
|
||||
|
||||
|
@ -62,43 +60,6 @@ class SurveyAdmin(admin.ModelAdmin):
|
|||
]
|
||||
|
||||
|
||||
class EventOptionChoiceInline(admin.TabularInline):
|
||||
model = EventOptionChoice
|
||||
|
||||
|
||||
@add_link_field(desc_text=lambda x: "Choix",
|
||||
link_text=lambda x: "Éditer les choix")
|
||||
class EventOptionInline(admin.TabularInline):
|
||||
model = EventOption
|
||||
|
||||
|
||||
class EventCommentFieldInline(admin.TabularInline):
|
||||
model = EventCommentField
|
||||
|
||||
|
||||
class EventOptionAdmin(admin.ModelAdmin):
|
||||
search_fields = ('event__title', 'name')
|
||||
inlines = [
|
||||
EventOptionChoiceInline,
|
||||
]
|
||||
|
||||
|
||||
class EventAdmin(admin.ModelAdmin):
|
||||
search_fields = ('title', 'location', 'description')
|
||||
inlines = [
|
||||
EventOptionInline,
|
||||
EventCommentFieldInline,
|
||||
]
|
||||
|
||||
|
||||
class EventRegistrationAdmin(admin.ModelAdmin):
|
||||
list_display = ('__unicode__' if six.PY2 else '__str__', 'event', 'user',
|
||||
'paid')
|
||||
list_filter = ('paid',)
|
||||
search_fields = ('user__username', 'user__first_name', 'user__last_name',
|
||||
'user__email', 'event__title')
|
||||
|
||||
|
||||
class PetitCoursAbilityAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'matiere', 'niveau', 'agrege')
|
||||
search_fields = ('user__username', 'user__first_name', 'user__last_name',
|
||||
|
@ -133,8 +94,6 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin):
|
|||
|
||||
admin.site.register(Survey, SurveyAdmin)
|
||||
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
|
||||
admin.site.register(Event, EventAdmin)
|
||||
admin.site.register(EventOption, EventOptionAdmin)
|
||||
admin.site.register(CofProfile)
|
||||
admin.site.register(PetitCoursSubject)
|
||||
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
|
||||
|
@ -142,4 +101,3 @@ admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
|
|||
admin.site.register(PetitCoursAttributionCounter,
|
||||
PetitCoursAttributionCounterAdmin)
|
||||
admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin)
|
||||
admin.site.register(EventRegistration, EventRegistrationAdmin)
|
||||
|
|
30
cof/apps.py
30
cof/apps.py
|
@ -1,6 +1,36 @@
|
|||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
def setup_groups(sender, apps, **kwargs):
|
||||
"""
|
||||
Add the appropriate permissions to the "member" and "buro" groups after the
|
||||
`post_migrate` signal since the permissions will only be inserted in the
|
||||
database at the very end of the migrations.
|
||||
"""
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
|
||||
# Buro members have perms cof.* and gestion.*
|
||||
buro, _ = Group.objects.get_or_create(name="cof_buro")
|
||||
app_perms = Permission.objects.filter(
|
||||
content_type__app_label__in=["cof", "gestion"]
|
||||
)
|
||||
buro.permissions.add(*app_perms)
|
||||
|
||||
# Members have perm cof.member
|
||||
members, _ = Group.objects.get_or_create(name="cof_members")
|
||||
perm = Permission.objects.get(
|
||||
codename="member",
|
||||
content_type__app_label="cof"
|
||||
)
|
||||
members.permissions.add(perm)
|
||||
|
||||
|
||||
class COFConfig(AppConfig):
|
||||
name = "cof"
|
||||
verbose_name = "Application de gestion du COF"
|
||||
|
||||
def ready(self):
|
||||
# https://docs.djangoproject.com/en/1.11/ref/signals/#post-migrate
|
||||
post_migrate.connect(setup_groups, sender=self)
|
||||
|
|
|
@ -67,63 +67,6 @@
|
|||
"model": "cof.surveyquestionanswer",
|
||||
"pk": 5
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"old": false,
|
||||
"description": "On va casser du romain.",
|
||||
"end_date": "2016-09-12T00:00:00Z",
|
||||
"title": "Bataille de Gergovie",
|
||||
"image": "",
|
||||
"location": "Gergovie",
|
||||
"registration_open": true,
|
||||
"start_date": "2016-09-09T00:00:00Z"
|
||||
},
|
||||
"model": "cof.event",
|
||||
"pk": 1
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"default": "",
|
||||
"event": 1,
|
||||
"fieldtype": "text",
|
||||
"name": "Commentaires"
|
||||
},
|
||||
"model": "cof.eventcommentfield",
|
||||
"pk": 1
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"multi_choices": true,
|
||||
"event": 1,
|
||||
"name": "Potion magique"
|
||||
},
|
||||
"model": "cof.eventoption",
|
||||
"pk": 1
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "Je suis alergique"
|
||||
},
|
||||
"model": "cof.eventoptionchoice",
|
||||
"pk": 1
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "J'en veux"
|
||||
},
|
||||
"model": "cof.eventoptionchoice",
|
||||
"pk": 2
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "Je suis tomb\u00e9 dans la marmite quand j'\u00e9tais petit"
|
||||
},
|
||||
"model": "cof.eventoptionchoice",
|
||||
"pk": 3
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"name": "Bagarre"
|
||||
|
|
47
cof/forms.py
47
cof/forms.py
|
@ -12,58 +12,15 @@ from django.forms.formsets import BaseFormSet, formset_factory
|
|||
from django.db.models import Max
|
||||
from django.core.validators import MinLengthValidator
|
||||
|
||||
from .models import CofProfile, EventCommentValue, CalendarSubscription
|
||||
from .models import CofProfile, CalendarSubscription
|
||||
from .widgets import TriStateCheckbox
|
||||
|
||||
from gestion.models import Profile
|
||||
from gestion.models import Profile, EventCommentValue
|
||||
from gestion.shared import lock_table, unlock_table
|
||||
|
||||
from bda.models import Spectacle
|
||||
|
||||
|
||||
class EventForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
event = kwargs.pop("event")
|
||||
self.event = event
|
||||
current_choices = kwargs.pop("current_choices", None)
|
||||
super(EventForm, self).__init__(*args, **kwargs)
|
||||
choices = {}
|
||||
if current_choices:
|
||||
for choice in current_choices.all():
|
||||
if choice.event_option.id not in choices:
|
||||
choices[choice.event_option.id] = [choice.id]
|
||||
else:
|
||||
choices[choice.event_option.id].append(choice.id)
|
||||
all_choices = choices
|
||||
for option in event.options.all():
|
||||
choices = [(choice.id, choice.value)
|
||||
for choice in option.choices.all()]
|
||||
if option.multi_choices:
|
||||
initial = [] if option.id not in all_choices \
|
||||
else all_choices[option.id]
|
||||
field = forms.MultipleChoiceField(
|
||||
label=option.name,
|
||||
choices=choices,
|
||||
widget=CheckboxSelectMultiple,
|
||||
required=False,
|
||||
initial=initial)
|
||||
else:
|
||||
initial = None if option.id not in all_choices \
|
||||
else all_choices[option.id][0]
|
||||
field = forms.ChoiceField(label=option.name,
|
||||
choices=choices,
|
||||
widget=RadioSelect,
|
||||
required=False,
|
||||
initial=initial)
|
||||
field.option_id = option.id
|
||||
self.fields["option_%d" % option.id] = field
|
||||
|
||||
def choices(self):
|
||||
for name, value in self.cleaned_data.items():
|
||||
if name.startswith('option_'):
|
||||
yield (self.fields[name].option_id, value)
|
||||
|
||||
|
||||
class SurveyForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
survey = kwargs.pop("survey")
|
||||
|
|
|
@ -23,27 +23,16 @@ def create_profile(apps, schema_editor):
|
|||
|
||||
|
||||
def preserve_perms(apps, schema_editor):
|
||||
# from django.contrib.auth.management import create_permissions
|
||||
COFProfile = apps.get_model("cof", "CofProfile")
|
||||
|
||||
# apps.models_module = True
|
||||
# create_permissions(apps, verbosity=0)
|
||||
# apps.models_module = None
|
||||
|
||||
CofProfile = apps.get_model("cof", "CofProfile")
|
||||
# memberp = Permission.objects.get(codename='member')
|
||||
# burop = Permission.objects.get(codename='buro')
|
||||
|
||||
# creates the groups for COF members and
|
||||
# create the groups for COF members and staff
|
||||
member = Group.objects.create(name='cof_members')
|
||||
buro = Group.objects.create(name='cof_buro')
|
||||
|
||||
# associate permissions to the respective groups.
|
||||
# buro.permissions = [burop, memberp]
|
||||
# member.permissions = [memberp]
|
||||
|
||||
for cofp in CofProfile.objects.filter(is_cof=True):
|
||||
cprofiles = COFProfile.objects.select_related("profile__user")
|
||||
for cofp in cprofiles.filter(is_cof=True):
|
||||
cofp.profile.user.groups.add(member)
|
||||
for cofp in CofProfile.objects.filter(is_buro=True):
|
||||
for cofp in cprofiles.filter(is_buro=True):
|
||||
cofp.profile.user.groups.add(buro)
|
||||
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_cof_group(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Group.objects.get_or_create(name="cof_members")
|
||||
|
||||
|
||||
def create_buro_group(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Group.objects.get_or_create(name="cof_buro")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cof', '0009_generic_profiles'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_cof_group, migrations.RunPython.noop),
|
||||
migrations.RunPython(create_buro_group, migrations.RunPython.noop),
|
||||
]
|
|
@ -7,7 +7,7 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cof', '0010_create_cof_group'),
|
||||
('cof', '0009_generic_profiles'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
96
cof/migrations/0013_move_events_to_gestion.py
Normal file
96
cof/migrations/0013_move_events_to_gestion.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-26 18:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cof', '0012_remove_club'),
|
||||
("gestion", "0003_association_and_events")
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='eventcommentfield',
|
||||
name='event',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventcommentvalue',
|
||||
name='commentfield',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventcommentvalue',
|
||||
name='registration',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventoption',
|
||||
name='event',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventoptionchoice',
|
||||
name='event_option',
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='eventregistration',
|
||||
unique_together=set([]),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventregistration',
|
||||
name='event',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventregistration',
|
||||
name='filledcomments',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventregistration',
|
||||
name='options',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventregistration',
|
||||
name='user',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='petitcoursattribution',
|
||||
name='matiere',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cof.PetitCoursSubject', verbose_name='Matière'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='petitcoursattributioncounter',
|
||||
name='matiere',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cof.PetitCoursSubject', verbose_name='Matiere'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='petitcoursattributioncounter',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='petitcoursdemande',
|
||||
name='traitee_par',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Event',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EventCommentField',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EventCommentValue',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EventOption',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EventOptionChoice',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EventRegistration',
|
||||
),
|
||||
]
|
120
cof/models.py
120
cof/models.py
|
@ -17,11 +17,6 @@ TYPE_COTIZ_CHOICES = (
|
|||
('exterieur', _("Extérieur")),
|
||||
)
|
||||
|
||||
TYPE_COMMENT_FIELD = (
|
||||
('text', _("Texte long")),
|
||||
('char', _("Texte court")),
|
||||
)
|
||||
|
||||
|
||||
class CofProfile(models.Model):
|
||||
profile = models.OneToOneField(Profile,
|
||||
|
@ -79,121 +74,6 @@ class CofProfile(models.Model):
|
|||
return self.profile.user.username
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Event(models.Model):
|
||||
title = models.CharField("Titre", max_length=200)
|
||||
location = models.CharField("Lieu", max_length=200)
|
||||
start_date = models.DateTimeField("Date de début", blank=True, null=True)
|
||||
end_date = models.DateTimeField("Date de fin", blank=True, null=True)
|
||||
description = models.TextField("Description", blank=True)
|
||||
image = models.ImageField("Image", blank=True, null=True,
|
||||
upload_to="imgs/events/")
|
||||
registration_open = models.BooleanField("Inscriptions ouvertes",
|
||||
default=True)
|
||||
old = models.BooleanField("Archiver (événement fini)", default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Événement"
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(self.title)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventCommentField(models.Model):
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="commentfields"
|
||||
)
|
||||
name = models.CharField("Champ", max_length=200)
|
||||
fieldtype = models.CharField("Type", max_length=10,
|
||||
choices=TYPE_COMMENT_FIELD, default="text")
|
||||
default = models.TextField("Valeur par défaut", blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Champ"
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(self.name)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventCommentValue(models.Model):
|
||||
commentfield = models.ForeignKey(
|
||||
EventCommentField,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="values"
|
||||
)
|
||||
registration = models.ForeignKey(
|
||||
"EventRegistration",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="comments"
|
||||
)
|
||||
content = models.TextField("Contenu", blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return "Commentaire de %s" % self.commentfield
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventOption(models.Model):
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="options"
|
||||
)
|
||||
name = models.CharField("Option", max_length=200)
|
||||
multi_choices = models.BooleanField("Choix multiples", default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Option"
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(self.name)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventOptionChoice(models.Model):
|
||||
event_option = models.ForeignKey(
|
||||
EventOption,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="choices"
|
||||
)
|
||||
value = models.CharField("Valeur", max_length=200)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Choix"
|
||||
verbose_name_plural = "Choix"
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(self.value)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventRegistration(models.Model):
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
options = models.ManyToManyField(EventOptionChoice)
|
||||
filledcomments = models.ManyToManyField(EventCommentField,
|
||||
through=EventCommentValue)
|
||||
paid = models.BooleanField("A payé", default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Inscription"
|
||||
unique_together = ("user", "event")
|
||||
|
||||
def __str__(self):
|
||||
return "Inscription de %s à %s" % (six.text_type(self.user),
|
||||
six.text_type(self.event.title))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Survey(models.Model):
|
||||
title = models.CharField("Titre", max_length=200)
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
<div class="container">
|
||||
<div class="home-menu row">
|
||||
<div class="{% if user.profile.cof.is_buro %}col-sm-6 {% else %}col-sm-8 col-sm-offset-2 col-xs-12 {%endif%}normal-user-hm">
|
||||
<!-- {% if open_events %}
|
||||
<h3 class="block-title">Événements<span class="pull-right glyphicon glyphicon-calendar"></span></h3>
|
||||
<div class="hm-block">
|
||||
<ul>
|
||||
{% for event in open_events %}
|
||||
<li><a href="{% url "event" event.id %}">{{ event.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %} -->
|
||||
{% if open_surveys %}
|
||||
<h3 class="block-title">Sondages en cours<span class="pull-right glyphicon glyphicon-stats"></span></h3>
|
||||
<div class="hm-block">
|
||||
|
|
|
@ -53,15 +53,6 @@ surveys_patterns = [
|
|||
name="survey"),
|
||||
]
|
||||
|
||||
events_patterns = [
|
||||
url(r'^(?P<event_id>\d+)$',
|
||||
views.event,
|
||||
name="event"),
|
||||
url(r'^(?P<event_id>\d+)/status$',
|
||||
views.event_status,
|
||||
name="event.status"),
|
||||
]
|
||||
|
||||
calendar_patterns = [
|
||||
url(r'^subscription$',
|
||||
views.calendar,
|
||||
|
|
155
cof/views.py
155
cof/views.py
|
@ -15,18 +15,19 @@ from django.utils import timezone
|
|||
from django.contrib import messages
|
||||
import django.utils.six as six
|
||||
|
||||
from gestion.models import (
|
||||
Event, EventRegistration, EventOption, EventOptionChoice,
|
||||
EventCommentField, EventCommentValue
|
||||
)
|
||||
|
||||
from .models import Survey, SurveyAnswer, SurveyQuestion, \
|
||||
SurveyQuestionAnswer
|
||||
from .models import Event, EventRegistration, EventOption, \
|
||||
EventOptionChoice
|
||||
from .models import EventCommentField, EventCommentValue, \
|
||||
CalendarSubscription
|
||||
SurveyQuestionAnswer, CalendarSubscription
|
||||
from .models import CofProfile
|
||||
from .decorators import buro_required, cof_required
|
||||
from .forms import (
|
||||
EventStatusFilterForm, SurveyForm, SurveyStatusFilterForm,
|
||||
SurveyForm, SurveyStatusFilterForm,
|
||||
RegistrationUserForm, RegistrationProfileForm, RegistrationCofProfileForm,
|
||||
EventForm, CalendarForm, EventFormset, RegistrationPassUserForm
|
||||
CalendarForm, EventFormset, RegistrationPassUserForm
|
||||
)
|
||||
|
||||
from bda.models import Tirage, Spectacle
|
||||
|
@ -37,7 +38,6 @@ from gestion.models import Profile
|
|||
@login_required
|
||||
def home(request):
|
||||
data = {"surveys": Survey.objects.filter(old=False).all(),
|
||||
"events": Event.objects.filter(old=False).all(),
|
||||
"open_surveys":
|
||||
Survey.objects.filter(survey_open=True, old=False).all(),
|
||||
"open_events":
|
||||
|
@ -125,72 +125,6 @@ def survey(request, survey_id):
|
|||
})
|
||||
|
||||
|
||||
def get_event_form_choices(event, form):
|
||||
all_choices = []
|
||||
for option_id, choices_ids in form.choices():
|
||||
option = get_object_or_404(EventOption, id=option_id,
|
||||
event=event)
|
||||
if type(choices_ids) != list:
|
||||
choices_ids = [choices_ids]
|
||||
if not option.multi_choices and len(choices_ids) > 1:
|
||||
raise Http404
|
||||
for choice_id in choices_ids:
|
||||
if not choice_id:
|
||||
continue
|
||||
choice_id = int(choice_id)
|
||||
choice = EventOptionChoice.objects.get(
|
||||
id=choice_id,
|
||||
event_option=option)
|
||||
all_choices.append(choice)
|
||||
return all_choices
|
||||
|
||||
|
||||
def update_event_form_comments(event, form, registration):
|
||||
for commentfield_id, value in form.comments():
|
||||
field = get_object_or_404(EventCommentField, id=commentfield_id,
|
||||
event=event)
|
||||
if value == field.default:
|
||||
continue
|
||||
(storage, _) = EventCommentValue.objects.get_or_create(
|
||||
commentfield=field,
|
||||
registration=registration)
|
||||
storage.content = value
|
||||
storage.save()
|
||||
|
||||
|
||||
@login_required
|
||||
def event(request, event_id):
|
||||
event = get_object_or_404(Event, id=event_id)
|
||||
if (not event.registration_open) or event.old:
|
||||
raise Http404
|
||||
success = False
|
||||
if request.method == "POST":
|
||||
form = EventForm(request.POST, event=event)
|
||||
if form.is_valid():
|
||||
all_choices = get_event_form_choices(event, form)
|
||||
(current_registration, _) = \
|
||||
EventRegistration.objects.get_or_create(user=request.user,
|
||||
event=event)
|
||||
current_registration.options = all_choices
|
||||
current_registration.save()
|
||||
success = True
|
||||
else:
|
||||
try:
|
||||
current_registration = \
|
||||
EventRegistration.objects.get(user=request.user, event=event)
|
||||
form = EventForm(event=event,
|
||||
current_choices=current_registration.options)
|
||||
except EventRegistration.DoesNotExist:
|
||||
form = EventForm(event=event)
|
||||
# Messages
|
||||
if success:
|
||||
messages.success(request, "Votre inscription a bien été enregistrée ! "
|
||||
"Vous pouvez cependant la modifier jusqu'à "
|
||||
"la fin des inscriptions.")
|
||||
return render(request, "cof/event.html",
|
||||
{"event": event, "form": form})
|
||||
|
||||
|
||||
def clean_post_for_status(initial):
|
||||
d = initial.copy()
|
||||
for k, v in d.items():
|
||||
|
@ -200,46 +134,6 @@ def clean_post_for_status(initial):
|
|||
return d
|
||||
|
||||
|
||||
@buro_required
|
||||
def event_status(request, event_id):
|
||||
event = get_object_or_404(Event, id=event_id)
|
||||
registrations_query = EventRegistration.objects.filter(event=event)
|
||||
post_data = clean_post_for_status(request.POST)
|
||||
form = EventStatusFilterForm(post_data or None, event=event)
|
||||
if form.is_valid():
|
||||
for option_id, choice_id, value in form.filters():
|
||||
if option_id == "has_paid":
|
||||
if value == "yes":
|
||||
registrations_query = registrations_query.filter(paid=True)
|
||||
elif value == "no":
|
||||
registrations_query = registrations_query.filter(
|
||||
paid=False)
|
||||
continue
|
||||
choice = get_object_or_404(EventOptionChoice, id=choice_id,
|
||||
event_option__id=option_id)
|
||||
if value == "none":
|
||||
continue
|
||||
if value == "yes":
|
||||
registrations_query = registrations_query.filter(
|
||||
options__id__exact=choice.id)
|
||||
elif value == "no":
|
||||
registrations_query = registrations_query.exclude(
|
||||
options__id__exact=choice.id)
|
||||
user_choices = registrations_query.prefetch_related("user").all()
|
||||
options = EventOption.objects.filter(event=event).all()
|
||||
choices_count = {}
|
||||
for option in options:
|
||||
for choice in option.choices.all():
|
||||
choices_count[choice.id] = 0
|
||||
for user_choice in user_choices:
|
||||
for choice in user_choice.options.all():
|
||||
choices_count[choice.id] += 1
|
||||
return render(request, "event_status.html",
|
||||
{"event": event, "user_choices": user_choices,
|
||||
"options": options, "choices_count": choices_count,
|
||||
"form": form})
|
||||
|
||||
|
||||
@buro_required
|
||||
def survey_status(request, survey_id):
|
||||
survey = get_object_or_404(Survey, id=survey_id)
|
||||
|
@ -342,6 +236,39 @@ def registration_form2(request, login_clipper=None, username=None,
|
|||
"event_formset": event_formset})
|
||||
|
||||
|
||||
def get_event_form_choices(event, form):
|
||||
all_choices = []
|
||||
for option_id, choices_ids in form.choices():
|
||||
option = get_object_or_404(EventOption, id=option_id,
|
||||
event=event)
|
||||
if type(choices_ids) != list:
|
||||
choices_ids = [choices_ids]
|
||||
if not option.multi_choices and len(choices_ids) > 1:
|
||||
raise Http404
|
||||
for choice_id in choices_ids:
|
||||
if not choice_id:
|
||||
continue
|
||||
choice_id = int(choice_id)
|
||||
choice = EventOptionChoice.objects.get(
|
||||
id=choice_id,
|
||||
event_option=option)
|
||||
all_choices.append(choice)
|
||||
return all_choices
|
||||
|
||||
|
||||
def update_event_form_comments(event, form, registration):
|
||||
for commentfield_id, value in form.comments():
|
||||
field = get_object_or_404(EventCommentField, id=commentfield_id,
|
||||
event=event)
|
||||
if value == field.default:
|
||||
continue
|
||||
(storage, _) = EventCommentValue.objects.get_or_create(
|
||||
commentfield=field,
|
||||
registration=registration)
|
||||
storage.content = value
|
||||
storage.save()
|
||||
|
||||
|
||||
@buro_required
|
||||
def registration(request):
|
||||
if request.POST:
|
||||
|
|
|
@ -43,6 +43,7 @@ INSTALLED_APPS = (
|
|||
'channels',
|
||||
'widget_tweaks',
|
||||
'custommail',
|
||||
'nested_admin',
|
||||
'bda.apps.BdAConfig',
|
||||
'bds.apps.BDSConfig',
|
||||
'cof.apps.COFConfig',
|
||||
|
|
|
@ -16,7 +16,7 @@ from django.contrib.auth import views as django_views
|
|||
|
||||
from cof import views as cof_views
|
||||
from cof.urls import export_patterns, petitcours_patterns, \
|
||||
surveys_patterns, events_patterns, calendar_patterns
|
||||
surveys_patterns, calendar_patterns
|
||||
from cof.autocomplete import autocomplete
|
||||
|
||||
from gestion import views as gestion_views
|
||||
|
@ -40,8 +40,6 @@ urlpatterns = [
|
|||
url(r'^petitcours/', include(petitcours_patterns)),
|
||||
# Les sondages
|
||||
url(r'^survey/', include(surveys_patterns)),
|
||||
# Evenements
|
||||
url(r'^event/', include(events_patterns)),
|
||||
# Calendrier
|
||||
url(r'^calendar/', include(calendar_patterns)),
|
||||
# Infos persos
|
||||
|
@ -83,6 +81,7 @@ urlpatterns = [
|
|||
cof_views.liste_bdarevente,
|
||||
name="liste_bdarevente"),
|
||||
url(r'^k-fet/', include(kfet.urls)),
|
||||
url(r"^_nested_admin/", include("nested_admin.urls")),
|
||||
]
|
||||
|
||||
if 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import nested_admin
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import User
|
||||
from django.forms.models import modelform_factory
|
||||
|
||||
from .models import Profile, Club
|
||||
from .models import (
|
||||
Association, Profile, Club, Event, Location,
|
||||
EventOption, EventCommentField, EventOptionChoice
|
||||
)
|
||||
|
||||
|
||||
# ---
|
||||
|
@ -19,7 +25,6 @@ class UserProfileAdmin(UserAdmin):
|
|||
ProfileInline,
|
||||
]
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserProfileAdmin)
|
||||
|
||||
|
@ -38,3 +43,62 @@ class ClubAdmin(admin.ModelAdmin):
|
|||
ClubUserInline,
|
||||
]
|
||||
exclude = ('members',)
|
||||
|
||||
|
||||
# ---
|
||||
# Events
|
||||
# ---
|
||||
|
||||
class EventOptionChoiceInline(nested_admin.NestedTabularInline):
|
||||
model = EventOptionChoice
|
||||
extra = 1
|
||||
|
||||
|
||||
class EventOptionInline(nested_admin.NestedTabularInline):
|
||||
model = EventOption
|
||||
extra = 1
|
||||
inlines = [EventOptionChoiceInline]
|
||||
|
||||
|
||||
class EventCommentFieldInline(nested_admin.NestedTabularInline):
|
||||
model = EventCommentField
|
||||
extra = 1
|
||||
|
||||
|
||||
@admin.register(Event)
|
||||
class EventAdmin(nested_admin.NestedModelAdmin):
|
||||
search_fields = ['title', 'location', 'description']
|
||||
inlines = [
|
||||
EventOptionInline,
|
||||
EventCommentFieldInline,
|
||||
]
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Restrict the event you can edit"""
|
||||
qs = super().get_queryset(request)
|
||||
if request.user.is_superuser:
|
||||
return qs
|
||||
else:
|
||||
assocs = Association.objects.filter(staff_group__user=request.user)
|
||||
return qs.filter(associations__in=assocs)
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
"""Restrict the applications you can attach to an event"""
|
||||
if request.user.is_superuser:
|
||||
return super().get_form(request, obj, **kwargs)
|
||||
else:
|
||||
assocs = Association.objects.filter(staff_group__user=request.user)
|
||||
|
||||
def formfield_callback(f, **kwargs):
|
||||
if f.name == "associations":
|
||||
kwargs["queryset"] = assocs
|
||||
return f.formfield(**kwargs)
|
||||
|
||||
return modelform_factory(
|
||||
Event,
|
||||
exclude=[],
|
||||
formfield_callback=formfield_callback
|
||||
)
|
||||
|
||||
|
||||
admin.site.register(Location)
|
||||
|
|
193
gestion/fixtures/events.json
Normal file
193
gestion/fixtures/events.json
Normal file
|
@ -0,0 +1,193 @@
|
|||
[
|
||||
{
|
||||
"model": "gestion.location",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Gergovie"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.location",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Lut\u00e8ce"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.event",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"title": "Bataille de gergovie",
|
||||
"location": 1,
|
||||
"start_date": "2017-06-25T18:00:00Z",
|
||||
"end_date": "2017-06-27T18:00:00Z",
|
||||
"description": "Bataille historique \u00e0 ne pas manquer.\r\nOpen sanglier \u00e0 la fin.",
|
||||
"image": "",
|
||||
"registration_open": true,
|
||||
"old": false,
|
||||
"associations": [
|
||||
["COF"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.event",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"title": "Course de chars",
|
||||
"location": 2,
|
||||
"start_date": "2017-06-25T13:00:00Z",
|
||||
"end_date": "2017-06-25T16:00:00Z",
|
||||
"description": "Finale de la league nationale de char \u00e0 b\u0153ufs",
|
||||
"image": "",
|
||||
"registration_open": true,
|
||||
"old": false,
|
||||
"associations": [
|
||||
["BDS"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventcommentfield",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"event": 1,
|
||||
"name": "Commentaires",
|
||||
"fieldtype": "text",
|
||||
"default": "",
|
||||
"ordering": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoption",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"event": 1,
|
||||
"name": "Potion magique",
|
||||
"multi_choices": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoption",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"event": 1,
|
||||
"name": "Extras pendant le buffet",
|
||||
"multi_choices": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoption",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"event": 2,
|
||||
"name": "Type de si\u00e8ge",
|
||||
"multi_choices": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoption",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"event": 2,
|
||||
"name": "Forfaits suppl\u00e9mentaires",
|
||||
"multi_choices": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "Oui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "Non"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"event_option": 1,
|
||||
"value": "Je suis tomb\u00e9 dans la marmitte quand j'\u00e9tais petit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"event_option": 2,
|
||||
"value": "Sanglier"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"event_option": 2,
|
||||
"value": "Cervoise"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"event_option": 2,
|
||||
"value": "Pot\u00e9e auvergnate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"event_option": 2,
|
||||
"value": "Saint Nectaire"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"event_option": 3,
|
||||
"value": "Debout"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"event_option": 3,
|
||||
"value": "Assis standard"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"event_option": 3,
|
||||
"value": "Premier rang"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"event_option": 4,
|
||||
"value": "Popcorn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gestion.eventoptionchoice",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"event_option": 4,
|
||||
"value": "Coussin moelleux"
|
||||
}
|
||||
}
|
||||
]
|
289
gestion/migrations/0003_association_and_events.py
Normal file
289
gestion/migrations/0003_association_and_events.py
Normal file
|
@ -0,0 +1,289 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-26 18:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def create_apps(apps, schema_editor):
|
||||
Association = apps.get_model("gestion", "Association")
|
||||
Group = apps.get_model("auth", "Group")
|
||||
|
||||
cof_m = Group.objects.get(name="cof_members")
|
||||
cof_b = Group.objects.get(name="cof_buro")
|
||||
bds_m = Group.objects.get(name="bds_members")
|
||||
bds_b = Group.objects.get(name="bds_buro")
|
||||
|
||||
Association.objects.bulk_create([
|
||||
Association(id=1, name="COF", staff_group=cof_b, members_group=cof_m),
|
||||
Association(id=2, name="BDS", staff_group=bds_b, members_group=bds_m),
|
||||
])
|
||||
|
||||
|
||||
def import_events(apps, schema_editor):
|
||||
# Fetching the models that have be moved from cof to gestion
|
||||
model_names = [
|
||||
"Event", "EventCommentField", "EventCommentValue", "EventOption",
|
||||
"EventOptionChoice", "EventRegistration"
|
||||
]
|
||||
models = [
|
||||
(apps.get_model("cof", name), apps.get_model("gestion", name))
|
||||
for name in model_names
|
||||
]
|
||||
|
||||
# The old Event.location field becomes a table: we need to create an entry
|
||||
# in this table for each value of the old `location` field.
|
||||
OldEvent, NewEvent = models[0]
|
||||
Location = apps.get_model("gestion", "Location")
|
||||
locations = set() # A set to prevent duplicate entries
|
||||
events = []
|
||||
for event in OldEvent.objects.values():
|
||||
locations.add(event["location"])
|
||||
events.append(event)
|
||||
Location.objects.bulk_create([Location(name=name) for name in locations])
|
||||
map_loc = {
|
||||
loc.name: loc
|
||||
for loc in Location.objects.all()
|
||||
}
|
||||
for event in events:
|
||||
event["location"] = map_loc[event["location"]]
|
||||
NewEvent.objects.bulk_create([
|
||||
NewEvent(**event)
|
||||
for event in events
|
||||
])
|
||||
|
||||
# Do not forget to link all the existing event to the COF group
|
||||
cof = apps.get_model("gestion", "Association").objects.get(name="COF")
|
||||
for event in NewEvent.objects.all():
|
||||
event.associations.add(cof)
|
||||
|
||||
# Migrating the other models is straigtforward
|
||||
for OldModel, NewModel in models[1:]:
|
||||
NewModel.objects.bulk_create([
|
||||
NewModel(**values)
|
||||
for values in OldModel.objects.values()
|
||||
])
|
||||
|
||||
|
||||
def restore_events(apps, schema_editor):
|
||||
# TODO?
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
('gestion', '0002_club_support'),
|
||||
("cof", "0009_generic_profiles"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Association',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=30, unique=True, verbose_name="nom de l'association")),
|
||||
('members_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='member_group_of', to='auth.Group', verbose_name='groupe des membres')),
|
||||
('staff_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='staff_group_of', to='auth.Group', verbose_name='groupe des membres du bureau')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'association',
|
||||
'verbose_name_plural': 'associations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Event',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200, verbose_name='titre')),
|
||||
('start_date', models.DateTimeField(blank=True, null=True, verbose_name="début de l'événement")),
|
||||
('end_date', models.DateTimeField(blank=True, null=True, verbose_name="fin de l'événement")),
|
||||
('description', models.TextField(blank=True, verbose_name='description')),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='public/imgs/events/', verbose_name='image')),
|
||||
('registration_open', models.NullBooleanField(default=True, help_text="Indéfini signifie « l'inscription n'est pas requise\xa0»", verbose_name='les inscriptions sont ouvertes')),
|
||||
('old', models.BooleanField(default=False, verbose_name='archiver (événement fini)')),
|
||||
('associations', models.ManyToManyField(related_name='events', to='gestion.Association', verbose_name='associations')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'événement',
|
||||
'verbose_name_plural': 'événements',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventCommentField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom du champ')),
|
||||
('fieldtype', models.CharField(choices=[('text', 'Texte long'), ('char', 'Texte court')], default='text', max_length=10, verbose_name='type de champ')),
|
||||
('default', models.TextField(blank=True, verbose_name='valeur par défaut')),
|
||||
('ordering', models.IntegerField(default=False, help_text='plus petit en premier, ordre alphabétique sur le nom si ambiguïté', verbose_name='ordre des champs')),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='commentfields', to='gestion.Event', verbose_name='événement')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'champ',
|
||||
'verbose_name_plural': 'champs',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventCommentValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('content', models.TextField(blank=True, verbose_name='contenu')),
|
||||
('commentfield', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='gestion.EventCommentField')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventOption',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='option')),
|
||||
('multi_choices', models.BooleanField(default=False, verbose_name='choix multiples')),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='gestion.Event', verbose_name='événement')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'option des événements',
|
||||
'verbose_name_plural': 'options des événements',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventOptionChoice',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.CharField(max_length=200, verbose_name='Valeur')),
|
||||
('event_option', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='choices', to='gestion.EventOption', verbose_name='événement')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'choix',
|
||||
'verbose_name_plural': 'choix',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventRegistration',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('paid', models.BooleanField(default=False, verbose_name='a payé')),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gestion.Event', verbose_name='événement')),
|
||||
('filledcomments', models.ManyToManyField(through='gestion.EventCommentValue', to='gestion.EventCommentField', verbose_name='commentaires')),
|
||||
('options', models.ManyToManyField(to='gestion.EventOptionChoice', verbose_name='choix')),
|
||||
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='utilisateur')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'inscription',
|
||||
'verbose_name_plural': 'inscriptions',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Location',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom du lieu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'lieu',
|
||||
'verbose_name_plural': 'lieux',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='profile',
|
||||
options={'verbose_name': 'profil', 'verbose_name_plural': 'profils'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='club',
|
||||
name='associations',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='club',
|
||||
name='cotisation_frequency',
|
||||
field=models.CharField(blank=True, choices=[('ANN', 'Annuel'), ('SEM', 'Semestriel'), ('COU', 'Au cours')], default='ANN', max_length=3, verbose_name='fréquence de la cotisation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='club',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, verbose_name='description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='club',
|
||||
name='members',
|
||||
field=models.ManyToManyField(blank=True, related_name='in_clubs', through='gestion.ClubUser', to=settings.AUTH_USER_MODEL, verbose_name='membres du club'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='club',
|
||||
name='name',
|
||||
field=models.CharField(max_length=200, unique=True, verbose_name='nom'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='club',
|
||||
name='price',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=0, max_digits=5, verbose_name='cotisation (€)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clubuser',
|
||||
name='has_paid',
|
||||
field=models.BooleanField(verbose_name='a payé sa cotisation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clubuser',
|
||||
name='is_respo',
|
||||
field=models.BooleanField(verbose_name='est responsable du club'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='comments',
|
||||
field=models.TextField(blank=True, verbose_name="commentaires visibles par l'utilisateur"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='departement',
|
||||
field=models.CharField(blank=True, max_length=50, verbose_name='département'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='login_clipper',
|
||||
field=models.CharField(blank=True, max_length=8, verbose_name='login clipper'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='occupation',
|
||||
field=models.CharField(choices=[('exterieur', 'Extérieur'), ('1A', '1A'), ('2A', '2A'), ('3A', '3A'), ('4A', '4A'), ('archicube', 'Archicube'), ('doctorant', 'Doctorant'), ('CST', 'CST')], default='1A', max_length=9, verbose_name='occupation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='phone',
|
||||
field=models.CharField(blank=True, max_length=20, verbose_name='téléphone'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL, verbose_name='utilisateur'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='eventcommentvalue',
|
||||
name='registration',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='gestion.EventRegistration'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='location',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='gestion.Location', verbose_name='lieux'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='club',
|
||||
name='association',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, related_name='clubs', to='gestion.Association', verbose_name='association'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='eventregistration',
|
||||
unique_together=set([('user', 'event')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='eventcommentvalue',
|
||||
unique_together=set([('commentfield', 'registration')]),
|
||||
),
|
||||
migrations.RunPython(create_apps, migrations.RunPython.noop),
|
||||
migrations.RunPython(import_events, restore_events),
|
||||
]
|
26
gestion/migrations/0004_event_location_not_mandatory.py
Normal file
26
gestion/migrations/0004_event_location_not_mandatory.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11b1 on 2017-08-07 19:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gestion', '0003_association_and_events'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='location',
|
||||
field=models.ForeignKey(
|
||||
blank=True, null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
to='gestion.Location',
|
||||
verbose_name='lieux'
|
||||
),
|
||||
),
|
||||
]
|
|
@ -7,6 +7,11 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from cof.petits_cours_models import choices_length
|
||||
|
||||
|
||||
# ---
|
||||
# User management
|
||||
# ---
|
||||
|
||||
OCCUPATION_CHOICES = (
|
||||
('exterieur', _("Extérieur")),
|
||||
('1A', _("1A")),
|
||||
|
@ -23,23 +28,29 @@ class Profile(models.Model):
|
|||
user = models.OneToOneField(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="profile"
|
||||
related_name="profile",
|
||||
verbose_name=_("utilisateur"),
|
||||
)
|
||||
login_clipper = models.CharField("Login clipper", max_length=8, blank=True)
|
||||
phone = models.CharField("Téléphone", max_length=20, blank=True)
|
||||
occupation = models.CharField(_("Occupation"),
|
||||
login_clipper = models.CharField(
|
||||
_("login clipper"),
|
||||
max_length=8,
|
||||
blank=True,
|
||||
)
|
||||
phone = models.CharField(_("téléphone"), max_length=20, blank=True)
|
||||
occupation = models.CharField(_("occupation"),
|
||||
default="1A",
|
||||
choices=OCCUPATION_CHOICES,
|
||||
max_length=choices_length(
|
||||
OCCUPATION_CHOICES))
|
||||
departement = models.CharField(_("Département"), max_length=50,
|
||||
departement = models.CharField(_("département"), max_length=50,
|
||||
blank=True)
|
||||
comments = models.TextField(
|
||||
"Commentaires visibles par l'utilisateur", blank=True)
|
||||
_("commentaires visibles par l'utilisateur"), blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Profil"
|
||||
verbose_name_plural = "Profils"
|
||||
verbose_name = _("profil")
|
||||
verbose_name_plural = _("profils")
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
@ -56,6 +67,49 @@ def post_delete_user(sender, instance, *args, **kwargs):
|
|||
instance.user.delete()
|
||||
|
||||
|
||||
class AssociationManager(models.Manager):
|
||||
def get_by_natural_key(self, name):
|
||||
return self.get(name=name)
|
||||
|
||||
|
||||
class Association(models.Model):
|
||||
objects = AssociationManager()
|
||||
|
||||
name = models.CharField(
|
||||
_("nom de l'association"),
|
||||
unique=True,
|
||||
max_length=30
|
||||
)
|
||||
staff_group = models.ForeignKey(
|
||||
Group,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="staff_group_of",
|
||||
blank=True, null=True,
|
||||
verbose_name=_("groupe des membres du bureau"),
|
||||
)
|
||||
members_group = models.ForeignKey(
|
||||
Group,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="member_group_of",
|
||||
blank=True, null=True,
|
||||
verbose_name=_("groupe des membres"),
|
||||
)
|
||||
|
||||
def natural_key(self):
|
||||
return [self.name]
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("association")
|
||||
verbose_name_plural = _("associations")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# ---
|
||||
# Clubs
|
||||
# ---
|
||||
|
||||
class Club(models.Model):
|
||||
ANNUAL = "ANN"
|
||||
SEMESTER = "SEM"
|
||||
|
@ -67,24 +121,30 @@ class Club(models.Model):
|
|||
(COURSE, _("Au cours"))
|
||||
]
|
||||
|
||||
associations = models.ManyToManyField(Group, related_name="clubs")
|
||||
name = models.CharField(_("Nom"), max_length=200, unique=True)
|
||||
description = models.TextField("Description", blank=True)
|
||||
association = models.ForeignKey(
|
||||
Association,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="clubs",
|
||||
verbose_name=_("association"),
|
||||
)
|
||||
name = models.CharField(_("nom"), max_length=200, unique=True)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
members = models.ManyToManyField(
|
||||
User,
|
||||
through="ClubUser",
|
||||
related_name="in_clubs",
|
||||
blank=True
|
||||
blank=True,
|
||||
verbose_name=_("membres du club"),
|
||||
)
|
||||
price = models.DecimalField(
|
||||
_("Cotisation (€)"),
|
||||
_("cotisation (€)"),
|
||||
decimal_places=2,
|
||||
max_digits=5,
|
||||
blank=True,
|
||||
default=0
|
||||
)
|
||||
cotisation_frequency = models.CharField(
|
||||
_("Fréquence de la cotisation"),
|
||||
_("fréquence de la cotisation"),
|
||||
default=ANNUAL,
|
||||
choices=COTISATION_FREQUENCY_CHOICES,
|
||||
max_length=3,
|
||||
|
@ -94,10 +154,10 @@ class Club(models.Model):
|
|||
def __str__(self):
|
||||
template = (
|
||||
self.needs_cotiz and
|
||||
"{name} ({price}€ / {cotisation_frequency})" or
|
||||
"{name}"
|
||||
"%(name)s (%(price).2f€ / %(cotisation_frequency)s)" or
|
||||
"%(name)s"
|
||||
)
|
||||
return template.format(**vars(self))
|
||||
return template % vars(self)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('gestion:club_detail', args=[str(self.pk)])
|
||||
|
@ -113,5 +173,202 @@ class Club(models.Model):
|
|||
class ClubUser(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
club = models.ForeignKey(Club, on_delete=models.CASCADE)
|
||||
is_respo = models.BooleanField(_("Est responsable du club"))
|
||||
has_paid = models.BooleanField(_("A payé sa cotisation"))
|
||||
is_respo = models.BooleanField(_("est responsable du club"))
|
||||
has_paid = models.BooleanField(_("a payé sa cotisation"))
|
||||
|
||||
|
||||
# ---
|
||||
# Events
|
||||
# ---
|
||||
|
||||
class Location(models.Model):
|
||||
name = models.CharField(_("nom du lieu"), max_length=200)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("lieu")
|
||||
verbose_name_plural = _("lieux")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Event(models.Model):
|
||||
associations = models.ManyToManyField(
|
||||
Association,
|
||||
related_name="events",
|
||||
verbose_name=_("associations"),
|
||||
)
|
||||
title = models.CharField(_("titre"), max_length=200)
|
||||
location = models.ForeignKey(
|
||||
Location,
|
||||
blank=True, null=True,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("lieux"),
|
||||
)
|
||||
start_date = models.DateTimeField(
|
||||
_("début de l'événement"),
|
||||
blank=True, null=True,
|
||||
)
|
||||
end_date = models.DateTimeField(
|
||||
_("fin de l'événement"),
|
||||
blank=True, null=True,
|
||||
)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
image = models.ImageField(
|
||||
_("image"),
|
||||
blank=True, null=True,
|
||||
upload_to="public/imgs/events/",
|
||||
)
|
||||
registration_open = models.NullBooleanField(
|
||||
_("les inscriptions sont ouvertes"),
|
||||
help_text=_("Indéfini signifie « l'inscription n'est pas requise »"),
|
||||
default=True,
|
||||
)
|
||||
old = models.BooleanField(_("archiver (événement fini)"), default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("événement")
|
||||
verbose_name_plural = _("événements")
|
||||
|
||||
def __str__(self):
|
||||
title, location = self.title, self.location
|
||||
start = self.start_date.date()
|
||||
end = self.end_date.date()
|
||||
if not self.start_date.date():
|
||||
return "{} @ {}".format(title, location)
|
||||
elif start == end:
|
||||
return "{} @ {} ({})".format(title, location, start)
|
||||
else:
|
||||
return "{} @ {} ({} → {})".format(title, location, start, end)
|
||||
|
||||
|
||||
class EventCommentField(models.Model):
|
||||
TEXT = "text"
|
||||
CHAR = "char"
|
||||
|
||||
FIELD_TYPE = [
|
||||
(TEXT, _("Texte long")),
|
||||
(CHAR, _("Texte court")),
|
||||
]
|
||||
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="commentfields",
|
||||
verbose_name=_("événement"),
|
||||
)
|
||||
name = models.CharField(_("nom du champ"), max_length=200)
|
||||
fieldtype = models.CharField(
|
||||
_("type de champ"),
|
||||
max_length=10,
|
||||
choices=FIELD_TYPE, default=TEXT,
|
||||
)
|
||||
default = models.TextField(_("valeur par défaut"), blank=True)
|
||||
ordering = models.IntegerField(
|
||||
_("ordre des champs"),
|
||||
default=0,
|
||||
help_text=_(
|
||||
"plus petit en premier, ordre alphabétique sur le nom si "
|
||||
"ambiguïté"
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("champ")
|
||||
verbose_name_plural = _("champs")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EventCommentValue(models.Model):
|
||||
commentfield = models.ForeignKey(
|
||||
EventCommentField,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="values"
|
||||
)
|
||||
registration = models.ForeignKey(
|
||||
"EventRegistration",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="comments"
|
||||
)
|
||||
content = models.TextField(_("contenu"), blank=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("commentfield", "registration")
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
_("Commentaire de %(field_name)s")
|
||||
% {"field_name": self.commentfield}
|
||||
)
|
||||
|
||||
|
||||
class EventOption(models.Model):
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="options",
|
||||
verbose_name=_("événement"),
|
||||
)
|
||||
name = models.CharField(_("option"), max_length=200)
|
||||
multi_choices = models.BooleanField(_("choix multiples"), default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("option des événements")
|
||||
verbose_name_plural = _("options des événements")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EventOptionChoice(models.Model):
|
||||
event_option = models.ForeignKey(
|
||||
EventOption,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="choices",
|
||||
verbose_name=_("événement"),
|
||||
)
|
||||
value = models.CharField("Valeur", max_length=200)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("choix")
|
||||
verbose_name_plural = _("choix")
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class EventRegistration(models.Model):
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
verbose_name=_("utilisateur"),
|
||||
)
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("événement"),
|
||||
)
|
||||
options = models.ManyToManyField(
|
||||
EventOptionChoice,
|
||||
verbose_name=_("choix"),
|
||||
)
|
||||
filledcomments = models.ManyToManyField(
|
||||
EventCommentField,
|
||||
through=EventCommentValue,
|
||||
verbose_name=_("commentaires"),
|
||||
)
|
||||
paid = models.BooleanField(_("a payé"), default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("inscription")
|
||||
verbose_name_plural = _("inscriptions")
|
||||
unique_together = ("user", "event")
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
_("Inscription de %(user)s à %(event)s")
|
||||
% {"user": self.user, "event": self.event}
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
source ~/venv/bin/activate
|
||||
python manage.py migrate
|
||||
python manage.py loaddata gestion sites articles
|
||||
python manage.py loaddata gestion events sites articles
|
||||
python manage.py loaddevdata
|
||||
python manage.py syncmails
|
||||
python manage.py collectstatic --noinput
|
||||
|
|
|
@ -20,3 +20,4 @@ django-widget-tweaks==1.4.1
|
|||
git+https://git.eleves.ens.fr/cof-geek/django_custommail.git#egg=django_custommail
|
||||
ldap3
|
||||
git+https://github.com/Aureplop/channels.git#egg=channels
|
||||
django-nested-admin==3.0.17
|
||||
|
|
Loading…
Reference in a new issue