Skip to content

Commit

Permalink
Add search day-care funconallity to dog-owner homepage.
Browse files Browse the repository at this point in the history
The dog-owner user can search for day-care that relevant for him.
If the user didnt search for day-care he will see all Day-Cares in the application.

Signed-off-by: tamirmatok <[email protected]>
  • Loading branch information
tamirmatok committed May 18, 2022
1 parent de1deb7 commit 97dd0a4
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 41 deletions.
25 changes: 25 additions & 0 deletions daycare/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django import forms
from .models import Area


class DayCareSearchForm(forms.Form):
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.IntegerField(required=False, label='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
63 changes: 47 additions & 16 deletions dogowner/templates/dogowner/dog_owner_homepage.html
Original file line number Diff line number Diff line change
@@ -1,26 +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 row-cols-1 g-3 cards">
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
{% for daycare in daycares %}
<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 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="/daycare/{{ daycare.id }}" class="btn btn-warning">Daycare Profile</a>
</div>
</div>
{% endfor %}
</div>
</div>
<p class="card-text">{{ daycare.description | truncatechars:250 }}</p>
<a href="/daycare/{{ daycare.id }}" class="btn btn-primary">Daycare Profile</a>
</div>
</div>
{% endfor %}
</div>
</div>

{% endblock %}
{% endblock %}
18 changes: 17 additions & 1 deletion dogowner/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from daycare.models import DayCare
from daycare.forms import DayCareSearchForm
from orders.models import Order


@login_required()
def dog_owner_home(request):
form = DayCareSearchForm(request.POST or None)
day_care_queryset = DayCare.objects.all()
if request.method == 'POST':
if form.is_valid():
filter_day_cares = DayCare.objects.filter(area__startswith=form['area'].value(),
city__icontains=form['city'].value(),
name__icontains=form['name'].value(),
price_per_day__lte=form['price_per_day'].value())

available_day_cares = Order.get_all_day_cares_available_on_dates(form['start_date'].value(),
form['end_date'].value())
day_care_queryset = filter_day_cares.intersection(available_day_cares)

context = {
'daycares': DayCare.objects.all(),
'day_care_queryset': day_care_queryset,
'form': form
}
return render(request, 'dogowner/dog_owner_homepage.html', context)
1 change: 0 additions & 1 deletion main/templates/main/base_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
{% if request.user.is_authenticated %}
<li class="nav-item"><a class="nav-link text-white" href="/profile">Profile</a></li>
<li class="nav-item"><a class="nav-link text-white" href="">Orders</a></li>
<li class="nav-item"><a class="nav-link text-white" href="">Search</a></li>
<li class="nav-item"><a class="nav-link text-white" href="">Chats</a></li>
{% endif %}
<li class="nav-item"><a class="nav-link text-white" href="/about">About</a></li>
Expand Down
64 changes: 62 additions & 2 deletions main/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pytest
from dogowner.models import DogOwner
from daycare.models import DayCare
from orders.models import Order


@pytest.mark.django_db
Expand Down Expand Up @@ -72,8 +74,66 @@ def test_unlogged_user_access_to_homepage(self, client):
assert response.status_code == 302
assert response['Location'] == '/login/?next=/homepage/'

def test_dog_owner_homepage_is_visible_for_dog_owner(self, client, create_dog_owner_user):

@pytest.mark.django_db
class TestDogOwnerHomePageView:
def test_dog_owner_homepage_present_all_daycares(self, client, create_dog_owner_user):
client.force_login(user=create_dog_owner_user.user)
response = client.get("/homepage/")
assert response.status_code == 200
assert list(response.context['daycares']) == list(DayCare.objects.all())
assert set(response.context['day_care_queryset']) == set(DayCare.objects.all())

def test_daycare_not_appears_in_search_after_reduce_capacity_on_dates_to_0(self, client,
create_dog_owner_user,
create_daycare_user):
search_form = {'area': "",
'city': "",
'price_per_day': 100,
'name': "",
'start_date': "2022-05-03",
'end_date': "2022-05-08",
}
daycare_user = create_daycare_user
client.force_login(user=create_dog_owner_user.user)
response = client.post('/homepage/', search_form, follow=True)
day_care_queryset = response.context['day_care_queryset']
assert daycare_user in day_care_queryset

for _ in range(daycare_user.capacity):
Order.create(dog_owner_id=DogOwner.objects.get(id=1), daycare_id=daycare_user,
start_date="2022-05-02", end_date="2022-08-02", price_per_day=100).approve_order()
response = client.post('/homepage/', search_form, follow=True)
assert daycare_user not in response.context['day_care_queryset']

def test_successful_dog_owner_search_for_day_care(self, client, create_dog_owner_user):
client.force_login(user=create_dog_owner_user.user)
search_form = {'area': "C",
'city': "tel aviv",
'price_per_day': 800,
'name': "",
'start_date': "2022-05-03",
'end_date': "2022-05-08",
}
response = client.post('/homepage/', search_form, follow=True)
day_care_queryset = response.context['day_care_queryset']
available_day_cares = Order.get_all_day_cares_available_on_dates("2022-05-03", "2022-05-08")
filter_day_cares = DayCare.objects.filter(area__startswith='C',
city__icontains="tel aviv",
price_per_day__lte=800)
expected_queryset = available_day_cares.intersection(filter_day_cares)
assert set(day_care_queryset) == set(expected_queryset)

def test_search_for_day_care_with_start_date_greater_than_end_date_show_error_and_present_all_daycares(
self, client, create_dog_owner_user):
client.force_login(user=create_dog_owner_user.user)
search_form = {'area': "C",
'city': "tel aviv",
'price_per_day': 800,
'name': "",
'start_date': "2022-05-03",
'end_date': "2022-05-01",
}
response = client.post('/homepage/', search_form, follow=True)
day_care_queryset = response.context['day_care_queryset']
assert set(day_care_queryset) == set(DayCare.objects.all())
assert response.context['form']._errors['end_date']
61 changes: 40 additions & 21 deletions static/CSS/dog_owner_homepage.css
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
.cards
{
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-left: 50px;
justify-content: center;
align-items: center;
float:left;
margin: 0 0 0 5em;
position:relative;
width:100%;
}

.row
{
height: 30rem;
width: 120rem;
margin-left: 200px;
#searchResult {
font-weight:bold;
position: relative;
left: 6.3em;
top:30px;
}

.card
{
width: 18.5%;
width: 330px;
height: 30rem;
margin: 60px 60px 0 60px;
max-width: 100%;
}

.card-body
{
display: flex;
flex-direction: column;
float:left;
margin: 60px 20px 0 20px;
}

.card-body .btn
Expand All @@ -38,4 +30,31 @@
.card-img-top
{
height: 200px;
}
}

#searchBoxArea {
background-color:#dcdcdc;
left:5em;
top: 5.2em;
max-width:100%;
padding: 2em 2em 2em 2em;
}

#searchBoxArea span {
font-weight:bold;
font-size:18px;
color:#3e3e3e;
padding: 5px 0 1em 0;
float:left;
}

#searchBoxArea input[type="submit"] {
background-color:#3e3e3e;
border: 0;
border-radius:12px;
color:#FFF;
}

#searchBoxArea input[type="submit"]:hover {
background-color:#1e1e1e;
}

0 comments on commit 97dd0a4

Please sign in to comment.