diff --git a/archilance/management/commands/generate_portfolios.py b/archilance/management/commands/generate_portfolios.py index c50b957..0cf8efa 100644 --- a/archilance/management/commands/generate_portfolios.py +++ b/archilance/management/commands/generate_portfolios.py @@ -38,7 +38,7 @@ class Command(BaseCommand): def create_portfolio(i): portf = Portfolio( name='Portforlio %s' % i, - description="Portforlio %s's description" % i, + description=util.lorem(_.random(5, 30)), budget=util.random_amount(), currency=_.sample(CURRENCIES)[0], term=_.random(0, 20), diff --git a/archilance/management/commands/generate_projects.py b/archilance/management/commands/generate_projects.py index 7ff5779..4ef30fe 100644 --- a/archilance/management/commands/generate_projects.py +++ b/archilance/management/commands/generate_projects.py @@ -63,7 +63,7 @@ class Command(BaseCommand): deal_type=_.sample(Project.DEAL_TYPES)[0], term=_.random(0, 20), term_type=_.sample(TERMS)[0], - text='Project %s text' % i, + text=util.lorem(_.random(5, 30)), work_type=_.sample(Project.WORK_TYPES)[0], state='active', ) diff --git a/archilance/management/commands/generate_reviews.py b/archilance/management/commands/generate_reviews.py index 649a162..d902b73 100644 --- a/archilance/management/commands/generate_reviews.py +++ b/archilance/management/commands/generate_reviews.py @@ -23,7 +23,7 @@ class Command(BaseCommand): review.project = Project.objects.order_by('?').first() review.stars = _.random(1, 5) - review.text = 'This is a review %s text' % i + review.text = util.lorem(_.random(5, 15)) review.is_secured = _.sample((True, False)) review.save() diff --git a/archilance/management/commands/generate_users.py b/archilance/management/commands/generate_users.py index e46aac1..72c81cb 100644 --- a/archilance/management/commands/generate_users.py +++ b/archilance/management/commands/generate_users.py @@ -79,7 +79,8 @@ class Command(BaseCommand): username = 'user-%s' % i return User.objects.create( - first_name='User-%s' % i, + first_name='Василий', + last_name='Пупкин', username=username, email='%s@example.com' % username, is_active=True, diff --git a/archilance/settings/base.py b/archilance/settings/base.py index 8b68b49..93ab74a 100644 --- a/archilance/settings/base.py +++ b/archilance/settings/base.py @@ -240,7 +240,7 @@ REST_FRAMEWORK = { 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', ], - 'PAGE_SIZE': 10, + 'PAGE_SIZE': 100, 'DEFAULT_FILTER_BACKENDS': ('rest_framework_filters.backends.DjangoFilterBackend',), # djangorestframework-filters } diff --git a/archilance/util.py b/archilance/util.py index c96141b..c2edc56 100644 --- a/archilance/util.py +++ b/archilance/util.py @@ -1,3 +1,4 @@ +from django.shortcuts import _get_queryset from django.utils import timezone from pprint import pprint, pformat import pydash as _; _.map = _.map_; _.filter = _.filter_ @@ -41,6 +42,22 @@ def random_amount(): return random.random() * random.choice((100, 1000, 10000)) +def get_or_none(klass, *args, **kwargs): + queryset = _get_queryset(klass) + + try: + return queryset.get(*args, **kwargs) + except queryset.model.DoesNotExist: + return None + + +def get_attr_or_none(klass, *args, attr=None, **kwargs): + object = get_or_none(klass, *args, **kwargs) + + if object and attr and isinstance(attr, str): + return getattr(object, attr, None) + + def model_fields(model, width=200): pprint([( f.name, diff --git a/common/utils.py b/common/utils.py deleted file mode 100644 index 4a75d6a..0000000 --- a/common/utils.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.shortcuts import _get_queryset - -def get_or_none(klass, *args, **kwargs): - queryset = _get_queryset(klass) - - try: - return queryset.get(*args, **kwargs) - except queryset.model.DoesNotExist: - return None diff --git a/projects/templates/customer_project_detail.html b/projects/templates/customer_project_detail.html index 3906342..481dce8 100644 --- a/projects/templates/customer_project_detail.html +++ b/projects/templates/customer_project_detail.html @@ -201,6 +201,7 @@ {% endfor %} +
{% include 'partials/pagination.html' %}
diff --git a/projects/templates/project_detail.html b/projects/templates/project_detail.html index 695dfad..04f3f27 100644 --- a/projects/templates/project_detail.html +++ b/projects/templates/project_detail.html @@ -242,6 +242,7 @@ {% endfor %} +
{% include 'partials/pagination.html' %}
diff --git a/projects/templates/project_filter.html b/projects/templates/project_filter.html index f8231f2..2443d41 100644 --- a/projects/templates/project_filter.html +++ b/projects/templates/project_filter.html @@ -166,85 +166,78 @@ - - - - - - - -
- {% for proj in projects %} -
-
-

- {{ proj }} -

-
    -
  • - Объект "{{ proj.realty.name }}" -
  • -
  • - 0 ответ от имени группы -
  • -
-

{{ proj.text }}

- - {% if TEMPLATE_DEBUG %} -
{{ proj|inspect }}

Specialization: {{ proj.specialization }}

Realty location: {{ proj.realty.location }}

Constr. type: {{ proj.realty.construction_type }}

Build. classif.: {{ proj.realty.building_classification }}
+ + + + + +
+ {% for proj in projects %} +
+
+

+ {{ proj }} +

+
    +
  • + Объект "{{ proj.realty.name }}" +
  • +
  • + 0 ответ от имени группы +
  • +
+

{{ proj.text }}

+ + {% if TEMPLATE_DEBUG %} +
{{ proj|inspect }}

Specialization: {{ proj.specialization }}

Realty location: {{ proj.realty.location }}

Constr. type: {{ proj.realty.construction_type }}

Build. classif.: {{ proj.realty.building_classification }}
+ {% endif %} + +
    +
  • {{ proj.created }}
  • +
  • 0
  • +
  • {{ proj.answers.count }}
  • +
  • {{ proj.user }}
  • +
+
+
+

+ {{ proj.budget }} +

+
    + {% if proj.secure_deal %} +
  • Безопасная сделка
  • {% endif %} - -
      -
    • {{ proj.created }}
    • -
    • 0
    • -
    • {{ proj.answers.count }}
    • -
    • {{ proj.user }}
    • -
    -
-
-

- {{ proj.budget }} -

-
    - {% if proj.secure_deal %} -
  • Безопасная сделка
  • - {% endif %} - -
  • - Стадия: "П" -
  • - -
  • - Отказаться и переместить - в корзину -
  • -
-
+ +
  • + Стадия: "П" +
  • + +
  • + Отказаться и переместить + в корзину +
  • +
    - {% endfor %} -
    - - - - -
    - {% include 'partials/pagination.html' %} -
    - - - - - +
    + {% endfor %} +
    + + +
    + {% include 'partials/pagination.html' %} +
    + + {% include 'partials/footer.html' %}
    diff --git a/specializations/templatetags/specializtions_tags.py b/specializations/templatetags/specializtions_tags.py index 8e6579a..8f067c9 100644 --- a/specializations/templatetags/specializtions_tags.py +++ b/specializations/templatetags/specializtions_tags.py @@ -1,13 +1,15 @@ from django import template -from common.utils import get_or_none + +from archilance import util from users.models import User + register = template.Library() @register.inclusion_tag('templatetags/specializations_widget.html', takes_context=True) def specialization_widget(context, user_id): user_id = int(user_id) - user = get_or_none(User, pk=user_id) + user = util.get_or_none(User, pk=user_id) if user: specializations = user.contractor_specializations.all() else: diff --git a/templates/partials/pagination.html b/templates/partials/pagination.html index bf07867..db632a0 100644 --- a/templates/partials/pagination.html +++ b/templates/partials/pagination.html @@ -1,29 +1,51 @@ {% if is_paginated %} + + {% endif %} diff --git a/users/forms.py b/users/forms.py index cf3b0ec..0812d8f 100644 --- a/users/forms.py +++ b/users/forms.py @@ -1,10 +1,11 @@ from django import forms from django.forms import ModelForm import itertools +import pydash as _; _.map = _.map_; _.filter = _.filter_ from .models import User, ContractorFinancialInfo from common.models import Location -from projects.models import Project, BuildingClassfication, ConstructionType +from projects.models import Project, Realty, BuildingClassfication, ConstructionType from specializations.models import Specialization @@ -110,6 +111,26 @@ class ContractorFilterForm(forms.Form): super().__init__(*args, **kwargs) +class CustomerProfileProjectRealtyForm(forms.Form): + def __init__(self, *args, **kwargs): + self.request = kwargs.pop('request') + self.customer = kwargs.pop('customer') + + super().__init__(*args, **kwargs) + + realties = _.uniq(tuple(p.realty for p in self.customer.projects.all())) + + self.fields['realty'] = forms.ChoiceField( + widget=forms.Select(attrs={ + 'class': 'selectpicker', + 'onchange': "$(this).closest('form').submit()", + }), + + choices=(('', 'Все объекты'),) + tuple((r.pk, r.name) for r in realties), + required=False, + ) + + class ContractorFinancicalInfoForm(ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/users/templates/contractor_filter.html b/users/templates/contractor_filter.html index 0eded61..9ec79b4 100644 --- a/users/templates/contractor_filter.html +++ b/users/templates/contractor_filter.html @@ -163,281 +163,281 @@ - - - - {% for obj in coll %} - {% if obj|class_name == 'Team' %} - {% with team=obj %} -
    -
    -
    - execitor-image -
    -

    - {{ team.name }} + + + + + {% for obj in coll %} + {% if obj|class_name == 'Team' %} + {% with team=obj %} +

    +
    +
    + execitor-image +
    +

    + {{ team.name }} +

    + +
    Свободен
    +
    + + + + + +
    +
    +

    + Специализации:

    - -
    Свободен
    -
    - - - - - -
    -
    -

    - Специализации: -

    -
    - Интерьеры - 2-й -
    +
    + Интерьеры + 2-й +
    +
    + Визуализация/3D + 45-й +
    +
    + Экстерьеры + 10-й +
    +
    - Визуализация/3D - 45-й + Архитектура + 3-й
    - Экстерьеры - 10-й -
    -
    -
    - Архитектура - 3-й -
    -
    - 3D Моделирование - 100-й -
    + 3D Моделирование + 100-й
    -
    +
    - - -
    - -
    -
    -

    Есть допуск СРО

    -
    +
    + + +
    + +
    +
    +

    Есть допуск СРО

    - - - -{# {% if TEMPLATE_DEBUG %}#} -{#
    #} -{#
    ######## Team owner #########

    Specializations: {{ team.owner.contractor_specializations.all }}

    Location: {{ team.owner.location }}

    Build. classif-s: {% for o in team.owner.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}

    Constr. types: {% for o in team.owner.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}

    CRO: {{ team.owner.cro }}

    Work types: {% for o in team.owner.orders.all %}{{ o.project.work_type }}, {% endfor %}



    ######## Team users #########

    Specializations: {% for u in team.users.all %}{{ u.contractor_specializations.all }}, {% endfor %}

    Location: {% for u in team.users.all %}{{ u.location }}, {% endfor %}

    Build. classif-s: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}; {% endfor %}

    Constr. types: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}; {% endfor %}

    CRO: {% for u in team.users.all %}{{ u.cro }}, {% endfor %}

    Work types: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.work_type }}, {% endfor %}; {% endfor %}
    #} -{#
    #} -{# {% endif %}#} - -
    - {% for portf in team.portfolios.all|slice:':4' %} - - {% endfor %} +
    + + + {% if TEMPLATE_DEBUG %} +
    +
    ######## Team owner #########

    Specializations: {{ team.owner.contractor_specializations.all }}

    Location: {{ team.owner.location }}

    Build. classif-s: {% for o in team.owner.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}

    Constr. types: {% for o in team.owner.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}

    CRO: {{ team.owner.cro }}

    Work types: {% for o in team.owner.orders.all %}{{ o.project.work_type }}, {% endfor %}



    ######## Team users #########

    Specializations: {% for u in team.users.all %}{{ u.contractor_specializations.all }}, {% endfor %}

    Location: {% for u in team.users.all %}{{ u.location }}, {% endfor %}

    Build. classif-s: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}; {% endfor %}

    Constr. types: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}; {% endfor %}

    CRO: {% for u in team.users.all %}{{ u.cro }}, {% endfor %}

    Work types: {% for u in team.users.all %}{% for o in u.orders.all %}{{ o.project.work_type }}, {% endfor %}; {% endfor %}
    + {% endif %} + + +
    + {% for portf in team.portfolios.all|slice:':4' %} + + {% endfor %}
    - {% endwith %} - {% elif obj|class_name == 'User' %} - {% with contractor=obj %} -
    - + {% endwith %} + {% elif obj|class_name == 'User' %} + {% with contractor=obj %} +
    +
    +
    + execitor-image +
    +

    + {{ contractor.get_full_name }} [{{ contractor.username }}] +

    + +
    Свободен
    +
    + + + + + +
    +
    +

    + Специализации:

    - -
    Свободен
    -
    - - - - - -
    -
    -

    - Специализации: -

    -
    - Интерьеры - 2-й -
    +
    + Интерьеры + 2-й +
    +
    + Визуализация/3D + 45-й +
    +
    + Экстерьеры + 10-й +
    +
    - Визуализация/3D - 45-й + Архитектура + 3-й
    - Экстерьеры - 10-й -
    -
    -
    - Архитектура - 3-й -
    -
    - 3D Моделирование - 100-й -
    + 3D Моделирование + 100-й
    -
    +
    - - -
    - -
    -
    -

    Есть допуск СРО

    -
    +
    + + +
    + +
    +
    +

    Есть допуск СРО

    - - -{# {% if TEMPLATE_DEBUG %}#} -{#
    #} -{#
    Specializations: {{ contractor.contractor_specializations.all }}

    Location: {{ contractor.location }}

    Build. classif-s: {% for o in contractor.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}

    Constr. types: {% for o in contractor.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}

    CRO: {{ contractor.cro }}

    Work types: {% for o in contractor.orders.all %}{{ o.project.work_type }}, {% endfor %}
    #} -{#
    #} -{# {% endif %}#} - -
    - {% for portf in contractor.portfolios.all|slice:':4' %} - - {% endfor %} +
    + + + {% if TEMPLATE_DEBUG %} +
    +
    Specializations: {{ contractor.contractor_specializations.all }}

    Location: {{ contractor.location }}

    Build. classif-s: {% for o in contractor.orders.all %}{{ o.project.realty.building_classification }}, {% endfor %}

    Constr. types: {% for o in contractor.orders.all %}{{ o.project.realty.construction_type }}, {% endfor %}

    CRO: {{ contractor.cro }}

    Work types: {% for o in contractor.orders.all %}{{ o.project.work_type }}, {% endfor %}
    + {% endif %} + +
    + {% for portf in contractor.portfolios.all|slice:':4' %} + + {% endfor %}
    - {% endwith %} - {% endif %} - {% endfor %} - - -
    - {% include 'partials/pagination.html' %} -
    - +
    + {% endwith %} + {% endif %} + {% endfor %} + + +
    + {% include 'partials/pagination.html' %} +
    {% include 'partials/footer.html' %} diff --git a/users/templates/customer_profile_open_projects.html b/users/templates/customer_profile_open_projects.html index 69157cd..1c060a1 100644 --- a/users/templates/customer_profile_open_projects.html +++ b/users/templates/customer_profile_open_projects.html @@ -1,7 +1,6 @@ {% extends 'partials/base.html' %} -{% load staticfiles %} - + {% block content %} {% include 'partials/header.html' %} @@ -9,36 +8,35 @@
    {% include 'partials/customer_profile_info_block.html' %} -
    + + +
    {% for proj in projects %}
    @@ -80,11 +78,10 @@
  • -
    + {% csrf_token %} - - Переместить в корзину + Переместить в корзину
  • @@ -93,6 +90,10 @@ {% endfor %}
    +
    + {% include 'partials/pagination.html' %} +
    + {% include 'partials/footer.html' %}
    diff --git a/users/templates/customer_profile_reviews.html b/users/templates/customer_profile_reviews.html index 46f4490..6b43394 100644 --- a/users/templates/customer_profile_reviews.html +++ b/users/templates/customer_profile_reviews.html @@ -35,7 +35,7 @@ {% endif %}
    -

    {{ review.text }}{% if TEMPLATE_DEBUG %}. {% lorem %}{% endif %}

    +

    {{ review.text }}

    {% endfor %} diff --git a/users/templates/customer_profile_trashed_projects.html b/users/templates/customer_profile_trashed_projects.html index 4be67cb..e5e221b 100644 --- a/users/templates/customer_profile_trashed_projects.html +++ b/users/templates/customer_profile_trashed_projects.html @@ -11,21 +11,19 @@
    - +
    + {{ form.realty }} +
    @@ -34,6 +32,7 @@

    +
    {% for proj in projects %}
    @@ -94,6 +93,10 @@
    {% endfor %}
    + +
    + {% include 'partials/pagination.html' %} +
    {% include 'partials/footer.html' %}
    diff --git a/users/templates/partials/customer_profile_info_block.html b/users/templates/partials/customer_profile_info_block.html index 0f9b96e..64617ac 100644 --- a/users/templates/partials/customer_profile_info_block.html +++ b/users/templates/partials/customer_profile_info_block.html @@ -1,4 +1,6 @@ -{% load staticfiles %} +{% load projects_tags %} + +
    @@ -11,16 +13,15 @@

    - {{ object.get_full_name }} [ivanov_petr] + {{ customer.get_full_name }} [{{ customer.username }}]

    -

    {{ object.location.name }}

    - +

    {{ customer.location.name }}

    +
    - {% load projects_tags %} - {% ratings_widget customer.pk %} + {% ratings_widget customer.pk %}
    diff --git a/users/templatetags/user_tags.py b/users/templatetags/user_tags.py index 3bae815..be8033c 100644 --- a/users/templatetags/user_tags.py +++ b/users/templatetags/user_tags.py @@ -7,6 +7,7 @@ def has_group(user, group_name): groups = user.groups.all().values_list('name', flat=True) return True if group_name in groups else False + @register.simple_tag def test(): return 'Hello there' diff --git a/users/views.py b/users/views.py index 98b5286..1da9d36 100644 --- a/users/views.py +++ b/users/views.py @@ -13,16 +13,23 @@ import itertools import natsort import pydash as _; _.map = _.map_; _.filter = _.filter_ -from .forms import UserEditForm, ContractorFilterForm, ContractorFinancicalInfoForm from .mixins import CheckForUserMixin from .models import User, Team, ContractorFinancialInfo +from archilance import util from archilance.mixins import BaseMixin -from common.utils import get_or_none from projects.forms import PortfolioForm +from projects.models import Project from reviews.models import Review from specializations.models import Specialization from work_sell.forms import WorkSellForm +from .forms import ( + ContractorFilterForm, + ContractorFinancicalInfoForm, + CustomerProfileProjectRealtyForm, + UserEditForm, +) + def send_mail_test(request): send_mail('Subject here', 'Here is the message.Mukhtar hello ', '', ['muhtarzubanchi05@gmail.com'], fail_silently=False) @@ -244,26 +251,100 @@ class ContractorOfficeDetailView(DetailView): context_object_name = 'contractor' -class CustomerProfileOpenProjectsView(BaseMixin, DetailView): - model = User +class CustomerProfileOpenProjectsView(BaseMixin, View): template_name = 'customer_profile_open_projects.html' - context_object_name = 'customer' + form_class = CustomerProfileProjectRealtyForm - def get_context_data(self, **kwargs): - c = super().get_context_data(**kwargs) - c['projects'] = self.object.projects.filter(state='active') - return c + def get(self, request, *args, **kwargs): + context = self.get_context_data(**_.merge({}, request.GET, kwargs)) + customer = get_object_or_404(User.customer_objects, pk=kwargs.get('pk')) + form = self.form_class(request.GET, request=request, customer=customer) + projects = customer.projects.filter(state='active') + + if form.is_valid(): + realty = form.cleaned_data.get('realty') + + if realty: + projects = projects.filter(realty=realty) + else: + if form.errors: + messages.info(request, ( + '

    Произошла ошибка (form)

    ' + '
    {form}
    ' + ).format(form=pformat(form.errors))) + + paginator = Paginator(projects, settings.PAGE_SIZE) + page = request.GET.get('page') + + try: + projects = paginator.page(page) + except PageNotAnInteger: + projects = paginator.page(1) + except EmptyPage: + projects = paginator.page(paginator.num_pages) + + context.update({ + 'form': form, + + 'projects': projects, + 'customer': customer, + + 'open_project_count': projects.paginator.count, + 'trashed_project_count': customer.projects.filter(state='trashed').count(), + + 'is_paginated': True, + 'page_obj': projects, + }) + + return render(request, self.template_name, context) -class CustomerProfileTrashedProjectsView(BaseMixin, DetailView): - model = User +class CustomerProfileTrashedProjectsView(BaseMixin, View): template_name = 'customer_profile_trashed_projects.html' - context_object_name = 'customer' + form_class = CustomerProfileProjectRealtyForm - def get_context_data(self, **kwargs): - c = super().get_context_data(**kwargs) - c['projects'] = self.object.projects.filter(state='trashed') - return c + def get(self, request, *args, **kwargs): + context = self.get_context_data(**_.merge({}, request.GET, kwargs)) + customer = get_object_or_404(User.customer_objects, pk=kwargs.get('pk')) + form = self.form_class(request.GET, request=request, customer=customer) + projects = customer.projects.filter(state='trashed') + + if form.is_valid(): + realty = form.cleaned_data.get('realty') + + if realty: + projects = projects.filter(realty=realty) + else: + if form.errors: + messages.info(request, ( + '

    Произошла ошибка (form)

    ' + '
    {form}
    ' + ).format(form=pformat(form.errors))) + + paginator = Paginator(projects, settings.PAGE_SIZE) + page = request.GET.get('page') + + try: + projects = paginator.page(page) + except PageNotAnInteger: + projects = paginator.page(1) + except EmptyPage: + projects = paginator.page(paginator.num_pages) + + context.update({ + 'form': form, + + 'projects': projects, + 'customer': customer, + + 'open_project_count': customer.projects.filter(state='active').count(), + 'trashed_project_count': projects.paginator.count, + + 'is_paginated': True, + 'page_obj': projects, + }) + + return render(request, self.template_name, context) class CustomerProfileCurrentProjectsView(BaseMixin, DetailView): @@ -319,7 +400,7 @@ class ContractorProfileEditView(CheckForUserMixin, View): def get(self, request, *args, **kwargs): instance = get_object_or_404(User, pk=request.user.pk) form = self.form_class(instance=instance) - root = get_or_none(Specialization, name='_root') + root = util.get_or_none(Specialization, name='_root') nodes = root.get_descendants('lft') return render(request, self.template_name, {'form': form, 'nodes': nodes}) diff --git a/work_sell/templates/worksells_list.html b/work_sell/templates/worksells_list.html index 5a4ed00..bbb975b 100644 --- a/work_sell/templates/worksells_list.html +++ b/work_sell/templates/worksells_list.html @@ -1,8 +1,10 @@ {% extends 'partials/base.html' %} + {% load staticfiles %} {% load thumbnail %} {% block content %} + {% include 'partials/header.html' %}
    @@ -108,83 +110,50 @@
    - -
    - {% for work in object_list %} -
    -
    - - {% thumbnail work.img "265x265" crop="center" as im %} -
    -
    -
    - {% endthumbnail %} -
    -
    -
    - {{ work.budget }} + + + +
    + {% for work in object_list %} +
    +
    + + {% thumbnail work.img "265x265" crop="center" as im %} +
    +
    + {% endthumbnail %} +
    +
    +
    + {{ work.budget }}
    -
    -

    {{ work }}

    +
    +
    +

    {{ work }}

    - - {% endfor %} -
    - - - - -{# {% if is_paginated %}#} -{#
    #} -{# #} -{#
    #} -{# {% endif %}#} - - -
    - {% include 'partials/pagination.html' %} -
    - +
    + {% endfor %} +
    + + +
    + {% include 'partials/pagination.html' %} +
    {% include 'partials/footer.html' %}