fix: пререндер-шаблоны работали некорректно в prod

This commit is contained in:
2026-05-19 22:45:56 +03:00
parent 9575e0e0d9
commit cb9dab9e56
10 changed files with 785 additions and 95 deletions

View File

@@ -132,40 +132,47 @@ TechArticle: описывает страницу как технический
</div>
</div>{# <!--- Хлебные крошки: КОНЕЦ ---> #}
<div class="row">
<div class="col-md-12">{{ THIS_SERIA_DESCRIPTION|safe }}</div>
<div class="col-md-12">
{# ВЕРХНЯЯ СТАТЬЯ: рендерится динамически (из БД), можно редактировать через админку #}
<div>{{ THIS_SERIA_DESCRIPTION|safe }}</div>
</div>
</div>
<div class="row">
<div class="col-lg-10">
<h2 class="header">Дома серии {{ THIS_SERIA_NAME }}: типовые размеры и&nbsp;схемы открывания</h2>
<div class="col-lg-10">
<h2 class="header">Дома серии {{ THIS_SERIA_NAME }}: типовые размеры и&nbsp;схемы открывания</h2>
</div>
<div class="col-lg-12" style="padding:1em 0 0 0;margin-left:-1em">
{# СХЕМЫ ОТКРЫВАНИЯ: статическая часть, если используется кеш #}
{% if PRE_RENDERED_STATIC_FLAPS_PATH %}
{% include PRE_RENDERED_STATIC_FLAPS_PATH %}
{% else %}
{% include 'report/show_big_flap_pictures.html' %}
{% endif %}
</div>
</div>
<div class="col-lg-12" style="padding:1em 0 0 0;margin-left:-1em">
{% include 'report/show_big_flap_pictures.html' %}
</div>
</div>
<div class="row">
<div class="col-lg-8 col-xs-12 col-md-offset-1">
<h3 class="header">Оконные проёмы в&nbsp;типовых квартирах <nobr>серии {{ THIS_SERIA_NAME }}</nobr></h3>
</div>
<div class="col-lg-8 col-xs-12 col-md-offset-1">
{# --- ОСНОВНОЙ БЛОК С ТАБЛИЦЕЙ --- #}
{# Если есть кешированный файл, включаем его. Иначе - рендерим блок на лету. #}
{% if PRE_RENDERED_INCLUDE_PATH %}
{% include PRE_RENDERED_INCLUDE_PATH %}
{% else %}
{% include "seria_info/all_seria_info_pre_light_include.html" %}
{% endif %}
{# --- КОНЕЦ ОСНОВНОГО БЛОКА --- #}
{# ТАБЛИЦА ОКОН: часть, которая считается при каждом запросе #}
{% include "seria_info/all_seria_info_pre_light_dynamic_include.html" %}
</div>
</div>
<div class="row">
<div class="col-md-9"><a name="s_graph"></a>
<h2 class="header">Здания серия {{ THIS_SERIA_NAME }}: ввод в&nbsp;эксплуатацию по&nbsp;годам</h2>
</div>
<div class="col-md-9 col-md-offset-1" style="height:300px;font-size:large;">
{% include 'seria_info/yaer_graph.html' %}
<div class="row">
<div class="col-md-9"><a name="s_graph"></a>
<h2 class="header">Здания серия {{ THIS_SERIA_NAME }}: ввод в&nbsp;эксплуатацию по&nbsp;годам</h2>
</div>
<div class="col-md-9 col-md-offset-1" style="height:300px;font-size:large;" id="graph">
{# ГРАФИК: статическая часть, если используется кеш #}
{% if PRE_RENDERED_STATIC_GRAPH_PATH %}
{% include PRE_RENDERED_STATIC_GRAPH_PATH %}
{% else %}
{% include 'seria_info/all_seria_info_pre_light_static_graph.html' %}
{% endif %}
</div>
<div class="col-md-9 col-md-offset-1">
<div style="font-size: xx-small;float: right">© 2015-{% now "Y" %}, данные: oknardia.ru</div>
@@ -176,26 +183,31 @@ TechArticle: описывает страницу как технический
<div class="col-md-7"><a name="s_map"></a>
<h2 class="header">Строения серии {{ THIS_SERIA_NAME }} на&nbsp;карте</h2>
</div>
<div class="col-md-7 col-lg-offset-1">
<div class="col-md-7 col-lg-offset-1">
<p><small>Чтобы посмотреть цены на&nbsp;установку и&nbsp;замену окон от&nbsp;партнёров &laquo;Окнардия&raquo; в&nbsp;своей квартире: найдите дом на&nbsp;карте; кликните на&nbsp;него; перейдите по&nbsp;ссылке &laquo;Смотреть коммерческие предложения&raquo;. При необходимости смените типовую планировку квартиры (на&nbsp;странице ценовой выдачи, справа от&nbsp;изображения типовых проёмов и&nbsp;схем открывания).</small></p>
<div style="height:350px;">
{% include 'seria_info/geo_map.html' with first_apart_id=TABLE_OF_WINDOWS.0.APART_ID %}
<div style="height:350px;">
<div id="SeriaMap" style="height: 100%;"></div>
</div>
<div style="font-size: xx-small;float: right">© 2015-{% now "Y" %}, данные: oknardia.ru</div>
</div>
<diV class="col-md-4">
<h3 class="header">Статистика <nobr>серии {{ THIS_SERIA_NAME }}</nobr></h3>
<p>Совокупно во&nbsp;всех зданиях типового проекта:</p>
<ul>
<li><strong>{{ ACCOUNTS|price_format }}</strong> квартир.</li>
<li>Проживает <strong>{{ APARTMENTS|price_format }}</strong> семей <small>(<strong>{{ RESIDENTS|price_format }}</strong> человек)</small>.</li>
<li><strong>{{ RESIDENTIAL_M2|stringformat:".1f"|price_format }} м²</strong> жилых помещений.</li>
<li><strong>{{ MUNICIPAL_M2|stringformat:".1f"|price_format }} м²</strong>&nbsp;— муниципальное жильё.</li>
<li><strong>{{ GOVERNMENT_M2|stringformat:".1f"|price_format }} м²</strong> занимают государственные и&nbsp;городские службы, учреждения бытового обслуживания, магазины, офисы и&nbsp;тому подобное.</li>
<li>Максимальный износ жилого фонда серии {{ THIS_SERIA_NAME }}&nbsp;<strong>{{ CONDITION_MAX|stringformat:".2f" }}%</strong>. Минимальный&nbsp;<strong>{{ CONDITION_MIN|stringformat:".2f" }}%</strong>. </li>
</ul>
</diV>
{# КАРТА И СТАТИСТИКА: статическая часть, если используется кеш #}
{% if PRE_RENDERED_STATIC_MAP_STATS_PATH %}
{% include PRE_RENDERED_STATIC_MAP_STATS_PATH %}
{% else %}
<diV class="col-md-4">
<h3 class="header">Статистика <nobr>серии {{ THIS_SERIA_NAME }}</nobr></h3>
<p>Совокупно во&nbsp;всех зданиях типового проекта:</p>
<ul>
<li><strong>{{ ACCOUNTS|price_format }}</strong> квартир.</li>
<li>Проживает <strong>{{ APARTMENTS|price_format }}</strong> семей <small>(<strong>{{ RESIDENTS|price_format }}</strong> человек)</small>.</li>
<li><strong>{{ RESIDENTIAL_M2|stringformat:".1f"|price_format }} м²</strong> жилых помещений.</li>
<li><strong>{{ MUNICIPAL_M2|stringformat:".1f"|price_format }} м²</strong>&nbsp;— муниципальное жильё.</li>
<li><strong>{{ GOVERNMENT_M2|stringformat:".1f"|price_format }} м²</strong> занимают государственные и&nbsp;городские службы, учреждения бытового обслуживания, магазины, офисы и&nbsp;тому подобное.</li>
<li>Максимальный износ жилого фонда серии {{ THIS_SERIA_NAME }}&nbsp;<strong>{{ CONDITION_MAX|stringformat:".2f" }} %</strong>. Минимальный&nbsp;<strong>{{ CONDITION_MIN|stringformat:".2f" }} %</strong>. </li>
</ul>
</diV>
{% endif %}
</div>
<div class="row">

View File

@@ -1,5 +1,10 @@
{# ============================================================================ #}
{# ДИНАМИЧЕСКИЕ ДАННЫЕ ДЛЯ СЕРИИ (НЕ кешируемая часть) #}
{# Содержит: Таблица раскладки окон по квартирам + статистика предложений #}
{# ЗАМЕЧАНИЕ: этот блок ЧАСТО меняется (при добавлении новых предложений) #}
{# ============================================================================ #}
<div class="col-md-9 col-xs-12" style="padding:0;">
<!--- прешаблон начало --->
<table style="padding:2px;">
{% for row in TABLE_OF_WINDOWS %}
<tr class="tr2">
@@ -34,5 +39,5 @@
<td></td>
</tr>
</table>
<!--- прешаблон конец --->
</div>

View File

@@ -0,0 +1,21 @@
{# ============================================================================ #}
{# СХЕМЫ ОТКРЫВАНИЯ И РАЗМЕРЫ (кешируемая статическая часть) #}
{# ============================================================================ #}
{% load static %}
{% load filters %}
{% if WIN_DIM %}
{% for I_WIN_DIM in FLAP_DIM %}
<div class="win_discr pull-left" id="flap{{ forloop.counter0 }}">
<div><img src="{% static I_WIN_DIM.url2img %}" alt="{{ I_WIN_DIM.sDescription }}. Размер {{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0x{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0 (Ш х В, мм.). Типовая схема открывания." title="{{ I_WIN_DIM.sDescription }}. Размер {{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0x{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0 (Ш х В, мм.). Типовая схема открывания." itemprop="image" /></div>
<div class="caption" style="width:{{ I_WIN_DIM.W }}px;min-width:13ex;">
<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="/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 %}
{% else %}
<h1>Нет данных о проемах и рекомендованных схемах открывания окон</h1>
{% endif %}

View File

@@ -0,0 +1,55 @@
{# ============================================================================ #}
{# ГРАФИК ВВОДА В ЭКСПЛУАТАЦИЮ (кешируемая статическая часть) #}
{# ============================================================================ #}
<script type="text/javascript" src="https://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
let data = google.visualization.arrayToDataTable([
["Год", "Введено в эксплуатацию", {role:'style'}],{% for row in DATA4GRAPH %}
["{{ row.YEAR }}",{{ row.NUMS }},"color: #99{{ row.CLRS }}99"]{% if not forloop.last %},{% endif %}{% endfor %}
]);
let view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2
]);
let options = {
animation:{
duration: 1500,
easing: 'in',
startup: true
},
backgroundColor: "#EEEEEE",
bar: {groupWidth: "76.4%"},
chartArea: {left: "2%", top: "5%", width: '96%', height: '85%'},
dataOpacity: 0.76,
explorer:{
maxZoomIn: 0.20,
maxZoomOut: 32 },
vAxis: {
baselineColor:'grey',
gridlines:{color: 'silver', count: 7},
minorGridlines:{color: '#dddddd', count: 3},
textPosition: 'in',
textStyle: {fontSize: 10}
},
hAxis: { textStyle: {fontSize: 10} },
isStacked: true,
tooltip: {
textStyle:{color: 'grey', fontSize: 10 },
trigger: 'selection'
},
annotations: {textStyle: { fontSize: 8, bold: true, color: 'black', opacity: 0.8 }},
legend: { position: "none" }
};
let chart = new google.visualization.ColumnChart(document.getElementById("graph"));
chart.draw(data, options);
}
</script>

View File

@@ -0,0 +1,93 @@
{# ============================================================================ #}
{# КАРТА И СТАТИСТИКА СЕРИИ (кешируемая статическая часть) #}
{# ============================================================================ #}
{% load humanize %}
{% load filters %}
{# БЛОК КАРТА: левая часть (col-md-7) #}
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>
<script type="text/javascript">
// Функция для декодирования Base64-обфускованных геоданных (защита координат)
function decodeGeoData(b64str) {
try {
var json = atob(b64str);
return JSON.parse(json);
} catch(e) {
console.error('Ошибка декодирования геоданных:', e);
return [];
}
}
ymaps.ready(function () {
let myMap = new ymaps.Map('SeriaMap', {
center: [55.75, 37.57],
zoom: 10,
behaviors: ['default', 'scrollZoom'],
controls: [ 'rulerControl', 'zoomControl', 'geolocationControl', 'fullscreenControl' ]
});
// Создадим кластеризатор, вызвав функцию-конструктор.
clusterer = new ymaps.Clusterer({
preset: 'islands#invertedGrayClusterIcons',
groupByCoordinates: false,
hasHint: false,
viewportMargin: 0,
zoomMargin: 16,
clusterDisableClickZoom: false,
gridSize: 80
});
geoObjects = [];
const linkText = 'Смотреть коммерческие предложения</a>';
const hintText = '<b>Здание серии {{ THIS_SERIA_NAME }}</b>';
const apartmentId = {{ first_apart_id|default:0 }};
const seriaId = {{ THIS_SERIA_ID }};
const seriaSlug = '{{ THIS_SERIA_NAME_T }}';
// Декодируем обфускованные геоданные: [lat, lon, addr_id, seria_id]
var geoData = decodeGeoData('{{ DATA4GEO_B64 }}');
// Создаем метки для каждого здания серии
for(var i = 0, len = geoData.length; i < len; i++) {
const latitude = geoData[i][0];
const longitude = geoData[i][1];
const buildingId = geoData[i][2];
// Формируем SEO-URL для каждой метки
const balloonLink = `<a href="/price/seriaID${seriaId}--${seriaSlug}/appartID${apartmentId}/addressID${buildingId}--null">`;
geoObjects[i] = new ymaps.Placemark( [longitude, latitude],
{ // Содержимое иконки, балуна и хинта.
balloonContent: balloonLink + linkText,
hintContent: hintText
},
{ preset:'islands#circleIcon',iconColor: 'silver'} );
geoObjects[i].events
.add('mouseenter', function (e) {
e.get('target').options.set('preset', 'islands#yellowCircleIcon');
})
.add('mouseleave', function (e) {
e.get('target').options.set('preset', 'islands#grayCircleIcon');
});
}
// Добавляем метки в кластеризатор.
clusterer.add(geoObjects);
myMap.geoObjects.add(clusterer);
// позиционирование карты так, чтобы на ней были видны все объекты кластера.
myMap.setBounds(clusterer.getBounds(), { checkZoomRange: true });
});
</script>
{# БЛОК СТАТИСТИКА: правая часть (col-md-4) #}
<diV class="col-md-4">
<h3 class="header">Статистика <nobr>серии {{ THIS_SERIA_NAME }}</nobr></h3>
<p>Совокупно во&nbsp;всех зданиях типового проекта:</p>
<ul>
<li><strong>{{ ACCOUNTS|price_format }}</strong> квартир.</li>
<li>Проживает <strong>{{ APARTMENTS|price_format }}</strong> семей <small>(<strong>{{ RESIDENTS|price_format }}</strong> человек)</small>.</li>
<li><strong>{{ RESIDENTIAL_M2|stringformat:".1f"|price_format }} м²</strong> жилых помещений.</li>
<li><strong>{{ MUNICIPAL_M2|stringformat:".1f"|price_format }} м²</strong>&nbsp;— муниципальное жильё.</li>
<li><strong>{{ GOVERNMENT_M2|stringformat:".1f"|price_format }} м²</strong> занимают государственные и&nbsp;городские службы, учреждения бытового обслуживания, магазины, офисы и&nbsp;тому подобное.</li>
<li>Максимальный износ жилого фонда серии {{ THIS_SERIA_NAME }}&nbsp;<strong>{{ CONDITION_MAX|stringformat:".2f" }} %</strong>. Минимальный&nbsp;<strong>{{ CONDITION_MIN|stringformat:".2f" }} %</strong>. </li>
</ul>
</diV>