removing old proposals app

This commit is contained in:
Élie Bouttier 2017-08-12 14:00:25 +02:00
parent 623b94fccd
commit 06deb53ef9
32 changed files with 0 additions and 2192 deletions

View File

@ -41,16 +41,13 @@ INSTALLED_APPS = [
'ponyconf',
'cfp',
'mailing',
#'proposals',
#'planning',
#'volunteers',
# external apps
'djangobower',
'bootstrap3',
#'registration',
'django_select2',
#'avatar',
'crispy_forms',
# build-in apps

View File

@ -1 +0,0 @@
default_app_config = 'proposals.apps.ProposalsConfig'

View File

@ -1,47 +0,0 @@
from django.contrib import admin
from django.contrib.sites.shortcuts import get_current_site
from proposals.models import Conference, Talk, Topic, Track, Event, Attendee
from planning.models import Room
from ponyconf.admin import SiteAdminMixin
class TalkAdmin(SiteAdminMixin, admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(TalkAdmin, self).get_form(request, obj, **kwargs)
site = get_current_site(request)
form.base_fields['topics'].queryset = Topic.objects.filter(site=site)
form.base_fields['track'].queryset = Track.objects.filter(site=site)
form.base_fields['event'].queryset = Event.objects.filter(site=site)
form.base_fields['room'].queryset = Room.objects.filter(site=site)
return form
class TopicAdmin(SiteAdminMixin, admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
site = get_current_site(request)
form.base_fields['track'].queryset = Track.objects.filter(site=site)
return form
class TrackAdmin(SiteAdminMixin, admin.ModelAdmin):
pass
class EventAdmin(SiteAdminMixin, admin.ModelAdmin):
pass
class AttendeeAdmin(admin.ModelAdmin):
list_display = ('get_name', 'get_email')
admin.site.register(Conference)
admin.site.register(Topic, TopicAdmin)
admin.site.register(Track, TrackAdmin)
admin.site.register(Talk, TalkAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(Attendee, AttendeeAdmin)

View File

@ -1,10 +0,0 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
class ProposalsConfig(AppConfig):
name = 'proposals'
def ready(self):
import proposals.signals # noqa
post_migrate.connect(proposals.signals.call_first_site_post_save, sender=self)

View File

@ -1,9 +0,0 @@
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from .models import Conference
def conference(request):
conference = Conference.objects.get(site=get_current_site(request))
return {'conference': conference}

View File

@ -1,209 +0,0 @@
from django import forms
from django.db.utils import OperationalError
from django.forms.models import modelform_factory
from django.utils.translation import ugettext_lazy as _
from django_select2.forms import Select2TagWidget
from accounts.models import User, Participation, Transport
from proposals.models import Conference, Event, Talk, Topic, Track
from planning.models import Room
STATUS_CHOICES = [
('pending', 'Pending decision'),
('accepted', 'Accepted'),
('declined', 'Declined'),
]
STATUS_VALUES = [
('pending', None),
('accepted', True),
('declined', False),
]
class TalkForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
site = kwargs.pop('site')
staff = kwargs.pop('staff')
super(TalkForm, self).__init__(*args, **kwargs)
self.fields['topics'].queryset = Topic.objects.filter(site=site)
self.fields['track'].queryset = Track.objects.filter(site=site)
self.fields['materials'].required = False
if staff:
self.fields['event'].queryset = Event.objects.filter(site=site)
else:
self.fields['event'].queryset = Conference.objects.get(site=site).opened_events
for field in ['track', 'duration', 'start_date', 'room', 'registration_required', 'attendees_limit']:
self.fields.pop(field)
if self.instance.pk is not None:
self.fields['title'].disabled = True
class Meta:
model = Talk
fields = ['title', 'abstract', 'description', 'topics', 'track', 'notes', 'event', 'speakers', 'materials', 'video', 'duration', 'start_date', 'room', 'registration_required', 'attendees_limit']
widgets = {'topics': forms.CheckboxSelectMultiple(), 'speakers': Select2TagWidget()}
help_texts = {
'abstract': _('Should be less than 255 characters'),
'notes': _('If you want to add some precisions for the organizers.'),
}
class TalkFilterForm(forms.Form):
kind = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[],
)
status = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=STATUS_CHOICES,
)
topic = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[],
)
track = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[],
)
vote = forms.NullBooleanField(help_text=_('Filter talks you already / not yet voted for'))
room = forms.NullBooleanField(help_text=_('Filter talks already / not yet affected to a room'))
scheduled = forms.NullBooleanField(help_text=_('Filter talks already / not yet scheduled'))
materials = forms.NullBooleanField(help_text=_('Filter talks with / without materials'))
video = forms.NullBooleanField(help_text=_('Filter talks with / without video'))
def __init__(self, *args, **kwargs):
site = kwargs.pop('site')
super().__init__(*args, **kwargs)
events = Event.objects.filter(site=site)
self.fields['kind'].choices = events.values_list('pk', 'name')
topics = Topic.objects.filter(site=site)
self.fields['topic'].choices = topics.values_list('slug', 'name')
tracks = Track.objects.filter(site=site)
self.fields['track'].choices = [('none', 'Not assigned')] + list(tracks.values_list('slug', 'name'))
class TalkActionForm(forms.Form):
talks = forms.MultipleChoiceField(choices=[])
decision = forms.NullBooleanField(label=_('Accept talk?'))
track = forms.ChoiceField(required=False, choices=[], label=_('Assign to a track'))
room = forms.ChoiceField(required=False, choices=[], label=_('Put in a room'))
def __init__(self, *args, **kwargs):
site = kwargs.pop('site')
talks = kwargs.pop('talks')
super().__init__(*args, **kwargs)
self.fields['talks'].choices = [(talk.slug, None) for talk in talks.all()]
tracks = Track.objects.filter(site=site)
self.fields['track'].choices = [(None, "---------")] + list(tracks.values_list('slug', 'name'))
rooms = Room.objects.filter(site=site)
self.fields['room'].choices = [(None, "---------")] + list(rooms.values_list('slug', 'name'))
def get_options(option):
try:
options = list(option.objects.values_list('pk', 'name'))
except OperationalError:
# happens when db doesn't exist yet
options = []
return options
class SpeakerFilterForm(forms.Form):
status = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=STATUS_CHOICES,
)
topic = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[],
)
track = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[],
)
transport = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[('unanswered', 'Not answered'), ('unspecified', 'Not specified')] + get_options(Transport),
)
transport_booked = forms.NullBooleanField()
accommodation= forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[('unknown', 'Not specified')] + list(Participation.ACCOMMODATION_CHOICES),
)
accommodation_booked = forms.NullBooleanField()
sound = forms.NullBooleanField()
def __init__(self, *args, **kwargs):
site = kwargs.pop('site')
super().__init__(*args, **kwargs)
topics = Topic.objects.filter(site=site)
self.fields['topic'].choices = topics.values_list('slug', 'name')
tracks = Track.objects.filter(site=site)
self.fields['track'].choices = [('none', 'Not assigned')] + list(tracks.values_list('slug', 'name'))
class TopicForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.site = kwargs.pop('site')
super().__init__(*args, **kwargs)
self.fields['track'].queryset = Track.objects.filter(site=self.site)
class Meta:
model = Topic
fields = ['name', 'description', 'reviewers', 'track']
widgets = {'reviewers': Select2TagWidget()}
def clean_name(self):
name = self.cleaned_data['name']
if self.instance and name != self.instance.name and Topic.objects.filter(site=self.site, name=name).exists():
raise self.instance.unique_error_message(self._meta.model, ['name'])
return name
class TrackForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.site = kwargs.pop('site')
super().__init__(*args, **kwargs)
if 'instance' in kwargs:
reviewers = User.objects.filter(topic__track=kwargs['instance'])
if reviewers.exists():
self.fields['managers'].help_text = 'Suggestion: ' + ', '.join([str(u) for u in reviewers.all()])
class Meta:
model = Track
fields = ['name', 'description', 'managers']
widgets = {'managers': Select2TagWidget()}
def clean_name(self):
name = self.cleaned_data['name']
if self.instance and name != self.instance.name and Track.objects.filter(site=self.site, name=name).exists():
raise self.instance.unique_error_message(self._meta.model, ['name'])
return name
class SubscribeForm(forms.Form):
email = forms.EmailField()
name = forms.CharField(max_length=128, label=_('Name or nickname'))
captcha = forms.IntegerField(label=_('How much is 3+4?'), help_text=_('Anti-bot'))
def clean_captcha(self):
value = self.cleaned_data['captcha']
if value != 7:
raise forms.ValidationError(_("Please re-do the maths."))
return value
ConferenceForm = modelform_factory(Conference,
fields=['subscriptions_open', 'venue', 'city', 'home'],
widgets={
'venue': forms.Textarea(attrs={'rows': 4}),
})

