diff --git a/daycare/forms.py b/daycare/forms.py new file mode 100644 index 0000000..f6e6067 --- /dev/null +++ b/daycare/forms.py @@ -0,0 +1,31 @@ +from django import forms +from .models import DayCare, Area +from django.db.models import Max, Min + + +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 diff --git a/dogowner/templates/dogowner/dog_owner_homepage.html b/dogowner/templates/dogowner/dog_owner_homepage.html index 2baf69f..cb6ff3d 100644 --- a/dogowner/templates/dogowner/dog_owner_homepage.html +++ b/dogowner/templates/dogowner/dog_owner_homepage.html @@ -1,4 +1,5 @@ {% extends "main/base_template.html" %} +{% load crispy_forms_tags %} {% load static %} {% block stylesheets %} @@ -6,20 +7,51 @@ {% endblock %} {% block content %} -
-
- {% for daycare in daycares %} -
- {{ daycare.name }} image -
-
-
{{ daycare.name }}
+ +
+
{% csrf_token %} +
+ Search for daycare: +
+ {{ form.start_date|as_crispy_field }} +
+
+ {{ form.end_date|as_crispy_field }} +
+
+ {{ form.price_per_day|as_crispy_field }} +
+
+ {{ form.area|as_crispy_field }} +
+
+ {{ form.city|as_crispy_field }} +
+
+ {{ form.name|as_crispy_field }} +
+ +
+
+
+ Found {{ day_care_queryset.count }} results for your dog! +
+ {% for daycare in day_care_queryset %} +
+ {{ daycare.name }} image +
+
+
{{ daycare.name }}
+
+

{{ daycare.area | truncatechars:20 }}

+

{{ daycare.city | truncatechars:20 }}

+

{{ daycare.price_per_day | truncatechars:20 }}

+

{{ daycare.description | truncatechars:35}}

+ Daycare Profile +
+
+ {% endfor %} +
-

{{ daycare.description | truncatechars:250 }}

- Daycare Profile -
- {% endfor %} -
-
{% endblock %} diff --git a/dogowner/views.py b/dogowner/views.py index 53333ed..073bad3 100644 --- a/dogowner/views.py +++ b/dogowner/views.py @@ -1,11 +1,32 @@ 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() context = { - 'daycares': DayCare.objects.all(), + 'day_care_queryset': day_care_queryset, + 'form': form } + 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 = { + 'day_care_queryset': day_care_queryset, + 'form': form + } + return render(request, 'dogowner/dog_owner_homepage.html', context) diff --git a/main/templates/main/base_template.html b/main/templates/main/base_template.html index b045033..87360af 100644 --- a/main/templates/main/base_template.html +++ b/main/templates/main/base_template.html @@ -26,7 +26,6 @@ {% if request.user.is_authenticated %} - {% endif %} diff --git a/main/tests.py b/main/tests.py index 3d3107c..d5111fe 100644 --- a/main/tests.py +++ b/main/tests.py @@ -1,5 +1,24 @@ import pytest +from dogowner.models import DogOwner from daycare.models import DayCare +from orders.models import Order + + +@pytest.fixture +def create_dog_owner_user(): + return DogOwner.create(email='dogowner@address.com', + username='dogOwnerUser01', + password='password123', + dog_name='dog name', + first_name='test', + last_name='user', + phone_number=1234567890, + dog_race='dog race', + dog_picture_url='https://www.google.com/user1.jpg', + dog_age=4, + dog_weight=2, + dog_gender='M' + ) @pytest.mark.django_db @@ -77,3 +96,64 @@ def test_dog_owner_homepage_is_visible_for_dog_owner(self, client, create_dog_ow response = client.get("/homepage/") assert response.status_code == 200 assert list(response.context['daycares']) == list(DayCare.objects.all()) + + +@pytest.mark.django_db +class TestDogOwnerHomePageView: + def test_dog_owner_present_all_day_cares_at_entrypoint(self, client, create_dog_owner_user): + client.force_login(user=create_dog_owner_user.user) + response = client.get("/homepage/") + day_care_queryset = response.context['day_care_queryset'] + assert set(day_care_queryset) == set(DayCare.objects.all()) + + def test_search_daycare_present_only_available_daycares_on_specific_dates(self, client, create_dog_owner_user): + test_day_care = DayCare.create(username='testuser', email='test@email.com', password='test_password', + name='name', description='test description', price_per_day=100, + capacity=1, area='CENTER', city='tel aviv', address='address') + client.force_login(user=create_dog_owner_user.user) + search_form = {'area': "", + 'city': "", + 'price_per_day': 100, + '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'] + assert test_day_care in day_care_queryset + Order.create(dog_owner_id=DogOwner.objects.get(id=1), daycare_id=test_day_care, + 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 test_day_care 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") + filters_day_cares = DayCare.objects.filter(area__startswith='C', + city__icontains="tel aviv", + price_per_day__lte=800) + assert set(day_care_queryset) == set(available_day_cares.intersection(filters_day_cares)) + + 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'] diff --git a/static/CSS/dog_owner_homepage.css b/static/CSS/dog_owner_homepage.css index a18c9b8..bddcdce 100644 --- a/static/CSS/dog_owner_homepage.css +++ b/static/CSS/dog_owner_homepage.css @@ -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 @@ -40,3 +32,30 @@ 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; +} +