Merge branch 'Kerl/descriptions_spectacles' into 'master'

Description des spectacles

Affiche la liste des descriptions des spectacles d'un tirage.

C'est accessible sans authentification, destiné à apparaître sur le site du BdA à travers un iframe en attendant de faire mieux.

Ce patch étend le modèle `Spectacle` pour y ajouter des informations et ajoute un modèle `Quote`.

Il est possible de filtrer le résultat de la page `/bda/description/<tirage_id>` à l'aide des variables `GET`
`location` (entier)  et `category` (chaîne de caractères)

Un lien vers cette page est disponible dans dans la vue “Liste des spectacles” accessibles aux membres du burô.

Fixes #35 

See merge request !74
This commit is contained in:
Martin Pepin 2016-08-31 00:05:20 +02:00
commit bb2afccdc7
9 changed files with 217 additions and 22 deletions

View file

@ -9,7 +9,7 @@ from django.core.mail import send_mail
from django.contrib import admin from django.contrib import admin
from django.db.models import Sum, Count from django.db.models import Sum, Count
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\ from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\
Attribution, Tirage Attribution, Tirage, Quote, CategorieSpectacle
from django import forms from django import forms
from datetime import timedelta from datetime import timedelta
@ -182,7 +182,12 @@ class ChoixSpectacleAdmin(admin.ModelAdmin):
'spectacle__title') 'spectacle__title')
class QuoteInline(admin.TabularInline):
model = Quote
class SpectacleAdmin(admin.ModelAdmin): class SpectacleAdmin(admin.ModelAdmin):
inlines = [QuoteInline]
model = Spectacle model = Spectacle
list_display = ("title", "date", "tirage", "location", "slots", "price", list_display = ("title", "date", "tirage", "location", "slots", "price",
"listing") "listing")
@ -205,6 +210,7 @@ class SalleAdmin(admin.ModelAdmin):
search_fields = ('name', 'address') search_fields = ('name', 'address')
admin.site.register(CategorieSpectacle)
admin.site.register(Spectacle, SpectacleAdmin) admin.site.register(Spectacle, SpectacleAdmin)
admin.site.register(Salle, SalleAdmin) admin.site.register(Salle, SalleAdmin)
admin.site.register(Participant, ParticipantAdmin) admin.site.register(Participant, ParticipantAdmin)

View file

@ -74,7 +74,6 @@
"description": "Jazz / Funk", "description": "Jazz / Funk",
"title": "Un super concert", "title": "Un super concert",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2016-09-30T18:00:00Z", "date": "2016-09-30T18:00:00Z",
@ -91,7 +90,6 @@
"description": "Homemade", "description": "Homemade",
"title": "Une super pi\u00e8ce", "title": "Une super pi\u00e8ce",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 3, "location": 3,
"date": "2016-09-29T14:00:00Z", "date": "2016-09-29T14:00:00Z",
@ -108,7 +106,6 @@
"description": "Plein air, soleil, bonne musique", "description": "Plein air, soleil, bonne musique",
"title": "Concert pour la f\u00eate de la musique", "title": "Concert pour la f\u00eate de la musique",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-09-21T15:00:00Z", "date": "2016-09-21T15:00:00Z",
@ -125,7 +122,6 @@
"description": "Sous le regard s\u00e9v\u00e8re de Louis Pasteur", "description": "Sous le regard s\u00e9v\u00e8re de Louis Pasteur",
"title": "Op\u00e9ra sans d\u00e9cors", "title": "Op\u00e9ra sans d\u00e9cors",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 4, "location": 4,
"date": "2016-10-06T19:00:00Z", "date": "2016-10-06T19:00:00Z",
@ -142,7 +138,6 @@
"description": "Buffet \u00e0 la fin", "description": "Buffet \u00e0 la fin",
"title": "Concert Trouv\u00e8re", "title": "Concert Trouv\u00e8re",
"price": 20.0, "price": 20.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 5, "location": 5,
"date": "2016-11-30T12:00:00Z", "date": "2016-11-30T12:00:00Z",
@ -159,7 +154,6 @@
"description": "Vive les maths", "description": "Vive les maths",
"title": "Dessin \u00e0 la craie sur tableau noir", "title": "Dessin \u00e0 la craie sur tableau noir",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 6, "location": 6,
"date": "2016-12-15T07:00:00Z", "date": "2016-12-15T07:00:00Z",
@ -176,7 +170,6 @@
"description": "Une pi\u00e8ce \u00e0 un personnage", "description": "Une pi\u00e8ce \u00e0 un personnage",
"title": "D\u00e9cors, d\u00e9montage en musique", "title": "D\u00e9cors, d\u00e9montage en musique",
"price": 0.0, "price": 0.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 3, "location": 3,
"date": "2016-12-26T07:00:00Z", "date": "2016-12-26T07:00:00Z",
@ -193,7 +186,6 @@
"description": "Annulera, annulera pas\u00a0?", "description": "Annulera, annulera pas\u00a0?",
"title": "La Nuit", "title": "La Nuit",
"price": 27.0, "price": 27.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-11-14T23:00:00Z", "date": "2016-11-14T23:00:00Z",
@ -210,7 +202,6 @@
"description": "Le boum fait sa carte blanche", "description": "Le boum fait sa carte blanche",
"title": "Turbomix", "title": "Turbomix",
"price": 10.0, "price": 10.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2017-01-10T20:00:00Z", "date": "2017-01-10T20:00:00Z",
@ -227,7 +218,6 @@
"description": "Unique repr\u00e9sentation", "description": "Unique repr\u00e9sentation",
"title": "Carinettes et trombone", "title": "Carinettes et trombone",
"price": 15.0, "price": 15.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 5, "location": 5,
"date": "2017-01-02T14:00:00Z", "date": "2017-01-02T14:00:00Z",
@ -244,7 +234,6 @@
"description": "Suivi d'une jam session", "description": "Suivi d'une jam session",
"title": "Percussion sur rondins", "title": "Percussion sur rondins",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 4, "location": 4,
"date": "2017-01-13T14:00:00Z", "date": "2017-01-13T14:00:00Z",
@ -261,7 +250,6 @@
"description": "\u00c9preuve sportive et artistique", "description": "\u00c9preuve sportive et artistique",
"title": "Bassin aux ernests, nage libre", "title": "Bassin aux ernests, nage libre",
"price": 5.0, "price": 5.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-11-17T09:00:00Z", "date": "2016-11-17T09:00:00Z",
@ -278,7 +266,6 @@
"description": "Sonore", "description": "Sonore",
"title": "Chant du barde", "title": "Chant du barde",
"price": 13.0, "price": 13.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 2, "location": 2,
"date": "2017-02-26T07:00:00Z", "date": "2017-02-26T07:00:00Z",
@ -295,7 +282,6 @@
"description": "Cocorico", "description": "Cocorico",
"title": "Chant du coq", "title": "Chant du coq",
"price": 4.0, "price": 4.0,
"priority": 1000,
"rappel_sent": null, "rappel_sent": null,
"location": 1, "location": 1,
"date": "2016-12-17T04:00:00Z", "date": "2016-12-17T04:00:00Z",

