mod: Рефакторинг страницы цен одного окна (вьюшки, шаблоны, тесты, новый canonical-роутинг)

This commit is contained in:
2026-04-26 14:53:49 +03:00
parent 21501799ca
commit 3479b31f0e
10 changed files with 777 additions and 164 deletions

View File

@@ -70,7 +70,7 @@ CollectionPage + ItemList помогают поисковику понять с
"@type": "Thing",
"name": "{{ i.DESCRIPTION|escapejs }}",
"description": "{{ i.DESCRIPTION_L|escapejs }}",
"url": "{{ request.scheme }}://{{ request.get_host }}/tsena-odnogo-okna/{{ i.W|stringformat:'.0f' }}x{{ i.H|stringformat:'.0f' }}mm/tip{{ i.ID }}",
"url": "{{ request.scheme }}://{{ request.get_host }}/catalog/standard_opening/price-{{ i.W|stringformat:'.0f' }}x{{ i.H|stringformat:'.0f' }}mm-tip{{ i.ID }}",
"image": "{{ request.scheme }}://{{ request.get_host }}{% static i.URL2IMG %}",
"additionalProperty": [
{"@type": "PropertyValue", "name": "Ширина", "value": "{{ i.W|stringformat:'.0f' }} мм"},
@@ -133,7 +133,7 @@ CollectionPage + ItemList помогают поисковику понять с
<td data-sort="{% if i.IS_DOOR %}1{% else %}0{% endif %}">{% if i.IS_DOOR %}да{% else %}—{% endif %}</td>
<td>{{ i.DESCRIPTION }}</td>
<td>{% for j in i.INCLUDING_IN_SERIA %}<a href="/catalog/seria/{{ j.NAME_T }}/all{{ j.ID }}">{{ j.NAME }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td>
<td><a class="btn btn-default btn-xs" href="/tsena-odnogo-okna/{{ i.W|stringformat:".0f" }}x{{ i.H|stringformat:".0f" }}mm/tip{{ i.ID }}">цены</a></td>
<td><a class="btn btn-default btn-xs" href="/catalog/standard_opening/price-{{ i.W|stringformat:".0f" }}x{{ i.H|stringformat:".0f" }}mm-tip{{ i.ID }}">цены</a></td>
</tr>{% endfor %}
</tbody>
</table>

View File

@@ -5,11 +5,12 @@
{% block Add_Body_Attribute %} style="padding-top:70px;"{% endblock %}
{# SEO блоки дат:#}
{# - Date4Meta: дата публикации (первого появления) — используем дату модификации данных. #}
{# - Last4Meta: дата последнего обновления — будет по умолчанию now из base.html. #}
{% block Date4Meta %}{{ META_DATA_PUBLISH|date:"Y-m-d" }}{% endblock %}
{% block Last4Meta %}{{ META_DATA_PUBLISH|date:"Y-m-d" }}{% endblock %}
{% block Top_JS4 %}
{% block Top_JS4 %}{# Для построения круговой диаграммы #}
<script type="text/javascript" src="//www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load("current", {packages: ["corechart"]});
@@ -35,6 +36,140 @@
}
</script>{% endblock %}
{% block ADD_TO_HEAD %}{% comment %}
JSON-LD микроразметка для поисковых систем (Schema.org):
- BreadcrumbList: хлебные крошки для навигации в поиске
- Organization: информация о бренде/компании
- Product: типовое окно с полной информацией
- Рейтинги и цены берутся из таблицы предложений (price_offers_for_one_window_frame.html)
{% endcomment %}<script type="application/ld+json">
[
{
"@context": "https://schema.org/",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Главная",
"item": "{{ request.scheme }}://{{ request.get_host }}/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Каталог",
"item": "{{ request.scheme }}://{{ request.get_host }}/catalog"
},
{
"@type": "ListItem",
"position": 3,
"name": "Оконные проёмы и балконные блоки",
"item": "{{ request.scheme }}://{{ request.get_host }}/catalog/standard_opening/"
},
{
"@type": "ListItem",
"position": 4,
"name": "Окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth_mm|floatformat:0 }}×{{ I_WIN_DIM.iWinHight_mm|floatformat:0 }}{% endfor %} мм",
"item": "{{ request.scheme }}://{{ request.get_host }}{{ request.path }}"
}
]
},
{
"@context": "https://schema.org/",
"@type": "Organization",
"name": "ОКНАРДИЯ — агрегатор цен на окна",
"url": "{{ request.scheme }}://{{ request.get_host }}/",
"logo": "{{ request.scheme }}://{{ request.get_host }}{% static 'img/oknardia_logo.svg' %}",
"description": "Сравнение цен на установку оконных конструкций в типовых жилых домах России",
"contactPoint": {
"@type": "ContactPoint",
"contactType": "Customer Service"
}
},
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Типовое пластиковое окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}×{{ I_WIN_DIM.iWinHight|floatformat:0 }} см{% endfor %}",
"size": "{% for I_WIN_DIM in FLAP_DIM %}{% if not forloop.first %}, {% endif %}{{ I_WIN_DIM.iWinWidth_mm|floatformat:0 }}×{{ I_WIN_DIM.iWinHight_mm|floatformat:0 }} мм{% endfor %}",
"description": "Цены на пластиковое окно стандартного размера для типовых жилых домов серий {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}{{ I.sName }}{% endfor %}. Сравните предложения различных производителей и установщиков, узнайте актуальные цены, технические характеристики стеклопакетов, профилей, фурнитуры и условия доставки/монтажа.",
"image": {
"@type": "ImageObject",
"url": "{{ request.scheme }}://{{ request.get_host }}{% static 'img/oknardia_logo.svg' %}"
},
"brand": {
"@type": "Brand",
"name": "ОКНАРДИЯ"
},
"url": "{{ request.scheme }}://{{ request.get_host }}{{ request.path }}",
"offers": {
"@type": "AggregateOffer",
"priceCurrency": "RUB",
"itemCondition": "https://schema.org/NewCondition",
"availability": "https://schema.org/InStock",
"offerCount": "{{ NUM_TOTAL_OFFER_N_WORD|safe }}"
},
"datePublished": "{{ META_DATA_PUBLISH|date:'Y-m-d' }}",
"dateModified": "{{ META_DATA_PUBLISH|date:'Y-m-d' }}"
},
{
"@context": "https://schema.org/",
"@type": "ItemList",
"name": "Коммерческие предложения для типового окна",
"numberOfItems": "{{ PRICE_FRAME|length }}",
"itemListElement": [
{% for CurOffer in PRICE_FRAME %}
{
"@type": "ListItem",
"position": {{ forloop.counter }},
"item": {
"@type": "Offer",
"name": "{{ CurOffer.SETS_NAME|striptags|escapejs }}",
"url": "{{ request.scheme }}://{{ request.get_host }}{{ request.path }}#btn{{ CurOffer.SETS_ID }}",
"price": "{{ CurOffer.FIN_PRICE|stringformat:'.2f' }}",
"priceCurrency": "RUB",
"itemCondition": "https://schema.org/NewCondition",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "{{ CurOffer.MERCHANT|striptags|escapejs }}"
},
"itemOffered": {
"@type": "Product",
"name": "{{ CurOffer.SETS_NAME|striptags|escapejs }}",
"additionalProperty": [
{
"@type": "PropertyValue",
"name": "Оконный профиль",
"value": "{{ CurOffer.PVC_NAME|striptags|escapejs }}{% if CurOffer.PVC_MANUFACTURER %} ({{ CurOffer.PVC_MANUFACTURER|striptags|escapejs }}){% endif %}"
}{% if CurOffer.PVC_MANUFACTURER %},
{
"@type": "PropertyValue",
"name": "Производитель профиля",
"value": "{{ CurOffer.PVC_MANUFACTURER|striptags|escapejs }}"
}{% endif %}{% if CurOffer.GLAZING_MARK %},
{
"@type": "PropertyValue",
"name": "Стеклопакет",
"value": "{{ CurOffer.GLAZING_MARK|striptags|escapejs }}"
}{% endif %}
]
},
"dateModified": "{{ CurOffer.SETS_DATA_MODIFY|date:'Y-m-d' }}"{% if CurOffer.SETS_RATING > -0.1 %},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "{{ CurOffer.SETS_RATING|stringformat:'.2f' }}",
"bestRating": "5",
"worstRating": "0",
"ratingCount": "1"
}{% endif %}
}
}{% if not forloop.last %},{% endif %}
{% endfor %}
]
}
]
</script>{% endblock %}
{% block Description %}Цены на типовое окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }} см. для домов серий {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}{{ I.sName }}{% endfor %}{% endfor %}.{% endblock %}
{% comment %}{% block Description %}Цены на пластиковые окна для серии {{ BASE_SERIA }} ({{ APART }} квартира, {{ ADDRESS }}) :: {% for CurOffer in PRICE_FRAME %}Поставщик: {{ CurOffer.MERCHANT }}; Комплектация: {{ CurOffer.SETS_NAME }}; Цена: {{ CurOffer.FIN_PRICE }}₽ :: {% endfor %}{% endblock %}{% endcomment %}
@@ -146,12 +281,14 @@ $(function () { // инициализация и обработка попове
<span itemscope itemtype="http://schema.org/Product">
<div class="row">
<div class="col-md-9">
<h1>Цены на окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth_mm|floatformat:0 }}&times;{{ I_WIN_DIM.iWinHight_mm|floatformat:0 }}{% endfor %}&nbsp;мм. <small>(типовое)</small></h1>
<h1 itemprop="name">Цены на окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth_mm|floatformat:0 }}&times;{{ I_WIN_DIM.iWinHight_mm|floatformat:0 }}{% endfor %}&nbsp;мм. <small>(типовое)</small></h1>
</div>
<div class="col-md-9">
<p>Типовой проём {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:1 }}&times;{{ I_WIN_DIM.iWinHight|floatformat:1 }}{% endfor %}&nbsp;cм. представлен в домах серий: {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}<a href="/catalog/seria/{{ I.sNameLat }}/all{{ I.id }}">{{ I.sName }}</a>{% endfor %}. База «Окнардии» размещено {{ NUM_TOTAL_OFFER_N_WORD }} цен для окон в такой проем (из них в архиве {{ NUM_ARCHIVE_OFFER }}). Предложено {{ NUM_FLAP_VARIATION_IN_WORD }} открывания от {{ NUM_TOTAL_FIRM_N_WORD }}.</p>
<p itemprop="description">Типовой проём {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:1 }}&times;{{ I_WIN_DIM.iWinHight|floatformat:1 }}{% endfor %}&nbsp;cм. представлен в домах серий: {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}<a href="/catalog/seria/{{ I.sNameLat }}/all{{ I.id }}">{{ I.sName }}</a>{% endfor %}. База «Окнардии» размещено {{ NUM_TOTAL_OFFER_N_WORD }} цен для окон в такой проем (из них в архиве {{ NUM_ARCHIVE_OFFER }}). Предложено {{ NUM_FLAP_VARIATION_IN_WORD }} открывания от {{ NUM_TOTAL_FIRM_N_WORD }}.</p>
</div>
{# Микроразмектка: названеи продукта #}<meta itemprop="name" content="Окна {{ APART|safe }} ({{ ADDRESS }})" />
{# Микроразметка: название продукта и марка #}
<meta itemprop="brand" content="ОКНАРДИЯ — агрегатор цен на окна" />
<meta itemprop="productionDate" content="{{ META_DATA_PUBLISH|date:'Y-m-d' }}" />
</div>
<div class="row ShowBigFlapPictures">
@@ -176,8 +313,6 @@ $(function () { // инициализация и обработка попове
<div class="col-md-12">
<p id="tab-note">В таблице представлены только цены поставщиков из базы «Окнардия». Клик на&nbsp;названии набора отобразит детальную спецификацию каждого предложения: профиль рамы и&nbsp;створки, схему стеклопакета, фурнитуру, элементы отлива, подоконника, откоса, системы <nobr>климат-контроля</nobr>) и&nbsp;сопутствующие услуги. Предложения выводятся блоками. Очередной блок выводится кнопкой &laquo;Ещё коммерческие предложения окон&raquo; под таблицей. Детальные технические характеристики стеклопакетов, профилей и&nbsp;описание сопутствующих услуг можно посмотреть и сравнить с&nbsp;помощью кнопки &laquo;Сравнить выбранные&raquo;.</p>
</div>
{# Микроразмектка: названеи продукта #}
<meta itemprop="name" content="Окна {{ APART|safe }} ({{ ADDRESS }})"/>
</div>

View File

@@ -6,8 +6,8 @@
{% if forloop.first %}
<th rowspan="{% if CurOffer.DIM|length == 1 %}2{% else %}{{ CurOffer.DIM|length }}{% endif %}" title="Добавить коммерческое предложение окна к сравнению">{# красивые чекбоксы BEGIN #}<div class="checkbox"><label><input id="CHK{{ CurOffer.SETS_ID }}" type="checkbox" name="ForCompare" value="{{ CurOffer.SETS_ID }}" onChange="ChangeCountCheckedBox({{ CurOffer.SETS_ID }});" /><span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span></label></div>{# красивые чекбоксы END #}</th>
<td rowspan="{% if CurOffer.DIM|length == 1 %}2{% else %}{{ CurOffer.DIM|length }}{% endif %}"{% if CurOffer.IS_COMMERCIAL %} style="background-image: url(/media/{{ CurOffer.MERCHANT_LOGO }})"{% endif %} title="Краткая спецификация коммерческого предложения">
<span itemprop="description">
<h3 class="set-name shake-trigger" id="btn{{ CurOffer.SETS_ID }}"><a href="javascript://" onclick="show_dtl({{ CurOffer.SETS_ID }})">{{ CurOffer.MERCHANT }}{{ CurOffer.SETS_NAME }}<i class="glyphicon glyphicon-chevron-down shake-vertical"></i></a></h3>
<span>
<h3 class="set-name shake-trigger" id="btn{{ CurOffer.SETS_ID }}"><a href="javascript://" onclick="show_dtl({{ CurOffer.SETS_ID }})">{{ CurOffer.MERCHANT }} {{ CurOffer.SETS_NAME }}<i class="glyphicon glyphicon-chevron-down shake-vertical"></i></a></h3>
<DiV id="dtl{{ CurOffer.SETS_ID }}" class="collapse">■ Профиль: <a href="/catalog/profile/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_MANUFACTURER_T }}/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_NAME_T }}">{{ CurOffer.PVC_NAME|safe }}</a> (<a href="/catalog/profile/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_MANUFACTURER_T }}">{{ CurOffer.PVC_MANUFACTURER }}</a>)
&nbsp;{{ CurOffer.GLAZING_NAME_B|safe }} <nobr>({{ CurOffer.GLAZING_MARK }})</nobr>
@@ -29,17 +29,15 @@
</DiV>
<!-- Дата обновления -->
<nobr class="badge badge4price" title="Дата обновления коммерческого предложения окон — {{ CurOffer.SETS_DATA_MODIFY|date:"d.M.Y" }}"><b class="glyphicon glyphicon-calendar"></b> {{ CurOffer.SETS_DATA_MODIFY|date:"d.M.Y" }}</nobr>
<!-- Звездочки рейтинга -->
<nobr class="badge badge4price" title="Рейтинг «Окнардии»{% if CurOffer.SETS_RATING > -0.1 %} — {{ CurOffer.SETS_RATING|stringformat:".2f" }} баллов{% endif %}"><a
<!-- Звездочки рейтинга с микроразметкой Rating -->
<nobr class="badge badge4price" title="Рейтинг «Окнардии»{% if CurOffer.SETS_RATING > -0.1 %} — {{ CurOffer.SETS_RATING|stringformat:".2f" }} баллов{% endif %}">
<a
href="javascript://"
id-set="{{ CurOffer.SETS_ID }}"
data-trigger="focus" tabindex="0"
title="{% if CurOffer.SETS_RATING > 0.01 %}<b> Рейтинг {{ CurOffer.SETS_RATING|stringformat:".2f" }}</b> для оконого набора «{{ CurOffer.SETS_NAME }}» компании «{{ CurOffer.MERCHANT }}» состоит&nbsp;из:{% else %}Рейтинг не присвоен{% endif %}"
data-toggle="popover">рейтинг</a>:&nbsp;{% for Star in CurOffer.SETS_RATING_STARTS %}{% if Star == 0 %}<b class="glyphicon glyphicon-star-empty"></b>{% else %}<b class="glyphicon glyphicon-star"></b>{% endif %}{% endfor %} {% if CurOffer.SETS_RATING > -0.1 %} {{ CurOffer.SETS_RATING|stringformat:".2f" }}{% endif %}</nobr>
data-toggle="popover">рейтинг</a>:&nbsp;{% for Star in CurOffer.SETS_RATING_STARTS %}{% if Star == 0 %}<b class="glyphicon glyphicon-star-empty"></b>{% else %}<b class="glyphicon glyphicon-star"></b>{% endif %}{% endfor %} {% if CurOffer.SETS_RATING > -0.1 %}{{ CurOffer.SETS_RATING|stringformat:".2f" }}{% endif %}</nobr>
</span>
<span itemprop="brand" itemscope itemtype="http://schema.org/Brand">
<meta itemprop="name" content="{{ CurOffer.MERCHANT }}" />
<meta itemprop="logo" content="{{ request.scheme }}://{{ request.get_host }}/media/{{ CurOffer.MERCHANT_LOGO }}" />
</span></td>
<!--- Конец большой ячейки со спецификацией оконного предложения --->
{% endif %}
@@ -50,10 +48,8 @@
<td class="rnw" title="Стоимость {{ CurOffer.TOTAL|stringformat:".2f" }} рублей за все окна квартиры {{ APART|safe }}.">{{ CurOffer.TOTAL|stringformat:".2f"|price_format }}</td>
<th{% if CurOffer.DISCOUNT_COLOR2 != "" %} style="background-color:{{ CurOffer.DISCOUNT_COLOR2 }};"{% endif %} title="{% if CurOffer.DISCOUNT < 0.1 %}Нет скидки{% else %}Скидка — {{ CurOffer.DISCOUNT|stringformat:".1f" }}%{% endif %}">{% if CurOffer.DISCOUNT < 0.1 %}{% else %}&minus;{{ CurOffer.DISCOUNT|stringformat:".1f" }}%{% endif %}</th>
<th{% if CurOffer.DISCOUNT_COLOR1 != "" %} style="background-color:{{ CurOffer.DISCOUNT_COLOR1 }};"{% endif %} itemprop="offers" itemscope itemtype="http://schema.org/Offer" title="Итого за все окна с учетом скидки: {{ CurOffer.FIN_PRICE|stringformat:".2f" }} рублей">
<th{% if CurOffer.DISCOUNT_COLOR1 != "" %} style="background-color:{{ CurOffer.DISCOUNT_COLOR1 }};"{% endif %} title="Итого за все окна с учетом скидки: {{ CurOffer.FIN_PRICE|stringformat:".2f" }} рублей">
Итого: {{ CurOffer.FIN_PRICE|stringformat:".2f"|price_format }}&thinsp;<small class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></small>
<meta itemprop="price" content="{{ CurOffer.FIN_PRICE }}" />
<meta itemprop="priceCurrency" content="RUB" />
</th>
{% if CurOffer.DIM|length == 1 %}

View File

@@ -6,7 +6,7 @@
<nobr>{{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0×{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0&thinsp;мм.</nobr><br />{% if not I_WIN_DIM.iQuantity == 0 %}
<nobr><b>{{ I_WIN_DIM.iQuantity }}&thinsp;шт.</b>{% for I_II in I_WIN_DIM.qStr %}<span class="color-bullet" style="background-image:url('{% static 'img/svg/mark' %}{{ I_II }}.svg');"></span>{% endfor %}</nobr><br />{% endif %}
{{ I_WIN_DIM.sDescription }}{% if not I_WIN_DIM.iQuantity == 0 %}<br />
<a href="/tsena-odnogo-okna/{{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0x{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0mm/tip{{ I_WIN_DIM.id }}">цены только этого типового окна</a>{% endif %}
<a href="/catalog/standard_opening/price-{{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0x{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0mm-tip{{ I_WIN_DIM.id }}">цены только этого типового окна</a>{% endif %}
</div>
</div>{% endfor %}{% comment %}
<script type="text/javascript">

View File

@@ -157,7 +157,7 @@ TechArticle: описывает страницу как технический
</tr>{% templatetag openblock %} endfor {% templatetag closeblock %}
<tr class="trZ">
<td style="font-size: xx-small;vertical-align:text-top">© 2015-{% now "Y" %}, данные: oknardia.ru</td>{% templatetag openblock %} for i in WIN_OFFER_AND_MERCHANT {% templatetag closeblock %}
<td class="cntr" style="background:#f9f9f9;"><a href="/tsena-odnogo-okna/{% templatetag openvariable %} i.WIN_W|floatformat:0 {% templatetag closevariable %}0x{% templatetag openvariable %} i.WIN_H|floatformat:0 {% templatetag closevariable %}0mm/tip{% templatetag openvariable %} i.WIN_ID {% templatetag closevariable %}" class="badge" title="Ценовых предложений для окна: {% templatetag openvariable %} i.WIN_OFFER {% templatetag closevariable %}"><small class="glyphicon glyphicon-tags" aria-hidden="true"></small>&nbsp;{% templatetag openvariable %} i.WIN_OFFER {% templatetag closevariable %}</a></td>{% templatetag openblock %} endfor {% templatetag closeblock %}
<td class="cntr" style="background:#f9f9f9;"><a href="/catalog/standard_opening/price-{% templatetag openvariable %} i.WIN_W|floatformat:0 {% templatetag closevariable %}0x{% templatetag openvariable %} i.WIN_H|floatformat:0 {% templatetag closevariable %}0mm-tip{% templatetag openvariable %} i.WIN_ID {% templatetag closevariable %}" class="badge" title="Ценовых предложений для окна: {% templatetag openvariable %} i.WIN_OFFER {% templatetag closevariable %}"><small class="glyphicon glyphicon-tags" aria-hidden="true"></small>&nbsp;{% templatetag openvariable %} i.WIN_OFFER {% templatetag closevariable %}</a></td>{% templatetag openblock %} endfor {% templatetag closeblock %}
<td></td>
</tr>
</table>