Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

[WIP] Plan app (rebased onto current master state) #188

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dashboard/dashboard_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def get_navigation_links(cls):
(_("Start"), cls.get_index_link()),
(_("Persönliche Übersicht"), reverse_lazy('dashboard:personal_overview')),
(_("Personal"), reverse_lazy('dashboard:staff:index')),
(_("Plan"), reverse_lazy('dashboard:plan:overview')),
(_("Ersties"), reverse_lazy('dashboard:students:index')),
(_("Klausur"), reverse_lazy('dashboard:exam:assignment')),
(_("Kleidung"), reverse_lazy('dashboard:clothing:order_overview')),
Expand Down
1 change: 1 addition & 0 deletions dashboard/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
url(r'^students/', include('students.dashboard_urls', namespace='students')),
url(r'^exam/', include('exam.dashboard_urls', namespace='exam')),
url(r'^clothing/', include('clothing.dashboard_urls', namespace='clothing')),
url(r'^plan/', include('plan.dashboard_urls', namespace='plan')),
]
Empty file added plan/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions plan/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html

from plan.admin_actions import update_attendees
from plan.models import TimeSlot, SlotType, Event, Booking
from django.utils.translation import ugettext as _


@admin.register(SlotType)
class SlotTypeAdmin(admin.ModelAdmin):
list_display = ['name', 'split_groups']


@admin.register(TimeSlot)
class TimeSlotAdmin(admin.ModelAdmin):
list_display = ['name', 'begin', 'end', 'relevant_for', 'link_attendance_list']
actions = [update_attendees]

@staticmethod
def link_attendance_list(event):
return format_html('<a href="{url}?event__id__exact={id}">{name}</a>',
url=reverse('admin:staff_attendance_changelist'),
id=event.pk,
name=_('Teilnehmerliste'))


@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
list_display = ['timeslot', 'room', 'tutorgroup']


@admin.register(Booking)
class BookingAdmin(admin.ModelAdmin):
list_display = ['room', 'begin', 'end', 'status']
13 changes: 13 additions & 0 deletions plan/admin_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from staff.models import Attendance
from django.utils.translation import ugettext as _


def update_attendees(modeladmin, request, queryset):
for event in queryset:
existing_pesons = event.attendance_set.values("person")
relevant_for_queryset = event.relevant_for.get_filtered_staff()
new_attendees = relevant_for_queryset.exclude(pk__in=existing_pesons)

Attendance.objects.bulk_create([Attendance(event=event, person=person, status='x') for person in new_attendees])
modeladmin.message_user(request, _("Teilnehmerliste aktualisiert."))
update_attendees.short_description = _("Liste der Teilnehmer aktualisieren (anhand des Zielgruppenfilters)")
5 changes: 5 additions & 0 deletions plan/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class PlanConfig(AppConfig):
name = 'plan'
13 changes: 13 additions & 0 deletions plan/dashboard_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.conf.urls import url

from plan.dashboard_views import PlanOverview, TimeSlotCreateView, TimeSlotCreateSuccessView, PlanCategoryView, \
PlanCategoryPublicView

app_name = 'plan'
urlpatterns = [
url(r'^schedule/$', PlanOverview.as_view(), name='overview'),
url(r'^schedule/(?P<slug>[^/]+)/$', PlanCategoryView.as_view(), name='overview_category'),
url(r'^schedule/(?P<slug>[^/]+)/public$', PlanCategoryPublicView.as_view(), name='overview_category_public'),
url(r'^timeslot/new/$', TimeSlotCreateView.as_view(), name='timeslot_create'),
url(r'^timeslot/new/success/$', TimeSlotCreateSuccessView.as_view(), name='timeslot_create_success'),
]
63 changes: 63 additions & 0 deletions plan/dashboard_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from django.urls import reverse_lazy
from django.views.generic import CreateView, ListView, TemplateView, DetailView
from django.utils.translation import ugettext_lazy as _

from dashboard.components import DashboardAppMixin
from ophasebase.models import OphaseCategory, Ophase
from plan.forms import TimeSlotForm
from plan.models import TimeSlot


class PlanAppMixin(DashboardAppMixin):
app_name_verbose = "Plan"
app_name = 'plan'
permissions = ['plan.add_timeslot']

@property
def sidebar_links(self):
return [
(_('Übersicht'), self.prefix_reverse_lazy('overview')),
(_('Neuer Timeslot'), self.prefix_reverse_lazy('timeslot_create')),
]


class PlanOverview(PlanAppMixin, ListView):
model = TimeSlot
context_object_name = "time_slots"
template_name = "plan/schedule.html"

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["categories"] = Ophase.current().ophaseactivecategory_set.all()
return context


class PlanCategoryView(PlanAppMixin, DetailView):
model = OphaseCategory
context_object_name = "category"
template_name = "plan/schedule_category.html"

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["categories"] = Ophase.current().ophaseactivecategory_set.all()
context["time_slots"] = context["category"].timeslot_set.all()
return context


