Ценовая выдача для одного окна

This commit is contained in:
2022-12-30 15:30:05 +03:00
parent 5a38181cd3
commit 6c4dcd1cb7
5 changed files with 859 additions and 9 deletions

View File

@@ -18,7 +18,7 @@ from django.contrib import admin
from django.urls import path, re_path
from django.conf.urls.static import static
from oknardia.settings import *
from web import views, autocomplete_addr, user_manager, blog, diagrams, report2, catalog
from web import views, autocomplete_addr, user_manager, blog, diagrams, report2, catalog, prices
urlpatterns = [
@@ -67,7 +67,9 @@ urlpatterns = [
# --- --- Каталог производителей окон
re_path(r'^catalog/company[/*]$', catalog.catalog_company),
re_path(r'^catalog/company/(?P<company_id>\d+)-(?P<company_name_slug>\S*)[/*]$', catalog.catalog_company_detail),
# ЦЕНОВЫЕ ПРЕДЛОЖЕНИЯ
re_path(r'^tsena-odnogo-okna/(?P<win_width_mm>\d+)x(?P<win_height_mm>\d+)mm/tip(?P<win_id>\d+)[/*]$',
prices.report_one_win_price),
]

View File

@@ -0,0 +1,141 @@
{% load static %}{% load filters %}{% for CurOffer in PRICE_FRAME %}
<tr>
<td colspan="7" style="font-size:xx-small;border-top:double black;border-bottom: solid 1px white;padding: 0;"></td>
</tr>{% for CurInOffer in CurOffer.DIM %}
<tr>
{% 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>
<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>
&nbsp;Тонирование:&nbsp;{{ CurOffer.GLAZING_TONING }}
{% if CurOffer.PVC_SEAL %}■&nbsp;Уплотнитель:&nbsp;{{ CurOffer.PVC_SEAL }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT %}■&nbsp;Фурнитура:&nbsp;{{ CurOffer.SETS_IMPLEMENT|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT_R %}■&nbsp;Ручки:&nbsp;{{ CurOffer.SETS_IMPLEMENT_R|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT_P %}■&nbsp;Петли:&nbsp;{{ CurOffer.SETS_IMPLEMENT_P|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT_Z %}■&nbsp;Запоры:&nbsp;{{ CurOffer.SETS_IMPLEMENT_Z|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT_O %}■&nbsp;Ограничители:&nbsp;{{ CurOffer.SETS_IMPLEMENT_O|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_IMPLEMENT_F %}■&nbsp;Фиксаторы:&nbsp;{{ CurOffer.SETS_IMPLEMENT_F|capfirst|safe }}
{% endif %}{% if CurOffer.SETS_CLIMATE_CONTROL %}<span {% if CurOffer.SETS_CLIMATE_CONTROL|capfirst != "Нет" or CurOffer.SETS_CLIMATE_CONTROL|length > 3 %}class="bullet-green"{% endif %}>■&nbsp;Климат-контроль:&nbsp;{{ CurOffer.SETS_CLIMATE_CONTROL|capfirst|safe }}</span>
{% endif %}{% if CurOffer.SETS_SILL %}<span {% if CurOffer.SETS_SILL|capfirst == "Нет" or CurOffer.SETS_SILL|length < 3 %}class="bullet-red"{% endif %}>&nbsp;Подоконник:&nbsp;{{ CurOffer.SETS_SILL|capfirst|safe }}</span>
{% endif %}{% if CurOffer.SETS_SLOPE %}<span {% if CurOffer.SETS_SLOPE|capfirst == "Нет" or CurOffer.SETS_SLOPE|length < 3 %}class="bullet-red"{% endif %}>&nbsp;Откос:&nbsp;{{ CurOffer.SETS_SLOPE|capfirst|safe }}</span>
{% endif %}{% if CurOffer.SETS_PANES %}<span {% if CurOffer.SETS_PANES|capfirst == "Нет" or CurOffer.SETS_PANES|length < 3 %}class="bullet-red"{% endif %}>&nbsp;Водоотлив:&nbsp;{{ CurOffer.SETS_PANES|capfirst|safe }}</span>
{% endif %}{% if CurOffer.SETS_UNINSTALL_INSTALL %}<span {% if CurOffer.SETS_UNINSTALL_INSTALL_B %}class="bullet-green"{% else %}class="bullet-red"{% endif %}>&nbsp;Демонтаж/монтаж:&nbsp;{{ CurOffer.SETS_UNINSTALL_INSTALL|capfirst|safe }}</span>
{% endif %}{% if CurOffer.SETS_DELIVERY %}<span {% if CurOffer.SETS_DELIVERY_B %}class="bullet-green"{% else %}class="bullet-red"{% endif %}>&nbsp;Доставка:&nbsp;{{ CurOffer.SETS_DELIVERY|capfirst|safe }}</span>{% endif %}{% comment %} {% if CurOffer.SETS_OTHER %}■&nbsp;Прочие условия:&nbsp;{{ CurOffer.SETS_OTHER|capfirst|safe }}
{% endif %}{% endcomment %}
</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
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>
</span>
<span itemprop="brand" itemscope itemtype="http://schema.org/Brand">
<meta itemprop="name" content="{{ CurOffer.MERCHANT }}" />
<meta itemprop="logo" content="https://oknardia.ru/media/{{ CurOffer.MERCHANT_LOGO }}" />
</span></td>
<!--- Конец большой ячейки со спецификацией оконного предложения --->
{% endif %}
<td class="rnw" title="Схема открывания окна или двери {{ CurInOffer.WIDTH|stringformat:".0f" }}0x{{ CurInOffer.HIGHT|stringformat:".0f" }}0 мм."><img src="{% static CurInOffer.IMG_MINI %}" alt="Схема открывания: {{ CurInOffer.DESCRIPTION }} — {{ CurInOffer.WIDTH|stringformat:".0f" }}0x{{ CurInOffer.HIGHT|stringformat:".0f" }}0 мм." /></td>
{% if forloop.first %}<!-- FIRST -->
<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" }} рублей">
Итого: {{ 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 %}
</tr><tr>
<td colspan="2">&nbsp;</td>
<td colspan="3">
{% endif %}
{% elif forloop.counter0 == 1 %}
<td colspan="3" rowspan="{{ CurOffer.DIM|length|add:"-1" }}">
{% endif %}
{% if forloop.counter0 == 1 or CurOffer.DIM|length == 1 %}
<stong>{{ CurOffer.OFFICE_NAME }}</stong><br />
<div id="hid{{ CurOffer.SETS_ID }}" class="collapse in">
<a class="btn btn-info btn-xs shake-trigger" data-toggle="collapse" data-target="#tel{{ CurOffer.SETS_ID }}" onclick="show_phone_num({{ CurOffer.SETS_ID }})"><b class="glyphicon glyphicon-phone-alt shake-vertical"></b> Показать телефон</a>
</div>
<div id="tel{{ CurOffer.SETS_ID }}" class="collapse">
<small>{{ CurOffer.OFFICE_ADDRESS }}</small><br /> <b class="glyphicon glyphicon-phone-alt"></b> {{ CurOffer.OFFICE_PHONES }}<br />
<small><nobr>Пожалуйста, скажите, что нашли цены на oknardia.ru</nobr></small>
</div>
</td>
{% endif %}
</tr>{% endfor %}<!----- ПРОВЕРКА --->{% endfor %}
<Tr id="preloader_{{ N }}">
<tH colspan="7"><center><img src="{% static "img/preloader.gif" %}" alt="подгружаю цены на окна..." height="64" width="64" /></center></tH>
</Tr>
<tR id="load_and_banner_{{ N }}">
<!-- сюда подгружаем следующий фрейм прайслиста --->
<script type="application/javascript">
function get_next_pricelist_frame( ) {
try{yaCounter32997984.reachGoal("MoreOffer");}catch(e){}
// заменяем ряд с кнопочками кнопочками на рекламу
// $("#load_and_banner_{{ N }}").html("<th colspan=10>РЕКЛАМА TMP</th>");
// добавляем ячейки с дополнительными ценами и новые кнопочки
$("#load_and_banner_{{ N }}").css({"display":"none"});
$("#preloader_{{ N }}").css({"display":"table-row","transition":"1s"});// подсчитать сколько прочеканных оферов для сравнения
$("#tmp").load(
"/next_price_one_flap_frame/idW{{ WIN_ID }}N{{ N }}",
function (){
window.frame = $("#tmp").html();
$("#price-list > tbody").append(window.frame);
}
);
window.setTimeout(
function show_adv() {
$("#load_and_banner_{{ N }}").html("<td colspan='8' bgcolor='#CCCCCC' align='center'><center><a href='https://api.ozon.ru/partner-tools.affiliates/pankarta/11052421746600/link?partner=e-serg&&utm_content=banner&width=728&height=90' target='_blank'><img src='https://api.ozon.ru/partner-tools.affiliates/pankarta/11052421746600/image?partner=e-serg&utm_content=banner&width=728&height=90' width='728px' height='90px'><small><br/>↑ Здесь могла бы быть ваша реклама ↑</small></center></td>");
$("#load_and_banner_{{ N }}").css({"display":"table-row","transition":"all 5s"});
$("#preloader_{{ N }}").css({"display":"none","transition":"1s"});
// (adsbygoogle=window.adsbygoogle || []).push({});
window.count = 0;
for (var i=0; i < ForCompare.length; i++)
if (ForCompare[i].checked)
window.count++;
$("#NumberOfSelected").html(window.count);
$('[data-toggle="popover"]').popover({ // реинициализировать подгружаемые поповеры
placement: "top",
html: true,
content: function () {
// сохранить текущий контекст
var _this = this;
$('#shadow_buffer').load('/show_rating_components/' + $(_this).attr("id-set"),
function (response, status, xhr) {
if (status == "error")
$(_this).attr({'data-content': "Ошибка " + xhr.status + " (" + xhr.statusText + ")"});
else
$(_this).attr({'data-content': response});
$(_this).popover('show');
try{yaCounter32997984.reachGoal("ShowRating");}catch(e){};
});
}
});
},
2800);
}
</script>
<td colspan="2"><a class="btn btn-info btn-block" data-toggle="collapse" onclick="Click2CompareOffers();">Сравнить выбранные (<span id="NumberOfSelected">0</span>) <small class="glyphicon-stats glyphicon"></small></a><p id="tmp"></p></td>
<td colspan="5">{% if N != "-1" %}<a href="javascript://" class="btn btn-info btn-block" onclick="get_next_pricelist_frame();">Ещё коммерческие предложения окон &nbsp;<small class="glyphicon-refresh glyphicon"></small></a>{% endif %}</td>
</tR>

View File

@@ -0,0 +1,245 @@
{% extends "base.html" %}{% load static %}
{% load filters %}
{% block Title %}Цены на типовое окно {% 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 %}
{% block Add_Body_Attribute %} style="padding-top:70px;"{% endblock %}
{% block Date4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock %}
{% block Last4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock %}
{% 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"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Схема открывания', 'Число предложений'], {% for I in LIST_FLAP_VARIATION %}
['{{ I.STR_NUM }}', {{ I.id }}]{% if not forloop.last %}, {% endif %}{% endfor %}
]);
var options = {
legend: {position: "none"},
backgroundColor: 'none',
pieHole: 0.386,
chartArea: {width: '80%', height: '90%'},
pieStartAngle: 5,
pieSliceTextStyle: {fontSize: 12},
is3D: true,
slices: {0: {offset: 0.1}},
tooltip: {textStyle: {fontSize: 11, color: 'black', opacity: 0.8}, showColorCode: true}
};
var chart = new google.visualization.PieChart(document.getElementById('donutchart'));
chart.draw(data, options);
}
</script>{% endblock %}
{% block Description %}Цены на типовое окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} cм. для домов серий {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}{{ I.sName }}{% 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 %}
{% block Keywords %}цены на окно, типовое окно, {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} cм., {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} см., стоимость окна, {% for I in SERIA_FOR_WIN %}серия {{ I.sName }}, {% endfor %}типовой проём, {{ META_KEYWORDS|default:"" }}{% endblock %}
{% block Top_JS3%}<script type="text/javascript">
function show_phone_num( id ){ // колапсатор для отображения контатной информации постафшика окон
$('#tel'+id).collapse('show');
$('#hid'+id).collapse('hide');
try {
yaCounter32997984.reachGoal('ShowPhone')
} catch (e) {
}
try {
var _tmr = window._tmr || (window._tmr = []);
_tmr.push({id: "2018432", type: "reachGoal", goal: "ViewPhone"});
} catch (e) {
}
}
function show_dtl( id ){ // колапсаторы для отобращения детальной информации оконого набора
$('#dtl'+id).collapse('show');
$('#btn'+id+' a .glyphicon-chevron-down').remove();
var ContentInA = $('#btn'+id+' a').text();
$('#btn'+id).empty();
$('#btn'+id).text(ContentInA);
try {
yaCounter32997984.reachGoal('ShowDetails')
} catch (e) {
}
try {
var _tmr = window._tmr || (window._tmr = []);
_tmr.push({id: "2018432", type: "reachGoal", goal: "ViewDetails"});
} catch (e) {
}
}
$(window).load( // уменьшение картинок .half для ретина-дисплеев
function(){var images = $('.half');images.each(function(i){$(this).width($(this).width()/2);});}
);
var count = 0;
var ForCompare = document.getElementsByName('ForCompare');
$(window).load( // подсчитать сколько прочеканных оферов для сравнения (нужно, когда на страничку возращаются через back, ее подгрущают или она приходит из кеша
function(){
for (var i=0; i < ForCompare.length; i++)
if (ForCompare[i].checked)
count++;
// alert(count);
$('#NumberOfSelected').html(count)
}
);
var ToURL = '';
function Click2CompareOffers(){ // вызывается при клике "сравнить коммерческие предложения"
try{yaCounter32997984.reachGoal('CompareOffers')}catch(e){}
//ForCompare = $('[name]=ForCompare');
for (var i=0; i < ForCompare.length; i++)
if (ForCompare[i].checked)
ToURL += ForCompare[i].value +',';
if (ToURL.match(/[,]/g).length > 1 && ToURL.match(/[,]/g).length < 7 )
location.href = '/compare_offers/' + ToURL.slice(0,-1);
else {
$('#modal-exclamation').modal('show');
ToURL = '';
}
}
function ChangeCountCheckedBox(CHK_ID){ // изменение числа выбранных для сравнения оферов
if ($("#CHK"+CHK_ID).is(":checked")) count++;
else count--;
$("#NumberOfSelected").html(count)
}
function get_more_info_about_building() {
try{yaCounter32997984.reachGoal("ClickBuildingDetails");}catch(e){}
}
$(function () { // инициализация и обработка поповеров рейтинга
$('[data-toggle="popover"]').popover({
placement: "top",
html: true,
content: function () {
// сохранить текущий контекст
var _this = this;
$('#shadow_buffer').load('/show_rating_components/' + $(_this).attr("id-set"), function (response, status, xhr) {
if (status == "error")
$(_this).attr({'data-content': "Ошибка " + xhr.status + " (" + xhr.statusText + ")"});
else
$(_this).attr({'data-content': response});
$(_this).popover('show');
try{yaCounter32997984.reachGoal("ShowRating");}catch(e){};
});
}
})
})
</script>{% endblock %}
{% block Top_CSS1 %}<link rel="stylesheet" type="text/css" href="{% static "css/csshake-vertical.min.css" %}">{% endblock %}
{% block Main_Content %}<div class="container-fluid">
{# <!--- Хлебные крошки: НАЧАЛО --> #}<div class="row">
<div class="col-md-11 col-xs-12">
<ol class="breadcrumb">
<li><a href="/">Главная</a></li>
<li><a href="/catalog">Каталог</a></li>
<li><a href="/catalog/standard_opening/">Оконные проёмы и балконные блоки</a></li>
<li>Окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}0x{{ I_WIN_DIM.iWinHight|floatformat:0 }}0{% endfor %}&nbsp;мм. </li>
</ol>
<h1>Стандартные оконные проёмы и&nbsp;балконные блоки</h1>
</div>
</div>{# <!--- Хлебные крошки: КОНЕЦ ---> #}
<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|floatformat:0 }}0x{{ I_WIN_DIM.iWinHight|floatformat:0 }}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 }}x{{ 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 }})" />
</div>
<div class="row ShowBigFlapPictures">
<div class="col-sm-7 col-xs-12 ">{% include 'report/show_big_flap_pictures.html' %}</div>
<div class="col-sm-2 col-xs-7 ap_list">
<h6>Наиболее частые предложения схем открывания:</h6>
<ul>{% for I in LIST_FLAP_VARIATION %}
{% if not I.IMG_MINI == '' %}<li><img src="{% static I.IMG_MINI %}" alt="Схема открывания {{ I.sOfferFlapConfig }}" title="Схема открывания {{ I.sOfferFlapConfig }} — {{ I.id }} шт." vspace="2" /> — {{ I.STR_NUM }}</li>{% endif %}{% endfor %}
</ul>
<div id="donutchart" style="width:100%;"></div>
</div>
<div class="col-sm-3 col-xs-5 ap_list">
<h6>Типовые серии домой, в которых встречается данный типовой проём:</h6>
<ul>{% for I in SERIA_FOR_WIN %}
<li><a href="/catalog/seria/{{ I.sNameLat }}/all{{ I.id }}">{{ I.sName }}</a>
<small>входит в {{ I.num_variation_of_apartment }}</small></li>
{% endfor %}</ul>
</div>
</div>
<div class="row">
<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>
<form class="row" method="get" action="\" id="compare_offers"><div class="col-md-12">
{% csrf_token %}
<table class=" table" id="price-list">
<thead>
<tr>
<th><b class="glyphicon glyphicon-th-list" title="Добавление набора окон в список сравнения" aria-label="Добавление набора окон в список сравнения"></b></th>
<th>Поставщик окон и название набора<small><br />
кликните чтобы отобразить описание и характеристики предложения</small>
</th>
<th>Схема<small><br />
открывания</small></th>
<!-- th>Цена,<b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
одно окно</small></th>
<th>N</th>
<th colspan="2" class="hidden-xs hidden-sm">Cумма,<b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
однотипные окна</small></th -->
<th style="text-align:right;">Стоимость,&nbsp;<b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
окна для типового проёма</small></th>
<th style="text-align:center;">Скидка</th>
<th>Итого<small><br />
за типовое окно с учётом скидки</small></th>
</tr>
</thead>
<tbody id="offers">
{% include "report/report_precelist_one_flap_frame.html" %}
</tbody>
</table></div>
</form></span><p id="shadow_buffer"></p>
{# --- Баннер: НАЧАЛО --- #}
<div class="row"><div class="col-md-12 col-xs-12"><hr class="dotted-black" />{% include "ad/bannet-wide.html" %}</div></div>
{# --- Баннер: конец --- #}
<div class="row">
{% include "report/report_last_user_visit.html" %}
{% include "report/report_log_user_visit.html" %}
</div>
</div>
{# модальное окно #}
<div class="modal fade bs-example-modal-sm" id="modal-exclamation" tabindex="-1" role="dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="mySmallModalLabel">Внимание:</h4>
</div>
<div class="modal-body">
<p>Извините, для сравнения коммерческих предложений окон нужно выбрать не менее двух и не более шести позиций.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Спасибо, все понятно.</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% comment %}
{% block Top_Nav_Bar %}
{# ОТЛАДКА, ГАСИМ ВЕРХНЕЕ МЕНЮ #}
{% endblock %}
{% endcomment %}

View File

@@ -84,7 +84,7 @@ def get_rating_set_for_stars(rating: float = 0.) -> list:
#
#
# # рассчитывает дистанцию в км. между двумя геокоординатами
# def GetGeoDistance(lon1, lat1, lat2, lon2):
# def get_geo_distance(lon1, lat1, lat2, lon2):
# lonA, latA, latB, lonB = map(math.radians, [lon1, lat1, lat2, lon2])
# distance = 2 * math.asin(math.sqrt(math.sin((latB - latA) / 2) ** 2 + math.cos(latA) * math.cos(latB) * math.sin(
# (lonB - lonA) / 2) ** 2)) * 6371.032 # РАДИУС ЗЕМЛИ 6371.032 КМ.
@@ -416,7 +416,7 @@ def make_flap_mini_pictures(path_to_img_file: str, str_flap_config: str) -> None
local_h_ratio_max += j["hRatio"]
if local_h_ratio_max > h_ratio_max:
h_ratio_max = local_h_ratio_max
img = Image.new("RGBA", (h_ratio_max*PICT_MINWI+(h_ratio_max+1)*3, PICT_MINIH+6), (255, 255, 255, 0))
img = Image.new("RGBA", (h_ratio_max * PICT_MINWI + (h_ratio_max + 1) * 3, PICT_MINIH + 6), (255, 255, 255, 0))
top = 0
left = 0
bottom = img.size[1]
@@ -490,17 +490,38 @@ def make_flap_mini_pictures(path_to_img_file: str, str_flap_config: str) -> None
return
def get_flaps_for_mini_pictures(flap_cofig: str) -> str:
image_file_name = flap_cofig
image_file_name = image_file_name.replace(">", u"G")
def get_flaps_for_mini_pictures(flap_config: str) -> str:
"""
Функция возвращает строку с именем файла мини-картинки для схемы открывания полученной в flap_config
:param flap_config: str - строка с схемой открывания.
:return: str - строка с именем файла мини-картинки.
"""
image_file_name = flap_config.upper()
image_file_name = image_file_name.replace(">", "G")
image_file_name = image_file_name.replace("<", "L")
image_file_name = image_file_name.replace("|", "I")
image_file_name = image_file_name.replace("[", "(")
image_file_name = image_file_name.replace("]", ")")
image_file_name = image_file_name.replace("/", "-")
image_file_name = image_file_name.replace("\\", "-")
image_file_name = image_file_name.replace(".", "-") + u".png"
image_file_name = image_file_name.replace(".", "-") + ".png"
image_file_name = f"{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}/{image_file_name}"
if not os.path.isfile(f"{STATIC_BASE_PATH}/{image_file_name}"):
make_flap_mini_pictures(f"{STATIC_BASE_PATH}/{image_file_name}", flap_cofig)
make_flap_mini_pictures(f"{STATIC_BASE_PATH}/{image_file_name}", flap_config.upper())
return image_file_name
def get_geo_distance(lon1: float, lat1: float, lat2: float, lon2: float) -> float:
""" Функция возвращает расстояние в км. между двумя геокоординатами.
:param lon1: float - долгота первой точки.
:param lat1: float - широта первой точки.
:param lat2: float - широта второй точки.
:param lon2: float - долгота второй точки.
:return: float - расстояние в км. между двумя геокоординатами.
"""
lon_a, lat_a, lat_b, lon_b = map(math.radians, [lon1, lat1, lat2, lon2])
distance = 2 * math.asin(math.sqrt(math.sin((lat_b - lat_a) / 2) ** 2 + math.cos(lat_a) * math.cos(lat_b)
* math.sin((lon_b - lon_a) / 2) ** 2)) * 6371.032 # РАДИУС ЗЕМЛИ 6371.032 КМ.
return distance

441
oknardia/web/prices.py Normal file
View File

@@ -0,0 +1,441 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import render, redirect
from django.http import HttpRequest, HttpResponse
from oknardia.models import Win_MountDim, PriceOffer
from oknardia.settings import *
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
from web.add_func import normalize, get_rating_set_for_stars, get_flaps_for_big_pictures, get_flaps_for_mini_pictures,\
get_geo_distance
import django.utils.dateformat
import time
import os
import re
import pytils
def report_price_frame(apartment_id: int, mount_dim_per_offer: int, address_longitude: float, address_latitude: float,
frame_begin_n: int = 0, brand_id: int = 0, win_id: int = 0) -> dict:
""" Формируем выдачу цен для фрейма
:param apartment_id: int -- ID типа квартиры, для которой получаем ценовые предложения
:param mount_dim_per_offer: int -- число различных оконых проемов в этой квартире (чтобы отсеять предложения,
в которых не представлены все проемы)
:param address_longitude: float -- долгота адреса (геокоордината), чтобы рассчитать удаленность компании
предоставившей коммерческие предложения
:param address_latitude: float -- широта адреса (геокоордината), чтобы рассчитать удаленность компании
предоставившей коммерческие предложения
:param frame_begin_n: int -- Номер записи с которой начинается фрейм с ценами (с какого предложения начинать)
:param brand_id: int -- ID бренда, если выбран бренд, то отображаем только предложения этого бренда
(нужно для виджета, где отображаются предложения только от одной компании)
если 0, то отображаем все предложения
:param win_id: int -- ID окна, если выбрано окно, то отображаем только предложения этого окна
:return: dict -- словарь данных для отображения в фрейме (цены предложений и их характеристики)
"""
# ценовая выдача
time_for_meta = 0 # время для мета-данных в HTML-кода
apartment_id = int(apartment_id)
mount_dim_per_offer = int(mount_dim_per_offer)
address_longitude = float(address_longitude)
address_latitude = float(address_latitude)
frame_begin_n = int(frame_begin_n)
brand_id = int(brand_id)
win_id = int(win_id)
add_to_sql_for_widget = ""
offer_per_frame = OFFER_PER_FRAME
if brand_id != 0:
# Это вывод для выджета. Нужны цены только по определенному поставщику
add_to_sql_for_widget = f" AND oknardia_merchantbrand.id = {brand_id} "
offer_per_frame = 1000 # Фреймовый вывод не нужен... фигачим сразу целую 1000 предложений.
if int(apartment_id) == 0 and int(win_id) != 0:
# если выводим цены только для одного проема
offer_per_frame = OFFER_PER_FRAME_FOR_ONE_FLAP
q_price_offer = PriceOffer.objects.raw(
f"SELECT"
f" oknardia_priceoffer.id, oknardia_priceoffer.iOfferImpressions,"
f" oknardia_priceoffer.fOfferPrice, oknardia_priceoffer.dOfferModify,"
f" oknardia_priceoffer.fOfferRating, oknardia_priceoffer.sOfferFlapConfig,"
f" oknardia_priceoffer.iOfferViews, oknardia_priceoffer.sOfferActive,"
f" oknardia_win_mountdim.sDescripion, oknardia_win_mountdim.id AS mID, "
f" oknardia_win_mountdim.bIsNearDoor, oknardia_win_mountdim.bIsDoor,"
f" oknardia_win_mountdim.iWinWidth, oknardia_win_mountdim.iWinHight,"
f" oknardia_setkit.id AS setID,"
f" oknardia_setkit.sSetName, oknardia_setkit.dSetModify,"
f" oknardia_setkit.sSetClimateControl, oknardia_setkit.sSetSill,"
f" oknardia_setkit.sSetImplementAll, oknardia_setkit.sSetImplementHandles,"
f" oknardia_setkit.sSetImplementHinges, oknardia_setkit.sSetImplementLatch,"
f" oknardia_setkit.sSetImplementLimiter, oknardia_setkit.sSetImplementCatch,"
f" oknardia_setkit.sSetPanes, oknardia_setkit.sSetSlope,"
f" oknardia_setkit.sSetOtherConditions, oknardia_setkit.sSetActive,"
f" oknardia_setkit.bSetDelivery, oknardia_setkit.sSetDelivery,"
f" oknardia_setkit.sSetUninstallInstall, oknardia_setkit.bSetUninstallInstall,"
f" oknardia_setkit.fSetRating, oknardia_setkit.iSetNumEval,"
f" oknardia_setkit.iSetImpressions, oknardia_setkit.iSetViews,"
f" (oknardia_setkit.dSetCommercialUntil > NOW()) AS bCommercial,"
f" oknardia_merchantoffice.sOfficePhones, "
f" oknardia_merchantoffice.sOfficeDiscountMetaFormula,"
f" oknardia_merchantoffice.sOfficeName, oknardia_merchantoffice.sOfficeAddress,"
f" oknardia_glazing.fGlazingRating,"
f" oknardia_glazing.sGlazingName, oknardia_glazing.sGlazingBriefDescription,"
f" oknardia_glazing.sGlazingMark, oknardia_glazing.sGlazingToning,"
f" oknardia_pvcprofiles.sProfileBriefDescription, oknardia_pvcprofiles.id AS pwc_id,"
f" oknardia_pvcprofiles.sProfileReinforcement, oknardia_pvcprofiles.sProfileSealDescription,"
f" oknardia_pvcprofiles.sProfileName, oknardia_pvcprofiles.sProfileColor,"
f" oknardia_pvcprofiles.fProfileRating, oknardia_pvcprofiles.sProfileManufacturer,"
f" oknardia_merchantbrand.sMerchantName, oknardia_merchantbrand.pMerchantLogo,"
f" oknardia_merchantbrand.sMerchantMainURL, oknardia_merchantbrand.id AS brand_id,"
f" 1 AS iQuantity, 0 AS fOfficeGeoCode_Longitude, 0 AS fOfficeGeoCode_Latitude "
f"FROM oknardia_priceoffer"
f" INNER JOIN oknardia_win_mountdim"
f" ON oknardia_priceoffer.kOffer2MountDim_id = oknardia_win_mountdim.id"
f" INNER JOIN oknardia_setkit"
f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id"
f" INNER JOIN oknardia_ouruser"
f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id"
f" INNER JOIN oknardia_merchantoffice"
f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id"
f" INNER JOIN oknardia_glazing"
f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id"
f" INNER JOIN oknardia_pvcprofiles"
f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id"
f" INNER JOIN oknardia_merchantbrand"
f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id "
f"WHERE oknardia_priceoffer.sOfferActive IS TRUE"
f" AND oknardia_setkit.sSetActive IS TRUE "
f" AND oknardia_win_mountdim.id = {int(win_id)}"
f" {add_to_sql_for_widget} "
f"ORDER BY"
f" oknardia_priceoffer.dOfferModify DESC "
f"LIMIT {int(frame_begin_n)}, 10000;")
else:
# если выводим цены для типовой квартиры
# print("Нужно несколько окон для квартиры")
q_price_offer = PriceOffer.objects.raw(
f"SELECT"
f" oknardia_priceoffer.*,"
f" oknardia_win_mountdim.*,"
f" oknardia_setkit.*,"
f" oknardia_merchantoffice.*,"
f" oknardia_glazing.*,"
f" oknardia_pvcprofiles.*,"
f" oknardia_merchantbrand.*,"
f" oknardia_mountdim2apartment.iQuantity,"
f" oknardia_win_mountdim.id AS mID, "
f" oknardia_setkit.id AS setID,"
f" (oknardia_setkit.dSetCommercialUntil > NOW()) AS bCommercial,"
f" oknardia_pvcprofiles.id AS pwc_id,"
f" oknardia_merchantbrand.id AS brand_id "
f"FROM oknardia_priceoffer"
f" INNER JOIN oknardia_win_mountdim"
f" ON oknardia_priceoffer.kOffer2MountDim_id = oknardia_win_mountdim.id"
f" INNER JOIN oknardia_setkit"
f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id"
f" INNER JOIN oknardia_ouruser"
f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id"
f" INNER JOIN oknardia_merchantoffice"
f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id"
f" INNER JOIN oknardia_glazing"
f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id"
f" INNER JOIN oknardia_pvcprofiles"
f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id"
f" INNER JOIN oknardia_mountdim2apartment"
f" ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id"
f" INNER JOIN oknardia_merchantbrand"
f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id "
f"WHERE oknardia_priceoffer.sOfferActive IS TRUE"
f" AND oknardia_mountdim2apartment.kApartment_id = {int(apartment_id)}"
f" AND oknardia_setkit.sSetActive IS TRUE {add_to_sql_for_widget} "
f"ORDER BY"
f" oknardia_setkit.dSetCreate DESC, " # Сейчас окна в наборе собираются через это
f" oknardia_win_mountdim.bIsNearDoor DESC,"
f" oknardia_win_mountdim.bIsDoor DESC,"
f" oknardia_win_mountdim.iWinWidth,"
f" oknardia_win_mountdim.iWinHight DESC "
f"LIMIT {int(frame_begin_n)} , 10000;")
# print list(qPO)
price_frame = []
count_mount_dim_in_offer = 0
dim_in_offer = []
total = 0
cur_bullet = 0
previous_set_id = 0
count_mount_dim_in_frame_page = 0
# Так как получен QuerySet начиная с frame_begin_n, то для того чтобы получить frame_begin_n следующего фрйма
# считаем не от нуля, а от старого frame_begin_n
n_begin = int(frame_begin_n)
# Проверяем есть ли папка для хранения мини-картинки конфигурации схемы открывания.
if not os.path.exists(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}"):
# создаем такую папку если её нет
os.makedirs(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}")
# print(">>>>>>>>>>>>>", apartment_id)
for i2 in q_price_offer:
n_begin += 1
count_mount_dim_in_frame_page += 1
# Случается, что в том или ином наборе поставщиком просчитаны не все проёмы.
# Чтобы не происходило формирование предложения (офера) из окон разных наборов делаем проверку
# является ли текущий проём в предложении из того же набора, что и предыдущий.
# Если он из другого набора, то удаляем предыдущий проём из предложения и начинаем
# формирование предложения сначала.
if count_mount_dim_in_offer == 0:
previous_set_id = i2.setID
else:
if previous_set_id != i2.setID:
# print("Сбой в наборе. Обнуляем набор")
previous_set_id = i2.setID
count_mount_dim_in_offer = 0
total = 0
cur_bullet = 0
dim_in_offer.pop()
dim_in_offer = []
# print("mID:", i2.mID, " || pID:", i2.id, " || price:", i2.fOfferPrice, " || N:", i2.iQuantity,
# " || set:", i2.sSetName, " || merchant:", i2.sOfficeName)
total += i2.fOfferPrice * i2.iQuantity
image_file = get_flaps_for_mini_pictures(i2.sOfferFlapConfig)
dim_in_offer.append({
'PRICE': i2.fOfferPrice,
'FLAP': i2.sOfferFlapConfig,
'DESCRIPTION': i2.sDescripion,
'WIDTH': i2.iWinWidth,
'HIGHT': i2.iWinHight,
'ID': i2.id,
'IMG_MINI': image_file,
'QUANTITY': i2.iQuantity,
'BULLET': [chr(65+cur_bullet+i) for i in range(i2.iQuantity)],
# 'BULLET': range(CurBullet, CurBullet+i2.iQuantity),
'SUBTOTAL': i2.fOfferPrice * i2.iQuantity,
})
cur_bullet += i2.iQuantity
count_mount_dim_in_offer += 1
if count_mount_dim_in_offer == mount_dim_per_offer:
# print("-----------------")
# узнаем скидку через разбор формулы на метаязыке
discount = 0
try:
meta_keys = eval(i2.sOfficeDiscountMetaFormula)
if KEY_DICSOUNT in meta_keys:
# скидки рассчитываются исходя из общей суммы
for CountVal in sorted(meta_keys[KEY_DICSOUNT]):
# print(CountVal, "::", meta_keys[KEY_DICSOUNT][CountVal])
if float(total) > float(CountVal):
discount = meta_keys[KEY_DICSOUNT][CountVal]
# # DiscountTXT += u"!!%d!!" % Discount
# print("Значит DISCOUNT: ", Discount)
except (ValueError, TypeError):
pass
fin_price = total * (100 - discount) / 100
# уточняем, есть ли в принципе геокоординаты?
if int(i2.fOfficeGeoCode_Longitude) != 0 and int(i2.fOfficeGeoCode_Latitude) != 0 and \
int(address_longitude) != 0 and int(address_latitude) != 0:
# рассчитываем дистанцию между адресом дома и офиса.
distance = get_geo_distance(i2.fOfficeGeoCode_Longitude, i2.fOfficeGeoCode_Latitude, address_longitude,
address_latitude)
# т.к. из-за изменений в api яндекс карт поменялась местами широта-долгота и вообще, то
# порядок переменных строчной выше... На самом деле должно быть как в закоментированной
# строке ниже
# distance = get_geo_distance(i2.fOfficeGeoCode_Longitude, i2.fOfficeGeoCode_Latitude, address_longitude,
# address_latitude)
else:
distance = -1
if discount > 99 or discount < 0.1:
discount_color1 = ""
discount_color2 = ""
else:
color_ratio = (discount + 0.) / 100
discount_color1 = f"#{255 - int(color_ratio * 128): 02x}ff{255 - int(color_ratio * 128) :02x}"
discount_color2 = f"#{255-int(color_ratio * 255): 02x}ff{255 - int(color_ratio * 255) :02x}"
price_frame.append({
'DISTANCE': distance,
'DIM': dim_in_offer,
'TOTAL': total,
'DISCOUNT': discount,
'DISCOUNT_COLOR1': discount_color1,
'DISCOUNT_COLOR2': discount_color2,
'FIN_PRICE': fin_price,
'OFFICE_NAME': i2.sOfficeName,
'OFFICE_ADDRESS': i2.sOfficeAddress,
'OFFICE_PHONES': i2.sOfficePhones,
'MERCHANT': i2.sMerchantName,
'MERCHANT_LOGO': i2.pMerchantLogo,
'MERCHANT_URL': i2.sMerchantMainURL,
'MERCHANT_URL_SHOT': re.sub(r"(?:^http://|^https://|/$|www\.)", "", i2.sMerchantMainURL),
'SETS_NAME': i2.sSetName,
'GLAZING_NAME_B': i2.sGlazingBriefDescription,
'GLAZING_MARK': i2.sGlazingMark,
'GLAZING_TONING': i2.sGlazingToning,
'PVC_ID': i2.pwc_id,
'PVC_NAME': i2.sProfileName,
'PVC_NAME_T': pytils.translit.slugify(i2.sProfileName).lower(),
'PVC_MANUFACTURER': i2.sProfileManufacturer,
'PVC_MANUFACTURER_T': pytils.translit.slugify(i2.sProfileManufacturer).lower(),
'PVC_SEAL': i2.sProfileSealDescription,
'SETS_CLIMATE_CONTROL': i2.sSetClimateControl,
'SETS_SILL': i2.sSetSill,
'SETS_IMPLEMENT': i2.sSetImplementAll,
'SETS_IMPLEMENT_R': i2.sSetImplementHandles,
'SETS_IMPLEMENT_P': i2.sSetImplementHinges,
'SETS_IMPLEMENT_Z': i2.sSetImplementLatch,
'SETS_IMPLEMENT_O': i2.sSetImplementLimiter,
'SETS_IMPLEMENT_F': i2.sSetImplementCatch,
'SETS_PANES': i2.sSetPanes,
'SETS_SLOPE': i2.sSetSlope,
'SETS_DELIVERY': i2.sSetDelivery,
'SETS_DELIVERY_B': i2.bSetDelivery,
'SETS_OTHER': i2.sSetOtherConditions,
'SETS_ID': i2.setID,
'SETS_UNINSTALL_INSTALL': i2.sSetUninstallInstall,
'SETS_UNINSTALL_INSTALL_B': i2.bSetUninstallInstall,
'SETS_RATING': i2.fSetRating,
'SETS_RATING_STARTS': get_rating_set_for_stars(i2.fSetRating),
'SETS_DATA_MODIFY': i2.dOfferModify,
'IS_COMMERCIAL': i2.bCommercial,
})
if len(price_frame) == offer_per_frame:
break
count_mount_dim_in_offer = 0
dim_in_offer = []
total = 0
cur_bullet = 0
# узнаем дату-время самого свежего ценового предложения для размещения в META-тега
if time_for_meta == 0 or django.utils.dateformat.format(time_for_meta, 'U') < \
django.utils.dateformat.format(i2.dOfferModify, 'U'):
time_for_meta = i2.dOfferModify
if time_for_meta == 0 or django.utils.dateformat.format(time_for_meta, 'U') < \
django.utils.dateformat.format(i2.dSetModify, 'U'):
time_for_meta = i2.dSetModify
# массив ценовых предложений (что бы предложения на одной дистанции были в случайном порядке)
# random.shuffle(PriceFrame)
# сортируем по удаленности
price_frame = sorted(price_frame, key=lambda item: item['DISTANCE'])
if len(price_frame) < offer_per_frame:
n_begin = '-1'
return {'META_DATA_PUBLISH': time_for_meta, 'PRICE_FRAME': price_frame, 'N': n_begin}
def report_one_win_price(request: HttpRequest, win_width_mm: str = '670', win_height_mm: str = '2160',
win_id: str = '16') -> HttpResponse:
""" Формируем выдачу цен для единичного ТИПОВОГО окна (т.е. проема из серийного дома).
:param request: HttpRequest -- входящий http-запрос
:param win_width_mm: str -- Ширина проема в миллиметрах (это SEO-параметр, в реальности он будет получен из базы)
:param win_height_mm: str -- Высота проема в миллиметрах (это SEO-параметр, в реальности он будет получен из базы)
:param win_id: str -- ID проема (см. таблицу oknardia_win_mountdim)
:return response: HttpResponse -- исходящий http-ответ
"""
time_start = time.time()
to_template = {}
try:
# т.к. для вызова GetFlapDim4BigPictures нужно иметь внутри queryset поле iQuantity нельзя использовать
# простой запрос (см. следующую строку).
# qWinInfo = Win_MountDim.objects.filter(id=int(win_id))
# Придется сделать запрос немного сложнее:
q_win_info = Win_MountDim.objects.raw(
f'SELECT oknardia_win_mountdim.iWinWidth,'
f' oknardia_win_mountdim.iWinHight, oknardia_win_mountdim.iWinDepth,'
f' oknardia_win_mountdim.sFlapConfig, oknardia_win_mountdim.bIsNearDoor,'
f' oknardia_win_mountdim.bIsDoor, oknardia_win_mountdim.sDescripion,'
f' oknardia_win_mountdim.id, 0 as iQuantity '
f'FROM oknardia_win_mountdim '
f'WHERE oknardia_win_mountdim.id = {int(win_id)};'
)
list_win_info = list(q_win_info)
# Если размеры типового проема не совпадают с размерами из базы, то подменяем
# на правильные и перевызываем страницу
if (list_win_info[0].iWinWidth * 10 != int(win_width_mm)) or \
(list_win_info[0].iWinHight * 10 != int(win_height_mm)):
return redirect(f"/tsena-odnogo-okna/{list_win_info[0].iWinWidth * 10}x{list_win_info[0].iWinHight * 10}"
f"mm/tip{win_id}")
except (ObjectDoesNotExist, ValueError, IndexError, TypeError):
return redirect("/tsena-odnogo-okna/670x2160mm/tip16")
# все хорошо, засылаем картинку в шаблон
to_template.update(get_flaps_for_big_pictures(list_win_info))
# получаем варианты схемы открывания (для графиков)
q_offer_flap_variation = PriceOffer.objects.raw(
f'SELECT'
f' COUNT(oknardia_priceoffer.sOfferFlapConfig) AS id,'
f' "" AS IMG_MINI,'
f' "" AS STR_NUM,'
f' oknardia_priceoffer.sOfferFlapConfig '
f'FROM oknardia_priceoffer '
f'WHERE oknardia_priceoffer.sOfferActive <> 0'
f' AND oknardia_priceoffer.kOffer2MountDim_id = {int(win_id)} '
f'GROUP BY oknardia_priceoffer.sOfferFlapConfig,'
f' oknardia_priceoffer.sOfferActive,'
f' oknardia_priceoffer.kOffer2MountDim_id '
f'ORDER BY id DESC;'
)
list_offer_flap_variation = list(q_offer_flap_variation)
for i in range(0, len(list_offer_flap_variation)):
if i < 3:
list_offer_flap_variation[i].STR_NUM = "вариант " + pytils.numeral.in_words(i + 1)
elif i == 3:
list_offer_flap_variation[i].STR_NUM = "остальные варианты"
continue
else:
list_offer_flap_variation[3].id += list_offer_flap_variation[i].id
continue
list_offer_flap_variation[i].IMG_MINI = get_flaps_for_mini_pictures(
list_offer_flap_variation[i].sOfferFlapConfig
)
to_template.update({'LIST_FLAP_VARIATION': list_offer_flap_variation[:4]})
to_template.update({'NUM_FLAP_VARIATION_IN_WORD': pytils.numeral.sum_string(len(list_offer_flap_variation),
pytils.numeral.MALE,
("вариант схемы",
"варианта схем",
"вариантов схем"))})
#
q = PriceOffer.objects.raw(f'SELECT'
f' COUNT(oknardia_priceoffer.kOfferFromUser_id) AS id,'
f' oknardia_priceoffer.kOfferFromUser_id,'
f' oknardia_priceoffer.kOffer2MountDim_id '
f'FROM oknardia_priceoffer '
f'WHERE oknardia_priceoffer.kOffer2MountDim_id = {int(win_id)} '
f'GROUP BY oknardia_priceoffer.kOffer2MountDim_id,'
f' oknardia_priceoffer.kOfferFromUser_id;')
to_template.update({'NUM_TOTAL_FIRM_N_WORD': pytils.numeral.get_plural(len(list(q)),
("компании", "компаний", "компаний"))})
q = PriceOffer.objects.filter(kOffer2MountDim_id=int(win_id))
to_template.update({'NUM_TOTAL_OFFER_N_WORD': pytils.numeral.get_plural(q.count(),
("готовый расчёт", "готовых расчёта",
"готовых расчётов"))})
to_template.update({'NUM_ARCHIVE_OFFER': q.filter(sOfferActive=0).count()})
#
q_seria_for_win = PriceOffer.objects.raw(
f'SELECT'
f' oknardia_seria_info.sName, oknardia_seria_info.id AS id,'
f' "" AS sNameLat,'
f' COUNT(oknardia_mountdim2apartment.id) AS num_variation_of_apartment '
f'FROM oknardia_apartment_type'
f' INNER JOIN oknardia_mountdim2apartment'
f' ON oknardia_mountdim2apartment.kApartment_id = oknardia_apartment_type.id'
f' INNER JOIN oknardia_win_mountdim'
f' ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id'
f' INNER JOIN oknardia_seria_info'
f' ON oknardia_apartment_type.kSeria_id = oknardia_seria_info.id '
f'WHERE oknardia_win_mountdim.id = {int(win_id)} '
f'GROUP BY oknardia_win_mountdim.id,'
f' oknardia_seria_info.sName,'
f' oknardia_seria_info.id '
f'ORDER BY oknardia_seria_info.sName;'
)
list_seria_for_win = list(q_seria_for_win)
for i in list_seria_for_win:
i.sNameLat = pytils.translit.slugify(i.sName)
i.num_variation_of_apartment = pytils.numeral.sum_string(i.num_variation_of_apartment,
pytils.numeral.MALE,
("типовую планировку квартиры",
"типовые планировки квартир",
"типовых планировок квартир"))
to_template.update(report_price_frame(0, 1, 0, 0, 0, 0, int(win_id)))
to_template.update({
'SERIA_FOR_WIN': list_seria_for_win,
'WIN_ID': int(win_id),
'MOUNT_DIM_PER_OFFER': 1,
# получаем последние визиты клиента через куки
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
# получаем последние визиты всех посетителей из базы
# id2log, log_visit = get_last_all_user_visit_list()
'LOG_VISIT': get_last_all_user_visit_list(),
'ticks': float(time.time() - time_start)
})
return render(request, "report/report_price-offers_for_one_window.html", to_template)