View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('bda', '0006_add_tirage_switch'),
]
operations = [
migrations.CreateModel(
name='CategorieSpectacle',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False,
auto_created=True, primary_key=True)),
('name', models.CharField(max_length=300, verbose_name='Nom',
unique=True)),
],
options={
'verbose_name': 'Cat\xe9gorie',
},
),
migrations.CreateModel(
name='Quote',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False,
auto_created=True, primary_key=True)),
('text', models.TextField(verbose_name='Citation')),
('author', models.CharField(max_length=200,
verbose_name='Auteur')),
],
),
migrations.AlterModelOptions(
name='spectacle',
options={'ordering': ('date', 'title'),
'verbose_name': 'Spectacle'},
),
migrations.RemoveField(
model_name='spectacle',
name='priority',
),
migrations.AddField(
model_name='spectacle',
name='ext_link',
field=models.CharField(
max_length=500,
verbose_name='Lien vers le site du spectacle',
blank=True),
),
migrations.AddField(
model_name='spectacle',
name='image',
field=models.ImageField(upload_to='imgs/shows/', null=True,
verbose_name='Image', blank=True),
),
migrations.AlterField(
model_name='tirage',
name='enable_do_tirage',
field=models.BooleanField(
default=False,
verbose_name='Le tirage peut \xeatre lanc\xe9'),
),
migrations.AlterField(
model_name='tirage',
name='tokens',
field=models.TextField(verbose_name='Graine(s) du tirage',
blank=True),
),
migrations.AddField(
model_name='spectacle',
name='category',
field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle',
null=True),
),
migrations.AddField(
model_name='spectacle',
name='vips',
field=models.TextField(verbose_name='Personnalit\xe9s',
blank=True),
),
migrations.AddField(
model_name='quote',
name='spectacle',
field=models.ForeignKey(to='bda.Spectacle'),
),
]

View file