View File

@ -1,163 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2017-01-13 10:49
from __future__ import unicode_literals
import autoslug.fields
import colorful.fields
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import proposals.models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('planning', '0001_initial'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.CreateModel(
name='Attendee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, default='', max_length=64)),
('email', models.EmailField(blank=True, default='', max_length=254)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Conference',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('home', models.TextField(blank=True, default='')),
('venue', models.TextField(blank=True, default='')),
('city', models.CharField(blank=True, default='', max_length=64)),
('subscriptions_open', models.BooleanField(default=False)),
('site', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('duration', models.PositiveIntegerField(default=0, verbose_name='Default duration (min)')),
('color', colorful.fields.RGBColorField(default='#ffffff', verbose_name='Color on program')),
('label', models.CharField(blank=True, default='', max_length=64, verbose_name='Label on program')),
('opening_date', models.DateTimeField(blank=True, default=None, null=True)),
('closing_date', models.DateTimeField(blank=True, default=None, null=True)),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
options={
'ordering': ('pk',),
},
),
migrations.CreateModel(
name='Talk',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('title', models.CharField(help_text='After submission, title can only be changed by the staff.', max_length=128, verbose_name='Title')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
('abstract', models.CharField(blank=True, max_length=255, verbose_name='Abstract')),
('description', models.TextField(blank=True, verbose_name='Description')),
('notes', models.TextField(blank=True, verbose_name='Notes')),
('accepted', models.NullBooleanField(default=None)),
('start_date', models.DateTimeField(blank=True, default=None, null=True)),
('duration', models.PositiveIntegerField(default=0, verbose_name='Duration (min)')),
('plenary', models.BooleanField(default=False)),
('registration_required', models.BooleanField(default=False)),
('attendees_limit', models.PositiveIntegerField(default=0, verbose_name='Max. number of attendees')),
('materials', models.FileField(help_text='You can use this field to share some materials related to your intervention.', null=True, upload_to=proposals.models.talk_materials_destination, verbose_name='Materials')),
('attendees', models.ManyToManyField(to='proposals.Attendee', verbose_name='Attendees')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proposals.Event', verbose_name='Intervention kind')),
('proposer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
('room', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='planning.Room')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
('speakers', models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Speakers')),
],
options={
'ordering': ('event__id',),
},
),
migrations.CreateModel(
name='Topic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
('description', models.TextField(blank=True, verbose_name='Description')),
('reviewers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Track',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name')),
('description', models.TextField(blank=True, verbose_name='Description')),
('managers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Managers')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Vote',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('vote', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(-2), django.core.validators.MaxValueValidator(2)])),
('talk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proposals.Talk')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='topic',
name='track',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='proposals.Track', verbose_name='Destination track'),
),
migrations.AddField(
model_name='talk',
name='topics',
field=models.ManyToManyField(blank=True, help_text='The topics can not be changed after submission.', to='proposals.Topic', verbose_name='Topics'),
),
migrations.AddField(
model_name='talk',
name='track',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='proposals.Track', verbose_name='Track'),
),
migrations.AlterUniqueTogether(
name='vote',
unique_together=set([('talk', 'user')]),
),
migrations.AlterUniqueTogether(
name='track',
unique_together=set([('site', 'name')]),
),
migrations.AlterUniqueTogether(
name='topic',
unique_together=set([('site', 'name')]),
),
migrations.AlterUniqueTogether(
name='event',
unique_together=set([('site', 'name')]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-01-16 21:47
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('proposals', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='talk',
name='video',
field=models.URLField(blank=True, default='', max_length=1000, verbose_name='URL vidéo'),
),
]

View File

@ -1,264 +0,0 @@
from enum import IntEnum
from datetime import timedelta
from os.path import join, basename
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.utils import timezone
from autoslug import AutoSlugField
from colorful.fields import RGBColorField
from accounts.models import Participation
from ponyconf.utils import PonyConfModel, enum_to_choices
from planning.models import Room
from .utils import query_sum
class Conference(models.Model):
site = models.OneToOneField(Site, on_delete=models.CASCADE)
home = models.TextField(blank=True, default="")
venue = models.TextField(blank=True, default="")
city = models.CharField(max_length=64, blank=True, default="")
subscriptions_open = models.BooleanField(default=False) # workshop subscription
def cfp_is_open(self):
events = Event.objects.filter(site=self.site)
return any(map(lambda x: x.is_open(), events))
@property
def opened_events(self):
now = timezone.now()
return Event.objects.filter(site=self.site)\
.filter(Q(opening_date__isnull=True) | Q(opening_date__lte=now))\
.filter(Q(closing_date__isnull=True) | Q(closing_date__gte=now))
def __str__(self):
return str(self.site)
class Track(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Name'))
slug = AutoSlugField(populate_from='name')
description = models.TextField(blank=True, verbose_name=_('Description'))
managers = models.ManyToManyField(User, blank=True, verbose_name=_('Managers'))
class Meta:
unique_together = ('site', 'name')
def estimated_duration(self):
return sum([talk.estimated_duration for talk in self.talk_set.all()])
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('list-talks') + '?track=%s' % self.slug
class Topic(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Name'))
slug = AutoSlugField(populate_from='name', unique=True)
description = models.TextField(blank=True, verbose_name=_('Description'))
track = models.ForeignKey(Track, blank=True, null=True, verbose_name=_('Destination track'))
reviewers = models.ManyToManyField(User, blank=True, verbose_name=_('Reviewers'))
class Meta:
unique_together = ('site', 'name')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('list-talks') + '?topic=%s' % self.slug
class Event(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
duration = models.PositiveIntegerField(default=0, verbose_name=_('Default duration (min)'))
color = RGBColorField(default='#ffffff', verbose_name=_("Color on program"))
label = models.CharField(max_length=64, verbose_name=_("Label on program"), blank=True, default="")
opening_date = models.DateTimeField(null=True, blank=True, default=None)
closing_date = models.DateTimeField(null=True, blank=True, default=None)
def is_open(self):
now = timezone.now()
if self.opening_date and now < self.opening_date:
return False
if self.closing_date and now > self.closing_date:
return False
return True
class Meta:
unique_together = ('site', 'name')
ordering = ('pk',)
def __str__(self):
return ugettext(self.name)
def get_absolute_url(self):
return reverse('list-talks') + '?kind=%d' % self.pk
class Attendee(PonyConfModel):
user = models.ForeignKey(User, null=True)
name = models.CharField(max_length=64, blank=True, default="")
email = models.EmailField(blank=True, default="")
def get_name(self):
if self.user:
return str(self.user.profile)
else:
return self.name
get_name.short_description = _('Name')
def get_email(self):
if self.user:
return self.user.email
else:
return self.email
get_email.short_description = _('Email')
def __str__(self):
return self.get_name()
def talk_materials_destination(talk, filename):
return join(talk.site.name, talk.slug, filename)
class Talk(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
proposer = models.ForeignKey(User, related_name='+')
speakers = models.ManyToManyField(User, verbose_name=_('Speakers'))
title = models.CharField(max_length=128, verbose_name=_('Title'), help_text=_('After submission, title can only be changed by the staff.'))
slug = AutoSlugField(populate_from='title', unique=True)
abstract = models.CharField(max_length=255, blank=True, verbose_name=_('Abstract'))
description = models.TextField(blank=True, verbose_name=_('Description'))
topics = models.ManyToManyField(Topic, blank=True, verbose_name=_('Topics'), help_text=_('The topics can not be changed after submission.'))
track = models.ForeignKey(Track, blank=True, null=True, verbose_name=_('Track'))
notes = models.TextField(blank=True, verbose_name=_('Notes'))
event = models.ForeignKey(Event, verbose_name=_('Intervention kind'))
accepted = models.NullBooleanField(default=None)
start_date = models.DateTimeField(null=True, blank=True, default=None)
duration = models.PositiveIntegerField(default=0, verbose_name=_('Duration (min)'))
room = models.ForeignKey(Room, blank=True, null=True, default=None)
plenary = models.BooleanField(default=False)
registration_required = models.BooleanField(default=False)
attendees = models.ManyToManyField(Attendee, verbose_name=_('Attendees'))
attendees_limit = models.PositiveIntegerField(default=0, verbose_name=_('Max. number of attendees'))
materials = models.FileField(null=True, upload_to=talk_materials_destination, verbose_name=_('Materials'),
help_text=_('You can use this field to share some materials related to your intervention.'))
video = models.URLField(max_length=1000, blank=True, default='', verbose_name='URL vidéo')
class Meta:
ordering = ('title',)
def __str__(self):
return self.title
def get_speakers_str(self):
speakers = [str(Participation.objects.get(site=self.site, user=speaker)) for speaker in self.speakers.all()]
if len(speakers) == 0:
return 'superman'
elif len(speakers) == 1:
return speakers[0]
else:
return ', '.join(speakers[:-1]) + ' & ' + str(speakers[-1])
@property
def estimated_duration(self):
return self.duration or self.event.duration
def get_absolute_url(self):
return reverse('show-talk', kwargs={'slug': self.slug})
def is_moderable_by(self, user):
if user.is_superuser:
return True
try:
participation = Participation.objects.get(site=self.site, user=user)
except Participation.DoesNotExists:
return False
if participation.orga:
return True
if self.topics.filter(reviewers=user).exists():
return True
if self.track and user in self.track.managers.all():
return True
return False
def is_editable_by(self, user):
return user == self.proposer or user in self.speakers.all() or self.is_moderable_by(user)
def score(self):
if self.vote_set.exists():
return query_sum(self.vote_set, 'vote') / len(self.vote_set.all())
else:
return 0
@property
def end_date(self):
if self.estimated_duration:
return self.start_date + timedelta(minutes=self.estimated_duration)
else:
return None
@property
def remaining_attendees(self):
if self.registration_required and self.attendees_limit:
return self.attendees_limit - self.attendees.count()
else:
return None # = infinity \o/
@property
def dtstart(self):
return self.start_date.strftime('%Y%m%dT%H%M%SZ')
@property
def dtend(self):
return self.end_date.strftime('%Y%m%dT%H%M%SZ')
@property
def materials_name(self):
return basename(self.materials.name)
class Meta:
ordering = ('event__id',)
class Vote(PonyConfModel):
talk = models.ForeignKey(Talk)
user = models.ForeignKey(User)
vote = models.IntegerField(validators=[MinValueValidator(-2), MaxValueValidator(2)], default=0)
class Meta:
unique_together = ('talk', 'user')
def __str__(self):
return "%+i by %s for %s" % (self.vote, self.user, self.talk)
def get_absolute_url(self):
return self.talk.get_absolute_url()

View File

@ -1,55 +0,0 @@
from django.db.models.signals import m2m_changed, post_save
from django.dispatch import Signal, receiver
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_noop
from django.conf import settings
from ponyconf.decorators import disable_for_loaddata
from accounts.models import Participation
from .models import Conference, Talk, Topic, Event
talk_added = Signal(providing_args=["sender", "instance", "author"])
talk_edited = Signal(providing_args=["sender", "instance", "author"])
@receiver(post_save, sender=Site, dispatch_uid="Create Conference for Site")
@disable_for_loaddata
def create_conference(sender, instance, **kwargs):
Conference.objects.get_or_create(site=instance)
@receiver(post_save, sender=Site, dispatch_uid="Create default events type for Site")
@disable_for_loaddata
def create_events(sender, instance, **kwargs):
if not Event.objects.filter(site=instance).exists():
Event.objects.bulk_create([
Event(site=instance, name=ugettext_noop('conference (short)')),
Event(site=instance, name=ugettext_noop('conference (long)')),
Event(site=instance, name=ugettext_noop('workshop')),
Event(site=instance, name=ugettext_noop('stand')),
Event(site=instance, name=ugettext_noop('other')),
])
def call_first_site_post_save(apps, **kwargs):
site = Site.objects.filter(id=getattr(settings, 'SITE_ID', 1))
if site.exists():
site.first().save()
@receiver(m2m_changed, sender=Talk.speakers.through, dispatch_uid="Create Participation for speakers")
def create_participation_for_speakers(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if action != "pre_add":
pass
for speaker in instance.speakers.all():
Participation.objects.get_or_create(user=speaker, site=instance.site)
@receiver(m2m_changed, sender=Topic.reviewers.through, dispatch_uid="Create Participation for reviewers")
def create_participation_for_reviewers(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if action != "pre_add":
pass
for reviewer in instance.reviewers.all():
Participation.objects.get_or_create(user=reviewer, site=instance.site)

View File

@ -1,25 +0,0 @@
var csrftoken = $.cookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$('a[href="#preview"]').on("show.bs.tab", function (e) {
$('#preview-content').html('Loading preview...');
var data = $('#markdown-content').val();
$.post(markdown_preview_url, {'data': data})
.done(function(data, textStatus) {
$('#preview-content').html(data);
})
.fail(function () {
$('#preview-content').html('Sorry, an error occured.');
});
})

View File

@ -1,24 +0,0 @@
{% load i18n %}
{% regroup talk_list by event as event_list %}
{% for event in event_list %}
<h3>{{ event.list.0.event }}</h3>
<ul>{% for talk in event.list %}
<li>
{{ talk.get_link }}
<i>{% trans "by" %}</i>
{% for speaker in talk.speakers.all %}
<a href="{% url 'show-participant' speaker.username %}">{{ speaker }}</a>
{% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %}
{% endfor %}
{% if talk.topics.exists %}
<i>{% trans "in" %}</i>
{% for topic in talk.topics.all %}
<a href="{{ topic.get_absolute_url }}">{{ topic }}</a>
{% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %}
{% endfor %}
{% endif %}
</li>
{% endfor %}
</ul>
{% empty %}{% trans "No talks" %}
{% endfor %}

View File

@ -1,53 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 staticfiles i18n %}
{% block conferencetab %} class=active{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3>{% trans "Home page" %}</h3>
</div>
<div class="panel-body">
<form method="post" role="form">
{% csrf_token %}
{% bootstrap_form form exclude='home' %}
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#editor" role="tab" data-toggle="tab">Editor</a></li>
<li><a href="#preview" role="tab" data-toggle="tab">Preview</a></li>
<li><a href="https://daringfireball.net/projects/markdown/syntax" target="blank" role="tab">Syntaxe</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="editor" style="max-height: 220px; margin-bottom: 20px;">
<div class="form-group">
<textarea style="width: 100%; height: 220px;" id="markdown-content" name="home">{{ conference.home }}</textarea>
</div>
</div>
<div class="tab-pane well" id="preview" style="min-height: 220px; magin-bottom: 20px;">
<div id="preview-content"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-pencil"></span> Submit</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block css %}
{{ block.super }}
{{ form.media.css }}
{% endblock css %}
{% block js_end %}
{{ block.super }}
{{ form.media.js }}
<script type="text/javascript">
var markdown_preview_url = "{% url 'markdown' %}";
</script>
<script src="{% static 'js/markdown-preview.js' %}"></script>
{% endblock js_end %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load proposals_tags i18n %}
{% block hometab %} class="active"{% endblock %}
{% block content %}
{% markdown site.conference.home %}
{% endblock %}

View File

@ -1,35 +0,0 @@
{% extends 'base.html' %}
{% load accounts_tags i18n %}
{% block exhibitortab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Participate" %}</h1>
{% if my_talks %}
<h2>{% trans "My talks:" %}</h2>
{% include "proposals/_talk_list.html" with talk_list=my_talks %}
{% endif %}
{% if proposed_talks %}
<h2>{% trans "Proposed talks for others speakers:" %}</h2>
{% include "proposals/_talk_list.html" with talk_list=proposed_talks %}
{% endif %}
<br />
{% if conference.cfp_is_open %}
{% trans "The Call for Participation is currently open for following categories:" %}
{% for event in conference.opened_events.all %}
{% if forloop.first %}<ul>{% endif %}
<li>{{ event }}{% if event.closing_date %} ({% blocktrans with closing_date=event.closing_date|date:"DATETIME_FORMAT" %}until {{ closing_date }}{% endblocktrans %}){% endif %}</li>
{% if forloop.last %}</ul>{% endif %}
{% endfor %}
<br />
<a class="btn btn-{% if conference.cfp_is_open %}success{% else %}danger{% endif %}" href="{% url 'add-talk' %}">{% trans "Propose a talk" %}</a>
{% else %}
{% trans "Sorry, the Call for Participation is closed." %}
{% endif %}
{% endblock %}

View File

@ -1,117 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 i18n %}
{% block speakerstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Speakers" %}</h1>
<a class="btn btn-primary" role="button" data-toggle="collapse" href="#filter" aria-expanded="{{ show_filters|yesno:"true,false" }}" aria-controles="filter">{% trans "Show filtering options…" %}</a>
<br /><br />
<div class="collapse{{ show_filters|yesno:" in," }}" id="filter">
<div class="well">
<form class="form-horizontal" method="get">
<div class="row">
<div class="col-md-6">
{% bootstrap_field filter_form.transport layout="horizontal" %}
{% bootstrap_field filter_form.transport_booked layout="horizontal" %}
{% bootstrap_field filter_form.sound layout="horizontal" %}
</div>
<div class="col-md-6">
{% bootstrap_field filter_form.accommodation layout="horizontal" %}
{% bootstrap_field filter_form.accommodation_booked layout="horizontal" %}
{% bootstrap_field filter_form.status layout="horizontal" %}
</div>
</div>
<div class="row">
<div class="col-md-6">
{% bootstrap_field filter_form.topic layout="horizontal" %}
</div>
<div class="col-md-6">
{% bootstrap_field filter_form.track layout="horizontal" %}
</div>
</div>
<input type="submit" class="btn btn-success" value="{% trans "Filter" %}">
</form>
</div>
</div>
<table class="table table-bordered table-hover">
<caption>{% trans "Total:" %} {{ speaker_list|length }} {% trans "speaker" %}{{ speaker_list|length|pluralize }}
</caption>
<thead>
<tr>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Fullname" %}</th>
<th class="text-center">{% trans "Talk count" %}</th>
<th class="text-center">{% blocktrans context "table column title" %}Need transport?{% endblocktrans %}</th>
<th class="text-center">{% blocktrans context "table column title" %}Need accommodation?{% endblocktrans %}</th>
<th class="text-center">{% trans "Need sound?" %}</th>
<th class="text-center"></th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="7">{% trans "Contact:" %} <a href="{{ contact_link }}">{% trans "link" %}</a></td>
</tr>
</tfoot>
{% for speaker in speaker_list %}
{% if forloop.first %}
<tbody>
{% endif %}
<tr>
<td><a href="{% url 'show-participant' username=speaker.user.username %}">{{ speaker.user.username }}</a></td>
<td>{{ speaker.user.get_full_name }}</td>
<td class="text-right">{{ speaker.not_refused_talk_set.count }}{% if speaker.pending_talk_set.count %} ({{ speaker.pending_talk_set.count }} pending){% endif %}</td>
{% if speaker.need_transport %}
<td class="{% if speaker.transport_booked %}success{% else %}warning{% endif %}">
{% for transport in speaker.transport.all %}
{% if not forloop.first %}, {% endif %}
{{ transport }}
{% empty %}
Yes
{% endfor %}
</td>
{% elif speaker.need_transport is None %}
<td>?</td>
{% else %}
<td>No</td>
{% endif %}
<td{% if speaker.accommodation is not None and speaker.accommodation != speaker.ACCOMMODATION_NO %} class="{% if speaker.accommodation_booked %}success{% else %}warning{% endif %}"{% endif %}>
{% if speaker.accommodation is None %}
?
{% else %}
{{ speaker.get_accommodation_display }}
{% endif %}
</td>
{% if speaker.sound %}
<td class="warning">Yes</td>
{% else %}
<td>No</td>
{% endif %}
<td>
<a class="btn btn-{% if speaker.conversation.messages.last.author == speaker.user %}primary{% else %}default{% endif %}" href="{% url 'user-conversation' speaker.user.username %}">{% trans "Contact" %}</a>
</td>
</tr>
{% if forloop.last %}
</tbody>
{% endif %}
{% endfor %}
</table>
{% endblock %}
{% block js_end %}
<script type="text/javascript">
jQuery(document).ready(function($) {
var anchor = window.location.hash.replace("#", "");
if (anchor == "filter") {
$("#filter").collapse('show');
}
});
</script>
{% endblock %}

View File

@ -1,26 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block listingtab %} active{% endblock %}
{% block content %}
<h1>{% if accept %}{% trans "Are you sure to accept this proposals?" %}{% else %}{% trans "Are you sure to decline this proposals?" %}{% endif %}</h1>
<h3>{% trans "Information about the proposals" %}</h3>
<b>{% trans "Title:" %}</b> {{ talk.title }}<br />
<b>{% trans "Kind:" %}</b> {{ talk.event }}<br />
<h3>{% trans "Information for the proposer" %}</h3>
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="message">{% trans "If you want to send a message to the proposer, please enter it below. Remember to indicate which talk your message is reffering." %}</label>
<textarea name="message" class="form-control" rows="5"></textarea>
</div>
<button type="submit" class="btn btn-{% if accept %}success{% else %}danger{% endif %}">{% if accept %}{% trans "Accept the proposal" %}{% else %}{% trans "Decline the proposal" %}{% endif %}</button>
<a class="btn btn-default" href="{% url 'show-talk' talk.slug %}">{% trans "Cancel" %}</a>
</form>
{% endblock %}

View File

@ -1,153 +0,0 @@
{% extends base_template %}
{% if staff %}
{% block talkstab %} class="active"{% endblock %}
{% else %}
{% block exhibitortab %} class="active"{% endblock %}
{% endif %}
{% load i18n %}
{% block content %}
<h1>{{ talk.title }}</h1>
{% if edit_perm %}
<a class="btn btn-success" href="{% url 'edit-talk' talk.slug %}">{% trans "Edit" %}</a><br />
{% endif %}
<p>{% if talk.abstract %}{{ talk.abstract }}{% else %}<i>{% trans "No abstract provided." %}</i>{% endif %}</p>
{% if moderate_perm %}
<dl class="dl-horizontal">
<dt>{% trans "Format" %}</dt>
<dd><a href="{{ talk.event.get_absolute_url }}">{{ talk.event }}</a></dd>
<dt>{% trans "Topics" %}</dt>
<dd>{% for topic in talk.topics.all %}
<a href="{{ topic.get_absolute_url }}">{{ topic }}</a>{% if not forloop.last %}, {% endif %}
{% empty %}
<i>{% trans "No topics." %}</i>
{% endfor %}</dd>
<dt>{% trans "Track" %}</dt>
<dd>{% if talk.track %}
<a href="{{ talk.track.get_absolute_url }}">{{ talk.track }}</a>
{% else %}
<em>{% trans "No assigned yet." %}</em>
{% endif %}</dd>
<dt>Horaire</dt>
<dd>{% if talk.start_date %}
<span class="date">{{ talk.start_date|date:"l d b" }}</span>,
<span class="time">{{ talk.start_date|date:"H:i" }} &ndash; {% if talk.end_date %}{{ talk.end_date|date:"H:i" }}{% else %}?{% endif %}</span>
{% else %}<em>{% trans "not defined" %}</em>
{% endif %}
</dd>
<dt>Salle</dt>
<dd>{% if talk.room %}
<a href="{{ talk.room.get_absolute_url }}">
<span class="label label-info">{{ talk.room }}</span>
</a>
{% else %}<em>{% trans "not defined" %}</em>
{% endif %}
</dd>
{% if talk.registration_required %}
<dt>{% trans "Registrations" %}</dt>
<dd>{% if talk.attendees_limit %}{{ talk.attendees.count }} / {{ talk.attendees_limit }}{% else %}{% trans "required but unlimited" %}{% endif %}</dd>
{% endif %}
{% if talk.materials %}
<dt>{% trans "Materials" %}</dt>
<dd><a href="{{ talk.materials.url }}">{{ talk.materials_name }}</a></dd>
{% endif %}
{% if talk.video %}
<dt>{% trans "Video" %}</dt>
<dd><a href="{{ talk.video }}">{% trans "download" %}</a></dd>
{% endif %}
</dl>
{% endif %}
<h3>{% trans "Description" %}</h3>
<p>{% if talk.description %}{{ talk.description|linebreaksbr }}{% else %}<i>{% trans "No description provided." %}</i>{% endif %}</p>
<h3>{% trans "Speakers" %}</h3>
{% for speaker in talk.speakers.all %}
{% if forloop.first %}<ul>{% endif %}
<li><a href="{% url 'show-participant' speaker.username %}">{{ speaker.profile }}</a></li>
{% if forloop.last %}</ul>{% endif %}
{% empty %}
<i>{% trans "No speakers." %}</i>
{% endfor %}
{% if moderate_perm %}
{% if not talk.track %}
<h3>{% trans "Track" %}</h3>
<p><em>{% trans "No assigned yet." %}</em></p>
{% for topic in talk.topics.distinct %}
{% if forloop.first %}<p>{% endif %}
{% if topic.track %}
<a class="btn btn-primary" href="{% url 'assign-talk-to-track' talk.slug topic.track.slug %}">{% trans "Assign to" %} {{ topic.track }}</a>
{% endif %}
{% if forloop.last %}</p>{% endif %}
{% endfor %}
{% endif %}
{% endif %}
<h3>{% trans "Notes" %}</h3>
<p>{% if talk.notes %}{{ talk.notes|linebreaksbr }}{% else %}<i>{% trans "No notes." %}</i>{% endif %}</p>
{% if moderate_perm %}
<h2>{% trans "Moderation" %}</h2>
<h3>{% trans "Status" %}</h3>
<span class="label label-{{ talk.accepted|yesno:"success,danger,warning" }}">{{ talk.accepted|yesno:"Accepted,Declined,Pending decision" }}</span><br />
{% if talk.accepted == None %}
<h3>{% trans "Vote" %}</h3>
<div class="btn-group" role="group" aria-label="vote">
<a class="btn {% if vote.vote == -2 %} active {% endif %}btn-danger" href="{% url 'vote' talk=talk.slug score='-2' %}">-2</a>
<a class="btn {% if vote.vote == -1 %} active {% endif %}btn-warning" href="{% url 'vote' talk=talk.slug score='-1' %}">-1</a>
<a class="btn {% if vote.vote == 0 %} active {% endif %}btn-default" href="{% url 'vote' talk=talk.slug score='0' %}"> 0</a>
<a class="btn {% if vote.vote == 1 %} active {% endif %}btn-info" href="{% url 'vote' talk=talk.slug score='1' %}">+1</a>
<a class="btn {% if vote.vote == 2 %} active {% endif %}btn-success" href="{% url 'vote' talk=talk.slug score='2' %}">+2</a>
</div>
<br /><br />
<p>{{ talk.vote_set.all|length }} {% trans "vote" %}{{ talk.vote_set.all|length|pluralize }}, {% trans "average:" %} {{ talk.score|floatformat:1 }}</p>
<a href="{% url 'accept-talk' talk.slug %}" class="btn btn-success">Accept</a>
<a href="{% url 'decline-talk' talk.slug %}" class="btn btn-danger">Decline</a>
{% endif %}
{% if talk.registration_required %}
<h3>{% trans "Attendees" %}</h3>
{% for attendee in talk.attendees.all %}
{% if forloop.first %}<ol>{% endif %}
<li><a href="mailto:{{ attendee.get_email }}">{{ attendee.get_name }}</a></li>
{% if forloop.last %}</ol>{% endif %}
{% empty %}
<em>{% trans "No attendees yet." %}</em>
{% endfor %}
{% endif %}
<h3>{% trans "Messages" %}</h3>
{% trans "These messages are for organization team only." %}<br /><br />
{% for message in talk.conversation.messages.all %}
{% include 'conversations/_message_detail.html' %}
{% endfor %}
{% include 'conversations/_message_form.html' %}
{% endif %}
{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends base_template %}
{% load i18n %}
{% block talkstab %}{% if base_template == 'staff.html' %} class="active"{% endif %}{% endblock %}
{% block exhibitortab %}{% if base_template == 'base.html' %} class="active"{% endif %}{% endblock %}
{% block content %}
<h1>{% if talk %}{% trans "Edit a talk" %}{% else %}{% trans "Propose a talk" %}{% endif %}</h1>
{% include "_form.html" with multipart=True %}
{% endblock %}
{% block css %}
{{ block.super }}
{{ form.media.css }}
{% endblock css %}
{% block js_end %}
{{ block.super }}
{{ form.media.js }}
<script type="text/javascript">
var update_attendees_limit = function() {
if ($("#id_registration_required").is(":checked")) {
$(".form-group:has(#id_attendees_limit)").show();
} else {
$(".form-group:has(#id_attendees_limit)").hide();
}
}
$(function() {
update_attendees_limit();
$("#id_registration_required").change(update_attendees_limit);
})
</script>
{% endblock js_end %}

View File

@ -1,122 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 i18n accounts_tags %}
{% block talkstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Talks" %}</h1>
<a class="btn btn-primary" role="button" data-toggle="collapse" href="#filter" aria-expanded="{{ show_filters|yesno:"true,false" }}" aria-controls="filter">{% trans "Show filtering options…" %}</a>
<br /><br />
<div class="collapse{{ show_filters|yesno:" in," }}" id="filter">
<div class="well">
<form class="form-horizontal" method="get">
<div class="row">
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.status layout="horizontal" %}
{% bootstrap_field filter_form.kind layout="horizontal" %}
{% bootstrap_field filter_form.vote layout="horizontal" %}
{% bootstrap_field filter_form.room layout="horizontal" %}
{% bootstrap_field filter_form.scheduled layout="horizontal" %}
{% bootstrap_field filter_form.materials layout="horizontal" %}
{% bootstrap_field filter_form.video layout="horizontal" %}
</div>
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.topic layout="horizontal" %}
</div>
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.track layout="horizontal" %}
</div>
</div>
<input type="submit" class="btn btn-success" value="{% trans "Filter" %}">
</form>
</div>
</div>
<form action="" method="post">
<table class="table table-bordered table-hover">
<caption>{% trans "Total:" %} {{ talk_list|length }} {% trans "talk" %}{{ talk_list|length|pluralize }}</caption>
<thead>
<tr>
<th></th>
<th class="text-center">{% trans "Title" %} <a href="?{{ sort_urls.title }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.title }} pull-right"></span></a></th>
<th class="text-center">{% trans "Intervention kind" %} <a href="?{{ sort_urls.kind }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.kind }} pull-right"></span></a></th>
<th class="text-center">{% trans "Speakers" %}</th>
<th class="text-center">{% trans "Topics" %}</th>
<th class="text-center">{% trans "Track" %}</th>
<th class="text-center">{% trans "Status" %} <a href="?{{ sort_urls.status }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.status }} pull-right"></span></a></th>
</tr>
</thead>
{% for talk in talk_list %}
{% if forloop.first %}
<tbody>
{% endif %}
<tr class="{{ talk.accepted|yesno:"success,danger,warning" }}">
<td><input type="checkbox" name="talks" value="{{ talk.slug }}"></td>
<td><a href="{% url 'show-talk' talk.slug %}">{{ talk.title }}</a></td>
<td>{{ talk.event }}</td>
<td>
{% for speaker in talk.speakers.all %}
<a href="{% url 'show-participant' speaker.username %}">{{ speaker.profile }}</a>
{% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %}
{% empty %}
{% endfor %}
</td>
<td>
{% for topic in talk.topics.all %}
<span class="badge">{{ topic }}</span>
{% endfor %}
</td>
<td>{{ talk.track|default:"" }}</td>
<td>
{% if talk.accepted == True %}
{% trans "Accepted" %}
{% elif talk.accepted == False %}
{% trans "Declined" %}
{% else %}
{% blocktrans with score=talk.score|floatformat:1 %}Pending, score: {{ score }}{% endblocktrans %}
{% endif %}
</td>
</tr>
{% if forloop.last%}
</tbody>
{% endif %}
{% endfor %}
</table>
{% if action_form %}
<div id="filter">
<div class="well">
<h4>{% trans "For selected talks:" %}</h4>
{% csrf_token %}
{% bootstrap_field action_form.decision %}
{% if request|orga %}
{% bootstrap_field action_form.track %}
{% bootstrap_field action_form.room %}
{% endif %}
{% buttons %}
<button type="submit" class="btn btn-primary">{% trans "Apply" %}</button>
{% endbuttons %}
</div>
</div>
{% endif %}
</form>
{% endblock %}
{% block js_end %}
<script type="text/javascript">
jQuery(document).ready(function($) {
var anchor = window.location.hash.replace("#", "");
if (anchor == "filter") {
$("#filter").collapse('show');
}
});
</script>
{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends 'base.html' %}
{% load bootstrap3 i18n %}
{% block wsregtab %} class="active"{% endblock %}
{% block css %}
{{ block.super }}
{{ form.media.css }}
{% endblock %}
{% block content %}
<h1>{% trans "Register for a workshop" %}<br /><small>{{ talk.title }}</small></h1>
<hr>
{% include "_form.html" %}
<p class="text-center">
<a href="{% url "login" %}?next={% url "register-for-a-talk" talk.slug %}">{% trans "Login to register with your account" %}</a>
</p>
{% endblock %}
{% block js_end %}
{{ block.super }}
{{ form.media.js }}
{% endblock %}

View File

@ -1,36 +0,0 @@
{% extends 'base.html' %}
{% load accounts_tags i18n %}
{% block wsregtab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Register to a workshop" %}</h1>
{% for talk in talks %}
{% if forloop.first %}<div class="list-group">{% endif %}
<div class="list-group-item{% if attendee in talk.attendees.all %} list-group-item-info{% endif %}">
<h4 clas="list-group-item-heading">{{ talk.title }}</h4>
<p class="list-group-item-text">
<p>{{ talk.description }}</p>
{% if talk.attendees_limit %}
<p><em>{% blocktrans count remaining=talk.remaining_attendees %}{{ remaining }} remaining seat{% plural %}{{ remaining }} remaining seats{% endblocktrans %}</em></p>
{% endif %}
{% if talk.remaining_attendees != 0 or attendee in talk.attendees.all %}
<p>
{% if attendee in talk.attendees.all %}
<a class="btn btn-danger" href="{% url 'register-for-a-talk' talk=talk.slug %}">{% trans "Unregister" %}</a>
{% else %}
<a class="btn btn-primary" href="{% url 'register-for-a-talk' talk=talk.slug %}">{% trans "Register" %}</a>
{% endif %}
</p>
{% endif %}
</p>
</div>
{% if forloop.last %}</div>{% endif %}
{% empty %}
<em>{% trans "There are no workshops requiring registration for now … come back later!" %}</em>
{% endfor %}
{% endblock %}

View File

@ -1,23 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 i18n %}
{% block topicstab %} class="active"{% endblock %}
{% block css %}
{{ block.super }}
{{ form.media.css }}
{% endblock %}
{% block content %}
<h1>{% trans "Topic" %}</h1>
{% include "_form.html" %}
{% endblock %}
{% block js_end %}
{{ block.super }}
{{ form.media.js }}
{% endblock %}

View File

@ -1,35 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 accounts_tags i18n %}
{% block topicstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Topics" %}</h1>
{% if request|orga %}
<p><a href="{% url 'add-topic' %}" class="btn btn-success">{% trans "Add a topic" %}</a><p>
{% endif %}
<div class="row">
{% for topic in topic_list %}
<div class="col-xs-6 col-sm-4">
<h2>{{ topic }}</h2>
<p>{{ topic.description }}</p>
{% if request|staff %}
{{ topic.reviewers.count }} {% trans "reviewer" %}{{ topic.reviewers.count|pluralize }}
|
<a href="{{ topic.get_absolute_url }}">{{ topic.talk_set.count }} {% trans "talk" %}{{ topic.talk_set.count|pluralize }}</a>
|
<a href="{% url 'edit-topic' topic.slug %}">{% bootstrap_icon "pencil" %}</a>
{% endif %}
</div>
{% cycle '' '<div class="clearfix visible-xs"></div>' %}
{% cycle '' '' '<div class="clearfix hidden-xs"></div>' %}
{% empty %}
<div class="col-xs-12"><em>{% trans "No topics." %}</em></div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,23 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 i18n %}
{% block trackstab %} class="active"{% endblock %}
{% block css %}
{{ block.super }}
{{ form.media.css }}
{% endblock %}
{% block content %}
<h1>{% trans "Track" %}</h1>
{% include "_form.html" %}
{% endblock %}
{% block js_end %}
{{ block.super }}
{{ form.media.js }}
{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends 'staff.html' %}
{% load bootstrap3 accounts_tags proposals_tags i18n %}
{% block trackstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Tracks" %}</h1>
{% if request|orga %}
<p><a href="{% url 'add-track' %}" class="btn btn-success">{% trans "Add a track" %}</a><p>
{% endif %}
<div class="row">
{% for track in track_list %}
<div class="col-xs-6 col-sm-4">
<h2>{{ track }}</h2>
<p>{{ track.description }}</p>
{% if request|staff %}
{{ track.managers.count }} {% trans "manager" %}{{ track.managers.count|pluralize }}
|
<a href="{{ track.get_absolute_url }}">{{ track.talk_set.count }} {% trans "talk" %}{{ track.talk_set.count|pluralize }}</a>
|
{{ track.estimated_duration|duration_format }}
|
<a href="{% url 'edit-track' track.slug %}">{% bootstrap_icon "pencil" %}</a>
{% endif %}
</div>
{% cycle '' '<div class="clearfix visible-xs"></div>' %}
{% cycle '' '' '<div class="clearfix hidden-xs"></div>' %}
{% empty %}
<div class="col-xs-12"><em>{% trans "No tracks." %}</em></div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,18 +0,0 @@
from django import template
from proposals.utils import markdown_to_html
register = template.Library()
@register.simple_tag
def markdown(value):
return markdown_to_html(value)
@register.filter('duration_format')
def duration_format(value):
value = int(value)
hours = int(value/60)
minutes = value%60
return '%d h %02d' % (hours, minutes)

View File

@ -1,95 +0,0 @@
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.test import TestCase
from accounts.models import Participation
from .models import Talk, Topic, Vote
class ProposalsTests(TestCase):
def setUp(self):
a, b, c = (User.objects.create_user(guy, email='%s@example.org' % guy, password=guy) for guy in 'abc')
Topic.objects.create(name='topipo', description='super topic', site=Site.objects.first())
c.is_superuser = True
c.save()
def test_everything(self):
# talk-edit
self.client.login(username='a', password='a')
self.client.post(reverse('add-talk'),
{'title': 'super talk',
'abstract': 'super',
'description': 'this is my super talk',
'notes': 'you can watch my previous talk videos',
'event': 1,
'topics': 1,
'speakers': 1})
talk = Talk.objects.first()
self.assertEqual(str(talk), 'super talk')
self.assertEqual(talk.abstract, 'super')
self.assertEqual(talk.description, 'this is my super talk')
self.assertEqual(talk.notes, 'you can watch my previous talk videos')
response = self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),
{'title': 'mega talk', 'description': 'mega',
'event': 1, 'speakers': 1, 'duration': 60,
'registration_required': False, 'attendees_limit': 0})
self.assertEqual(str(talk), 'super talk') # title is read only there
talk = Talk.objects.first()
self.assertEqual(talk.description, 'mega')
# Status Code
for view in ['home', 'participate-as-speaker', 'add-talk', 'list-topics']:
self.assertEqual(self.client.get(reverse(view)).status_code, 200)
for view in ['list-talks', 'list-speakers']:
self.assertEqual(self.client.get(reverse(view)).status_code, 403)
self.assertEqual(self.client.get(reverse('list-speakers')).status_code, 403)
self.assertEqual(self.client.get(reverse('edit-talk', kwargs={'talk': talk.slug})).status_code, 200)
self.assertEqual(self.client.get(reverse('show-talk', kwargs={'slug': talk.slug})).status_code, 200)
self.assertEqual(self.client.get(reverse('show-participant', kwargs={'username': 'a'})).status_code, 200)
self.client.login(username='b', password='b')
self.assertEqual(self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),
{'title': 'mega talk', 'description': 'mega', 'event': 1}).status_code, 403)
self.assertEqual(self.client.get(reverse('participate-as-speaker')).status_code, 200)
# Vote
self.assertEqual(talk.score(), 0)
self.assertEqual(self.client.get(reverse('vote', kwargs={'talk': talk.slug, 'score': 2})).status_code, 403)
self.client.login(username='c', password='c')
self.assertEqual(self.client.get(reverse('vote', kwargs={'talk': talk.slug, 'score': 2})).status_code, 302)
self.assertEqual(talk.score(), 2)
# Models str & get_asbolute_url
for model in [Talk, Topic, Vote]:
item = model.objects.first()
self.assertEqual(self.client.get(item.get_absolute_url()).status_code, 200)
self.assertTrue(str(item))
# Talk.is_{editable,moderable}_by
a, b, c = User.objects.all()
self.assertTrue(talk.is_editable_by(c)) # c is superuser
self.assertTrue(talk.is_moderable_by(c)) # c is superuser
self.assertFalse(talk.is_editable_by(b)) # b is not speaker
self.assertFalse(talk.is_moderable_by(b)) # b is not orga
self.client.login(username='a', password='a')
self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),
{'title': 'mega talk', 'description': 'mega', 'event': 1,
'speakers': (a.pk, b.pk), 'duration': 60,
'registration_required': False, 'attendees_limit': 0})
talk = Talk.objects.get(slug='super-talk')
self.assertTrue(b in talk.speakers.all())
self.assertTrue(talk.is_editable_by(b)) # b is speaker now
self.assertFalse(talk.is_moderable_by(b)) # b is not orga
def test_topic_edition_permissions(self):
# Only orga and superuser can edit topics
self.client.login(username='b', password='b')
self.assertFalse(Participation.objects.get(user__username='b').orga)
self.assertEqual(self.client.get(reverse('edit-topic', kwargs={'slug': 'topipo'})).status_code, 302)
Participation.objects.filter(user__username='b').update(orga=True)
self.assertEqual(self.client.get(reverse('edit-topic', kwargs={'slug': 'topipo'})).status_code, 200)
self.client.login(username='c', password='c') # superuser
self.assertEqual(self.client.get(reverse('edit-topic', kwargs={'slug': 'topipo'})).status_code, 200)
self.assertEqual(self.client.get(reverse('list-topics')).status_code, 200)

View File

@ -1,28 +0,0 @@
from django.conf.urls import url
from proposals import views
urlpatterns = [
url(r'^markdown/$', views.markdown_preview, name='markdown'),
url(r'^$', views.home, name='home'),
url(r'^staff/$', views.staff, name='staff'),
url(r'^conference/$', views.conference, name='edit-conference'),
url(r'^talk/propose/$', views.participate, name='participate-as-speaker'),
url(r'^talk/$', views.talk_list, name='list-talks'),
url(r'^talk/add/$', views.talk_edit, name='add-talk'),
url(r'^talk/edit/(?P<talk>[-\w]+)$', views.talk_edit, name='edit-talk'),
url(r'^talk/vote/(?P<talk>[-\w]+)/(?P<score>[-0-2]+)$', views.vote, name='vote'),
url(r'^talk/details/(?P<slug>[-\w]+)$', views.TalkDetail.as_view(), name='show-talk'),
url(r'^talk/accept/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': True}, name='accept-talk'),
url(r'^talk/decline/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': False}, name='decline-talk'),
url(r'^talk/assign-to-track/(?P<talk>[-\w]+)/(?P<track>[-\w]+)/$', views.talk_assign_to_track, name='assign-talk-to-track'),
url(r'^topic/$', views.TopicList.as_view(), name='list-topics'),
url(r'^topic/add/$', views.TopicCreate.as_view(), name='add-topic'),
url(r'^topic/(?P<slug>[-\w]+)/edit/$', views.TopicUpdate.as_view(), name='edit-topic'),
url(r'^track/$', views.TrackList.as_view(), name='list-tracks'),
url(r'^track/add/$', views.TrackCreate.as_view(), name='add-track'),
url(r'^track/(?P<slug>[-\w]+)/edit/$', views.TrackUpdate.as_view(), name='edit-track'),
url(r'^speakers/$', views.speaker_list, name='list-speakers'),
url(r'^register/$', views.talk_registrable_list, name='list-registrable-talks'),
url(r'^register/(?P<talk>[-\w]+)$', views.talk_register, name='register-for-a-talk'),
]

View File

@ -1,25 +0,0 @@
from django.contrib.sites.shortcuts import get_current_site
from django.db.models import Q, Sum
from django.db.models.functions import Coalesce
from django.utils.safestring import mark_safe
from accounts.models import Participation
from markdown import markdown
import bleach
def query_sum(queryset, field):
return queryset.aggregate(s=Coalesce(Sum(field), 0))['s']
def allowed_talks(talks, request):
if not Participation.objects.get(site=get_current_site(request), user=request.user).is_orga():
talks = talks.filter(Q(topics__reviewers=request.user) | Q(speakers=request.user) | Q(proposer=request.user))
return talks.distinct()
def markdown_to_html(md):
html = markdown(md)
allowed_tags = bleach.ALLOWED_TAGS + ['p', 'pre', 'span' ] + ['h%d' % i for i in range(1, 7) ]
html = bleach.clean(html, tags=allowed_tags)
return mark_safe(html)

View File

@ -1,459 +0,0 @@
from functools import reduce
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db.models import Q, Count
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from django.views.decorators.http import require_http_methods
from django.http import HttpResponse, Http404
from ponyconf.mixins import OnSiteFormMixin
from accounts.models import Participation
from accounts.mixins import OrgaRequiredMixin, StaffRequiredMixin
from accounts.decorators import orga_required, staff_required
from accounts.utils import is_staff, is_orga
from conversations.models import ConversationWithParticipant, ConversationAboutTalk, Message
from planning.models import Room
from .forms import TalkForm, TopicForm, TrackForm, ConferenceForm, TalkFilterForm, STATUS_VALUES, SpeakerFilterForm, TalkActionForm, SubscribeForm
from .models import Talk, Track, Topic, Vote, Conference, Attendee
from .signals import talk_added, talk_edited
from .utils import markdown_to_html
@login_required
@require_http_methods(["POST"])
def markdown_preview(request):
content = request.POST.get('data', '')
return HttpResponse(markdown_to_html(content))
def home(request):
return render(request, 'proposals/home.html')
@staff_required
def staff(request):
return render(request, 'staff.html')
@orga_required
def conference(request):
conference = Conference.objects.get(site=get_current_site(request))
form = ConferenceForm(request.POST or None, instance=conference)
if request.method == 'POST' and form.is_valid():
form.save()
messages.success(request, 'Conference updated!')
return redirect(reverse('edit-conference'))
return render(request, 'proposals/conference.html', {
'form': form,
})
@login_required
def participate(request):
site = get_current_site(request)
talks = Talk.objects.filter(site=site)
my_talks = talks.filter(speakers=request.user)
proposed_talks = talks.exclude(speakers=request.user).filter(proposer=request.user)
return render(request, 'proposals/participate.html', {
'my_talks': my_talks,
'proposed_talks': proposed_talks,
})
@staff_required
def talk_list(request):
show_filters = False
talks = Talk.objects.filter(site=get_current_site(request))
filter_form = TalkFilterForm(request.GET or None, site=get_current_site(request))
# Filtering
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['kind']):
show_filters = True
talks = talks.filter(reduce(lambda x, y: x | y, [Q(event__pk=pk) for pk in data['kind']]))
if len(data['status']):
show_filters = True
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(STATUS_VALUES)[status]) for status in data['status']]))
if len(data['topic']):
show_filters = True
talks = talks.filter(reduce(lambda x, y: x | y, [Q(topics__slug=topic) for topic in data['topic']]))
if len(data['track']):
show_filters = True
q = Q()
if 'none' in data['track']:
data['track'].remove('none')
q |= Q(track__isnull=True)
if len(data['track']):
q |= Q(track__slug__in=data['track'])
talks = talks.filter(q)
if data['vote'] != None:
show_filters = True
if data['vote']:
talks = talks.filter(vote__user=request.user)
else:
talks = talks.exclude(vote__user=request.user)
if data['room'] != None:
show_filters = True
talks = talks.filter(room__isnull=not data['room'])
if data['scheduled'] != None:
show_filters = True
talks = talks.filter(start_date__isnull=not data['scheduled'])
if data['materials'] != None:
show_filters = True
talks = talks.filter(materials__isnull=not data['materials'])
if data['video'] != None:
show_filters = True
if data['video']:
talks = talks.exclude(video__exact='')
else:
talks = talks.filter(video__exact='')
# Action
action_form = TalkActionForm(request.POST or None, talks=talks, site=get_current_site(request))
if not is_orga(request, request.user):
action_form.fields.pop('track')
action_form.fields.pop('room')
if request.method == 'POST':
if action_form.is_valid():
data = action_form.cleaned_data
permission_error = False
for talk in data['talks']:
talk = Talk.objects.get(site=get_current_site(request), slug=talk)
if data['decision'] != None:
if not talk.is_moderable_by(request.user):
permission_error = True
continue
# TODO: merge with talk_decide code
conversation = ConversationAboutTalk.objects.get(talk=talk)
if data['decision']:
note = "The talk has been accepted."
else:
note = "The talk has been declined."
Message.objects.create(conversation=conversation, author=request.user, content=note)
talk.accepted = data['decision']
if data['track']:
talk.track = Track.objects.get(site=get_current_site(request), slug=data['track'])
if data['room']:
talk.room = Room.objects.get(site=get_current_site(request), slug=data['room'])
talk.save()
if permission_error:
messages.warning(request, 'Some actions were ignored due to missing permissions.')
return redirect(request.get_full_path())
# Sorting
if request.GET.get('order') == 'desc':
reverse = True
else:
reverse = False
SORT_MAPPING = {
'title': 'title',
'kind': 'event',
'status': 'accepted',
}
sort = request.GET.get('sort')
if sort in SORT_MAPPING.keys():
if reverse:
talks = talks.order_by('-' + SORT_MAPPING[sort])
else:
talks = talks.order_by(SORT_MAPPING[sort])
# Sorting URLs
sort_urls = dict()
sort_glyphicons = dict()
for c in SORT_MAPPING.keys():
url = request.GET.copy()
url['sort'] = c
if c == sort:
if reverse:
del url['order']
glyphicon = 'sort-by-attributes-alt'
else:
url['order'] = 'desc'
glyphicon = 'sort-by-attributes'
else:
glyphicon = 'sort'
sort_urls[c] = url.urlencode()
sort_glyphicons[c] = glyphicon
return render(request, 'proposals/talk_list.html', {
'show_filters': show_filters,
'talk_list': talks,
'filter_form': filter_form,
'action_form': action_form,
'sort_urls': sort_urls,
'sort_glyphicons': sort_glyphicons,
})
@login_required
def talk_edit(request, talk=None):
site = get_current_site(request)
if talk: # edit existing talk
talk = get_object_or_404(Talk, slug=talk, site=site)
if not talk.is_editable_by(request.user):
raise PermissionDenied
else: # add new talk
conf = Conference.objects.get(site=site)
if not is_orga(request, request.user) and not conf.cfp_is_open():
raise PermissionDenied
staff = talk.is_moderable_by(request.user) if talk else is_orga(request, request.user)
form = TalkForm(request.POST or None, request.FILES or None, instance=talk, site=site, staff=staff)
if talk:
form.fields['topics'].disabled = True
if 'duration' in form.fields and talk.event.duration:
form.fields['duration'].help_text = 'Default value if zero: %d min' % talk.duration
if 'attendees_limit' in form.fields and talk.is_editable_by(request.user) and talk.room and talk.room.capacity:
form.fields['attendees_limit'].help_text=ungettext_lazy(
"Note: the room %(room)s has %(capacity)s seat.",
"Note: the room %(room)s has %(capacity)s seats.",
talk.room.capacity) % {'room': talk.room.name, 'capacity': talk.room.capacity}
else:
form.fields.pop('materials')
form.fields.pop('video')
form.fields['speakers'].initial = [request.user]
if request.method == 'POST' and form.is_valid():
if hasattr(talk, 'id'):
talk = form.save()
if request.user == talk.proposer or request.user in talk.speakers.all():
talk_edited.send(talk.__class__, instance=talk, author=request.user)
messages.success(request, _('Talk modified successfully!'))
else:
form.instance.site = get_current_site(request)
form.instance.proposer = request.user
talk = form.save()
talk_added.send(talk.__class__, instance=talk, author=request.user)
messages.success(request, _('Talk proposed successfully!'))
return redirect(talk.get_absolute_url())
return render(request, 'proposals/talk_edit.html', {
'base_template': 'staff.html' if staff else 'base.html',
'talk': talk,
'form': form,
})
@login_required
def talk_assign_to_track(request, talk, track):
talk = get_object_or_404(Talk, slug=talk, site=get_current_site(request))
if not talk.is_moderable_by(request.user):
raise PermissionDenied
track = get_object_or_404(Track, slug=track, site=get_current_site(request))
talk.track = track
talk.save()
messages.success(request, _('Talk assigned to track successfully!'))
next_url = request.GET.get('next') or reverse('show-talk', kwargs={'slug': talk.slug})
return redirect(next_url)
class TalkDetail(LoginRequiredMixin, DetailView):
def get_queryset(self):
return Talk.objects.filter(site=get_current_site(self.request)).all()
def get_context_data(self, **ctx):
if self.object.is_moderable_by(self.request.user):
vote = Vote.objects.filter(talk=self.object, user=self.request.user).first()
ctx.update(edit_perm=True, moderate_perm=True, vote=vote,
form_url=reverse('talk-conversation', kwargs={'talk': self.object.slug}))
else:
ctx['edit_perm'] = self.object.is_editable_by(self.request.user)
if is_staff(self.request, self.request.user):
ctx.update(base_template='staff.html')
else:
ctx.update(base_template='base.html')
return super().get_context_data(**ctx)
class TopicMixin(object):
def get_queryset(self):
return Topic.objects.filter(site=get_current_site(self.request)).all()
class TopicFormMixin(OnSiteFormMixin):
form_class = TopicForm
class TopicList(LoginRequiredMixin, TopicMixin, ListView):
pass
class TopicCreate(OrgaRequiredMixin, TopicMixin, TopicFormMixin, CreateView):
model = Topic
class TopicUpdate(OrgaRequiredMixin, TopicMixin, TopicFormMixin, UpdateView):
pass
class TrackFormMixin(OnSiteFormMixin):
form_class = TrackForm
class TrackMixin(object):
def get_queryset(self):
return Track.objects.filter(site=get_current_site(self.request)).all()
class TrackList(LoginRequiredMixin, TrackMixin, ListView):
pass
class TrackCreate(OrgaRequiredMixin, TrackMixin, TrackFormMixin, CreateView):
model = Track
class TrackUpdate(OrgaRequiredMixin, TrackMixin, TrackFormMixin, UpdateView):
pass
@login_required
def vote(request, talk, score):
talk = get_object_or_404(Talk, site=get_current_site(request), slug=talk)
if not talk.is_moderable_by(request.user):
raise PermissionDenied
vote, created = Vote.objects.get_or_create(talk=talk, user=request.user)
vote.vote = int(score)
vote.save()
messages.success(request, _('Vote successfully created') if created else _('Vote successfully updated'))
return redirect(talk.get_absolute_url())
@login_required
def talk_decide(request, talk, accepted):
site = get_current_site(request)
talk = get_object_or_404(Talk, site=site, slug=talk)
if not talk.is_moderable_by(request.user):
raise PermissionDenied
if request.method == 'POST':
# Does we need to send a notification to the proposer?
m = request.POST.get('message', '').strip()
if m:
participation = Participation.objects.get(site=site, user=talk.proposer)
conversation = ConversationWithParticipant.objects.get(participation=participation)
Message.objects.create(conversation=conversation, author=request.user, content=m)
# Save the decision in the talk's conversation
conversation = ConversationAboutTalk.objects.get(talk=talk)
if accepted:
note = "The talk has been accepted."
else:
note = "The talk has been declined."
Message.objects.create(conversation=conversation, author=request.user, content=note)
talk.accepted = accepted
talk.save()
messages.success(request, _('Decision taken in account'))
return redirect('show-talk', slug=talk.slug)
return render(request, 'proposals/talk_decide.html', {
'talk': talk,
'accept': accepted,
})
@staff_required
def speaker_list(request):
show_filters = False
site = get_current_site(request)
filter_form = SpeakerFilterForm(request.GET or None, site=site)
talks = Talk.objects.filter(site=site)
# Filtering
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['status']):
show_filters = True
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(STATUS_VALUES)[status]) for status in data['status']]))
if len(data['topic']):
show_filters = True
talks = talks.filter(reduce(lambda x, y: x | y, [Q(topics__slug=topic) for topic in data['topic']]))
if len(data['track']):
show_filters = True
q = Q()
if 'none' in data['track']:
data['track'].remove('none')
q |= Q(track__isnull=True)
if len(data['track']):
q |= Q(track__slug__in=data['track'])
talks = talks.filter(q)
speakers = Participation.objects.filter(site=site,user__talk__in=talks).order_by('pk').distinct()
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['transport']):
show_filters = True
q = Q()
if 'unanswered' in data['transport']:
data['transport'].remove('unanswered')
q |= Q(need_transport=None)
if 'unspecified' in data['transport']:
data['transport'].remove('unspecified')
speakers = speakers.annotate(transport_count=Count('transport'))
q |= Q(need_transport=True, transport_count=0)
if len(data['transport']):
q |= (Q(need_transport=True) & Q(reduce(lambda x, y: x | y, [Q(transport__pk=pk) for pk in data['transport']])))
speakers = speakers.filter(q)
if len(data['accommodation']):
show_filters = True
accommodations = list(map(lambda x: None if x == 'unknown' else x, data['accommodation']))
speakers = speakers.filter(reduce(lambda x, y: x | y, [Q(accommodation=value) for value in accommodations]))
if data['sound'] != None:
show_filters = True
speakers = speakers.filter(sound=data['sound'])
if data['transport_booked'] != None:
show_filters = True
speakers = speakers.filter(need_transport=True).filter(transport_booked=data['transport_booked'])
if data['accommodation_booked'] != None:
show_filters = True
speakers = speakers.exclude(accommodation=Participation.ACCOMMODATION_NO).filter(accommodation_booked=data['accommodation_booked'])
contact_link = 'mailto:' + ','.join([speaker.user.email for speaker in speakers.all() if speaker.user.email])
return render(request, 'proposals/speaker_list.html', {
'speaker_list': speakers,
'filter_form': filter_form,
'show_filters': show_filters,
'contact_link': contact_link,
})
def talk_registrable_list(request):
site = get_current_site(request)
if not Conference.objects.get(site=site).subscriptions_open:
raise Http404
talks = Talk.objects.filter(site=site, registration_required=True)
if request.user.is_authenticated():
attendee = Attendee.objects.filter(user=request.user).first() # None if it does not exists
else:
attendee = None
return render(request, 'proposals/talk_registrable_list.html', {
'talks': talks,
'attendee': attendee,
})
def talk_register(request, talk):
talk = get_object_or_404(Talk, site=get_current_site(request), registration_required=True, slug=talk)
form = SubscribeForm(request.POST or None)
if request.user.is_authenticated() or (request.method == 'POST' and form.is_valid()):
if request.user.is_authenticated():
attendee, created = Attendee.objects.get_or_create(user=request.user)
else:
attendee, created = Attendee.objects.get_or_create(email=form.cleaned_data['email'], name=form.cleaned_data['name'])
if attendee in talk.attendees.all():
if request.user.is_authenticated():
talk.attendees.remove(attendee)
messages.success(request, _("Unregistered :-("))
else:
messages.error(request, _("Already registered!"))
elif talk.remaining_attendees == 0:
raise PermissionDenied
else:
talk.attendees.add(attendee)
messages.success(request, _("Registered!"))
talk.save()
return redirect('list-registrable-talks')
return render(request, 'proposals/talk_register.html', {
'talk': talk,
'form': form,
})