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.db.models import Sum, Count
from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\
Attribution, Tirage
Attribution, Tirage, Quote, CategorieSpectacle
from django import forms
from datetime import timedelta
@ -182,7 +182,12 @@ class ChoixSpectacleAdmin(admin.ModelAdmin):
'spectacle__title')
class QuoteInline(admin.TabularInline):
model = Quote
class SpectacleAdmin(admin.ModelAdmin):
inlines = [QuoteInline]
model = Spectacle
list_display = ("title", "date", "tirage", "location", "slots", "price",
"listing")
@ -194,7 +199,7 @@ class SpectacleAdmin(admin.ModelAdmin):
class TirageAdmin(admin.ModelAdmin):
model = Tirage
list_display = ("title", "ouverture", "fermeture", "active",
"enable_do_tirage")
"enable_do_tirage")
readonly_fields = ("tokens", )
list_filter = ("active", )
search_fields = ("title", )
@ -205,6 +210,7 @@ class SalleAdmin(admin.ModelAdmin):
search_fields = ('name', 'address')
admin.site.register(CategorieSpectacle)
admin.site.register(Spectacle, SpectacleAdmin)
admin.site.register(Salle, SalleAdmin)
admin.site.register(Participant, ParticipantAdmin)

View file

@ -74,7 +74,6 @@
"description": "Jazz / Funk",
"title": "Un super concert",
"price": 10.0,
"priority": 1000,
"rappel_sent": null,
"location": 2,
"date": "2016-09-30T18:00:00Z",
@ -91,7 +90,6 @@
"description": "Homemade",
"title": "Une super pi\u00e8ce",
"price": 10.0,
"priority": 1000,
"rappel_sent": null,
"location": 3,
"date": "2016-09-29T14:00:00Z",
@ -108,7 +106,6 @@
"description": "Plein air, soleil, bonne musique",
"title": "Concert pour la f\u00eate de la musique",
"price": 5.0,
"priority": 1000,
"rappel_sent": null,
"location": 1,
"date": "2016-09-21T15:00:00Z",
@ -125,7 +122,6 @@
"description": "Sous le regard s\u00e9v\u00e8re de Louis Pasteur",
"title": "Op\u00e9ra sans d\u00e9cors",
"price": 5.0,
"priority": 1000,
"rappel_sent": null,
"location": 4,
"date": "2016-10-06T19:00:00Z",
@ -142,7 +138,6 @@
"description": "Buffet \u00e0 la fin",
"title": "Concert Trouv\u00e8re",
"price": 20.0,
"priority": 1000,
"rappel_sent": null,
"location": 5,
"date": "2016-11-30T12:00:00Z",
@ -159,7 +154,6 @@
"description": "Vive les maths",
"title": "Dessin \u00e0 la craie sur tableau noir",
"price": 10.0,
"priority": 1000,
"rappel_sent": null,
"location": 6,
"date": "2016-12-15T07:00:00Z",
@ -176,7 +170,6 @@
"description": "Une pi\u00e8ce \u00e0 un personnage",
"title": "D\u00e9cors, d\u00e9montage en musique",
"price": 0.0,
"priority": 1000,
"rappel_sent": null,
"location": 3,
"date": "2016-12-26T07:00:00Z",
@ -193,7 +186,6 @@
"description": "Annulera, annulera pas\u00a0?",
"title": "La Nuit",
"price": 27.0,
"priority": 1000,
"rappel_sent": null,
"location": 1,
"date": "2016-11-14T23:00:00Z",
@ -210,7 +202,6 @@
"description": "Le boum fait sa carte blanche",
"title": "Turbomix",
"price": 10.0,
"priority": 1000,
"rappel_sent": null,
"location": 2,
"date": "2017-01-10T20:00:00Z",
@ -227,7 +218,6 @@
"description": "Unique repr\u00e9sentation",
"title": "Carinettes et trombone",
"price": 15.0,
"priority": 1000,
"rappel_sent": null,
"location": 5,
"date": "2017-01-02T14:00:00Z",
@ -244,7 +234,6 @@
"description": "Suivi d'une jam session",
"title": "Percussion sur rondins",
"price": 5.0,
"priority": 1000,
"rappel_sent": null,
"location": 4,
"date": "2017-01-13T14:00:00Z",
@ -261,7 +250,6 @@
"description": "\u00c9preuve sportive et artistique",
"title": "Bassin aux ernests, nage libre",
"price": 5.0,
"priority": 1000,
"rappel_sent": null,
"location": 1,
"date": "2016-11-17T09:00:00Z",
@ -278,7 +266,6 @@
"description": "Sonore",
"title": "Chant du barde",
"price": 13.0,
"priority": 1000,
"rappel_sent": null,
"location": 2,
"date": "2017-02-26T07:00:00Z",
@ -295,7 +282,6 @@
"description": "Cocorico",
"title": "Chant du coq",
"price": 4.0,
"priority": 1000,
"rappel_sent": null,
"location": 1,
"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
@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
class Spectacle(models.Model):
title = models.CharField("Titre", max_length=300)
category = models.ForeignKey(CategorieSpectacle, blank=True, null=True)
date = models.DateTimeField("Date & heure")
location = models.ForeignKey(Salle)
vips = models.TextField('Personnalités', blank=True)
description = models.TextField("Description", 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")
slots = models.IntegerField("Places")
priority = models.IntegerField("Priorité", default=1000)
tirage = models.ForeignKey(Tirage)
listing = models.BooleanField("Les places sont sur listing")
rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True,
@ -64,7 +80,7 @@ class Spectacle(models.Model):
class Meta:
verbose_name = "Spectacle"
ordering = ("priority", "date", "title",)
ordering = ("date", "title",)
def __repr__(self):
return "[%s]" % self
@ -111,6 +127,13 @@ class Spectacle(models.Model):
# On renvoie la liste des destinataires
return members.values()
class Quote(models.Model):
spectacle = models.ForeignKey(Spectacle)
text = models.TextField('Citation')
author = models.CharField('Auteur', max_length=200)
PAYMENT_TYPES = (
("cash", "Cash"),
("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

@ -26,7 +26,7 @@
</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
<script type="text/javascript"
src="{% static "js/jquery.min.js" %}"></script>
@ -39,14 +39,15 @@
</script>
<script>
jQuery(document).ready(function($) {
$(".clickable-row").click(function() {
window.document.location = $(this).data("href");
});
$(".clickable-row").click(function() {
window.document.location = $(this).data("href");
});
});
</script>
<h3> Exports </h3>
<ul>
<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>
{% endblock %}

View file

@ -33,4 +33,6 @@ urlpatterns = [
views.unpaid,
name="bda-unpaid"),
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.core import serializers
from django.forms.models import inlineformset_factory
from django.http import HttpResponseBadRequest
import hashlib
from django.core.mail import send_mail
@ -364,3 +365,19 @@ def send_rappel(request, spectacle_id):
else:
ctxt['sent'] = False
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()})