@ -47,16 +47,32 @@ class Salle(models.Model):
return self.name return self.name
@python_2_unicode_compatible
class CategorieSpectacle(models.Model):
name = models.CharField('Nom', max_length=100, unique=True)
def __str__(self):
return self.name
class Meta:
verbose_name = "Catégorie"
@python_2_unicode_compatible @python_2_unicode_compatible
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)
date = models.DateTimeField("Date & heure") date = models.DateTimeField("Date & heure")
location = models.ForeignKey(Salle) location = models.ForeignKey(Salle)
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)
image = models.ImageField('Image', blank=True, null=True,
upload_to='imgs/shows/')
ext_link = models.CharField('Lien vers le site du spectacle', blank=True,
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")
priority = models.IntegerField("Priorité", default=1000)
tirage = models.ForeignKey(Tirage) tirage = models.ForeignKey(Tirage)
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,
@ -64,7 +80,7 @@ class Spectacle(models.Model):
class Meta: class Meta:
verbose_name = "Spectacle" verbose_name = "Spectacle"
ordering = ("priority", "date", "title",) ordering = ("date", "title",)
def __repr__(self): def __repr__(self):
return "[%s]" % self return "[%s]" % self
@ -111,6 +127,13 @@ class Spectacle(models.Model):
# On renvoie la liste des destinataires # On renvoie la liste des destinataires
return members.values() return members.values()
class Quote(models.Model):
spectacle = models.ForeignKey(Spectacle)
text = models.TextField('Citation')
author = models.CharField('Auteur', max_length=200)
PAYMENT_TYPES = ( PAYMENT_TYPES = (
("cash", "Cash"), ("cash", "Cash"),
("cb", "CB"), ("cb", "CB"),

Binary file not shown.

View file

@ -0,0 +1,71 @@
{% load staticfiles %}
<!doctype html>
<html>
<head>
<style>
@font-face {
font-family: josefinsans;
src: url({% static "fonts/josefinsans.ttf" %});
}
*::-moz-selection {
background: #B0B0B0;
}
*::selection {
background: #B0B0B0;
}
.descTable{
width: auto;
margin: 0 auto 1em;
border-collapse: collapse;
border-spacing: 0;
font-size: 14px;
line-height: 2;
max-width: 100%;
background-color: transparent;
font-family: 'josefinsans', 'Arial';
font-weight: 700;
color: #5a5a5a;
}
</style>
<meta charset="utf8" />
</head>
<body>
{% for show in shows %}
<table class="descTable">
<thead>
<tr>
<th colspan="2"><p style="text-align:center;font-size:22px;">{{ show.title }}</p></th>
</tr>
</thead>
<tbody>
<tr>
<td><p style="text-align: left;">{{ show.location }}</p></td><td class="column-2"><p style="text-align: right;">{{ show.category }}</p></td>
</tr>
<tr>
<td><p style="text-align: left;">{{ show.date }}</p></td><td class="column-2"><p style="text-align: right;">{{ show.slots }} place{{ show.slots|pluralize}} {% if slots_description != "" %}({{ show.slots_description }}){% endif %}- show.price</p></td>
</tr>
<tr>
<td colspan="2"><p style="text-align: justify;">{{ show.category }}</p></td>
</tr>
<tr>
<td colspan="2">
<p style="text-align: justify;">{{ show.description }}</p>
{% for quote in show.quote_set.all %}
<p style="text-align:center; font-style: italic;">«{{ quote.text }}»{% if show.quote.author %} - {{ quote.author }}{% endif %}</p>
{% endfor %}
</td>
</tr>
{% if show.image %}
<tr>
<td colspan="2"><p style="text-align:center;"><a href="{{ show.ext_link }}"><img style="display: inline;" src="{{ MEDIA_URL }}{{ show.image }}" alt="{{ show.title }}" width="200px"></a></p></td>
</tr>
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>

View file

@ -48,5 +48,6 @@
<h3> Exports </h3> <h3> Exports </h3>
<ul> <ul>
<li><a href="{% url 'bda-unpaid' tirage_id %}">Mailing list impayés</a> <li><a href="{% url 'bda-unpaid' tirage_id %}">Mailing list impayés</a>
<li><a href="{% url 'bda-descriptions' tirage_id %}">Lien vers les descriptions des spectacles, à utiliser dans une page wordpress</a>
</ul> </ul>
{% endblock %} {% endblock %}

View file

@ -33,4 +33,6 @@ urlpatterns = [
views.unpaid, views.unpaid,
name="bda-unpaid"), name="bda-unpaid"),
url(r'^mails-rappel/(?P<spectacle_id>\d+)$', views.send_rappel), url(r'^mails-rappel/(?P<spectacle_id>\d+)$', views.send_rappel),
url(r'^descriptions/(?P<tirage_id>\d+)$', views.descriptions_spectacles,
name='bda-descriptions'),
] ]

View file

@ -10,6 +10,7 @@ from django.db import models
from django.db.models import Count from django.db.models import Count
from django.core import serializers from django.core import serializers
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.http import HttpResponseBadRequest
import hashlib import hashlib
from django.core.mail import send_mail from django.core.mail import send_mail
@ -364,3 +365,19 @@ def send_rappel(request, spectacle_id):
else: else:
ctxt['sent'] = False ctxt['sent'] = False
return render(request, "mails-rappel.html", ctxt) return render(request, "mails-rappel.html", ctxt)
def descriptions_spectacles(request, tirage_id):
tirage = get_object_or_404(Tirage, id=tirage_id)
shows_qs = tirage.spectacle_set
category_name = request.GET.get('category', '')
location_id = request.GET.get('location', '')
if category_name:
shows_qs = shows_qs.filter(category__name=category_name)
if location_id:
try:
shows_qs = shows_qs.filter(location__id=int(location_id))
except ValueError:
return HttpResponseBadRequest(
"La variable GET 'location' doit contenir un entier")
return render(request, 'descriptions.html', {'shows': shows_qs.all()})