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

Create reservation for dog-owner user #107

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions Hotails/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@
path('homepage/', views.homepage, name='homepage'),
path('logout/', views.logout_view, name='logout'),
path('about/', views.about, name='about'),
path('visit_daycare_home/<int:daycare_id>', views.visit_daycare_home, name='visit_daycare_home'),
path('orders/', views.orders_view, name='orders'),
]
64 changes: 64 additions & 0 deletions daycare/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django import forms
from .models import DayCare, Area
from django.db.models import Max, Min
from django.core.validators import validate_email


class DateInput(forms.DateInput):
input_type = 'date'


class BookForm(forms.Form):
dog_name = forms.CharField(required=True, label='Dog guest name',
widget=forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'Enter Dog Name', 'col': 80}))
email = forms.EmailField(required=True, label='Email',
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Enter Email'}))
start_date = forms.DateField(required=True, label=' Check-in date',
widget=forms.widgets.DateInput(attrs={'class': 'form-control', 'type': 'date'}))
end_date = forms.DateField(required=True, label=' Check-out date',
widget=forms.widgets.DateInput(attrs={'class': 'form-control', 'type': 'date'}))
requests = forms.CharField(required=False, label='Special requests', widget=forms.Textarea
(attrs={'placeholder': 'Please write your requests here. Special requests cannot be guaranteed – '
'but the property will do its best to meet your needs', 'rows': 7, 'cols': 80}))
agree_terms = forms.BooleanField(required=True, label='I agree with the terms', widget=forms.CheckboxInput)

def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get("start_date")
end_date = cleaned_data.get("end_date")
email = cleaned_data.get("email")

if start_date and end_date:
if end_date < start_date:
self._errors['end_date'] = self.error_class(["End date should be greater!"])

return cleaned_data


class DayCareSearchForm(forms.Form):
min_price = DayCare.objects.all().aggregate(Min('price_per_day')).get('price_per_day__min')
max_price = DayCare.objects.all().aggregate(Max('price_per_day')).get('price_per_day__max')
start_date = forms.DateField(required=True, label=' Start date',
widget=forms.widgets.DateInput(attrs={'class': 'form-control', 'type': 'date'}))
end_date = forms.DateField(required=True, label=' End date',
widget=forms.widgets.DateInput(attrs={'class': 'form-control', 'type': 'date'}))

area = forms.ChoiceField(required=False, label='Area',
choices=(Area.choices + [('', 'All'), ]), initial="")
city = forms.CharField(required=False, label='City')
name = forms.CharField(required=False, label='Day care name')
price_per_day = forms.ChoiceField(required=False, label='Max price',
choices=((str(x), x) for x in range(min_price, max_price + 11, 10)),
initial=str(max_price))

def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get("start_date")
end_date = cleaned_data.get("end_date")

if start_date and end_date:
if end_date < start_date:
self._errors['end_date'] = self.error_class(["End date should be greater!"])

return cleaned_data
20 changes: 20 additions & 0 deletions daycare/migrations/0006_alter_daycare_area.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.0.3 on 2022-04-27 13:37

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('daycare', '0005_image_data_migration'),
]

operations = [
migrations.AlterField(
model_name='daycare',
name='area',
field=models.CharField(blank=True, choices=[('N', 'NORTH'), ('S', 'SOUTH'), ('C', 'CENTER')],
max_length=20, validators=[django.core.validators.MaxLengthValidator]),
),
]
17 changes: 14 additions & 3 deletions daycare/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
from django.core.exceptions import ObjectDoesNotExist


class Area(models.TextChoices):
North = 'N', 'NORTH'
South = 'S', 'SOUTH'
Center = 'C', 'CENTER'


class DayCare(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, default=None, blank=True, null=False, editable=True)
name = models.CharField(max_length=20, blank=True, unique=True, validators=[MaxLengthValidator])
description = models.TextField(blank=True, null=True)
price_per_day = models.IntegerField(blank=False, null=False, default=0)
capacity = models.IntegerField(null=False, blank=True)
area = models.CharField(max_length=20, blank=True, validators=[MaxLengthValidator])
area = models.CharField(max_length=20, blank=True, validators=[MaxLengthValidator], choices=Area.choices)
city = models.CharField(max_length=20, blank=True, validators=[MaxLengthValidator])
address = models.CharField(max_length=50, blank=True, validators=[MaxLengthValidator])

