667 lines
42 KiB
Python
667 lines
42 KiB
Python
# -*- 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, Apartment_Type, Seria_Info, LogVisitPriceReport
|
||
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 json
|
||
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
|
||
#p rint(discount)
|
||
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}"
|
||
#p rint(discount_color1, discount_color2)
|
||
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)
|
||
|
||
|
||
def report_price(request: HttpRequest, build_id: str = "22427", apart_id: str = "61",
|
||
slug: str = "g-moskva-ul-novorossijskaya-d-16") -> HttpResponse:
|
||
""" Страница с расчетом стоимости окон
|
||
|
||
:param request: HttpRequest -- входящий http-запрос
|
||
:param build_id: str - id здания (адрес в таблице oknardia_building_info.id)
|
||
:param apart_id: str - id типовой планировки квартиры (в таблице oknardia_apartment_type.id)
|
||
:param slug: str - slug адреса здания
|
||
:return: response: HttpResponse
|
||
"""
|
||
time_start = time.time()
|
||
msg = ""
|
||
to_template = {}
|
||
try:
|
||
build_id = int(build_id)
|
||
apart_id = int(apart_id)
|
||
except ValueError:
|
||
return redirect("/")
|
||
try:
|
||
# получаем все типы квартир для данного адреса (а заодно и попутную информацию о площади дома и пр.)
|
||
q_apart = Apartment_Type.objects.raw(
|
||
f'SELECT'
|
||
f' oknardia_apartment_type.sNameApartment, oknardia_apartment_type.id,'
|
||
f' oknardia_apartment_type.iSort, oknardia_seria_info.kRoot_id,'
|
||
f' oknardia_building_info.kSeria_Link_id, oknardia_building_info.sAddress,'
|
||
f' oknardia_building_info.fGeoCode_Latitude, oknardia_building_info.fGeoCode_Longitude,'
|
||
f' oknardia_building_info.fTotal_Area, oknardia_building_info.sCadastre_Num_Area,'
|
||
f' oknardia_building_info.fLand_Area, oknardia_building_info.sInventory_Num,'
|
||
f' oknardia_building_info.iNum_Apartments, oknardia_building_info.sType,'
|
||
f' oknardia_building_info.iStoreys, oknardia_building_info.fCommon_Area,'
|
||
f' oknardia_building_info.sEnergy_Efficiency, oknardia_building_info.iEntrances_Porchs,'
|
||
f' oknardia_building_info.fUninhabited_Area, oknardia_building_info.sManagement_Co,'
|
||
f' oknardia_building_info.iElevators, oknardia_building_info.fResidential_Area,'
|
||
f' oknardia_building_info.iNum_Residents, oknardia_building_info.fPrivate_Area,'
|
||
f' oknardia_building_info.iNum_Accounts, oknardia_building_info.iCommissioning_year,'
|
||
f' oknardia_building_info.fGovernment_Area, oknardia_building_info.fCondition_House,'
|
||
f' oknardia_building_info.fCondition_Foundation, oknardia_building_info.fCondition_Walls,'
|
||
f' oknardia_building_info.fCondition_Overlap, oknardia_building_info.fMunicipal_Area,'
|
||
f' oknardia_building_info.sSerias_Project '
|
||
f'FROM oknardia_seria_info '
|
||
f'INNER JOIN oknardia_apartment_type'
|
||
f' ON oknardia_seria_info.kRoot_id = oknardia_apartment_type.kSeria_id '
|
||
f' INNER JOIN oknardia_building_info'
|
||
f' ON oknardia_building_info.kSeria_Link_id = oknardia_seria_info.id '
|
||
f'WHERE oknardia_building_info.id = {build_id} '
|
||
f'ORDER BY oknardia_apartment_type.iSort;')
|
||
list_apart = list(q_apart)
|
||
# если кто-то нахимичит ID квартиры не для этого дома, то сделаем так, что он будет от этого дома!
|
||
apart_inside = False
|
||
for i in q_apart:
|
||
if i.id == apart_id:
|
||
apart_inside = True
|
||
break
|
||
if not apart_inside or slug != pytils.translit.slugify(list_apart[0].sAddress):
|
||
# Переадресация 302, если с apart_id (ID-квартиры нахимичили) или slug-ом.
|
||
# Нужно для склейки парных URL в поисковиках
|
||
# При переходе с карты apart_id выставляем в 0. Из-за этого тоже нужно 302-переадресация.
|
||
return redirect(f"/{build_id}/{list_apart[0].id}/{pytils.translit.slugify(list_apart[0].sAddress)}")
|
||
address_latitude = list_apart[0].fGeoCode_Latitude
|
||
address_longitude = list_apart[0].fGeoCode_Longitude
|
||
to_template.update({'BUILD_ID': build_id})
|
||
to_template.update({'APPARTMENT_ID': apart_id})
|
||
to_template.update({'ADDRESS_LAT': address_latitude})
|
||
to_template.update({'ADDRESS_LON': address_longitude})
|
||
to_template.update({'ADDRESS': list_apart[0].sAddress})
|
||
to_template.update({'ADDRESS_T': pytils.translit.slugify(list_apart[0].sAddress)})
|
||
to_template.update({'SERIA': list_apart[0].sSerias_Project})
|
||
# данные нужные для отображения информации о доме (метраж, число подъездов и пр.)
|
||
to_template.update({'CADASTRE_NUM': list_apart[0].sCadastre_Num_Area})
|
||
to_template.update({'INVENTORY_NUM': list_apart[0].sInventory_Num})
|
||
to_template.update({'TYPE_BUILDING': list_apart[0].sType})
|
||
to_template.update({'ENERGY_EFFICIENCY': list_apart[0].sEnergy_Efficiency})
|
||
if list_apart[0].fTotal_Area != -1.0:
|
||
to_template.update({'TOTAL_AREA': f"{list_apart[0].fTotal_Area:.1f}"})
|
||
if list_apart[0].fLand_Area != -1.0:
|
||
to_template.update({'LAND': f"{list_apart[0].fLand_Area:.1f}"})
|
||
if list_apart[0].iNum_Apartments != -1:
|
||
to_template.update({'NUM_APARTMENTS': list_apart[0].iNum_Apartments})
|
||
if list_apart[0].iStoreys != -1:
|
||
to_template.update({'STOREYS': list_apart[0].iStoreys})
|
||
if list_apart[0].fCommon_Area != -1.0:
|
||
to_template.update({'COMMON_AREA': f"{list_apart[0].fCommon_Area:.1f}"})
|
||
if list_apart[0].iEntrances_Porchs != -1:
|
||
to_template.update({'NUM_ENTERANCES': list_apart[0].iEntrances_Porchs})
|
||
if list_apart[0].fUninhabited_Area != -1.0:
|
||
to_template.update({'UNINHABITED_AREA': f"{list_apart[0].fUninhabited_Area:.1f}"})
|
||
if list_apart[0].sManagement_Co != u"N/A":
|
||
to_template.update({'MANAGEMENT_CO': list_apart[0].sManagement_Co})
|
||
if list_apart[0].iElevators != -1:
|
||
to_template.update({'NUM_ELEVATORS': list_apart[0].iElevators})
|
||
if list_apart[0].fResidential_Area != -1.0:
|
||
to_template.update({'RESIDENTIAL_AREA': f"{list_apart[0].fResidential_Area:.1f}"})
|
||
if list_apart[0].iNum_Residents != -1:
|
||
to_template.update({'NUM_RESIDENTS': list_apart[0].iNum_Residents})
|
||
if list_apart[0].fPrivate_Area != -1.0:
|
||
to_template.update({'PRIVATE_AREA': f"{list_apart[0].fPrivate_Area:.1f}"})
|
||
if list_apart[0].iNum_Accounts != -1:
|
||
to_template.update({'NUM_ACCOUNTS': list_apart[0].iNum_Accounts})
|
||
if list_apart[0].iCommissioning_year != "N/A":
|
||
to_template.update({'COMMISSIONING_YEAR': list_apart[0].iCommissioning_year})
|
||
if list_apart[0].fGovernment_Area != -1.0:
|
||
to_template.update({'GOVERNMENT_AREA': f"{list_apart[0].fGovernment_Area:.1f}"})
|
||
if list_apart[0].fCondition_House != -1.0:
|
||
to_template.update({'CONDITION_HOUSE': f"{list_apart[0].fCondition_House:.0f}%"})
|
||
if list_apart[0].fCondition_Foundation != -1.0:
|
||
to_template.update({'CONDITION_FOUNDATION': f"{list_apart[0].fCondition_Foundation:.0f}%"})
|
||
if list_apart[0].fCondition_Walls != -1.0:
|
||
to_template.update({'CONDITION_WALL': f"{list_apart[0].fCondition_Walls:.0f}%"})
|
||
if list_apart[0].fCondition_Overlap != -1.0:
|
||
to_template.update({'CONDITION_OVERLAP': f"{list_apart[0].fCondition_Overlap:.0f}%"})
|
||
if list_apart[0].fMunicipal_Area != -1.0:
|
||
to_template.update({'MUNICIPAL_AREA': f"{list_apart[0].fMunicipal_Area:.1f}"})
|
||
# заполняем массив квартир для отправки в шаблон
|
||
apart_in_building = []
|
||
for apartment_count in q_apart:
|
||
apartment_in = {}
|
||
if apartment_count.id != apart_id:
|
||
apartment_in.update({'APT_ID': apartment_count.id})
|
||
else:
|
||
apartment_in.update({'APT_ID': "!"})
|
||
apartment_in.update({'APT_NAME': apartment_count.sNameApartment})
|
||
apart_in_building.append(apartment_in)
|
||
to_template.update({'APARTMENT_IN_BUILDING': apart_in_building})
|
||
|
||
# узнаем базовую серию дома
|
||
q_base_seria = Seria_Info.objects.get(id=list_apart[0].kRoot_id)
|
||
base_seria_slug = pytils.translit.slugify(q_base_seria.sName)
|
||
to_template.update({'BASE_SERIA': q_base_seria.sName,
|
||
'BASE_SERIA_LAT': base_seria_slug,
|
||
'BASE_SERIA_ID': q_base_seria.id})
|
||
except (ValueError, IndexError, TypeError, ObjectDoesNotExist):
|
||
return redirect("/")
|
||
###############################################
|
||
# получаем массив окон для данной квартиры...
|
||
try:
|
||
q_md = Win_MountDim.objects.raw(
|
||
f'SELECT'
|
||
f' oknardia_apartment_type.sNameApartment, 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, oknardia_mountdim2apartment.iQuantity '
|
||
f'FROM oknardia_mountdim2apartment '
|
||
f'INNER JOIN oknardia_apartment_type'
|
||
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'WHERE oknardia_mountdim2apartment.kApartment_id = {apart_id} '
|
||
f'GROUP BY'
|
||
f' oknardia_apartment_type.sNameApartment, 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_apartment_type.bApartmentCheck, oknardia_win_mountdim.dMountXYZModify,'
|
||
f' oknardia_apartment_type.dApartmentModify, oknardia_win_mountdim.iWinLimit,'
|
||
f' oknardia_win_mountdim.id, oknardia_mountdim2apartment.iQuantity '
|
||
f'ORDER BY'
|
||
f' oknardia_win_mountdim.bIsNearDoor DESC,'
|
||
f' oknardia_win_mountdim.bIsDoor DESC,'
|
||
f' oknardia_win_mountdim.iWinWidth,'
|
||
f' oknardia_win_mountdim.iWinHight DESC;')
|
||
list_mount_dim_per_offer = list(q_md)
|
||
mount_dim_per_offer = len(list_mount_dim_per_offer)
|
||
to_template.update({'APART': list_mount_dim_per_offer[0].sNameApartment})
|
||
|
||
# получаем данные для отрисовки больших картинок с проемами.
|
||
to_template.update(get_flaps_for_big_pictures(q_md))
|
||
# <---
|
||
except (ValueError, IndexError, TypeError, ObjectDoesNotExist):
|
||
return redirect("/")
|
||
|
||
# получаем данные для фрейма ценовых предложений
|
||
price_frame = report_price_frame(apart_id, mount_dim_per_offer, address_longitude, address_latitude)
|
||
to_template.update(price_frame)
|
||
# print u"строк в querySet:", CountMountDimInFramePage
|
||
# dimension_to_template.update({'DISCOUNT_TXT': DiscountTXT})
|
||
to_template.update({'MOUNT_DIM_PER_OFFER': mount_dim_per_offer,
|
||
'META_DESCRIPTION': "Окнардия ",
|
||
'META_KEYWORDS': "Окнардия+ ",
|
||
'MSG': msg})
|
||
|
||
# получаем последние визиты всех посетителей из базы
|
||
log_visit = get_last_all_user_visit_list()
|
||
id_last_visit_log = log_visit[0]['id'] + 1
|
||
# print("id_last_visit_log:", id_last_visit_log)
|
||
to_template.update({'LOG_VISIT': log_visit})
|
||
if id_last_visit_log > MAX_LEN_RING_LOG_BUFFER: # максимальный размер циклического буфера
|
||
id_last_visit_log = 1 # ставим в начало буфера
|
||
try:
|
||
log_entry = LogVisitPriceReport.objects.get(id=id_last_visit_log)
|
||
log_entry.sLogAddress = to_template["ADDRESS"]
|
||
log_entry.sLogNameApartment = to_template["APART"]
|
||
log_entry.sLogURL = f"/{build_id}/{apart_id}/{to_template['ADDRESS_T']}"
|
||
log_entry.dLogVisitTime = time.time()
|
||
log_entry.save() # UPDATE
|
||
except ObjectDoesNotExist:
|
||
log_entry = LogVisitPriceReport(
|
||
sLogAddress=to_template["ADDRESS"],
|
||
sLogNameApartment=to_template["APART"],
|
||
sLogURL=f"/{build_id}/{apart_id}/{to_template['ADDRESS_T']}",
|
||
dLogVisitTime=time.time()
|
||
)
|
||
log_entry.save() # INSERT
|
||
|
||
# получаем последние визиты клиента через куки
|
||
last_visit = get_last_user_visit_cookies(request)
|
||
to_template.update({'LAST_VISIT': get_last_user_visit_list(last_visit)})
|
||
# подготавливаем данные о текущем посещении для помещения в cookie
|
||
Item = {
|
||
"LastURL": f"/{build_id}/{apart_id}/{to_template['ADDRESS_T']}",
|
||
"LastAddress": to_template["ADDRESS"],
|
||
"LastApart": to_template["APART"],
|
||
"Time": time.time()}
|
||
last_visit.insert(0, Item) # Добавляем текущий Item в начало
|
||
last_visit = json.dumps(last_visit[:3]) # упаковываем json без пробелов (три записи)
|
||
# print u"сейчас запишем вот эту куку:", LastVisit
|
||
to_template.update({'ticks': float(time.time() - time_start)})
|
||
response = render(request, "report/report_pricelist.html", to_template)
|
||
response.set_cookie("LastVisit", last_visit, max_age=7862400) # ставим или перезаписываем куки (91 день)
|
||
return response
|