confirmation of talk acceptance by speakers
This commit is contained in:
parent
10db5642df
commit
69d4dbbb70
39
cfp/forms.py
39
cfp/forms.py
|
@ -12,17 +12,28 @@ from django_select2.forms import ModelSelect2MultipleWidget
|
|||
from .models import Participant, Talk, TalkCategory, Track, Conference, Room, Volunteer
|
||||
|
||||
|
||||
STATUS_CHOICES = [
|
||||
ACCEPTATION_CHOICES = [
|
||||
('pending', _('Pending decision')),
|
||||
('accepted', _('Accepted')),
|
||||
('declined', _('Declined')),
|
||||
]
|
||||
STATUS_VALUES = [
|
||||
ACCEPTATION_VALUES = [
|
||||
('pending', None),
|
||||
('accepted', True),
|
||||
('declined', False),
|
||||
]
|
||||
|
||||
CONFIRMATION_CHOICES = [
|
||||
('waiting', _('Waiting')),
|
||||
('confirmed', _('Confirmed')),
|
||||
('desisted', _('Desisted')),
|
||||
]
|
||||
CONFIRMATION_VALUES = [
|
||||
('waiting', None),
|
||||
('confirmed', True),
|
||||
('desisted', False),
|
||||
]
|
||||
|
||||
|
||||
class TalkForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -69,11 +80,17 @@ class TalkFilterForm(forms.Form):
|
|||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=[],
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
label=_('Status'),
|
||||
accepted = forms.MultipleChoiceField(
|
||||
label=_('Accepted'),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=STATUS_CHOICES,
|
||||
choices=ACCEPTATION_CHOICES,
|
||||
)
|
||||
confirmed = forms.MultipleChoiceField(
|
||||
label=_('Confirmed'),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=CONFIRMATION_CHOICES,
|
||||
)
|
||||
track = forms.MultipleChoiceField(
|
||||
label=_('Track'),
|
||||
|
@ -145,11 +162,17 @@ class ParticipantFilterForm(forms.Form):
|
|||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=[],
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
label=_('Status'),
|
||||
accepted = forms.MultipleChoiceField(
|
||||
label=_('Accepted'),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=STATUS_CHOICES,
|
||||
choices=ACCEPTATION_CHOICES,
|
||||
)
|
||||
confirmed = forms.MultipleChoiceField(
|
||||
label=_('Confirmed'),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=CONFIRMATION_CHOICES,
|
||||
)
|
||||
track = forms.MultipleChoiceField(
|
||||
label=_('Track'),
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-10-06 16:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cfp', '0011_auto_20171005_2328'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='talk',
|
||||
name='confirmed',
|
||||
field=models.NullBooleanField(default=None),
|
||||
),
|
||||
]
|
|
@ -243,6 +243,7 @@ class TalkManager(models.Manager):
|
|||
qs = qs.annotate(score=Coalesce(Avg('vote__vote'), 0))
|
||||
return qs
|
||||
|
||||
|
||||
def talks_materials_destination(talk, filename):
|
||||
return join(talk.site.name, talk.slug, filename)
|
||||
|
||||
|
@ -258,7 +259,6 @@ class Talk(PonyConfModel):
|
|||
)
|
||||
|
||||
site = models.ForeignKey(Site, on_delete=models.CASCADE)
|
||||
|
||||
speakers = models.ManyToManyField(Participant, verbose_name=_('Speakers'))
|
||||
title = models.CharField(max_length=128, verbose_name=_('Talk Title'))
|
||||
slug = AutoSlugField(populate_from='title', unique=True)
|
||||
|
@ -276,6 +276,7 @@ class Talk(PonyConfModel):
|
|||
max_length=10, verbose_name=_("Video licence"))
|
||||
sound = models.BooleanField(_("I need sound"), default=False)
|
||||
accepted = models.NullBooleanField(default=None)
|
||||
confirmed = models.NullBooleanField(default=None)
|
||||
start_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Beginning date and time'))
|
||||
duration = models.PositiveIntegerField(default=0, verbose_name=_('Duration (min)'))
|
||||
room = models.ForeignKey(Room, blank=True, null=True, default=None)
|
||||
|
@ -283,15 +284,11 @@ class Talk(PonyConfModel):
|
|||
materials = models.FileField(null=True, upload_to=talks_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='Video URL')
|
||||
|
||||
token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
|
||||
conversation = models.OneToOneField(MessageThread)
|
||||
|
||||
|
||||
objects = TalkManager()
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ('title',)
|
||||
|
||||
|
@ -307,6 +304,32 @@ class Talk(PonyConfModel):
|
|||
else:
|
||||
return ', '.join(speakers[:-1]) + ' & ' + str(speakers[-1])
|
||||
|
||||
def get_status_str(self):
|
||||
if self.accepted is True:
|
||||
if self.confirmed is True:
|
||||
return _('Confirmed')
|
||||
elif self.confirmed is False:
|
||||
return _('Cancelled')
|
||||
else:
|
||||
return _('Waiting confirmation')
|
||||
elif self.accepted is False:
|
||||
return _('Refused')
|
||||
else:
|
||||
return _('Pending decision, score: %(score).1f') % {'score': self.score}
|
||||
|
||||
def get_status_color(self):
|
||||
if self.accepted is True:
|
||||
if self.confirmed is True:
|
||||
return 'success'
|
||||
elif self.confirmed is False:
|
||||
return 'danger'
|
||||
else:
|
||||
return 'info'
|
||||
elif self.accepted is False:
|
||||
return 'muted'
|
||||
else:
|
||||
return 'warning'
|
||||
|
||||
@property
|
||||
def estimated_duration(self):
|
||||
return self.duration or self.category.duration
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{% bootstrap_field filter_form.category layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.status layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.accepted layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.confirmed layout="horizontal" %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% bootstrap_field filter_form.track layout="horizontal" %}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<dd><a href="{{ talk.category.get_absolute_url }}">{{ talk.category }}</a></dd>
|
||||
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd><span class="label label-{{ talk.accepted|yesno:"success,danger,warning" }}">{{ talk.accepted|yesno:"Accepted,Declined,Pending decision" }}</span></dd>
|
||||
<dd><span class="label label-{{ talk.get_status_color }}">{{ talk.get_status_str }}</span></dd>
|
||||
|
||||
<dt>{% trans "Track" %}</dt>
|
||||
<dd>{% if talk.track %}
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
<form class="form-horizontal" method="get">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xs-6">
|
||||
{% bootstrap_field filter_form.status layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.accepted layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.confirmed layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.category layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.vote layout="horizontal" %}
|
||||
{% bootstrap_field filter_form.scheduled layout="horizontal" %}
|
||||
|
@ -49,7 +50,7 @@
|
|||
{% if forloop.first %}
|
||||
<tbody>
|
||||
{% endif %}
|
||||
<tr class="{{ talk.accepted|yesno:"success,danger,warning" }}">
|
||||
<tr class="{{ talk.get_status_color }}">
|
||||
<td><input type="checkbox" name="talks" value="{{ talk.token }}"></td>
|
||||
<td><a href="{% url 'talk-details' talk.token %}">{{ talk.title }}</a></td>
|
||||
<td>{{ talk.category }}</td>
|
||||
|
@ -62,13 +63,7 @@
|
|||
</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 %}
|
||||
{{ talk.get_status_str }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if forloop.last%}
|
||||
|
|
|
@ -8,6 +8,8 @@ urlpatterns = [
|
|||
url(r'^cfp/(?P<talk_id>[\w\-]+)/speaker/add/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-add'),
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/speaker/(?P<participant_id>[\w\-]+)/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-edit'),
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/$', views.talk_proposal, name='talk-proposal-edit'),
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/confirm/$', views.talk_acknowledgment, {'confirm': True}, name='talk-confirm'),
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/desist/$', views.talk_acknowledgment, {'confirm': False}, name='talk-desist'),
|
||||
url(r'^volunteer/$', views.volunteer_enrole, name='volunteer-enrole'),
|
||||
url(r'^volunteer/(?P<volunteer_id>[\w\-]+)/$', views.volunteer, name='volunteer'),
|
||||
url(r'^volunteer/(?P<volunteer_id>[\w\-]+)/join/(?P<activity>[\w\-]+)/$', views.volunteer_activity, {'join': True}, name='volunteer-join'),
|
||||
|
@ -19,6 +21,8 @@ urlpatterns = [
|
|||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/vote/(?P<score>[-+0-2]+)/$', views.talk_vote, name='talk-vote'),
|
||||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/accept/$', views.talk_decide, {'accept': True}, name='talk-accept'),
|
||||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/decline/$', views.talk_decide, {'accept': False}, name='talk-decline'),
|
||||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/confirm/$', views.talk_acknowledgment, {'confirm': True}, name='talk-confirm-by-staff'),
|
||||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/desist/$', views.talk_acknowledgment, {'confirm': False}, name='talk-cancel-by-staff'),
|
||||
url(r'^staff/talks/(?P<talk_id>[\w\-]+)/edit/$', views.TalkUpdate.as_view(), name='talk-edit'),
|
||||
url(r'^staff/speakers/$', views.participant_list, name='participant-list'),
|
||||
url(r'^staff/speakers/(?P<participant_id>[\w\-]+)/$', views.participant_details, name='participant-details'),
|
||||
|
|
61
cfp/views.py
61
cfp/views.py
|
@ -25,8 +25,8 @@ from .utils import is_staff
|
|||
from .models import Participant, Talk, TalkCategory, Vote, Track, Room, Volunteer, Activity
|
||||
from .forms import TalkForm, TalkStaffForm, TalkFilterForm, TalkActionForm, \
|
||||
ParticipantForm, ParticipantStaffForm, ParticipantFilterForm, \
|
||||
ConferenceForm, CreateUserForm, STATUS_VALUES, TrackForm, RoomForm, \
|
||||
VolunteerForm
|
||||
ConferenceForm, CreateUserForm, TrackForm, RoomForm, VolunteerForm, \
|
||||
ACCEPTATION_VALUES, CONFIRMATION_VALUES
|
||||
|
||||
|
||||
def home(request):
|
||||
|
@ -177,6 +177,47 @@ def talk_proposal_speaker_edit(request, talk_id, participant_id=None):
|
|||
})
|
||||
|
||||
|
||||
def talk_acknowledgment(request, talk_id, confirm, participant_id=None):
|
||||
# TODO: handle multiple speakers case
|
||||
talk = get_object_or_404(Talk, token=talk_id, site=request.conference.site)
|
||||
if participant_id:
|
||||
participant = get_object_or_404(Participant, token=participant_id, site=request.conference.site)
|
||||
elif not is_staff(request, request.user):
|
||||
raise PermissionDenied
|
||||
else:
|
||||
participant = None
|
||||
if not talk.accepted:
|
||||
raise PermissionDenied
|
||||
if talk.confirmed != confirm:
|
||||
talk.confirmed = confirm
|
||||
talk.save()
|
||||
if confirm:
|
||||
confirmation_message= _('Your participation has been taken into account, thank you!')
|
||||
if participant:
|
||||
thread_note = _('Speaker %(speaker)s confirmed his/her participation.')
|
||||
else:
|
||||
thread_note = _('The talk have been confirmed.')
|
||||
else:
|
||||
confirmation_message = _('We have noted your unavailability.')
|
||||
if participant:
|
||||
thread_note = _('Speaker %(speaker)s CANCELLED his/her participation.')
|
||||
else:
|
||||
thread_note = _('The talk have been cancelled.')
|
||||
if participant_id:
|
||||
thread_note = thread_note % {'speaker': participant}
|
||||
Message.objects.create(thread=talk.conversation, author=participant or request.user, content=thread_note)
|
||||
messages.success(request, confirmation_message)
|
||||
else:
|
||||
if confirm:
|
||||
messages.warning(request, _('You already confirmed your participation to this talk.'))
|
||||
else:
|
||||
messages.warning(request, _('You already cancelled your participation to this talk.'))
|
||||
if participant:
|
||||
return redirect(reverse('talk-proposal-edit', kwargs=dict(talk_id=talk_id, participant_id=participant_id)))
|
||||
else:
|
||||
return redirect(reverse('talk-details', kwargs=dict(talk_id=talk_id)))
|
||||
|
||||
|
||||
@staff_required
|
||||
def staff(request):
|
||||
return render(request, 'cfp/staff/base.html')
|
||||
|
@ -193,9 +234,13 @@ def talk_list(request):
|
|||
if len(data['category']):
|
||||
show_filters = True
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(category__pk=pk) for pk in data['category']]))
|
||||
if len(data['status']):
|
||||
if len(data['accepted']):
|
||||
show_filters = True
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(STATUS_VALUES)[status]) for status in data['status']]))
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(ACCEPTATION_VALUES)[status]) for status in data['accepted']]))
|
||||
if len(data['confirmed']):
|
||||
show_filters = True
|
||||
talks = talks.filter(accepted=True)
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(confirmed=dict(CONFIRMATION_VALUES)[status]) for status in data['confirmed']]))
|
||||
if data['room'] != None:
|
||||
show_filters = True
|
||||
talks = talks.filter(room__isnull=not data['room'])
|
||||
|
@ -355,9 +400,13 @@ def participant_list(request):
|
|||
if len(data['category']):
|
||||
show_filters = True
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(category__pk=pk) for pk in data['category']]))
|
||||
if len(data['status']):
|
||||
if len(data['accepted']):
|
||||
show_filters = True
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(STATUS_VALUES)[status]) for status in data['status']]))
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(ACCEPTATION_VALUES)[status]) for status in data['accepted']]))
|
||||
if len(data['confirmed']):
|
||||
show_filters = True
|
||||
talks = talks.filter(accepted=True)
|
||||
talks = talks.filter(reduce(lambda x, y: x | y, [Q(confirmed=dict(CONFIRMATION_VALUES)[status]) for status in data['confirmed']]))
|
||||
if len(data['track']):
|
||||
show_filters = True
|
||||
q = Q()
|
||||
|
|
Loading…
Reference in New Issue