Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a way to request reservations #174

Merged
merged 5 commits into from
Oct 18, 2021
Merged
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
350 changes: 177 additions & 173 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion website/orders/templates/orders/create_shift.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% load static %}

{% block styles %}
<link rel="stylesheet" href="{% static 'orders/css/forms.css' %}"/>
<link rel="stylesheet" href="{% static 'tosti/css/forms.css' %}"/>
{% endblock %}

{% block page %}
Expand Down
10 changes: 10 additions & 0 deletions website/tosti/settings/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""

import os
from django.contrib.messages import constants as messages


SECRET_KEY = "7c^z*je^r!@aw!0*vuc1t4cp1rfi+4+xu@x5pva@xc@rf%3#lt"
Expand Down Expand Up @@ -186,3 +187,12 @@
TANTALUS_ENDPOINT_URL = "http://localhost:8080/poscl/"
TANTALUS_USERNAME = "admin"
TANTALUS_PASSWORD = "AdminAdmin"

# Messages
MESSAGE_TAGS = {
messages.DEBUG: 'alert-info',
messages.INFO: 'alert-info',
messages.SUCCESS: 'alert-success',
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-danger',
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,36 @@ form {
margin: auto;
}

p label {
form p label {
text-align: left;
width: 100%;
}

p input {
form p input {
text-align: left;
width: 100%;
}

p select {
form p select {
text-align: left;
width: 100%;
}

p span {
form p span {
font-style: italic;
font-size: 0.7rem;
width: 100%;
}

input[type=text], select {
form input[type=text], select {
padding:5px;
border:2px solid #ccc;
}

input[type=text]:focus {
form input[type=text]:focus {
border-color:#333;
}

form textarea {
width: 100%;
}
4 changes: 2 additions & 2 deletions website/venues/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class ReservationAdminUserFilter(AutocompleteFilter):
class ReservationAdmin(admin.ModelAdmin):
"""Custom admin for reservations."""

list_display = ["title", "venue", "association", "start_time", "end_time", "user"]
list_filter = ["venue", "association", "start_time", ReservationAdminUserFilter]
list_display = ["title", "venue", "association", "start_time", "end_time", "user", "accepted"]
list_filter = ["venue", "association", "start_time", ReservationAdminUserFilter, "accepted"]
search_fields = ["title"]
date_hierarchy = "start_time"
form = ReservationAdminForm
Expand Down
4 changes: 2 additions & 2 deletions website/venues/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class VenueReservationListAPIView(ListAPIView):
"""

serializer_class = ReservationSerializer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the unaccepted reservations should be shown in a opaque color

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will experiment with that

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You decided not to do it?

queryset = Reservation.objects.all()
queryset = Reservation.objects.filter(accepted=True)

def get_queryset(self):
"""Get queryset."""
Expand Down Expand Up @@ -74,7 +74,7 @@ class ReservationListAPIView(ListAPIView):
"""

serializer_class = ReservationSerializer
queryset = Reservation.objects.all()
queryset = Reservation.objects.filter(accepted=True)

def get_queryset(self):
"""Get queryset."""
Expand Down
47 changes: 47 additions & 0 deletions website/venues/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils import timezone

from .models import Reservation
from .models import Venue


class ReservationForm(forms.ModelForm):
KiOui marked this conversation as resolved.
Show resolved Hide resolved
"""Reservation Form."""

def __init__(self, *args, **kwargs):
"""Initialise Reservation Form."""
request = kwargs.pop("request", None)
super(ReservationForm, self).__init__(*args, **kwargs)
self.fields["venue"].queryset = Venue.objects.filter(can_be_reserved=True)
if request is not None and request.user.is_authenticated and request.user.profile.association is not None:
self.fields["association"].initial = request.user.profile.association

def clean(self):
"""
Clean data.

Check whether there is no overlapping Reservation.
"""
if self.cleaned_data.get("start_time") is not None and self.cleaned_data.get("end_time") is not None:
start_time = self.cleaned_data.get("start_time").astimezone(timezone.get_current_timezone())
end_time = self.cleaned_data.get("end_time").astimezone(timezone.get_current_timezone())

if (
Reservation.objects.filter(venue=self.cleaned_data.get("venue"))
.filter(accepted=True)
.filter(
Q(start_time__lte=start_time, end_time__gt=start_time)
| Q(start_time__lt=end_time, end_time__gte=end_time)
| Q(start_time__gte=start_time, end_time__lte=end_time)
)
.exists()
):
raise ValidationError("An overlapping reservation for this venue already exists.")

class Meta:
"""Meta class."""

model = Reservation
fields = ["venue", "association", "start_time", "end_time", "title", "comment"]
18 changes: 18 additions & 0 deletions website/venues/migrations/0003_reservation_accepted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.8 on 2021-10-15 09:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('venues', '0002_auto_20211012_2042'),
]

operations = [
migrations.AddField(
model_name='reservation',
name='accepted',
field=models.BooleanField(blank=True, default=None, null=True),
),
]
1 change: 1 addition & 0 deletions website/venues/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Reservation(models.Model):
end_time = models.DateTimeField()
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name="reservations")
comment = models.TextField(null=True, blank=True)
accepted = models.BooleanField(default=None, null=True, blank=True)

def clean(self):
"""Clean model."""
Expand Down
19 changes: 19 additions & 0 deletions website/venues/templates/venues/calendar.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,23 @@ <h1>Reservations</h1>
<div class="container">
{% render_calendar %}
</div>
<div class="container pb-5 pt-5"></div>
{% endblock %}

{% block footer %}
{% if request.user.is_authenticated %}
<footer class="page-footer navbar navbar-expand-md fixed-bottom">
<div class="container text-center">
<div class="row flex-grow-1">
<div class="col">
<a href="{% url 'venues:add_reservation' %}">
<div class="btn-ml m-auto cursor-pointer btn-on">
<p class="font-footer"><i class="far fa-calendar-plus"></i> Request Reservation</p>
</div>
</a>
</div>
</div>
</div>
</footer>
{% endif %}
{% endblock %}
30 changes: 30 additions & 0 deletions website/venues/templates/venues/reservation_request.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% extends 'tosti/base.html' %}
{% load static %}

{% block styles %}
{{ block.super }}
<link href='{% static "tosti/css/forms.css" %}' rel='stylesheet' />
{% endblock %}

{% block page %}
<div class="container text-left">
<a href="{% url 'venues:calendar' %}"><i class="fas fa-arrow-left"></i> back to calendar</a>
</div>
<div class="container mt-5 mb-5 text-center">
<h1>Request a Reservation</h1>
</div>
<div class="container mt-5 mb-5 text-center">
{% if messages %}
KiOui marked this conversation as resolved.
Show resolved Hide resolved
{% for message in messages %}
<p class="alert {{ message.tags }}">{{ message }}</p>
{% endfor %}
{% endif %}
</div>
<div class="container-sm text-center mb-5">
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" class="btn-ml btn-on">
</form>
</div>
{% endblock %}
5 changes: 3 additions & 2 deletions website/venues/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.urls import path

from venues.views import VenueCalendarView
from venues import views

urlpatterns = [
path("calendar/", VenueCalendarView.as_view(), name="calendar"),
path("calendar/", views.VenueCalendarView.as_view(), name="calendar"),
KiOui marked this conversation as resolved.
Show resolved Hide resolved
path("calendar/add-reservation/", views.RequestReservationView.as_view(), name="add_reservation"),
]
38 changes: 37 additions & 1 deletion website/venues/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
from django.views.generic import TemplateView
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import TemplateView, FormView

from venues.forms import ReservationForm


class VenueCalendarView(TemplateView):
"""All venues calendar view."""

template_name = "venues/calendar.html"


class RequestReservationView(LoginRequiredMixin, FormView):
"""Request Reservation view."""

template_name = "venues/reservation_request.html"
form_class = ReservationForm

def get_form_kwargs(self):
"""Get the kwargs for rendering the form."""
kwargs = super(RequestReservationView, self).get_form_kwargs()
kwargs.update(
{
"request": self.request,
}
)
return kwargs

def form_valid(self, form):
"""Save the form and add User data."""
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
messages.success(self.request, "Reservation request added successfully.")
return redirect(reverse("venues:add_reservation"))

def get_context_data(self, **kwargs):
"""Get the context data for rendering the template."""
context = super(RequestReservationView, self).get_context_data(**kwargs)
return context