class PlanCategoryPublicView(PlanCategoryView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["time_slots"] = context["time_slots"].filter(public=True)
context["public"] = True
return context


class TimeSlotCreateView(PlanAppMixin, CreateView):
success_url = reverse_lazy("dashboard:plan:timeslot_create_success")
template_name = "plan/timeslot_create.html"
model = TimeSlot
form_class = TimeSlotForm


class TimeSlotCreateSuccessView(PlanAppMixin, TemplateView):
template_name = "plan/timeslot_create_success.html"
Empty file added plan/dashboard_widgets.py
Empty file.
14 changes: 14 additions & 0 deletions plan/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django import forms

from .models import TimeSlot


class TimeSlotForm(forms.ModelForm):
class Meta:
model = TimeSlot
fields = ['name', 'slottype', 'begin', 'end', 'category', 'relevant_for', 'attendance_required', 'public']
widgets = {
# use checkboxes for multipleChoice @see http://stackoverflow.com/a/16937145
'category': forms.CheckboxSelectMultiple,
# TODO Use DateTimePicker
}
53 changes: 53 additions & 0 deletions plan/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-10 14:47
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('staff', '0018_person_tutor_experience'),
('ophasebase', '0007_auto_20170215_1416'),
]

operations = [
migrations.CreateModel(
name='SlotType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('color', models.CharField(default='#FFFFFF', max_length=7)),
],
options={
'verbose_name_plural': 'Veranstaltungsarten',
'verbose_name': 'Veranstaltungsart',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='TimeSlot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('begin', models.DateTimeField(verbose_name='Beginn')),
('end', models.DateTimeField(verbose_name='Ende')),
('attendance_required', models.BooleanField(default=False)),
('public', models.BooleanField(default=False)),
('category', models.ManyToManyField(to='ophasebase.OphaseCategory')),
('ophase', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ophasebase.Ophase')),
('relevant_for', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='staff.StaffFilterGroup', verbose_name='Filterkriterium: Wer muss anwesend sein?')),
('room', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ophasebase.Room', verbose_name='Raum')),
('slottype', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plan.SlotType')),
],
options={
'verbose_name_plural': 'Zeitslots',
'verbose_name': 'Zeitslot',
'ordering': ['begin'],
},
),
]
47 changes: 47 additions & 0 deletions plan/migrations/0002_auto_20170410_1702.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-04-10 15:02
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('staff', '0018_person_tutor_experience'),
('ophasebase', '0007_auto_20170215_1416'),
('plan', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('room', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ophasebase.Room', verbose_name='Raum')),
],
options={
'verbose_name': 'Event',
'verbose_name_plural': 'Events',
},
),
migrations.RemoveField(
model_name='timeslot',
name='room',
),
migrations.AddField(
model_name='event',
name='timeslot',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plan.TimeSlot', verbose_name='Zeitslot'),
),
migrations.AddField(
model_name='event',
name='tutorgroup',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='staff.TutorGroup', verbose_name='Kleingruppe'),
),
migrations.AlterUniqueTogether(
name='event',
unique_together=set([('timeslot', 'tutorgroup')]),
),
]
37 changes: 37 additions & 0 deletions plan/migrations/0003_auto_20170410_1825.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-04-10 16:25
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('ophasebase', '0007_auto_20170215_1416'),
('plan', '0002_auto_20170410_1702'),
]

operations = [
migrations.CreateModel(
name='Booking',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('begin', models.DateTimeField(verbose_name='Anfang')),
('end', models.DateTimeField(verbose_name='Ende')),
('status', models.PositiveSmallIntegerField(choices=[(1, 'Gebucht'), (2, 'Blockiert'), (3, 'Anfragen?'), (4, 'Angefragt'), (5, 'Überlappung')])),
('comment', models.TextField(blank=True, verbose_name='Kommentar')),
('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ophasebase.Room', verbose_name='Raum')),
],
options={
'verbose_name': 'Raumbuchung',
'verbose_name_plural': 'Raumbuchungen',
},
),
migrations.AlterField(
model_name='timeslot',
name='relevant_for',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='staff.StaffFilterGroup', verbose_name='Filterkriterium: Wer könnte anwesend sein?'),
),
]
19 changes: 19 additions & 0 deletions plan/migrations/0004_auto_20170410_1946.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-04-10 17:46
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('plan', '0003_auto_20170410_1825'),
]

operations = [
migrations.AlterModelOptions(
name='booking',
options={'ordering': ['room', '-begin'], 'verbose_name': 'Raumbuchung', 'verbose_name_plural': 'Raumbuchungen'},
),
]
26 changes: 26 additions & 0 deletions plan/migrations/0005_auto_20170813_1338.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-08-13 11:38
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('plan', '0004_auto_20170410_1946'),
]

operations = [
migrations.AddField(
model_name='slottype',
name='split_groups',
field=models.BooleanField(default=False, verbose_name='In Kleingruppen aufteilen?'),
),
migrations.AlterField(
model_name='timeslot',
name='relevant_for',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='staff.StaffFilterGroup', verbose_name='Filterkriterium: Wer könnte anwesend sein?'),
),
]
Empty file added plan/migrations/__init__.py
Empty file.
Loading