From 0bc88869a5ffc5591e8e3c4f85e1f0811cb85085 Mon Sep 17 00:00:00 2001 From: erjemin Date: Sat, 9 May 2026 14:40:54 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20?= =?UTF-8?q?=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BE?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BD=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oknardia/oknardia/urls.py | 2 + oknardia/templates/catalog/catalog_sets.html | 385 +++++++++++++++++++ oknardia/web/catalog.py | 74 +++- 3 files changed, 457 insertions(+), 4 deletions(-) create mode 100644 oknardia/templates/catalog/catalog_sets.html diff --git a/oknardia/oknardia/urls.py b/oknardia/oknardia/urls.py index 720ed1a..1c5f25c 100644 --- a/oknardia/oknardia/urls.py +++ b/oknardia/oknardia/urls.py @@ -76,6 +76,8 @@ urlpatterns = [ re_path(r'^catalog/company[/*]$', catalog_companies.catalog_company), # СПИСОК ВСЕХ ПРОИЗВОДИТЕЛЕЙ ОКОН re_path(r'^catalog/company/(?P\d+)-(?P\S*)[/*]$', catalog_companies.catalog_company_detail), # КАРТОЧКА ПРОИЗВОДИТЕЛЯ-УСТАНОВЩИКА ОКОН + # --- --- КАТАЛОГ ОКОННЫХ НАБОРОВ (SetKit) — список комплектаций с переходом к сравнению + re_path(r'^catalog/sets[/*]$', catalog.catalog_sets), # ЦЕНОВЫЕ ПРЕДЛОЖЕНИЯ # --- ОДИНОЧНОЕ ОКНО re_path(r'^catalog/standard_opening/price-(?P\d+)x(?P\d+)mm-tip(?P\d+)[/*]$', diff --git a/oknardia/templates/catalog/catalog_sets.html b/oknardia/templates/catalog/catalog_sets.html new file mode 100644 index 0000000..96e97cf --- /dev/null +++ b/oknardia/templates/catalog/catalog_sets.html @@ -0,0 +1,385 @@ +{% extends "base.html" %} +{% load static %} +{% load filters %} + +{% block Title %}Оконные наборы: характеристики, комплектации и сравнение — каталог «Окнардия»{% endblock %} + +{% block Add_Body_Attribute %} style="padding-top:70px;"{% endblock %} + +{% block Description %}Каталог оконных наборов «Окнардия»: готовые комплектации для замены окон с профилем, стеклопакетом и монтажом в одном предложении. Подробные характеристики, рейтинг и сравнение от разных поставщиков.{% endblock %} + +{% block Keywords %}оконные наборы, комплектации окон, сравнение окон, профиль и стеклопакет, монтаж окон, окнардия{% endblock %} + +{% block Top_Meta1 %} + + + + + + + + + + + + + +{% endblock %} + +{% block ADD_TO_HEAD %} +{# JSON-LD: CollectionPage каталога наборов — BreadcrumbList + ItemList с кратким описанием каждого Product #} + + +{# CSS для плавающей панели выбора сравнения #} + +{% endblock %} + +{% block Main_Content %} +
+ + {# Хлебные крошки #} +
+
+ +

Оконные наборы: характеристики, комплектации и сравнение

+

Оконный набор — готовая комплектация для замены окон в вашем доме: профиль, стеклопакет, + фурнитура и монтаж в одном предложении от компаний-партнёров «Окнардии». + Отметьте несколько интересных наборов и сравните их детально по всем характеристикам.

+
+
+ + {# Список карточек #} + {% for item in SET_LIST %} +
+ + {# ---- ШАПКА КАРТОЧКИ: название + рейтинг + логотип ---- #} +
+
+ + {# Название + звёздочки рейтинга #} +
+

{{ item.kit.sSetName }}

+
+ {% for star in item.stars %}{% if star %}{% else %}{% endif %}{% endfor %}{% if item.kit.fSetRating > 0.1 %} {{ item.kit.fSetRating|stringformat:".2f" }}{% endif %} +
+
+ + {# Логотип компании — кликабельный, ведёт на карточку компании в каталоге #} +
+ {% if item.merchant_logo %} + {% if item.merchant_id %}{% endif %} + {{ item.merchant_name }} + {% if item.merchant_id %}{% endif %} + {% endif %} +
+ +
+
{# /panel-heading #} + + {# ---- ТЕЛО КАРТОЧКИ: три колонки — условия | профиль | стеклопакет ---- #} +
+
+ + {# == Колонка 1: компания и условия поставки == #} +
+

Поставщик

+ {% if item.merchant_id %} +

+ + {{ item.merchant_name }} + +

+ {% elif item.merchant_name %} +

{{ item.merchant_name }}

+ {% endif %} + + + {% if item.kit.sSetImplementAll %} + + {% endif %} + {% if item.kit.sSetImplementHandles %} + + {% endif %} + {% if item.kit.sSetImplementHinges %} + + {% endif %} + {% if item.kit.sSetImplementLatch %} + + {% endif %} + {% if item.kit.sSetImplementLimiter %} + + {% endif %} + {% if item.kit.sSetImplementCatch %} + + {% endif %} + {% if item.kit.sSetClimateControl|length > 3 %} + + {% endif %} + + + + + + + + + + + + + + + + + + + + + {% if item.kit.sSetOtherConditions %} + + {% endif %} +
Фурнитура:{{ item.kit.sSetImplementAll|capfirst }}
 Ручки:{{ item.kit.sSetImplementHandles|capfirst }}
 Петли:{{ item.kit.sSetImplementHinges|capfirst }}
 Запоры:{{ item.kit.sSetImplementLatch|capfirst }}
 Огранич.:{{ item.kit.sSetImplementLimiter|capfirst }}
 Фиксаторы:{{ item.kit.sSetImplementCatch|capfirst }}
Климат-конт.:{{ item.kit.sSetClimateControl|capfirst }}
Подоконник: + {% if item.kit.sSetSill %}{{ item.kit.sSetSill|capfirst }}{% else %}—{% endif %} +
Водоотлив: + {% if item.kit.sSetPanes %}{{ item.kit.sSetPanes|capfirst }}{% else %}—{% endif %} +
Откос: + {% if item.kit.sSetSlope %}{{ item.kit.sSetSlope|capfirst }}{% else %}—{% endif %} +
Доставка: + {{ item.kit.sSetDelivery|capfirst }} +
Монтаж: + {{ item.kit.sSetUninstallInstall|capfirst }} +
Прочее:{{ item.kit.sSetOtherConditions|capfirst }}
+
{# /col компания #} + + {# == Колонка 2: профиль == #} +
+

+ Профиль: + {{ item.profile.sProfileName }} + — {{ item.profile.sProfileManufacturer }} +

+ {% if item.profile.sProfileBriefDescription %} +

{{ item.profile.sProfileBriefDescription }}

+ {% endif %} + + + {% if item.profile.iProfileCameras %}{% endif %} + {% if item.profile.iProfileThickness > 5 %}{% endif %} + {% if item.profile.iProfileGlazingThickness > 4 %}{% endif %} + {% if item.profile.fProfileHeatTransf > 0.1 %}{% endif %} + {% if item.profile.fProfileSoundproofing > 1 %}{% endif %} + {% if item.profile.fProfileSeals > 0 %}{% endif %} + {% if item.profile.iProfileHeight > 15 %}{% endif %} + {% if item.profile.iProfileRabbet > 1 %}{% endif %} + {% if item.profile.sProfileColor %}{% endif %} + {% if item.profile.sProfileReinforcement %}{% endif %} + {% if item.profile.sProfileSealDescription %}{% endif %} + {% if item.profile.sProfileFillet %}{% endif %} + {% if item.profile.sProfileOther %}{% endif %} +
Производитель:{{ item.profile.sProfileManufacturer }}
Камер рамы/створки:{{ item.profile.iProfileCameras }} шт.
Монтажная ширина:{{ item.profile.iProfileThickness }} мм
Макс. толщина СП:{{ item.profile.iProfileGlazingThickness }} мм
Теплопередача Ro:{{ item.profile.fProfileHeatTransf }} м²·°C/Вт
Звукоизоляция:{{ item.profile.fProfileSoundproofing }} дБ
Контуры уплотнения:{{ item.profile.fProfileSeals }} шт.
Высота в проёме:{{ item.profile.iProfileHeight }} мм
Фальц рамы:{{ item.profile.iProfileRabbet }} мм
Цвет:{{ item.profile.sProfileColor|capfirst }}
Армирование:{{ item.profile.sProfileReinforcement }}
Уплотнитель:{{ item.profile.sProfileSealDescription|capfirst }}
Штапик:{{ item.profile.sProfileFillet }}
Прочие хар-ки:{{ item.profile.sProfileOther }}
+
{# /col профиль #} + + {# == Колонка 3: стеклопакет == #} +
+

+ Стеклопакет: {{ item.glazing.sGlazingName }} +

+ {% if item.glazing.sGlazingBriefDescription %} +

{{ item.glazing.sGlazingBriefDescription|capfirst }}

+ {% endif %} + + {% if item.glazing.sGlazingMark and item.glazing.sGlazingMark != "—" %}{% endif %} + {% if item.glazing.sGlazingManufacturer and item.glazing.sGlazingManufacturer != "—//—" and item.glazing.sGlazingManufacturer != "—" %}{% endif %} + {% if item.glazing.iGlazingCamerasN >= 1 %}{% endif %} + {% if item.glazing.iGlazingThickness >= 3 %}{% endif %} + {% if item.glazing.fGlazingHeatTransfer > 0.1 %}{% endif %} + {% if item.glazing.fGlazingSoundproofing >= 10 %}{% endif %} + {% if item.glazing.fGlazingLightTransmission >= 1 %}{% endif %} + {% if item.glazing.fGlazingPassingSun >= 1 %}{% endif %} + {% if item.glazing.sGlazingLightReflectance and item.glazing.sGlazingLightReflectance != "—/—" %}{% endif %} + {% if item.glazing.sGlazingReflectionAndAbsorptionOfHeat and item.glazing.sGlazingReflectionAndAbsorptionOfHeat != "—/—" %}{% endif %} + {% if item.glazing.sGlazingToning %}{% endif %} +
Схема:{{ item.glazing.sGlazingMark }}
Производитель:{{ item.glazing.sGlazingManufacturer }}
Камер:{{ item.glazing.iGlazingCamerasN }} шт.
Толщина:{{ item.glazing.iGlazingThickness }} мм
Теплопередача Ro:{{ item.glazing.fGlazingHeatTransfer }} м²·°C/Вт
Звукоизоляция:{{ item.glazing.fGlazingSoundproofing }} дБ
Светопропускание:{{ item.glazing.fGlazingLightTransmission }} %
Солнцепропускание:{{ item.glazing.fGlazingPassingSun }} %
Светоотражение:{{ item.glazing.sGlazingLightReflectance }} %
Теплоотражение/погл.:{{ item.glazing.sGlazingReflectionAndAbsorptionOfHeat }} %
Тонирование:{{ item.glazing.sGlazingToning|capfirst }}
+
{# /col стеклопакет #} + +
{# /row #} +
{# /panel-body #} + + {# ---- ПОДВАЛ КАРТОЧКИ: чекбокс «отметить» + кнопка сравнения ---- #} + + +
{# /panel kit-card #} + {% empty %} +
Нет доступных оконных наборов.
+ {% endfor %} + + {# --- Баннер --- #} +

{% include "ad/bannet-wide.html" %}
+ +
+ {% include "report/report_last_user_visit.html" %} + {% include "report/report_log_user_visit.html" %} +
+ +
{# /container-fluid #} + +{% endblock %} + +{% block Top_JS3 %}{% endblock %} + diff --git a/oknardia/web/catalog.py b/oknardia/web/catalog.py index c57184a..b09f747 100644 --- a/oknardia/web/catalog.py +++ b/oknardia/web/catalog.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- -from django.shortcuts import render, redirect -from django.http import HttpRequest, HttpResponse -from oknardia.models import Seria_Info -from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list import time +import pytils.translit +from django.http import HttpRequest, HttpResponse +from django.shortcuts import render, redirect + +from oknardia.models import Seria_Info, SetKit +from web.add_func import get_rating_set_for_stars +from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list + def catalog_root(request: HttpRequest) -> HttpResponse: """ Корневая страница каталога @@ -24,6 +28,68 @@ def catalog_root(request: HttpRequest) -> HttpResponse: return response +def catalog_sets(request: HttpRequest) -> HttpResponse: + """ Каталог оконных наборов (SetKit) — список всех активных комплектаций, отсортированных по рейтингу. + + Для каждого набора собирается dict с полями набора, профиля, стеклопакета и компании-установщика. + Цепочка FK: SetKit.kSet2User → OurUser.kMerchantOffice → MerchantOffice.kMerchantName (MerchantBrand). + Слаги URL формируются через pytils.translit.slugify. + + :param request: HttpRequest -- входящий http-запрос + :return response: HttpResponse -- исходящий http-ответ + """ + time_start = time.perf_counter() + + qs = ( + SetKit.objects + .filter(sSetActive=True) + .select_related( + 'kSet2PVCprofiles', + 'kSet2Glazing', + 'kSet2User__kMerchantOffice__kMerchantName', + ) + .order_by('-fSetRating') + ) + + kits: list[dict] = [] + for kit in qs: + # достаём бренд через цепочку FK (всё уже прогружено через select_related) + try: + office = kit.kSet2User.kMerchantOffice + brand = office.kMerchantName if office else None + except Exception: + office = brand = None + + profile = kit.kSet2PVCprofiles + glazing = kit.kSet2Glazing + + kits.append({ + 'kit': kit, + 'stars': get_rating_set_for_stars(kit.fSetRating), + 'profile': profile, + 'glazing': glazing, + # компания-установщик + 'merchant_id': brand.id if brand else None, + 'merchant_slug': pytils.translit.slugify(brand.sMerchantName) if brand else "", + 'merchant_name': brand.sMerchantName if brand else "", + 'merchant_logo': str(brand.pMerchantLogo) if brand and brand.pMerchantLogo else "", + 'merchant_url': brand.sMerchantMainURL if brand else "", + # слаги для ссылок на профиль в каталоге профилей + 'profile_manufacturer_slug': pytils.translit.slugify( + profile.sProfileManufacturer) if profile else "", + 'profile_slug': pytils.translit.slugify( + profile.sProfileName) if profile else "", + }) + + to_template: dict[str, object] = { + 'SET_LIST': kits, + 'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]), + 'LOG_VISIT': get_last_all_user_visit_list(), + 'ticks': float(time.perf_counter() - time_start), + } + return render(request, "catalog/catalog_sets.html", to_template) + + def report_all_info_seria_redirect(request: HttpRequest, seria_id: str = "12") -> HttpResponse: """ Переадресация старых URL, т.к. их сколько-то есть (было) во внешних ссылках