add room managment

This commit is contained in:
Élie Bouttier 2016-10-05 23:12:13 +02:00
parent 1d8433e1c3
commit 8510500480
20 changed files with 257 additions and 22 deletions

0
planning/__init__.py Normal file
View File

6
planning/admin.py Normal file
View File

@ -0,0 +1,6 @@
from django.contrib import admin
from planning.models import Room
admin.site.register(Room)

5
planning/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class PlanningConfig(AppConfig):
name = 'planning'

19
planning/forms.py Normal file
View File

@ -0,0 +1,19 @@
from django import forms
from .models import Room
class RoomForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.site = kwargs.pop('site')
super().__init__(*args, **kwargs)
class Meta:
model = Room
fields = ['name', 'capacity']
def clean_name(self):
name = self.cleaned_data['name']
if self.instance and name != self.instance.name and Room.objects.filter(site=self.site, name=name).exists():
raise self.instance.unique_error_message(self._meta.model, ['name'])
return name

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-10-05 20:53
from __future__ import unicode_literals
import autoslug.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
operations = [
migrations.CreateModel(
name='Room',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, default='')),
('capacity', models.IntegerField(default=0)),
('site', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name')),
],
),
migrations.AlterUniqueTogether(
name='room',
unique_together=set([('site', 'name')]),
),
]

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-10-05 21:08
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('planning', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='room',
name='name',
field=models.CharField(blank=True, default='', max_length=256),
),
migrations.AlterField(
model_name='room',
name='site',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site'),
),
]

View File

23
planning/models.py Normal file
View File

@ -0,0 +1,23 @@
from django.db import models
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from autoslug import AutoSlugField
class Room(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=256, blank=True, default="")
slug = AutoSlugField(populate_from='name')
capacity = models.IntegerField(default=0)
class Meta:
unique_together = ['site', 'name']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('list-rooms')

View File

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

View File

@ -0,0 +1,32 @@
{% extends 'base.html' %}
{% load bootstrap3 accounts_tags i18n %}
{% block planningtab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Rooms" %}</h1>
{% if request|orga %}
<p><a href="{% url 'add-room' %}" class="btn btn-success">{% trans "Add a room" %}</a><p>
{% endif %}
<div class="row">
{% for room in room_list %}
<div class="col-xs-6 col-sm-4">
<h2>{{ room }}</h2>
{{ room.capacity }} {% trans "place" %}{{ room.capacity|pluralize }}
{% if request|staff %}
|
<a href="{% url 'edit-room' room.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 rooms." %}</em></div>
{% endfor %}
</div>
{% endblock %}

3
planning/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
planning/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.conf.urls import url
from planning import views
urlpatterns = [
url(r'^room/$', views.RoomList.as_view(), name='list-rooms'),
url(r'^room/add/$', views.RoomCreate.as_view(), name='add-room'),
url(r'^room/(?P<slug>[-\w]+)/edit/$', views.RoomUpdate.as_view(), name='edit-room'),
]

29
planning/views.py Normal file
View File

@ -0,0 +1,29 @@
from django.shortcuts import render
from django.contrib.sites.shortcuts import get_current_site
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from accounts.mixins import OrgaRequiredMixin
from proposals.mixins import OnSiteFormMixin
from .models import Room
from .forms import RoomForm
class RoomMixin(object):
def get_queryset(self):
return Room.objects.filter(site=get_current_site(self.request)).all()
class RoomFormMixin(OnSiteFormMixin):
form_class = RoomForm
class RoomList(OrgaRequiredMixin, RoomMixin, ListView):
pass
class RoomCreate(OrgaRequiredMixin, RoomMixin, RoomFormMixin, CreateView):
model = Room
class RoomUpdate(OrgaRequiredMixin, RoomMixin, RoomFormMixin, UpdateView):
pass

View File

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'ponyconf',
'proposals',
'conversations',
'planning',
# external apps
'djangobower',

View File

@ -56,6 +56,14 @@
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
{% if request|staff %}
<li class="dropdown{% block planningtab %}{% endblock %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-calendar"></span>&nbsp;{% trans "Planning" %}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-rooms' %}"><span class="glyphicon glyphicon-tent"></span>&nbsp;{% trans "Rooms" %}</a>
</li>
</ul>
</li>
<li class="dropdown{% block admintab %}{% endblock %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-cog"></span>&nbsp;{% trans "Administration" %}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">

View File

@ -23,5 +23,6 @@ urlpatterns = [
url(r'^accounts/', include('accounts.urls')),
url(r'', include('proposals.urls')),
url(r'^conversations/', include('conversations.urls')),
url(r'^planning/', include('planning.urls')),
url(r'^select2/', include('django_select2.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-10-05 20:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('proposals', '0015_auto_20161005_1932'),
]
operations = [
migrations.AlterField(
model_name='event',
name='duration',
field=models.IntegerField(default=0, verbose_name='Duration (min)'),
),
migrations.AlterField(
model_name='talk',
name='duration',
field=models.IntegerField(default=0, verbose_name='Duration (min)'),
),
]

12
proposals/mixins.py Normal file
View File

@ -0,0 +1,12 @@
from django.contrib.sites.shortcuts import get_current_site
class OnSiteFormMixin:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'site': get_current_site(self.request)})
return kwargs
def form_valid(self, form):
form.instance.site = get_current_site(self.request)
return super().form_valid(form)

View File

@ -32,7 +32,7 @@ class ProposalsTests(TestCase):
self.assertEqual(talk.description, 'this is my super talk')
self.assertEqual(talk.notes, 'you can watch my previous talk videos')
self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),
{'title': 'mega talk', 'description': 'mega', 'event': 1, 'speakers': 1})
{'title': 'mega talk', 'description': 'mega', 'event': 1, 'speakers': 1, 'duration': 60})
self.assertEqual(str(talk), 'super talk') # title is read only there
talk = Talk.objects.first()
self.assertEqual(talk.description, 'mega')
@ -73,7 +73,7 @@ class ProposalsTests(TestCase):
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)})
{'title': 'mega talk', 'description': 'mega', 'event': 1, 'speakers': (a.pk, b.pk), 'duration': 60})
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

View File

@ -25,6 +25,7 @@ from .forms import TalkForm, TopicForm, TrackForm, ConferenceForm, TalkFilterFor
from .models import Talk, Track, Topic, Vote, Conference
from .signals import talk_added, talk_edited
from .utils import allowed_talks, markdown_to_html
from .mixins import OnSiteFormMixin
@login_required
@ -208,18 +209,9 @@ class TopicMixin(object):
return Topic.objects.filter(site=get_current_site(self.request)).all()
class TopicFormMixin(object):
class TopicFormMixin(OnSiteFormMixin):
form_class = TopicForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'site': get_current_site(self.request)})
return kwargs
def form_valid(self, form):
form.instance.site = get_current_site(self.request)
return super().form_valid(form)
class TopicList(LoginRequiredMixin, TopicMixin, ListView):
pass
@ -233,18 +225,9 @@ class TopicUpdate(OrgaRequiredMixin, TopicMixin, TopicFormMixin, UpdateView):
pass
class TrackFormMixin(object):
class TrackFormMixin(OnSiteFormMixin):
form_class = TrackForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'site': get_current_site(self.request)})
return kwargs
def form_valid(self, form):
form.instance.site = get_current_site(self.request)
return super().form_valid(form)
class TrackMixin(object):
def get_queryset(self):