Expand All @@ -40,8 +46,7 @@ def create(email, username, password, name, description, price_per_day, capacity
validate_max_length(address, 50, "address")
validate_price(price_per_day)

new_daycare = DayCare(user=User.objects.create_user(email=email, username=username, password=password,
),
new_daycare = DayCare(user=User.objects.create_user(email=email, username=username, password=password),
name=name, description=description, price_per_day=price_per_day,
capacity=capacity, area=area, city=city, address=address)

Expand All @@ -50,6 +55,12 @@ def create(email, username, password, name, description, price_per_day, capacity

return new_daycare

def get_daycare_primary_image_url(self):
daycare_images = Image.get_images_by_daycare_id(daycare_id=self.id)
if daycare_images is not None and daycare_images.first() is not None:
return daycare_images.first().url
return "../../static/images/daycare-default-profile-image.jpeg"


class Image(models.Model):
url = models.CharField(max_length=1000)
Expand Down
50 changes: 48 additions & 2 deletions daycare/templates/daycare/daycare-homepage.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{% extends "main/base_template.html" %}

{% load crispy_forms_tags %}
{% block stylesheets %}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css"
integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

<style>
/* Make the image fully responsive */
.img {
Expand All @@ -23,17 +24,27 @@
<div class="col-md-7">
<hr>
<div class="mt-5 mb-5">
{% if visited_user == 'daycare' %}
<h5> <i class="fas fa-user"></i> Hello {{ daycare.name }}</h5>
<h6>This is where you can manage your daycare.
<br> You will be able:</h6>
<p class="text-muted"># Manage your booking schedule</p>
<p class="text-muted"># Set filters of approved dogs and services you provide</p>
<p class="text-muted"># Read / write reviews of dog owners and their dogs</p>
<p class="text-muted"># Chat with dog owners</p>
{% elif visited_user == 'dogowner' %}
<h5> <i class="fas fa-user"></i> Welcome to {{ daycare.name }}</h5>
<h6>This is where you can book an order for {{ daycare.name }} daycare
<br> You will be able:</h6>
<p class="text-muted"># Look at the details of the daycare</p>
<p class="text-muted"># Read / write reviews of the daycare</p>
<p class="text-muted"># Chat with the daycare owners</p>
{% endif %}
</div>
<hr>
<div class="mt-5">
<h5>Have a look at your reviews</h5>
<div style="height: 1500px;overflow: scroll;">
<table class="table table-hover table-bordered table-responsive">
<thead class="table-dark">
<tr>
Expand All @@ -56,6 +67,7 @@ <h5>Have a look at your reviews</h5>
{% endfor %}
</tbody>
</table>
</div>

</div>
</div>
Expand Down Expand Up @@ -115,11 +127,45 @@ <h5 class="card-title">{{daycare.address}}, {{daycare.city}}</h5>
<h6><i class="fas fa-dog"></i> Capacity: {{daycare.capacity}}
<br>
<br>
<i class="fas fa-coins"></i> Price: {{daycare.price_per_day}}
<i class="fas fa-coins"></i> Price Per Night: {{daycare.price_per_day}}
</h6>
<br>
<h6><strong>About</strong></h6>
<p class="card-text">{{daycare.description}}</p>
{% if visited_user == 'dogowner' %}

<form method='POST' action=''>{% csrf_token %}
<div class="col-4" id="searchBoxArea">
<h2>Book now</h2>
<div class="form-group">
{{ form.dog_name|as_crispy_field }}
</div>
<div class="form-group">
{{ form.email|as_crispy_field }}
</div>
<div class="form-group">
{{ form.start_date|as_crispy_field }}
</div>
<div class="form-group">
{{ form.end_date|as_crispy_field }}
</div>
<div class="form-group">
{{ form.requests|as_crispy_field }}
</div>
<div class="form-group">
{{ form.agree_terms|as_crispy_field }}
</div>
<button type="submit" class="btn btn-primary">Complete Booking</button>
</div>
<br>
{% if order == 'No Capacity' %}
<h6>Failed: Your order could not be submit, there is no space available on the dates specified, please try again</h6>
{% elif order != 'null' %}
<h6>Your order has been successfully received and waiting for {{ daycare.name }} to approve</h6>
<p class="text-muted">You can see the details of the reservation <a href="{% url 'orders' %}">here</a></p>
{% endif %}
</form>
{% endif %}
</div>
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions daycare/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from .models import Image
from django.core.exceptions import ValidationError

DEFAULT_DAYCARE_PROFILE_URL = "../../static/images/daycare-default-profile-image.jpeg"


@pytest.mark.django_db()
class TestImageModel:
Expand All @@ -20,3 +22,12 @@ def test_image_creation_with_invalid_image_url(self, create_daycare_user):
with pytest.raises(ValidationError,
match="Invalid URL image - URL should end with \'.gif\', \'.png\', \'.jpg\' or \'.jpeg\'."):
Image.create(url="NOT_VALID_URL", daycare_id=DayCare.objects.get(id=create_daycare_user.id))

def test_daycare_has_customized_profile_image(self, create_image1, create_image2, create_daycare_user):
daycare_profile_image = create_daycare_user.get_daycare_primary_image_url()
assert daycare_profile_image != DEFAULT_DAYCARE_PROFILE_URL
assert daycare_profile_image is not None

def test_daycare_has_default_profile_image_when_no_customized_picture_was_found(self, create_daycare_user):
daycare_profile_image = create_daycare_user.get_daycare_primary_image_url()
assert daycare_profile_image == DEFAULT_DAYCARE_PROFILE_URL
42 changes: 38 additions & 4 deletions daycare/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,49 @@
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import Image
from orders.models import Order
from review.models import Review
from daycare.models import DayCare
from daycare.forms import BookForm
from dogowner.models import DogOwner


@login_required(login_url='login')
def daycare_home(request):
def daycare_home(request, daycare_id):
daycare = DayCare.objects.get(pk=daycare_id)
form = BookForm(request.POST or None)

if DogOwner.objects.filter(user=request.user):
visited_user = 'dogowner'
else:
visited_user = 'daycare'

context = {
'daycare': request.user.daycare,
'reviews': Review.get_review_by_daycare_id(request.user.daycare.id),
'images': Image.get_images_by_daycare_id(request.user.daycare.id)
'form': form,
'daycare': daycare,
'visited_user': visited_user,
'reviews': Review.get_review_by_daycare_id(daycare.id),
'images': Image.get_images_by_daycare_id(daycare.id),
'order': 'null'
}

if request.method == 'POST':
if form.is_valid():
if Order.are_order_dates_available(daycare_id, form.cleaned_data['start_date'],
form.cleaned_data['end_date']):
new_order = Order.create(request.user.dogowner, DayCare.objects.get(pk=daycare_id),
form.cleaned_data['start_date'], form.cleaned_data['end_date'],
DayCare.objects.get(pk=daycare_id).price_per_day)
else:
new_order = 'No Capacity'

context = {
'form': form,
'daycare': daycare,
'visited_user': visited_user,
'reviews': Review.get_review_by_daycare_id(daycare.id),
'images': Image.get_images_by_daycare_id(daycare.id),
'order': new_order
}

return render(request, 'daycare/daycare-homepage.html', context)
57 changes: 57 additions & 0 deletions dogowner/templates/dogowner/dog_owner_homepage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{% extends "main/base_template.html" %}
{% load crispy_forms_tags %}
{% load static %}

{% block stylesheets %}
<link rel="stylesheet" href="{% static 'CSS/dog_owner_homepage.css' %}">
{% endblock %}

{% block content %}

<div class="row">
<form method='POST' action=''>{% csrf_token %}
<div class="col-4" id="searchBoxArea">
<span>Search for daycare:</span>
<div class="form-group">
{{ form.start_date|as_crispy_field }}
</div>
<div class="form-group">
{{ form.end_date|as_crispy_field }}
</div>
<div class="form-group">
{{ form.price_per_day|as_crispy_field }}
</div>
<div class="form-group">
{{ form.area|as_crispy_field }}
</div>
<div class="form-group">
{{ form.city|as_crispy_field }}
</div>
<div class="form-group">
{{ form.name|as_crispy_field }}
</div>
<input type="submit" value='Search' style="height:30px;width:70px">
</div>
</form>
<div class="col-10">
<span id="searchResult">Found {{ day_care_queryset.count }} results for your dog!</span>
<div class="cards">
{% for daycare in day_care_queryset %}
<div class="card">
<img src="{{ daycare.get_daycare_primary_image_url }}" alt="{{ daycare.name }} image" class="card-img-top">
<div class="card-body">
<div style="display: flex; flex-direction: row">
<h5 class="card-title">{{ daycare.name }}</h5>
</div>
<p class="card-text">{{ daycare.area | truncatechars:20 }}</p>
<p class="card-text">{{ daycare.city | truncatechars:20 }}</p>
<p class="card-text">{{ daycare.price_per_day | truncatechars:20 }}</p>
<p class="card-text">{{ daycare.description | truncatechars:35}}</p>
<a href="/visit_daycare_home/{{daycare.id}}" class="btn btn-warning">Daycare Profile</a>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
Loading