Ajout d'un calendrier dynamique
Ce patch propose aux adhérents du COF de télécharger un calendrier dynamique (`.ics`). Il est configurable : - On peut s'abonner ou non aux événements du COF. - On peut choisir les spectacles auxquels on veut s'abonner.
This commit is contained in:
parent
a24ca5a19b
commit
8af49a1020
9 changed files with 158 additions and 12 deletions
|
@ -11,7 +11,7 @@ from django.contrib import admin
|
|||
from django.views.generic.base import TemplateView
|
||||
|
||||
from gestioncof.urls import export_patterns, petitcours_patterns, \
|
||||
surveys_patterns, events_patterns
|
||||
surveys_patterns, events_patterns, calendar_patterns
|
||||
|
||||
autocomplete_light.autodiscover()
|
||||
admin.autodiscover()
|
||||
|
@ -30,6 +30,8 @@ urlpatterns = patterns(
|
|||
url(r'^survey/', include(surveys_patterns)),
|
||||
# Evenements
|
||||
url(r'^event/', include(events_patterns)),
|
||||
# Calendrier
|
||||
url(r'^calendar/', include(calendar_patterns)),
|
||||
# Authentification
|
||||
url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'),
|
||||
name="cof-denied"),
|
||||
|
|
|
@ -6,10 +6,13 @@ from django.contrib.auth.models import User
|
|||
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
|
||||
from django.db.models import Max
|
||||
|
||||
from gestioncof.models import CofProfile, EventCommentValue
|
||||
from gestioncof.models import CofProfile, EventCommentValue, \
|
||||
CalendarSubscription
|
||||
from gestioncof.widgets import TriStateCheckbox
|
||||
from gestioncof.shared import lock_table, unlock_table
|
||||
|
||||
from bda.models import Spectacle
|
||||
|
||||
|
||||
class EventForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -330,3 +333,16 @@ class AdminEventForm(forms.Form):
|
|||
for name, value in self.cleaned_data.items():
|
||||
if name.startswith('comment_'):
|
||||
yield (self.fields[name].comment_id, value)
|
||||
|
||||
|
||||
class CalendarForm(forms.ModelForm):
|
||||
events = forms.BooleanField(initial=True, label="S'abonner aux événements")
|
||||
shows = forms.ModelMultipleChoiceField(
|
||||
label="S'abonner aux spectacles suivants",
|
||||
queryset=Spectacle.objects.filter(tirage__active=True).all(),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
initial=Spectacle.objects.filter(tirage__active=True).all())
|
||||
|
||||
class Meta:
|
||||
model = CalendarSubscription
|
||||
exclude = ['user']
|
||||
|
|
40
gestioncof/migrations/0005_add_calendar.py
Normal file
40
gestioncof/migrations/0005_add_calendar.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bda', '0004_mails-rappel'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('gestioncof', '0004_registration_mail'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CalendarSubscription',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('events', models.BooleanField(default=True)),
|
||||
('shows', models.ManyToManyField(to='bda.Spectacle')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(null=True, verbose_name=b'Date de fin',
|
||||
blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(null=True,
|
||||
verbose_name=b'Date de d\xc3\xa9but',
|
||||
blank=True),
|
||||
),
|
||||
]
|
|
@ -7,6 +7,8 @@ from django.db.models.signals import post_save
|
|||
|
||||
from petits_cours_models import *
|
||||
|
||||
from bda.models import Spectacle
|
||||
|
||||
OCCUPATION_CHOICES = (
|
||||
('exterieur', _(u"Extérieur")),
|
||||
('1A', _(u"1A")),
|
||||
|
@ -94,8 +96,7 @@ class CustomMail(models.Model):
|
|||
blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Mail personnalisable"
|
||||
verbose_name_plural = "Mails personnalisables"
|
||||
verbose_name = "Mails personnalisables"
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s: %s" % (self.shortname, self.title)
|
||||
|
@ -104,8 +105,8 @@ class CustomMail(models.Model):
|
|||
class Event(models.Model):
|
||||
title = models.CharField("Titre", max_length=200)
|
||||
location = models.CharField("Lieu", max_length=200)
|
||||
start_date = models.DateField("Date de début", blank=True, null=True)
|
||||
end_date = models.DateField("Date de fin", blank=True, null=True)
|
||||
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/")
|
||||
|
@ -159,7 +160,6 @@ class EventOptionChoice(models.Model):
|
|||
|
||||
class Meta:
|
||||
verbose_name = "Choix"
|
||||
verbose_name_plural = "Choix"
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.value)
|
||||
|
@ -232,3 +232,9 @@ class SurveyAnswer(models.Model):
|
|||
class Clipper(models.Model):
|
||||
username = models.CharField("Identifiant", max_length=20)
|
||||
fullname = models.CharField("Nom complet", max_length=200)
|
||||
|
||||
|
||||
class CalendarSubscription(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
shows = models.ManyToManyField(Spectacle)
|
||||
events = models.BooleanField(default=True)
|
||||
|
|
22
gestioncof/templates/calendar_subscription.html
Normal file
22
gestioncof/templates/calendar_subscription.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
{% if success %}
|
||||
<p class="success">Calendrier mis à jour avec succès</p>
|
||||
{% endif %}
|
||||
|
||||
{% if error %}
|
||||
<p class="error">{{ error }}</p>
|
||||
{% endif %}
|
||||
|
||||
<p>Calendrier disponible à
|
||||
<a href="{% url 'gestioncof.views.calendar_ics' %}">cette adresse</a></p>
|
||||
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Enregistrer" />
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<h3>Divers</h3>
|
||||
<ul>
|
||||
|
||||
<li><a href="{% url "gestioncof.views.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 %}
|
||||
|
||||
<li><a href="{% url "gestioncof.views.profile" %}">Éditer mon profil</a></li>
|
||||
|
|
|
@ -39,3 +39,8 @@ events_patterns = [
|
|||
url(r'^(?P<event_id>\d+)$', 'gestioncof.views.event'),
|
||||
url(r'^(?P<event_id>\d+)/status$', 'gestioncof.views.event_status'),
|
||||
]
|
||||
|
||||
calendar_patterns = [
|
||||
url(r'^subscription$', 'gestioncof.views.calendar'),
|
||||
url(r'^calendar.ics$', 'gestioncof.views.calendar_ics')
|
||||
]
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# coding: utf-8
|
||||
|
||||
import unicodecsv
|
||||
from datetime import timedelta
|
||||
from icalendar import Calendar, Event as Vevent
|
||||
|
||||
from django.shortcuts import redirect, get_object_or_404, render
|
||||
from django.shortcuts import redirect, get_object_or_404, render, \
|
||||
HttpResponse
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.views import login as django_login_view
|
||||
|
@ -12,13 +15,14 @@ from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
|
|||
SurveyQuestionAnswer
|
||||
from gestioncof.models import Event, EventRegistration, EventOption, \
|
||||
EventOptionChoice
|
||||
from gestioncof.models import EventCommentField, EventCommentValue
|
||||
from gestioncof.models import EventCommentField, EventCommentValue, \
|
||||
CalendarSubscription
|
||||
from gestioncof.shared import send_custom_mail
|
||||
from gestioncof.models import CofProfile, Clipper
|
||||
from gestioncof.decorators import buro_required
|
||||
from gestioncof.decorators import buro_required, cof_required
|
||||
from gestioncof.forms import UserProfileForm, EventStatusFilterForm, \
|
||||
SurveyForm, SurveyStatusFilterForm, RegistrationUserForm, \
|
||||
RegistrationProfileForm, AdminEventForm, EventForm
|
||||
RegistrationProfileForm, AdminEventForm, EventForm, CalendarForm
|
||||
|
||||
from bda.models import Tirage
|
||||
|
||||
|
@ -618,3 +622,53 @@ def liste_diffcof(request):
|
|||
personnes = CofProfile.objects.filter(mailing_cof=True, is_cof=True).all()
|
||||
return render(request, "liste_mails.html", {"titre": titre,
|
||||
"personnes": personnes})
|
||||
|
||||
|
||||
@cof_required
|
||||
def calendar(request):
|
||||
if request.method == 'POST':
|
||||
form = CalendarForm(request.POST)
|
||||
if form.is_valid():
|
||||
CalendarSubscription.objects.filter(user=request.user).delete()
|
||||
subscription = form.save(commit=False)
|
||||
subscription.user = request.user
|
||||
subscription.save()
|
||||
form.save_m2m()
|
||||
return render(request, "calendar_subscription.html",
|
||||
{'form': form, 'success': True})
|
||||
else:
|
||||
return render(request, "calendar_subscription.html",
|
||||
{'form': form, 'error': "Formulaire incorect"})
|
||||
else:
|
||||
try:
|
||||
subscription = CalendarSubscription.objects.get(user=request.user)
|
||||
form = CalendarForm(instance=subscription)
|
||||
except:
|
||||
form = CalendarForm()
|
||||
return render(request, "calendar_subscription.html", {'form': form})
|
||||
|
||||
|
||||
@cof_required
|
||||
def calendar_ics(request):
|
||||
subscription, _ = CalendarSubscription.objects.get_or_create(
|
||||
user=request.user)
|
||||
vcal = Calendar()
|
||||
for show in subscription.shows.all():
|
||||
vevent = Vevent()
|
||||
vevent.add('dtstart', show.date)
|
||||
vevent.add('dtend', show.date + timedelta(seconds=7200))
|
||||
vevent.add('summary', show.title)
|
||||
vevent.add('location', show.location.name)
|
||||
vcal.add_component(vevent)
|
||||
if subscription.events:
|
||||
for event in Event.objects.filter(old=False).all():
|
||||
vevent = Vevent()
|
||||
vevent.add('dtstart', event.start_date)
|
||||
vevent.add('dtend', event.end_date)
|
||||
vevent.add('summary', event.title)
|
||||
vevent.add('location', event.location)
|
||||
vevent.add('description', event.description)
|
||||
vcal.add_component(vevent)
|
||||
response = HttpResponse(content=vcal.to_ical())
|
||||
response['Content-Type'] = "text/calendar"
|
||||
return response
|
||||
|
|
|
@ -10,3 +10,4 @@ Pillow==2.9.0
|
|||
simplejson==3.8.2
|
||||
six==1.10.0
|
||||
unicodecsv==0.14.1
|
||||
icalendar==3.10
|
||||
|
|
Loading…
Reference in a new issue