337 lines
9.4 KiB
Python
337 lines
9.4 KiB
Python
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.http import HttpResponseRedirect
|
|
from django.views.generic import (
|
|
View,
|
|
CreateView,
|
|
UpdateView,
|
|
ListView,
|
|
FormView,
|
|
DeleteView,
|
|
)
|
|
from django import forms
|
|
from django.contrib.postgres.forms.ranges import RangeWidget
|
|
from django.utils import timezone
|
|
from django.urls import reverse
|
|
|
|
from psycopg2.extras import DateTimeTZRange
|
|
|
|
from camps.mixins import CampViewMixin
|
|
|
|
from ..models import (
|
|
Team,
|
|
TeamMember,
|
|
TeamShift,
|
|
)
|
|
|
|
|
|
class ShiftListView(LoginRequiredMixin, CampViewMixin, ListView):
|
|
model = TeamShift
|
|
template_name = "team_shift_list.html"
|
|
context_object_name = "shifts"
|
|
active_menu = "shifts"
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
return queryset.filter(team__slug=self.kwargs['team_slug'])
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['team'] = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
return context
|
|
|
|
|
|
def date_choices(camp):
|
|
|
|
index = 0
|
|
minute_choices = []
|
|
# To begin with we assume a shift can not be shorter than an hour
|
|
SHIFT_MINIMUM_LENGTH = 60
|
|
while index * SHIFT_MINIMUM_LENGTH < 60:
|
|
minutes = int(index * SHIFT_MINIMUM_LENGTH)
|
|
minute_choices.append(minutes)
|
|
index += 1
|
|
|
|
def get_time_choices(date):
|
|
time_choices = []
|
|
for hour in range(0, 24):
|
|
for minute in minute_choices:
|
|
time_label = "{hour:02d}:{minutes:02d}".format(
|
|
hour=hour,
|
|
minutes=minute
|
|
)
|
|
choice_value = "{} {}".format(date, time_label)
|
|
time_choices.append((choice_value, choice_value))
|
|
return time_choices
|
|
|
|
choices = []
|
|
|
|
current_date = camp.buildup.lower.date()
|
|
while current_date != camp.teardown.upper.date():
|
|
choices.append(
|
|
(
|
|
current_date,
|
|
get_time_choices(current_date.strftime("%Y-%m-%d"))
|
|
)
|
|
)
|
|
current_date += timezone.timedelta(days=1)
|
|
return choices
|
|
|
|
|
|
class ShiftForm(forms.ModelForm):
|
|
class Meta:
|
|
model = TeamShift
|
|
fields = [
|
|
'from_datetime',
|
|
'to_datetime',
|
|
'people_required'
|
|
]
|
|
|
|
def __init__(self, instance=None, **kwargs):
|
|
camp = kwargs.pop('camp')
|
|
super().__init__(instance=instance, **kwargs)
|
|
self.fields['from_datetime'].widget = forms.Select(choices=date_choices(camp))
|
|
self.fields['to_datetime'].widget = forms.Select(choices=date_choices(camp))
|
|
if instance:
|
|
current_tz = timezone.get_current_timezone()
|
|
lower = instance.shift_range.lower.astimezone(current_tz).strftime("%Y-%m-%d %H:%M")
|
|
upper = instance.shift_range.upper.astimezone(current_tz).strftime("%Y-%m-%d %H:%M")
|
|
self.fields['from_datetime'].initial = lower
|
|
self.fields['to_datetime'].initial = upper
|
|
|
|
from_datetime = forms.DateTimeField()
|
|
to_datetime = forms.DateTimeField()
|
|
|
|
def _get_from_datetime(self):
|
|
current_timezone = timezone.get_current_timezone()
|
|
return (
|
|
self.cleaned_data['from_datetime']
|
|
.astimezone(current_timezone)
|
|
)
|
|
|
|
def _get_to_datetime(self):
|
|
current_timezone = timezone.get_current_timezone()
|
|
return (
|
|
self.cleaned_data['to_datetime']
|
|
.astimezone(current_timezone)
|
|
)
|
|
|
|
def clean(self):
|
|
self.lower = self._get_from_datetime()
|
|
self.upper = self._get_to_datetime()
|
|
if self.lower > self.upper:
|
|
raise forms.ValidationError('Start can not be after end.')
|
|
|
|
def save(self, commit=True):
|
|
# self has .lower and .upper from .clean()
|
|
self.instance.shift_range = DateTimeTZRange(self.lower, self.upper)
|
|
return super().save(commit=commit)
|
|
|
|
|
|
class ShiftCreateView(LoginRequiredMixin, CampViewMixin, CreateView):
|
|
model = TeamShift
|
|
template_name = "team_shift_form.html"
|
|
form_class = ShiftForm
|
|
active_menu = "shifts"
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['camp'] = self.camp
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
shift = form.save(commit=False)
|
|
shift.team = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse(
|
|
'teams:shifts',
|
|
kwargs=self.kwargs
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['team'] = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
return context
|
|
|
|
|
|
class ShiftUpdateView(LoginRequiredMixin, CampViewMixin, UpdateView):
|
|
model = TeamShift
|
|
template_name = "team_shift_form.html"
|
|
form_class = ShiftForm
|
|
active_menu = "shifts"
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['camp'] = self.camp
|
|
return kwargs
|
|
|
|
def get_success_url(self):
|
|
self.kwargs.pop('pk')
|
|
return reverse(
|
|
'teams:shifts',
|
|
kwargs=self.kwargs
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['team'] = self.object.team
|
|
return context
|
|
|
|
|
|
class ShiftDeleteView(LoginRequiredMixin, CampViewMixin, DeleteView):
|
|
model = TeamShift
|
|
template_name = "team_shift_confirm_delete.html"
|
|
active_menu = "shifts"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['team'] = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
self.kwargs.pop('pk')
|
|
return reverse(
|
|
'teams:shifts',
|
|
kwargs=self.kwargs
|
|
)
|
|
|
|
|
|
class MultipleShiftForm(forms.Form):
|
|
|
|
def __init__(self, instance=None, **kwargs):
|
|
camp = kwargs.pop('camp')
|
|
super().__init__(**kwargs)
|
|
self.fields['from_datetime'].widget = forms.Select(choices=date_choices(camp))
|
|
|
|
from_datetime = forms.DateTimeField()
|
|
|
|
number_of_shifts = forms.IntegerField(
|
|
help_text="How many shifts?"
|
|
)
|
|
|
|
shift_length = forms.IntegerField(
|
|
help_text="How long should a shift be in minutes?"
|
|
)
|
|
|
|
people_required = forms.IntegerField()
|
|
|
|
|
|
class ShiftCreateMultipleView(LoginRequiredMixin, CampViewMixin, FormView):
|
|
template_name = "team_shift_form.html"
|
|
form_class = MultipleShiftForm
|
|
active_menu = "shifts"
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['camp'] = self.camp
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
team = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
current_timezone = timezone.get_current_timezone()
|
|
|
|
start_datetime = (
|
|
form.cleaned_data['from_datetime']
|
|
.astimezone(current_timezone)
|
|
)
|
|
number_of_shifts = form.cleaned_data['number_of_shifts']
|
|
shift_length = form.cleaned_data['shift_length']
|
|
people_required = form.cleaned_data['people_required']
|
|
|
|
shifts = []
|
|
for index in range(number_of_shifts + 1):
|
|
shift_range = DateTimeTZRange(
|
|
start_datetime,
|
|
start_datetime + timezone.timedelta(minutes=shift_length),
|
|
)
|
|
shifts.append(TeamShift(
|
|
team=team,
|
|
people_required=people_required,
|
|
shift_range=shift_range
|
|
))
|
|
start_datetime += timezone.timedelta(minutes=shift_length)
|
|
|
|
TeamShift.objects.bulk_create(shifts)
|
|
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse(
|
|
'teams:shifts',
|
|
kwargs=self.kwargs
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['team'] = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=self.kwargs['team_slug']
|
|
)
|
|
return context
|
|
|
|
|
|
class MemberTakesShift(LoginRequiredMixin, CampViewMixin, View):
|
|
|
|
http_methods = ['get']
|
|
|
|
def get(self, request, **kwargs):
|
|
shift = TeamShift.objects.get(id=kwargs['pk'])
|
|
team = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=kwargs['team_slug']
|
|
)
|
|
|
|
team_member = TeamMember.objects.get(team=team, user=request.user)
|
|
|
|
shift.team_members.add(team_member)
|
|
|
|
kwargs.pop('pk')
|
|
|
|
return HttpResponseRedirect(
|
|
reverse(
|
|
'teams:shifts',
|
|
kwargs=kwargs
|
|
)
|
|
)
|
|
|
|
|
|
class MemberDropsShift(LoginRequiredMixin, CampViewMixin, View):
|
|
|
|
http_methods = ['get']
|
|
|
|
def get(self, request, **kwargs):
|
|
shift = TeamShift.objects.get(id=kwargs['pk'])
|
|
team = Team.objects.get(
|
|
camp=self.camp,
|
|
slug=kwargs['team_slug']
|
|
)
|
|
|
|
team_member = TeamMember.objects.get(team=team, user=request.user)
|
|
|
|
shift.team_members.remove(team_member)
|
|
|
|
kwargs.pop('pk')
|
|
|
|
return HttpResponseRedirect(
|
|
reverse(
|
|
'teams:shifts',
|
|
kwargs=kwargs
|
|
)
|
|
)
|