Sith/launderette/views.py

511 lines
18 KiB
Python
Raw Normal View History

#
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# You can find the source code of the website at https://github.com/ae-utbm/sith
#
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
2024-09-23 08:25:27 +00:00
# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
2016-07-28 23:38:46 +00:00
from collections import OrderedDict
2024-06-24 11:07:36 +00:00
from datetime import datetime, timedelta
2024-07-18 15:33:14 +00:00
from datetime import timezone as tz
2016-07-28 23:38:46 +00:00
from django import forms
2024-06-24 11:07:36 +00:00
from django.conf import settings
from django.db import transaction
from django.template import defaultfilters
2024-06-24 11:07:36 +00:00
from django.urls import reverse_lazy
from django.utils import dateparse, timezone
from django.utils.translation import gettext as _
from django.views.generic import DetailView, ListView, TemplateView
from django.views.generic.edit import BaseFormView, CreateView, DeleteView, UpdateView
2016-07-28 18:05:56 +00:00
from club.models import Club
2024-06-24 11:07:36 +00:00
from core.models import Page, User
from core.views import CanCreateMixin, CanEditMixin, CanEditPropMixin, CanViewMixin
from counter.forms import GetUserForm
2024-06-24 11:07:36 +00:00
from counter.models import Counter, Customer, Selling
from launderette.models import Launderette, Machine, Slot, Token
2016-07-28 18:05:56 +00:00
# For users
2017-06-12 07:59:44 +00:00
2016-07-28 18:05:56 +00:00
class LaunderetteMainView(TemplateView):
2024-07-12 07:34:16 +00:00
"""Main presentation view."""
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_main.jinja"
2016-07-28 18:05:56 +00:00
def get_context_data(self, **kwargs):
2024-07-12 07:34:16 +00:00
"""Add page to the context."""
2024-06-27 12:46:43 +00:00
kwargs = super().get_context_data(**kwargs)
2018-10-04 19:29:19 +00:00
kwargs["page"] = Page.objects.filter(name="launderette").first()
2016-07-28 18:05:56 +00:00
return kwargs
2017-06-12 07:59:44 +00:00
2016-07-28 18:05:56 +00:00
class LaunderetteBookMainView(CanViewMixin, ListView):
2024-07-12 07:34:16 +00:00
"""Choose which launderette to book."""
2018-10-04 19:29:19 +00:00
2016-07-28 18:05:56 +00:00
model = Launderette
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_book_choose.jinja"
2016-07-28 18:05:56 +00:00
2017-06-12 07:59:44 +00:00
class LaunderetteBookView(CanViewMixin, DetailView):
2024-07-12 07:34:16 +00:00
"""Display the launderette schedule."""
2018-10-04 19:29:19 +00:00
2016-07-28 18:05:56 +00:00
model = Launderette
pk_url_kwarg = "launderette_id"
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_book.jinja"
2016-07-28 18:05:56 +00:00
def get(self, request, *args, **kwargs):
self.slot_type = "BOTH"
self.machines = {}
2024-06-27 12:46:43 +00:00
return super().get(request, *args, **kwargs)
2016-07-28 23:38:46 +00:00
def post(self, request, *args, **kwargs):
self.slot_type = "BOTH"
self.machines = {}
with transaction.atomic():
self.object = self.get_object()
if "slot_type" in request.POST:
2018-10-04 19:29:19 +00:00
self.slot_type = request.POST["slot_type"]
if "slot" in request.POST and request.user.is_authenticated:
2016-12-10 00:58:30 +00:00
self.subscriber = request.user
if self.subscriber.is_subscribed:
2018-10-04 19:29:19 +00:00
self.date = dateparse.parse_datetime(request.POST["slot"]).replace(
2024-07-18 15:33:14 +00:00
tzinfo=tz.utc
2018-10-04 19:29:19 +00:00
)
if self.slot_type in ["WASHING", "DRYING"]:
if self.check_slot(self.slot_type):
2018-10-04 19:29:19 +00:00
Slot(
user=self.subscriber,
start_date=self.date,
machine=self.machines[self.slot_type],
type=self.slot_type,
).save()
elif self.check_slot("WASHING") and self.check_slot(
"DRYING", self.date + timedelta(hours=1)
):
Slot(
user=self.subscriber,
start_date=self.date,
machine=self.machines["WASHING"],
type="WASHING",
).save()
Slot(
user=self.subscriber,
start_date=self.date + timedelta(hours=1),
machine=self.machines["DRYING"],
type="DRYING",
).save()
2024-06-27 12:46:43 +00:00
return super().get(request, *args, **kwargs)
2016-07-28 23:38:46 +00:00
2024-06-27 12:30:58 +00:00
def check_slot(self, machine_type, date=None):
2017-06-12 07:59:44 +00:00
if date is None:
date = self.date
2024-06-27 12:30:58 +00:00
for m in self.object.machines.filter(is_working=True, type=machine_type):
slot = Slot.objects.filter(start_date=date, machine=m).first()
if slot is None:
2024-06-27 12:30:58 +00:00
self.machines[machine_type] = m
return True
return False
2016-07-28 23:38:46 +00:00
@staticmethod
def date_iterator(startDate, endDate, delta=timedelta(days=1)):
currentDate = startDate
while currentDate < endDate:
yield currentDate
currentDate += delta
def get_context_data(self, **kwargs):
2024-07-12 07:34:16 +00:00
"""Add page to the context."""
2024-06-27 12:46:43 +00:00
kwargs = super().get_context_data(**kwargs)
2018-10-04 19:29:19 +00:00
kwargs["planning"] = OrderedDict()
kwargs["slot_type"] = self.slot_type
start_date = datetime.now().replace(
2024-07-18 15:33:14 +00:00
hour=0, minute=0, second=0, microsecond=0, tzinfo=tz.utc
2018-10-04 19:29:19 +00:00
)
for date in LaunderetteBookView.date_iterator(
start_date, start_date + timedelta(days=6), timedelta(days=1)
):
kwargs["planning"][date] = []
for h in LaunderetteBookView.date_iterator(
date, date + timedelta(days=1), timedelta(hours=1)
):
2016-07-28 23:38:46 +00:00
free = False
2018-10-04 19:29:19 +00:00
if (
(
self.slot_type == "BOTH"
and self.check_slot("WASHING", h)
and self.check_slot("DRYING", h + timedelta(hours=1))
)
2024-12-08 10:17:27 +00:00
or (self.slot_type == "WASHING" and self.check_slot("WASHING", h))
or (self.slot_type == "DRYING" and self.check_slot("DRYING", h))
2018-10-04 19:29:19 +00:00
):
free = True
2024-07-18 15:33:14 +00:00
if free and datetime.now().replace(tzinfo=tz.utc) < h:
2018-10-04 19:29:19 +00:00
kwargs["planning"][date].append(h)
2016-07-28 23:38:46 +00:00
else:
2018-10-04 19:29:19 +00:00
kwargs["planning"][date].append(None)
2016-07-28 23:38:46 +00:00
return kwargs
2016-07-28 18:05:56 +00:00
2017-06-12 07:59:44 +00:00
2016-12-15 15:11:43 +00:00
class SlotDeleteView(CanEditPropMixin, DeleteView):
2024-07-12 07:34:16 +00:00
"""Delete a slot."""
2018-10-04 19:29:19 +00:00
2016-12-15 15:11:43 +00:00
model = Slot
pk_url_kwarg = "slot_id"
2018-10-04 19:29:19 +00:00
template_name = "core/delete_confirm.jinja"
2016-12-15 15:11:43 +00:00
def get_success_url(self):
return self.request.user.get_absolute_url()
2016-07-28 18:05:56 +00:00
# For admins
2018-10-04 19:29:19 +00:00
class LaunderetteListView(CanEditPropMixin, ListView):
2024-07-12 07:34:16 +00:00
"""Choose which launderette to administer."""
2018-10-04 19:29:19 +00:00
2016-07-28 18:05:56 +00:00
model = Launderette
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_list.jinja"
2016-07-28 18:05:56 +00:00
2017-06-12 07:59:44 +00:00
class LaunderetteEditView(CanEditPropMixin, UpdateView):
2024-07-12 07:34:16 +00:00
"""Edit a launderette."""
2018-10-04 19:29:19 +00:00
2016-07-28 18:05:56 +00:00
model = Launderette
pk_url_kwarg = "launderette_id"
2018-10-04 19:29:19 +00:00
fields = ["name"]
template_name = "core/edit.jinja"
2016-07-28 18:05:56 +00:00
2017-06-12 07:59:44 +00:00
2016-07-28 18:05:56 +00:00
class LaunderetteCreateView(CanCreateMixin, CreateView):
2024-07-12 07:34:16 +00:00
"""Create a new launderette."""
2018-10-04 19:29:19 +00:00
2016-07-28 18:05:56 +00:00
model = Launderette
2018-10-04 19:29:19 +00:00
fields = ["name"]
template_name = "core/create.jinja"
def form_valid(self, form):
2018-10-04 19:29:19 +00:00
club = Club.objects.filter(
unix_name=settings.SITH_LAUNDERETTE_MANAGER["unix_name"]
).first()
c = Counter(name=form.instance.name, club=club, type="OFFICE")
c.save()
form.instance.counter = c
2024-06-27 12:46:43 +00:00
return super().form_valid(form)
2017-06-12 07:59:44 +00:00
2016-08-06 13:20:38 +00:00
class ManageTokenForm(forms.Form):
2018-10-04 19:29:19 +00:00
action = forms.ChoiceField(
choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))],
initial="BACK",
label=_("Action"),
widget=forms.RadioSelect,
)
token_type = forms.ChoiceField(
choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES,
label=_("Type"),
initial="WASHING",
widget=forms.RadioSelect,
)
tokens = forms.CharField(
max_length=512,
widget=forms.widgets.Textarea,
label=_("Tokens, separated by spaces"),
)
2016-08-06 13:20:38 +00:00
def process(self, launderette):
cleaned_data = self.cleaned_data
2018-10-04 19:29:19 +00:00
token_list = cleaned_data["tokens"].strip(" \n\r").split(" ")
token_type = cleaned_data["token_type"]
2016-08-06 13:20:38 +00:00
self.data = {}
if cleaned_data["action"] not in ["BACK", "ADD", "DEL"]:
return
tokens = list(
Token.objects.filter(
launderette=launderette, type=token_type, name__in=token_list
)
)
existing_names = {t.name for t in tokens}
if cleaned_data["action"] in ["BACK", "DEL"]:
for t in set(token_list) - existing_names:
self.add_error(
None,
_("Token %(token_name)s does not exists") % {"token_name": t},
)
2018-10-04 19:29:19 +00:00
if cleaned_data["action"] == "BACK":
Token.objects.filter(id__in=[t.id for t in tokens]).update(
borrow_date=None, user=None
)
elif cleaned_data["action"] == "DEL":
Token.objects.filter(id__in=[t.id for t in tokens]).delete()
2018-10-04 19:29:19 +00:00
elif cleaned_data["action"] == "ADD":
for name in existing_names:
self.add_error(
None,
_("Token %(token_name)s already exists") % {"token_name": name},
)
2016-08-06 13:20:38 +00:00
for t in token_list:
if t == "":
self.add_error(None, _("Token name can not be blank"))
else:
2016-08-06 13:20:38 +00:00
Token(launderette=launderette, type=token_type, name=t).save()
2017-06-12 07:59:44 +00:00
2016-08-06 13:20:38 +00:00
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
2024-07-12 07:34:16 +00:00
"""The admin page of the launderette."""
2018-10-04 19:29:19 +00:00
model = Launderette
pk_url_kwarg = "launderette_id"
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_admin.jinja"
2016-08-06 13:20:38 +00:00
form_class = ManageTokenForm
def get(self, request, *args, **kwargs):
self.object = self.get_object()
2024-06-27 12:46:43 +00:00
return super().get(request, *args, **kwargs)
2016-08-06 13:20:38 +00:00
def post(self, request, *args, **kwargs):
self.object = self.get_object()
2024-06-27 12:46:43 +00:00
return super().post(request, *args, **kwargs)
2016-08-06 13:20:38 +00:00
def form_valid(self, form):
2024-07-12 07:34:16 +00:00
"""We handle here the redirection, passing the user id of the asked customer."""
2016-08-06 13:20:38 +00:00
form.process(self.object)
if form.is_valid():
2024-06-27 12:46:43 +00:00
return super().form_valid(form)
2016-08-06 13:20:38 +00:00
else:
2024-06-27 12:46:43 +00:00
return super().form_invalid(form)
2016-08-06 13:20:38 +00:00
def get_context_data(self, **kwargs):
2024-07-12 07:34:16 +00:00
"""We handle here the login form for the barman."""
2024-06-27 12:46:43 +00:00
kwargs = super().get_context_data(**kwargs)
2016-08-06 13:20:38 +00:00
if self.request.method == "GET":
2018-10-04 19:29:19 +00:00
kwargs["form"] = self.get_form()
2016-08-06 13:20:38 +00:00
return kwargs
def get_success_url(self):
2018-10-04 19:29:19 +00:00
return reverse_lazy(
"launderette:launderette_admin", args=self.args, kwargs=self.kwargs
)
2017-06-12 07:59:44 +00:00
2016-08-01 22:32:55 +00:00
class GetLaunderetteUserForm(GetUserForm):
def clean(self):
2024-06-27 12:46:43 +00:00
cleaned_data = super().clean()
2018-10-04 19:29:19 +00:00
sub = cleaned_data["user"]
2016-08-01 22:32:55 +00:00
if sub.slots.all().count() <= 0:
raise forms.ValidationError(_("User has booked no slot"))
return cleaned_data
2017-06-12 07:59:44 +00:00
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
2024-07-12 07:34:16 +00:00
"""The click page of the launderette."""
2018-10-04 19:29:19 +00:00
model = Launderette
pk_url_kwarg = "launderette_id"
2018-10-04 19:29:19 +00:00
template_name = "counter/counter_main.jinja"
2019-11-04 12:46:09 +00:00
form_class = GetLaunderetteUserForm # Form to enter a client code and get the corresponding user id
2016-08-01 22:32:55 +00:00
def get(self, request, *args, **kwargs):
self.object = self.get_object()
2024-06-27 12:46:43 +00:00
return super().get(request, *args, **kwargs)
2016-08-01 22:32:55 +00:00
def post(self, request, *args, **kwargs):
self.object = self.get_object()
2024-06-27 12:46:43 +00:00
return super().post(request, *args, **kwargs)
2016-08-01 22:32:55 +00:00
def form_valid(self, form):
2024-07-12 07:34:16 +00:00
"""We handle here the redirection, passing the user id of the asked customer."""
2018-10-04 19:29:19 +00:00
self.kwargs["user_id"] = form.cleaned_data["user_id"]
2024-06-27 12:46:43 +00:00
return super().form_valid(form)
def get_context_data(self, **kwargs):
2024-07-12 07:34:16 +00:00
"""We handle here the login form for the barman."""
2024-06-27 12:46:43 +00:00
kwargs = super().get_context_data(**kwargs)
2018-10-04 19:29:19 +00:00
kwargs["counter"] = self.object.counter
kwargs["form"] = self.get_form()
kwargs["barmen"] = [self.request.user]
if "last_basket" in self.request.session:
2018-10-04 19:29:19 +00:00
kwargs["last_basket"] = self.request.session.pop("last_basket", None)
kwargs["last_customer"] = self.request.session.pop("last_customer", None)
kwargs["last_total"] = self.request.session.pop("last_total", None)
kwargs["new_customer_amount"] = self.request.session.pop(
"new_customer_amount", None
)
return kwargs
def get_success_url(self):
2018-10-04 19:29:19 +00:00
return reverse_lazy("launderette:click", args=self.args, kwargs=self.kwargs)
2017-06-12 07:59:44 +00:00
class ClickTokenForm(forms.BaseForm):
2016-08-01 22:32:55 +00:00
def clean(self):
with transaction.atomic():
2016-12-10 00:58:30 +00:00
operator = User.objects.filter(id=self.operator_id).first()
2016-08-01 22:32:55 +00:00
customer = Customer.objects.filter(user__id=self.subscriber_id).first()
counter = Counter.objects.filter(id=self.counter_id).first()
2016-12-10 00:58:30 +00:00
subscriber = customer.user
2016-08-01 22:32:55 +00:00
self.last_basket = {
2018-10-04 19:29:19 +00:00
"last_basket": [],
"last_customer": customer.user.get_display_name(),
2017-06-12 07:59:44 +00:00
}
2016-08-01 22:32:55 +00:00
total = 0
2017-06-12 07:59:44 +00:00
for k, t in self.cleaned_data.items():
2016-08-01 22:32:55 +00:00
if t is not None:
slot_id = int(k[5:])
slot = Slot.objects.filter(id=slot_id).first()
slot.token = t
slot.save()
t.user = subscriber
2024-07-18 15:33:14 +00:00
t.borrow_date = datetime.now().replace(tzinfo=tz.utc)
2016-08-01 22:32:55 +00:00
t.save()
price = settings.SITH_LAUNDERETTE_PRICES[t.type]
2018-10-04 19:29:19 +00:00
s = Selling(
label="Jeton " + t.get_type_display() + "" + t.name,
club=counter.club,
product=None,
counter=counter,
unit_price=price,
quantity=1,
seller=operator,
customer=customer,
)
2016-08-01 22:32:55 +00:00
s.save()
total += price
2018-10-04 19:29:19 +00:00
self.last_basket["last_basket"].append(
"Jeton " + t.get_type_display() + "" + t.name
)
self.last_basket["new_customer_amount"] = str(customer.amount)
self.last_basket["last_total"] = str(total)
2016-08-01 22:32:55 +00:00
return self.cleaned_data
2017-06-12 07:59:44 +00:00
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
2024-07-12 07:34:16 +00:00
"""The click page of the launderette."""
2018-10-04 19:29:19 +00:00
model = Launderette
pk_url_kwarg = "launderette_id"
2018-10-04 19:29:19 +00:00
template_name = "launderette/launderette_click.jinja"
def get_form_class(self):
fields = OrderedDict()
kwargs = {}
2018-10-04 19:29:19 +00:00
def clean_field_factory(field_name, slot):
def clean_field(self2):
t_name = str(self2.data[field_name])
if t_name != "":
2018-10-04 19:29:19 +00:00
t = Token.objects.filter(
name=str(self2.data[field_name]),
type=slot.type,
launderette=self.object,
user=None,
).first()
if t is None:
raise forms.ValidationError(_("Token not found"))
2016-08-01 22:32:55 +00:00
return t
2018-10-04 19:29:19 +00:00
return clean_field
2018-10-04 19:29:19 +00:00
for s in self.subscriber.slots.filter(
token=None, start_date__gte=timezone.now().replace(tzinfo=None)
).all():
field_name = "slot-%s" % (str(s.id))
2018-10-04 19:29:19 +00:00
fields[field_name] = forms.CharField(
max_length=5,
required=False,
label="%s - %s"
% (
s.get_type_display(),
defaultfilters.date(s.start_date, "j N Y H:i"),
),
)
2016-08-01 22:32:55 +00:00
# XXX l10n settings.DATETIME_FORMAT didn't work here :/
2017-06-12 07:59:44 +00:00
kwargs["clean_" + field_name] = clean_field_factory(field_name, s)
2018-10-04 19:29:19 +00:00
kwargs["subscriber_id"] = self.subscriber.id
kwargs["counter_id"] = self.object.counter.id
kwargs["operator_id"] = self.operator.id
kwargs["base_fields"] = fields
return type("ClickForm", (ClickTokenForm,), kwargs)
def get(self, request, *args, **kwargs):
2024-07-12 07:34:16 +00:00
"""Simple get view."""
2018-10-04 19:29:19 +00:00
self.customer = Customer.objects.filter(user__id=self.kwargs["user_id"]).first()
2016-12-10 00:58:30 +00:00
self.subscriber = self.customer.user
2016-08-01 22:32:55 +00:00
self.operator = request.user
2024-06-27 12:46:43 +00:00
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
2024-07-12 07:34:16 +00:00
"""Handle the many possibilities of the post request."""
self.object = self.get_object()
2018-10-04 19:29:19 +00:00
self.customer = Customer.objects.filter(user__id=self.kwargs["user_id"]).first()
2016-12-10 00:58:30 +00:00
self.subscriber = self.customer.user
2016-08-01 22:32:55 +00:00
self.operator = request.user
2024-06-27 12:46:43 +00:00
return super().post(request, *args, **kwargs)
def form_valid(self, form):
2024-07-12 07:34:16 +00:00
"""We handle here the redirection, passing the user id of the asked customer."""
2016-08-01 22:32:55 +00:00
self.request.session.update(form.last_basket)
2024-06-27 12:46:43 +00:00
return super().form_valid(form)
def get_context_data(self, **kwargs):
2024-07-12 07:34:16 +00:00
"""We handle here the login form for the barman."""
2024-06-27 12:46:43 +00:00
kwargs = super().get_context_data(**kwargs)
if "form" not in kwargs:
2018-10-04 19:29:19 +00:00
kwargs["form"] = self.get_form()
kwargs["counter"] = self.object.counter
kwargs["customer"] = self.customer
return kwargs
def get_success_url(self):
2018-10-04 19:29:19 +00:00
self.kwargs.pop("user_id", None)
return reverse_lazy(
"launderette:main_click", args=self.args, kwargs=self.kwargs
)
class MachineEditView(CanEditPropMixin, UpdateView):
2024-07-12 07:34:16 +00:00
"""Edit a machine."""
2018-10-04 19:29:19 +00:00
model = Machine
pk_url_kwarg = "machine_id"
2018-10-04 19:29:19 +00:00
fields = ["name", "launderette", "type", "is_working"]
template_name = "core/edit.jinja"
2017-06-12 07:59:44 +00:00
class MachineDeleteView(CanEditPropMixin, DeleteView):
2024-07-12 07:34:16 +00:00
"""Edit a machine."""
2018-10-04 19:29:19 +00:00
model = Machine
pk_url_kwarg = "machine_id"
2018-10-04 19:29:19 +00:00
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("launderette:launderette_list")
2017-06-12 07:59:44 +00:00
class MachineCreateView(CanCreateMixin, CreateView):
2024-07-12 07:34:16 +00:00
"""Create a new machine."""
2018-10-04 19:29:19 +00:00
model = Machine
2018-10-04 19:29:19 +00:00
fields = ["name", "launderette", "type"]
template_name = "core/create.jinja"
def get_initial(self):
2024-06-27 12:46:43 +00:00
ret = super().get_initial()
if "launderette" in self.request.GET:
2018-10-04 19:29:19 +00:00
obj = Launderette.objects.filter(
id=int(self.request.GET["launderette"])
).first()
if obj is not None:
2018-10-04 19:29:19 +00:00
ret["launderette"] = obj.id
return ret