diff --git a/cfp/forms.py b/cfp/forms.py
index e974ac1..86da851 100644
--- a/cfp/forms.py
+++ b/cfp/forms.py
@@ -141,3 +141,28 @@ class CreateUserForm(forms.ModelForm):
if commit:
user.save()
return user
+
+
+class TrackForm(forms.ModelForm):
+ class Meta:
+ model = Track
+ fields = ['name', 'description']
+
+ def __init__(self, *args, **kwargs):
+ self.conference = kwargs.pop('conference')
+ super().__init__(*args, **kwargs)
+
+ # we should manually check (site, name) uniqueness as the site is not part of the form
+ def clean_name(self):
+ name = self.cleaned_data['name']
+ if (not self.instance or self.instance.name != name) \
+ and Track.objects.filter(site=self.conference.site, name=name).exists():
+ raise self.instance.unique_error_message(self._meta.model, ['name'])
+ return name
+
+ def save(self, commit=True):
+ track = super().save(commit=False)
+ track.site = self.conference.site
+ if commit:
+ track.save()
+ return track
diff --git a/cfp/models.py b/cfp/models.py
index 3f8f4c1..1478e94 100644
--- a/cfp/models.py
+++ b/cfp/models.py
@@ -145,14 +145,14 @@ class Track(PonyConfModel):
class Meta:
unique_together = ('site', 'name')
- #def estimated_duration(self):
- # return sum([talk.estimated_duration for talk in self.talk_set.all()])
+ 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
+ def get_absolute_url(self):
+ return reverse('talk-list') + '?track=%s' % self.slug
class TalkCategory(models.Model): # type of talk (conf 30min, 1h, stand, …)
diff --git a/cfp/templates/cfp/staff/base.html b/cfp/templates/cfp/staff/base.html
index dcf58f2..c9be51d 100644
--- a/cfp/templates/cfp/staff/base.html
+++ b/cfp/templates/cfp/staff/base.html
@@ -8,7 +8,6 @@
{% comment %}
- {% trans "Topics" %}
- - {% trans "Tracks" %}
- {% trans "Volunteers" %}
- {% trans "Rooms" %}
- {% trans "Schedule" %}
@@ -17,6 +16,7 @@
{% endcomment %}
- {% trans "Talks" %}
- {% trans "Speakers" %}
+ - {% trans "Tracks" %}
- {% trans "Conference" %}
{% if request.user.is_staff %}
- Django-Admin
diff --git a/cfp/templates/cfp/staff/track_form.html b/cfp/templates/cfp/staff/track_form.html
new file mode 100644
index 0000000..2f463c3
--- /dev/null
+++ b/cfp/templates/cfp/staff/track_form.html
@@ -0,0 +1,23 @@
+{% extends 'cfp/staff/base.html' %}
+
+{% load bootstrap3 i18n %}
+
+{% block trackstab %} class="active"{% endblock %}
+
+{% block css %}
+{{ block.super }}
+{{ form.media.css }}
+{% endblock %}
+
+{% block content %}
+
+{% trans "Track" %}
+
+{% include "_form.html" %}
+
+{% endblock %}
+
+{% block js_end %}
+{{ block.super }}
+{{ form.media.js }}
+{% endblock %}
diff --git a/cfp/templates/cfp/staff/track_list.html b/cfp/templates/cfp/staff/track_list.html
new file mode 100644
index 0000000..67f065b
--- /dev/null
+++ b/cfp/templates/cfp/staff/track_list.html
@@ -0,0 +1,35 @@
+{% extends 'cfp/staff/base.html' %}
+
+{% load bootstrap3 cfp_tags i18n %}
+
+{% block trackstab %} class="active"{% endblock %}
+
+{% block content %}
+
+{% trans "Tracks" %}
+
+{% trans "Add a track" %}
+
+
+ {% for track in track_list %}
+
+ {% cycle '' '
' %}
+ {% cycle '' '' '
' %}
+ {% empty %}
+
{% trans "No tracks." %}
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/cfp/templatetags/cfp_tags.py b/cfp/templatetags/cfp_tags.py
index 5c7c33e..297676d 100644
--- a/cfp/templatetags/cfp_tags.py
+++ b/cfp/templatetags/cfp_tags.py
@@ -9,3 +9,10 @@ register = template.Library()
@register.filter
def staff(request):
return is_staff(request, request.user)
+
+@register.filter('duration_format')
+def duration_format(value):
+ value = int(value)
+ hours = int(value/60)
+ minutes = value%60
+ return '%d h %02d' % (hours, minutes)
diff --git a/cfp/urls.py b/cfp/urls.py
index 42d5e24..fdfc1cf 100644
--- a/cfp/urls.py
+++ b/cfp/urls.py
@@ -18,6 +18,9 @@ urlpatterns = [
url(r'^staff/talks/(?P[\w\-]+)/edit/$', views.TalkEdit.as_view(), name='talk-edit'),
url(r'^staff/speakers/$', views.participant_list, name='participant-list'),
url(r'^staff/speakers/(?P[\w\-]+)/$', views.participant_details, name='participant-details'),
+ url(r'^staff/tracks/$', views.TrackList.as_view(), name='track-list'),
+ url(r'^staff/tracks/add/$', views.TrackCreate.as_view(), name='track-add'),
+ url(r'^staff/tracks/(?P[-\w]+)/edit/$', views.TrackUpdate.as_view(), name='track-edit'),
url(r'^staff/add-user/$', views.create_user, name='create-user'),
url(r'^staff/select2/$', views.Select2View.as_view(), name='django_select2-json'),
diff --git a/cfp/views.py b/cfp/views.py
index 6685ae7..e28baf3 100644
--- a/cfp/views.py
+++ b/cfp/views.py
@@ -18,7 +18,7 @@ from .decorators import staff_required
from .mixins import StaffRequiredMixin
from .utils import is_staff
from .models import Participant, Talk, TalkCategory, Vote, Track
-from .forms import TalkForm, TalkStaffForm, TalkFilterForm, ParticipantForm, ConferenceForm, CreateUserForm, STATUS_VALUES
+from .forms import TalkForm, TalkStaffForm, TalkFilterForm, ParticipantForm, ConferenceForm, CreateUserForm, STATUS_VALUES, TrackForm
def home(request, conference):
@@ -346,6 +346,35 @@ class TalkEdit(StaffRequiredMixin, OnSiteMixin, UpdateView):
return kwargs
+class TrackMixin(OnSiteMixin):
+ model = Track
+
+
+class TrackList(StaffRequiredMixin, TrackMixin, ListView):
+ template_name = 'cfp/staff/track_list.html'
+
+
+class TrackFormMixin(TrackMixin):
+ template_name = 'cfp/staff/track_form.html'
+ form_class = TrackForm
+ success_url = reverse_lazy('track-list')
+
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
+ kwargs.update({
+ 'conference': self.kwargs['conference'],
+ })
+ return kwargs
+
+
+class TrackCreate(StaffRequiredMixin, TrackFormMixin, CreateView):
+ pass
+
+
+class TrackUpdate(StaffRequiredMixin, TrackFormMixin, UpdateView):
+ pass
+
+
@staff_required
def create_user(request, conference):
form = CreateUserForm(request.POST or None)