From 2eea80f55798d3e857e2a9a1a8a25d8d4409299d Mon Sep 17 00:00:00 2001 From: erjemin Date: Mon, 26 Dec 2022 17:19:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8C=D1=8E=D1=88=D0=BA=D0=B0:=20"=D0=9A?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=D0=BB=D0=BE=D0=B3=20/=20=D0=A2=D0=B8=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81=D0=B5=D1=80=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B9=20/=20=D0=98=D0=BD=D1=84?= =?UTF-8?q?=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B8=D0=B8"=20--=20=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oknardia/oknardia/settings.py | 16 +- oknardia/oknardia/urls.py | 1 + .../report/show_big_flap_pictures.html | 18 + .../seria_info/all_seria_info_pre_light.html | 120 ++++++ oknardia/templates/seria_info/geo_map.html | 56 +++ oknardia/templates/seria_info/seria_nav.html | 1 + oknardia/templates/seria_info/yaer_graph.html | 58 +++ oknardia/web/add_func.py | 298 ++++++++++++++ oknardia/web/catalog.py | 375 +++++++++++++++++- oknardia/web/diagrams.py | 24 +- 10 files changed, 928 insertions(+), 39 deletions(-) create mode 100755 oknardia/templates/report/show_big_flap_pictures.html create mode 100755 oknardia/templates/seria_info/all_seria_info_pre_light.html create mode 100755 oknardia/templates/seria_info/geo_map.html create mode 100755 oknardia/templates/seria_info/seria_nav.html create mode 100755 oknardia/templates/seria_info/yaer_graph.html diff --git a/oknardia/oknardia/settings.py b/oknardia/oknardia/settings.py index b99c051..a912571 100644 --- a/oknardia/oknardia/settings.py +++ b/oknardia/oknardia/settings.py @@ -28,10 +28,10 @@ SECRET_KEY = 'django-insecure-pd&1$j6z*1w#(j*16b+(@@#&2)+@x^^ot4)zqt-e67*1+$^qch # SECURITY WARNING: don't run with debug turned on in production! # ПРЕДУПРЕЖДЕНИЕ БЕЗОПАСНОСТИ: не работайте в режиме DEBUG в продашене! if socket.gethostname() in MY_HOST_DEV: - DEBUG = True + DEBUG = TEMPLATE_DEBUG = True else: # Все остальные хосты (подразумевается продакшн) - DEBUG = False + DEBUG = TEMPLATE_DEBUG = False ALLOWED_HOSTS = MY_ALLOWED_HOSTS @@ -181,11 +181,11 @@ OFFER_PER_FRAME = 5 OFFER_PER_FRAME_FOR_ONE_FLAP = 10 # папка для хранения изображений PATH_FOR_IMG = "img" -PATH_FOR_IMG_BLOG = u"img_for_blog/" -PATH_FOR_IMG_AVATAR = u"img_avatar/" -PATH_FOR_IMG_LOGOS = u"logos_img/" -PATH_FOR_IMG_APARTMENT = u"img_apart/" -PATH_FOR_IMG_SERIA = u"img_seria/" +PATH_FOR_IMG_BLOG = "img_for_blog/" +PATH_FOR_IMG_AVATAR = "img_avatar/" +PATH_FOR_IMG_LOGOS = "logos_img/" +PATH_FOR_IMG_APARTMENT = "img_apart/" +PATH_FOR_IMG_SERIA = "img_seria/" # папка для хранения мини-картинок со схемами открывания внутри PATH_FOR_IMG PATH_FOR_BIGIMGFLAPCONFIG = "_flap.cfg" @@ -195,7 +195,7 @@ PATH_FOR_JS = "js" PATH_FOR_JS_MAP = "js/4maps" SUFFIX_FOR_JS_MAP = "_seria_on_map.js" SUFFIX_FOR_MINI_JS_MAP = "_seria_on_map.mini.js" -PATH_FOR_SERIA_INFO_HTML_INCLUDE = "SeriaInfo/prepared/" +PATH_FOR_SERIA_INFO_HTML_INCLUDE = "seria_info/prepared/" # переменные # высота картинки diff --git a/oknardia/oknardia/urls.py b/oknardia/oknardia/urls.py index f432b95..094c96e 100644 --- a/oknardia/oknardia/urls.py +++ b/oknardia/oknardia/urls.py @@ -61,6 +61,7 @@ urlpatterns = [ catalog.catalog_profile_manufacture), # --- --- Каталог серий типового строительства re_path(r'^catalog/seria[/*]$', catalog.catalog_seria), + re_path(r'^catalog/seria/(?P[^/]*)/all(?P\d+)[/*]$', catalog.catalog_seria_info), ] diff --git a/oknardia/templates/report/show_big_flap_pictures.html b/oknardia/templates/report/show_big_flap_pictures.html new file mode 100755 index 0000000..5d83ee4 --- /dev/null +++ b/oknardia/templates/report/show_big_flap_pictures.html @@ -0,0 +1,18 @@ +{# Отрисовка больших картинок с проемами и схамаи открывания #}{% load static %}{% if WIN_DIM %} + {% for I_WIN_DIM in FLAP_DIM %} +
+
{{ I_WIN_DIM.sDescription }}. Размер {{ I_WIN_DIM.iWinWidth|stringformat:
+
+ {{ I_WIN_DIM.iWinWidth|stringformat:".0f" }}0×{{ I_WIN_DIM.iWinHight|stringformat:".0f" }}0 мм.
{% if not I_WIN_DIM.iQuantity == 0 %} + {{ I_WIN_DIM.iQuantity }} шт.{% for I_II in I_WIN_DIM.qStr %}{% endfor %}
{% endif %} + {{ I_WIN_DIM.sDescription }}{% if not I_WIN_DIM.iQuantity == 0 %}
+ цены только этого типового окна{% endif %} +
+
{% endfor %}{% comment %} +{% endcomment %}{% else %}

Нет данных о проемах и рекомендованных схемах открывания окон

{% endif %} \ No newline at end of file diff --git a/oknardia/templates/seria_info/all_seria_info_pre_light.html b/oknardia/templates/seria_info/all_seria_info_pre_light.html new file mode 100755 index 0000000..4c1f614 --- /dev/null +++ b/oknardia/templates/seria_info/all_seria_info_pre_light.html @@ -0,0 +1,120 @@ +{% extends "base.html" %} +{% load static %} +{% load filters %} +{% load humanize %} + +{% block Title %}серия {{ THIS_SERIA_NAME }} — типовые проёмы, размеры окон, схемы открывания, планировки, здания на карте, история и статистика{% 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 Description %}Информация по зданиям серии {{ THIS_SERIA_NAME }} и установке окон в них{% endblock %} + +{% block Keywords %}серии {{ THIS_SERIA_NAME }}, серия {{ THIS_SERIA_NAME }}, проект {{ THIS_SERIA_NAME }}, года простойки, размеры окон, размеры проемов, оконные проемы, {{ THIS_SERIA_NAME }} на карте, установка окон, цены на пластиковые окна{% endblock %} + +{% block Top_JS1 %} + {% endblock %} + +{% block Main_Content %}
+ {# #}
+
+ +

СЕРИЯ {{ THIS_SERIA_NAME }}

+
+
{# #} +
+
{{ THIS_SERIA_DESCRIPTION|safe }}
+
+ +
+
+

Окна в серии {{ THIS_SERIA_NAME }}: типовые размеры и схемы открывания

+
+
+ {% include 'report/show_big_flap_pictures.html' %} +
+
+ +
+
+

Оконные проёмы в типовых квартирах серии {{ THIS_SERIA_NAME }}

+
+
+ {% templatetag openblock %} for row in TABLE_OF_WINDOWS {% templatetag closeblock %} + + {% templatetag openblock %} for col in row.WIN_IN_APART {% templatetag closeblock %} + {% templatetag openblock %} endfor {% templatetag closeblock %} + + {% templatetag openblock %} endfor {% templatetag closeblock %} + + {% templatetag openblock %} for i in WIN_OFFER_AND_MERCHANT {% templatetag closeblock %} + {% templatetag openblock %} endfor {% templatetag closeblock %} + + +
{% templatetag openvariable %} row.APART_NAME|safe {% templatetag closevariable %}{% templatetag openblock %} if col.WIN_ID {% templatetag closeblock %}{% templatetag openblock %} for I_II in col.WIN_NUM {% templatetag closeblock %} {% templatetag openblock %} endfor {% templatetag closeblock %}{% templatetag openblock %} else {% templatetag closeblock %}—{% templatetag openblock %} endif {% templatetag closeblock %} {% templatetag openvariable %} row.NUM_OFFERS {% templatetag closevariable %}
© 2015-{% now "Y" %}, данные: oknardia.ru {% templatetag openvariable %} i.WIN_OFFER {% templatetag closevariable %}
+
+
+ +
+
+

Серия {{ THIS_SERIA_NAME }}: ввод в эксплуатацию по годам

+
+
+ {% include 'seria_info/yaer_graph.html' %} +
+
+
© 2015-{% now "Y" %}, данные: oknardia.ru
+
+
+ +
+
+

Серия {{ THIS_SERIA_NAME }}: жилой фонд проекта на карте

+
+
+

Чтобы посмотреть цены на установку и замену окон от партнёров «Окнардия» в своей квартире: найдите дом на карте; кликните на него; перейдите по ссылке «Смотреть коммерческие предложения». При необходимости смените типовую планировку квартиры (на странице ценовой выдачи, справа от изображения типовых проёмов и схем открывания).

+
{% include 'seria_info/geo_map.html' %}
+
© 2015-{% now "Y" %}, данные: oknardia.ru
+
+ +
+

Статистика серии {{ THIS_SERIA_NAME }}

+

Совокупно во всех зданиях типового проекта:

+
    +
  • {{ ACCOUNTS|price_format }} квартир.
  • +
  • Проживает {{ APARTMENTS|price_format }} семей ({{ RESIDENTS|price_format }} человек).
  • +
  • {{ RESIDENTIAL_M2|stringformat:".1f"|price_format }} м² жилых помещений.
  • +
  • {{ MUNICIPAL_M2|stringformat:".1f"|price_format }} м² — муниципальное жильё.
  • +
  • {{ GOVERNMENT_M2|stringformat:".1f"|price_format }} м² занимают государственные и городские службы, учреждения бытового обслуживания, магазины, офисы и тому подобное.
  • +
  • Максимальный износ жилого фонда серии {{ THIS_SERIA_NAME }} — {{ CONDITION_MAX|stringformat:".2f" }} %. Минимальный — {{ CONDITION_MIN|stringformat:".2f" }} %.
  • +
+
+
+ +
+
{#
#} +
Информация о других, отличных от {{ THIS_SERIA_NAME }}, типовых сериях в базе «Окнардия», типовых планировках квартир и оконных проёмах в них, а также рекомендации по замене окон:
+
{% include 'seria_info/seria_nav.html' %}
+
+
+
+ {% templatetag openblock %}include "report/report_last_user_visit.html" {% templatetag closeblock %} + {% templatetag openblock %} include "report/report_log_user_visit.html" {% templatetag closeblock %} +
+
+{% endblock %} \ No newline at end of file diff --git a/oknardia/templates/seria_info/geo_map.html b/oknardia/templates/seria_info/geo_map.html new file mode 100755 index 0000000..509b5af --- /dev/null +++ b/oknardia/templates/seria_info/geo_map.html @@ -0,0 +1,56 @@ +{# Скрипт Yandex.Chart для отображения клястеров на карте #}{% load static %} + {% block Top_JS5 %} +{% if MAP_JS %}{% else %}{% endif %}{% endblock %} +
+ +
\ No newline at end of file diff --git a/oknardia/templates/seria_info/seria_nav.html b/oknardia/templates/seria_info/seria_nav.html new file mode 100755 index 0000000..198e447 --- /dev/null +++ b/oknardia/templates/seria_info/seria_nav.html @@ -0,0 +1 @@ +{# Выводит навигацию по сериям домов #}{% for CountSeria in SERIA_NAV_DIM %}{% if CountSeria.SERIA_L == "" %}{{ CountSeria.SERIA_R }}{% else %}{{ CountSeria.SERIA_R }}{% endif %}{% endfor %} \ No newline at end of file diff --git a/oknardia/templates/seria_info/yaer_graph.html b/oknardia/templates/seria_info/yaer_graph.html new file mode 100755 index 0000000..b93224c --- /dev/null +++ b/oknardia/templates/seria_info/yaer_graph.html @@ -0,0 +1,58 @@ +{# Скрипт Google.Chart для рисования графика #}{% block Top_JS3 %} +{# #} +{% endblock %} +
+

Для отображения гистограммы ввода в эксплуатацию зданий серии {{ THIS_SERIA_NAME }} нужна поддержка SVG.

+
\ No newline at end of file diff --git a/oknardia/web/add_func.py b/oknardia/web/add_func.py index d5a8361..c02fef2 100644 --- a/oknardia/web/add_func.py +++ b/oknardia/web/add_func.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- __author__ = 'Sergei Erjemin' # from transliterate import translit +from PIL import Image, ImageDraw from oknardia.settings import * + +import os import re import math @@ -97,3 +100,298 @@ def normalize(val: float, val_max: int = 5, val_min: int = 0) -> float: :return: float: float -- нормализованное значение """ return float(val - val_min) / float(val_max - val_min) + + +def get_flaps_for_big_pictures(query_set) -> dict: + """ Возвращает словарь с размерами картинок для больших картинок. + + :param query_set: QuerySet -- QuerySet с объектами + :return: dict: dict -- словарь с размерами картинок для больших картинок + """ + result = {} + mount_max_h = 0 # максимальная высота оконного проема в квартире + door_h = 0 # высота двери + for i in query_set: # найдём максимальную высоту проёма окна и двери + if i.iWinHight >= mount_max_h: + mount_max_h = i.iWinHight + if i.bIsDoor >= door_h: + door_h = i.iWinHight + # Проверяем есть ли папка для хранения картинки конфигурации проема и схемы открывания для данной серии. + if not os.path.exists(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_BIGIMGFLAPCONFIG}"): + # создаем такую папку если ее нет + os.makedirs(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_BIGIMGFLAPCONFIG}") + flaps_dim = [] # список словарей, через который будут передаваться данные в шаблон + mount_bulk = 0 # размещение дверной перемычки + i_through = -1 + for i in query_set: + img_file_name = "%03dx%03dH%03d" % (i.iWinWidth, i.iWinHight, mount_max_h) + # img_file_name = f"{i.iWinWidth:03d}x{i.iWinHight:03d}H{mount_max_h:03d}" + if i.bIsDoor: + img_file_name += u"D" # добавляем букву D если это дверь + else: + img_file_name += u"W" # добавляем букву W если это окно + if i.bIsNearDoor: + img_file_name += u"N" # добавляем букву N если это окно рядом с дверью + mount_bulk = i.iWinHight # размещение дверной перемычки + else: + img_file_name += u"-" # добавляем символ отдельное окно (не рядом с дверью) + # маскируем символы схемы открывания, которые не допустимы в названии файлов + img_file_name += i.sFlapConfig + img_file_name = img_file_name.replace(">", "G") + img_file_name = img_file_name.replace("<", "L") + img_file_name = img_file_name.replace("[", "(") + img_file_name = img_file_name.replace("]", ")") + img_file_name = img_file_name.replace("|", "I") + img_file_name = f"{PATH_FOR_BIGIMGFLAPCONFIG}/{img_file_name}.png" + # проверяем, есть ли файл с нужной картинкой схемам открывания? + if not os.path.isfile(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{img_file_name}"): + # картинки нет, вызываем функцию для ее создания + make_big_img_win_flap(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{img_file_name}", i.iWinWidth, i.iWinHight, + i.bIsDoor, i.sFlapConfig, mount_max_h, mount_bulk, door_h) + # чтобы получить разноцветные маркеры меток количества проемов + # получаем последовательность тип: AB, CD, E, FG, H + q_local = "" + for i_local in range(0, i.iQuantity): + i_through += 1 + q_local += chr(65 + i_through) # 65 -- код "A" + flaps_dim.append({ + 'url2img': f"img/{img_file_name}", + 'iWinWidth': i.iWinWidth, + 'iWinHight': i.iWinHight, + 'iWinDepth': i.iWinDepth, + 'iQuantity': i.iQuantity, + 'sDescription': i.sDescripion, + 'id': i.id, + 'qStr': q_local, + 'W': int((i.iWinWidth*250) / mount_max_h) + }) + result.update({'FLAP_DIM': flaps_dim, + 'WIN_DIM': query_set}) + return result + + +def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, is_door: bool, + flap_config: str, height_max: int, height_mount_bulk: int, height_door: int) -> None: + """ + Функция создает png-картинку схемы открывания окна или двери + :param img_file_name_with_path: str -- полное имя файла картинки + :param width: int -- ширина окна или двери (см.) + :param height: int -- высота окна или двери + :param is_door: bool -- True - дверь, False - окно + :param flap_config: str -- схема открывания + :param height_max: int -- максимальная высота окна или двери (высота картинки) + :param height_mount_bulk: int -- высота расположения дверной перемычки + :param height_door: int -- высота дверного проема + :return: None + """ + # создаем картинку с нужными размерами + img = Image.new("RGBA", (width * PICT_H / height_max, PICT_H), (255, 255, 255, 0)) + # print(img_file_name_with_path) + # находим крайние точки периметра (если окно -- выравнено вверх; если дверь -- вниз) + top = 0 + left = 0 + bottom = img.size[1] + right = img.size[0] + draw = ImageDraw.Draw(img) + draw.rectangle((left, top, right-1, bottom-1), fill=(200, 200, 200, 50), outline=None) + if is_door: # это дверь. Выравнивание по нижней кромке + top = bottom - (height * PICT_H / height_max) + else: # это не дверь... Выравниваем по верхней кромке + if height < height_door: + top = bottom - (height_door * PICT_H / height_max) + bottom = top + (height * PICT_H / height_max) + flap_h = bottom - top + # рисуем внешнюю рамку + draw.line((left, bottom, right, bottom), fill=(125, 125, 125), width=25) + draw.line((left, top, right, top), fill=(125, 125, 125), width=25) + draw.line((left, top, left, bottom), fill=(125, 125, 125), width=25) + draw.line((right, top, right, bottom-6), fill=(125, 125, 125), width=25) + + dim_flap = flap_analiz(flap_config) + + ############################################################ + # НАЧИНАЕМ ОТРИСОВКУ СТВОРОК ОКНА НА КРТИНКУ + ############################################################ + v_ratio_max = 0 # число всех долей (частей) для построения пропорций по вертикали + for i in dim_flap: # цикл по рядам створок + v_ratio_max += i["vRatio"] + local_top = top + local_bottom = top + for i in dim_flap: # цикл по рядам створок + local_bottom += i["vRatio"]*flap_h/v_ratio_max + h_ratio_max = 0 # число всех долей (частей) для построения пропорций по горизонтали + for j in i["row"]: + h_ratio_max += j["hRatio"] + local_right = 0 + local_left = 0 + for j in i["row"]: + local_right += j["hRatio"] * right / h_ratio_max + # отрисовка схему открывания створки + if "M" in j["flap"] or "m" in j["flap"] or "м" in j["flap"] or "М" in j["flap"]: # москитная сетка + for k in range(local_left + 3, local_right - 3, 12): + draw.line((k, local_top + 7, k, local_bottom - 7), fill=(225, 225, 225, 255), width=2) + for k in range(local_top + 3, local_bottom - 3, 12): + draw.line((local_left + 7, k, local_right - 7, k), fill=(225, 225, 225), width=2) + if is_door: # Это дверь. Выравнивание по нижней кромке + top = bottom - (height * PICT_H / height_max) + # рисуем серединную перегородку = + draw.line((left, top + (height_mount_bulk * PICT_H / height_max) - 8, + right - 2, top + (height_mount_bulk * PICT_H / height_max) - 8), + fill=(125, 125, 125), width=8) + draw.line((left + 6, top + (height_mount_bulk * PICT_H / height_max) - 4, + right - 12, bottom - 12), fill=(125, 125, 125), width=3) + draw.line((right - 6, top + (height_mount_bulk * PICT_H / height_max) - 4, + left + 12, bottom - 12), fill=(125, 125, 125), width=3) + if "|" in j["flap"]: # вертикальная перегородка | + for k in range(j["flap"].count("|") + 1): + draw.line((local_left + k * (local_right - local_left) / (j["flap"].count("|") + 1), local_top, + local_left + k * (local_right - local_left) / (j["flap"].count("|") + 1), local_bottom), + fill=(125, 125, 125), width=4) + if "=" in j["flap"]: # горизонтальная перегородка = + for k in range(j["flap"].count("=") + 1): + draw.line((local_left, local_top + k * (local_bottom - local_top) / (j["flap"].count("=") + 1), + local_right, local_top + k * (local_bottom - local_top) / (j["flap"].count("=") + 1)), + fill=(125, 125, 125), width=4) + if "V" in j["flap"]: # откидное открывание V + draw.line((local_right - 12, local_bottom - 12, (local_right + local_left) / 2, local_top + 12), + fill=(225, 125, 125), width=1) + draw.line((local_left + 12, local_bottom - 12, (local_right + local_left) / 2, local_top + 12), + fill=(225, 125, 125), width=1) + if ">" in j["flap"] or "G" in j["flap"]: # поворотное влево > + draw.line((local_left + 12, local_top + 12, local_right - 12, (local_bottom + local_top) / 2), + fill=(225, 125, 125), width=1) + draw.line((local_left + 12, local_bottom - 12, local_right - 12, (local_bottom + local_top) / 2), + fill=(225, 125, 125), width=1) + if "<" in j["flap"] or "L" in j["flap"]: # поворотное вправо < + draw.line((local_right - 12, local_bottom - 12, local_left + 12, (local_bottom + local_top) / 2), + fill=(225, 125, 125), width=1) + draw.line((local_right - 12, local_top + 12, local_left + 12, (local_bottom + local_top) / 2), + fill=(225, 125, 125), width=1) + if "X" in j["flap"] or "x" in j["flap"] or "х" in j["flap"] or "Х" in j["flap"] or \ + "+" in j["flap"]: # глухое окно + + draw.line(((local_right + local_left) / 2 - 11, (local_bottom + local_top) / 2, + (local_right + local_left) / 2 + 11, (local_bottom + local_top) / 2), + fill=(225, 125, 125), width=2) + draw.line(((local_right + local_left) / 2, (local_bottom + local_top) / 2 - 11, + (local_right + local_left) / 2, (local_bottom + local_top) / 2 + 11), + fill=(225, 125, 125), width=2) + if u"Z" in j["flap"] or "z" in j["flap"] or "S" in j["flap"] or "s" in j["flap"]: # РАСШИРИТЕЛЬ (спейсер) + draw.line(((local_left * 3 + local_right) / 4, (local_top * 3 + local_bottom) / 4, + (local_right * 3 - local_left) / 4, (local_top * 3 + local_bottom) / 4), + fill=(225, 125, 125), width=4) + draw.line(((local_left * 3 + local_right) / 4, (local_bottom * 3 + local_top) / 4, + (local_right * 3 - local_left) / 4, (local_bottom * 3 + local_top) / 4), + fill=(225, 125, 125), width=4) + draw.line(((local_left * 3 + local_right) / 4, (local_top * 3 + local_bottom) / 4, + (local_right * 3 - local_left) / 4, (local_bottom * 3 + local_top) / 4), + fill=(225, 125, 125), width=4) + # Отрисовка створки. ПЕРИМЕТР. + draw.line((local_left, local_bottom, local_right, local_bottom), fill=(125, 125, 125), width=18) + draw.line((local_left, local_top, local_right, local_top), fill=(125, 125, 125), width=18) + draw.line((local_left, local_top, local_left, local_bottom), fill=(125, 125, 125), width=18) + draw.line((local_right, local_top, local_right, local_bottom), fill=(125, 125, 125), width=18) + + local_left = local_right + local_top = local_bottom + # второй проход -- рисуем тоненькую рамочку внутри толстых линий + local_top = top + local_bottom = top + for i in dim_flap: # цикл по рядам створок + local_bottom += i["vRatio"] * flap_h / v_ratio_max + h_ratio_max = 0 # число всех долей (частей) для построения пропорций по горизонтали + for j in i["row"]: + h_ratio_max += j["hRatio"] + local_right = 0 + local_left = 0 + for j in i["row"]: + local_right += j["hRatio"]*right/h_ratio_max + # Отрисовка створки. ПЕРИМЕТР + draw.rectangle((local_left, local_top, local_right, local_bottom), fill=None, outline=(0, 0, 0)) + local_left = local_right + local_top = local_bottom + # рисуем крест-на-крест перечеркивание всего проема (просто так, для отладки) + # draw.line((left+6, top+6, right-6, bottom-6), fill=(200,200,200), width=1) + # draw.line((left+6, bottom-6, right-6, top+6), fill=(200,200,200), width=1) + # для оконо ниже чем самый высокий проем рисуем прямоугольник под подоконником + draw.rectangle((left, top + (height * PICT_H / height_max), right - 1, PICT_H), + fill=(255, 255, 255, 0), outline=None) + # для окон ниже чем дверь рисуем прямоугольник над проемом + draw.rectangle((left, 0, right - 1, top), fill=(255, 255, 255, 0), outline=None) + if is_door: # Это дверь. Надо нарисовать сверху прямоугольник, на случай если есть очень высокое окно + draw.rectangle((left, 0, right - 1, (height_max - height) * PICT_H / height_max), + fill=(255, 255, 255, 0), outline=None) + # рисуем внешнюю тоненькую окантовку + draw.rectangle((left, top, right-1, bottom-1), fill=None, outline=(0, 0, 0)) + del draw + # сохраняем картинку + # img.info = {"Comment": "123456"} + img.save(img_file_name_with_path) + return + + +def flap_analiz(flap_config: str) -> list: + # анализ схем открывания. + dim_flap = [] # массив для хранения полной схемы открывания + i_end = len(flap_config) + j = -1 # счётчик горизонтальных рядов (формула, оконный блок) + k = -1 # счётчик вертикальных рядов внутри горизонтальных (створки) + in_flap = False # признак, где идёт разбор: внутри ли сворки или снаружи (схемы открывания или описания рядов) + # начинаем разбор + for i in range(0, i_end): + # посимвольный разбор строки + if i == 0 or flap_config[i - 1] == ".": + dim_flap.append({}) # надо создать новый ряд (фрамуги или ряд створок) + j += 1 + k = -1 + dim_flap[j].update({"row": []}) # добавляем в ряд пустой список створок + dim_flap[j].update({"vRatio": 1}) # добавляем число характеризующее пропорцию ряда относительно других + if not in_flap and (flap_config[i].isdigit() and flap_config[i - 1].isdigit()): + dim_flap[j].update({"vRatio": dim_flap[j]["vRatio"] * 10 + int(flap_config[i])}) + continue + if not in_flap and flap_config[i].isdigit(): + dim_flap[j].update({"vRatio": int(flap_config[i])}) + continue + # получен символ начала описания створки + if flap_config[i] == "[" and not in_flap: + in_flap = True + dim_flap[j]["row"].append({}) + k += 1 + dim_flap[j]["row"][k].update({"flap": ""}) + dim_flap[j]["row"][k].update({"hRatio": 1}) + continue + # получен символ окончание описания створки + if in_flap and flap_config[i] == "]": + in_flap = False + continue + # символ увеличения пропорции + if in_flap and (flap_config[i] == "-" or flap_config[i] == "_"): # для управления пропорциями створок + dim_flap[j]["row"][k]["hRatio"] += 1 + continue + if in_flap and (flap_config[i].isdigit() and flap_config[i - 1].isdigit()): + dim_flap[j]["row"][k].update({"hRatio": dim_flap[j]["row"][k]["hRatio"]*10 + int(flap_config[i])}) + continue + if in_flap and flap_config[i].isdigit(): + dim_flap[j]["row"][k].update({"hRatio": int(flap_config[i])}) + continue + if in_flap and (flap_config[i] == "V" # откидное открывание + or flap_config[i] == ">" # поворотное влево + or flap_config[i] == "G" # ^ + or flap_config[i] == "L" # поворотное вправо + or flap_config[i] == "<" # ^ + or flap_config[i] == "|" # вертикальная перегородка + or flap_config[i] == "=" # горизонтальная перегородка + or flap_config[i] == "X" # глухое окно + or flap_config[i] == "x" # ^ + or flap_config[i] == u"х" # ^ + or flap_config[i] == u"Х" # ^ + or flap_config[i] == "+" # ^ + or flap_config[i] == "M" # москитная сетка + or flap_config[i] == "m" # ^ + or flap_config[i] == "м" # ^ + or flap_config[i] == "М" # ^ + or flap_config[i] == "Z" # расширитель (спейсер) + or flap_config[i] == "S" # ^ + or flap_config[i] == "z" # ^ + or flap_config[i] == "s"): # ^ + dim_flap[j]["row"][k].update({"flap": dim_flap[j]["row"][k]["flap"] + flap_config[i]}) + return dim_flap diff --git a/oknardia/web/catalog.py b/oknardia/web/catalog.py index 8e6a474..fee7d49 100644 --- a/oknardia/web/catalog.py +++ b/oknardia/web/catalog.py @@ -2,15 +2,18 @@ from django.core.exceptions import ObjectDoesNotExist from django.shortcuts import render, redirect from django.http import HttpRequest, HttpResponse +from django.template.loader import render_to_string from django.utils import timezone from oknardia.settings import * -from oknardia.models import PVCprofiles, Seria_Info +from oknardia.models import PVCprofiles, Seria_Info, Win_MountDim, Building_Info 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 +from web.add_func import normalize, get_rating_set_for_stars, get_flaps_for_big_pictures import time import json import random import re +import os +import math import pytils @@ -193,7 +196,7 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture list_other = [] for i in q_pvc_by_id.sProfileOther.split(";"): j = i.find(":") - list_other.append(u"" + i[:j+1] + u"" + i[j+1:]) + list_other.append(u"" + i[:j + 1] + u"" + i[j + 1:]) to_template.update({"LIST_OTHER": list_other}) q_merchant = PVCprofiles.objects.raw(f"SELECT" f" COUNT(oknardia_priceoffer.id) AS offers_by_merchant," @@ -279,7 +282,7 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture # получаем последние визиты всех посетителей из базы # id2log, log_visit = get_last_all_user_visit_list() 'LOG_VISIT': get_last_all_user_visit_list(), - 'ticks': float(time.time()-time_start) + 'ticks': float(time.time() - time_start) }) return render(request, "catalog/catalog_of_profiles_model.html", to_template) @@ -300,7 +303,7 @@ def catalog_profile_manufacture(request: HttpRequest, manufacture_id: int, manuf return redirect(f'/catalog/profile/{manufacture_id}-' f'{pytils.translit.slugify(q_pvc_by_id.sProfileManufacturer)}') else: - q_pvc_by_id = PVCprofiles.objects.order_by('id')\ + q_pvc_by_id = PVCprofiles.objects.order_by('id') \ .filter(sProfileManufacturer=q_pvc_by_id.sProfileManufacturer).first() if q_pvc_by_id.id != manufacture_id: return redirect(f'/catalog/profile/{q_pvc_by_id.id}-' @@ -332,7 +335,7 @@ def catalog_profile_manufacture(request: HttpRequest, manufacture_id: int, manuf 0, re.IGNORECASE)}) to_template.update({'TIZER': re.sub(r'||', '', to_template["CONTENT"], 0, re.IGNORECASE)}) - except (ObjectDoesNotExist, IndexError, TypeError, KeyError, ): + except (ObjectDoesNotExist, IndexError, TypeError, KeyError,): pass q_profiles = PVCprofiles.objects.raw( f"SELECT oknardia_pvcprofiles.id," @@ -373,8 +376,8 @@ def catalog_profile_manufacture(request: HttpRequest, manufacture_id: int, manuf to_template.update({ 'OFFERS_BY_MAUFACTURE': q_share_of_offers.offers_by_maufacture, 'OFFERS_OTHER': q_share_of_offers.offers_other, - 'OFFERS_ANGLE': 90+180*normalize(q_share_of_offers.offers_by_maufacture, - q_share_of_offers.offers_other + q_share_of_offers.offers_by_maufacture) + 'OFFERS_ANGLE': 90 + 180 * normalize(q_share_of_offers.offers_by_maufacture, + q_share_of_offers.offers_other + q_share_of_offers.offers_by_maufacture) }) if q_share_of_offers is not None and q_share_of_offers.offers_by_maufacture != 0: q_merchant = PVCprofiles.objects.raw( @@ -410,15 +413,15 @@ def catalog_profile_manufacture(request: HttpRequest, manufacture_id: int, manuf "MERCHANT_OFFERS": i.offers_by_merchant }) to_template.update({'MERCHANTS': list_merchant}) - except (ObjectDoesNotExist, IndexError, TypeError): # вообще-то, запрос q_share_of_offers всегда что-то вернёт, - pass # но на всякий случай + except (ObjectDoesNotExist, IndexError, TypeError): # вообще-то, запрос q_share_of_offers всегда что-то вернёт, + pass # но на всякий случай to_template.update({ # получаем последние визиты клиента через куки '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) + 'ticks': float(time.time() - time_start) }) return render(request, "catalog/catalog_of_profiles_manufacture.html", to_template) @@ -449,7 +452,7 @@ def catalog_seria(request: HttpRequest) -> HttpResponse: "NAME_T": pytils.translit.slugify(i.sName) }) to_template = {'SERIAS': list_seria} - except (ObjectDoesNotExist, ): + except (ObjectDoesNotExist,): to_template = {} to_template.update({ # получаем последние визиты клиента через куки @@ -459,4 +462,350 @@ def catalog_seria(request: HttpRequest) -> HttpResponse: 'LOG_VISIT': get_last_all_user_visit_list(), 'ticks': float(time.time() - time_start) }) - return render(request, "catalog/catalog_seria.html", to_template) \ No newline at end of file + return render(request, "catalog/catalog_seria.html", to_template) + + +def catalog_seria_info(request: HttpRequest, seria_name_translit: str = "II-49", seria_id: int = 12) -> HttpResponse: + """ + КАТАЛОГ ТИПОВЫЙ СЕРИЙ: страница детальной информацией по серии зданий + + :param request: HttpRequest -- входящий http-запрос + :param seria_name_translit: str -- имя серии здания (транслитерированное pytils.translit.slugify()) + :param seria_id: int -- id серии + :return response: HttpResponse -- исходящий http-ответ + """ + time_start = time.time() + msg = "" + try: + seria_id = int(seria_id) + q_seria = Seria_Info.objects.get(id=seria_id) + if q_seria.id != q_seria.kRoot_id or seria_name_translit != pytils.translit.slugify(q_seria.sName): + return redirect(f"/catalog/seria/{pytils.translit.slugify(q_seria.sName)}/all{seria_id}") + except(ObjectDoesNotExist, ValueError,): + return redirect("/catalog/") + # если есть "облегченный" шаблон с частичным пре-рендером, то используем его. + light_template = f"{PATH_FOR_SERIA_INFO_HTML_INCLUDE}{str(seria_id)}_id.html" + light_template_w_path = f"{TEMPLATES[0]['DIRS'][0]}/{light_template}" + # print(f"{TEMPLATES[0]['DIRS'][0]}/{light_template}") + # print(light_template_w_path) + if os.path.isfile(light_template_w_path): + is_hard_template = False + else: + is_hard_template = True + to_template = {} + # получаем проемы использующиеся в данной серии домов + q_windows_in_seria = Win_MountDim.objects.raw( + f"SELECT DISTINCT" + f" oknardia_win_mountdim.iWinWidth, oknardia_win_mountdim.iWinHight," + f" oknardia_win_mountdim.sDescripion, oknardia_win_mountdim.bIsDoor," + f" oknardia_win_mountdim.bIsNearDoor, oknardia_win_mountdim.sFlapConfig," + f" oknardia_win_mountdim.iWinDepth, oknardia_win_mountdim.id," + f" 1 AS iQuantity " + f"FROM oknardia_mountdim2apartment" + f" INNER JOIN oknardia_win_mountdim" + f" ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id" + f" INNER JOIN oknardia_apartment_type" + f" ON oknardia_mountdim2apartment.kApartment_id = oknardia_apartment_type.id " + f"WHERE oknardia_apartment_type.kSeria_id = {seria_id}" + f" ORDER BY oknardia_win_mountdim.bIsNearDoor DESC," + f" oknardia_win_mountdim.bIsDoor DESC," + f" oknardia_win_mountdim.iWinWidth," + f" oknardia_win_mountdim.iWinHight DESC;") + if is_hard_template: + # Получаем данные для отрисовки больших картинок с проёмами и передаём в "тяжёлый" шаблон + to_template.update(get_flaps_for_big_pictures(q_windows_in_seria)) + # формируем строку для включения в SQL-запрос вида "(2,8,16,46,1)" + str_for_sql_in = "(" + for count in q_windows_in_seria: + str_for_sql_in += str(count.id) + "," + str_for_sql_in = str_for_sql_in[:-1] + ")" + # print StringForSqlIN + # Получаем данные для таблички Окон по типам квартирах в серии дома + # " IFNULL(oknardia_mountdim2apartment.iQuantity, 0) AS iQuantity," \ + # tStart2 = time.time() # замер времени + q_win_in_apartment_in_seria = Win_MountDim.objects.raw( + f"SELECT" + f" oknardia_win_mountdim.id," + f" oknardia_apartment_type.sNameApartment," + f" oknardia_win_mountdim.iWinWidth," + f" oknardia_win_mountdim.iWinHight," + f" oknardia_apartment_type.id AS id_apart," + f" IFNULL(oknardia_mountdim2apartment.iQuantity, 0) AS iQuantity," + f" COUNT(oknardia_priceoffer.id) AS NumOffers " + f"FROM oknardia_apartment_type" + f" INNER JOIN oknardia_win_mountdim" + f" LEFT OUTER JOIN oknardia_mountdim2apartment" + f" ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id" + f" AND oknardia_mountdim2apartment.kApartment_id = oknardia_apartment_type.id" + f" LEFT OUTER JOIN oknardia_priceoffer" + f" ON oknardia_priceoffer.kOffer2MountDim_id = oknardia_win_mountdim.id" + f" LEFT OUTER JOIN oknardia_ouruser" + f" ON oknardia_ouruser.id = oknardia_priceoffer.kOfferFromUser_id " + f"WHERE oknardia_apartment_type.kSeria_id = {seria_id} " + f"AND oknardia_win_mountdim.id IN {str_for_sql_in} " + f"GROUP BY oknardia_apartment_type.id," + f" oknardia_apartment_type.sNameApartment," + f" oknardia_win_mountdim.id," + f" oknardia_mountdim2apartment.iQuantity " + f"ORDER BY oknardia_apartment_type.iSort," + f" oknardia_win_mountdim.bIsNearDoor DESC," + f" oknardia_win_mountdim.bIsDoor DESC," + f" oknardia_win_mountdim.iWinWidth," + f" oknardia_win_mountdim.iWinHight DESC;") + list_win_in_seria = list(q_windows_in_seria) + total_column = len(list_win_in_seria) - 1 + count_column = 0 + min_offer_in_row = 1000000000 + table_of_win_in_seria_by_apartmment = [] + row_for_table = [] + offer_and_merchant_per_win = [ + { + "WIN_OFFER": 0, + "WIN_MERCHANT": 0, + "WIN_W": list_win_in_seria[i].iWinWidth, + "WIN_H": list_win_in_seria[i].iWinHight, + "WIN_ID": list_win_in_seria[i].id + } for i in range(total_column + 1)] + for count in q_win_in_apartment_in_seria: + if count.iQuantity != 0: + row_for_table.append({ + "WIN_NUM": [chr(65 + count_column)], + "WIN_Q": count.iQuantity, + "WIN_ID": count.id, + "WIN_WIDTH": list_win_in_seria[count_column].iWinWidth, + "WIN_HEIGHT": list_win_in_seria[count_column].iWinHight, + "WIN_DESCRIPTION": list_win_in_seria[count_column].sDescripion, + "WIN_FLAPCFG": list_win_in_seria[count_column].sFlapConfig + }) + if min_offer_in_row > count.NumOffers: + min_offer_in_row = count.NumOffers + if offer_and_merchant_per_win[count_column]["WIN_OFFER"] < count.NumOffers: + offer_and_merchant_per_win[count_column]["WIN_OFFER"] = count.NumOffers + else: + row_for_table.append({"WIN_NUM": "—"}) + if count_column < total_column: + count_column += 1 + else: + # print row_for_table + table_of_win_in_seria_by_apartmment.append({"WIN_IN_APART": row_for_table, + "APART_NAME": count.sNameApartment, + "APART_ID": count.id_apart, + "NUM_OFFERS": min_offer_in_row}) + count_column = 0 + min_offer_in_row = 10000 + row_for_table = [] + # print(table_of_win_in_seria_by_apartmment) + # print(f"==============>{float(time.time()-tStart2)}<==============") + # print NumOffersPerColumn, NumMerchantPerColumn + to_template.update({"WIN_OFFER_AND_MERCHANT": offer_and_merchant_per_win, + "TABLE_OF_WINDOWS": table_of_win_in_seria_by_apartmment}) + # для "тяжелого шаблона" получаем навигацию страницы, данные для карты и графика ввода в эксплуатацию + if is_hard_template: + # если вызывается "тяжелый" шаблон, то нужно подготовить тяжелые данные для построения навигации + seria_id, for_seria_nav = seria_nav(seria_id) + to_template.update(for_seria_nav) # данные для навигации по сериям + to_template.update(seria_info_year(seria_id)) # данные для графика ввода зданий серии в эксплуатацию + to_template.update(seria_info_geo_code(seria_id)) # данные для карты + # т.к. обрабатывается "тяжелый шаблон" надо создать "легкий шаблон" + # для его использования в будущем. + string_prerender = render_to_string("seria_info/all_seria_info_pre_light.html", to_template) + file = open(light_template_w_path, 'w') + # file.write(AA.encode('utf-8')) + file.write(string_prerender) + file.close() + else: + seria_name = Seria_Info.objects.get(id=seria_id).sName + to_template.update({'THIS_SERIA_NAME': seria_name}) + + # to_template.update({'LOG_VISIT': GetLastAllUserVisitSeriaList(SeriaName), + # 'ticks': float(time.time()-time_start)}) + to_template.update({ + # получаем последние визиты клиента через куки + '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, light_template, to_template) + + +def seria_nav(seria_id: int = 12) -> (int, dict): + """ + Возвращает корректный seria_id и кортеж для построения навигации по сериям дома + + :param seria_id: id серии + :return: + """ + q_seria = Seria_Info.objects.raw( + 'SELECT oknardia_seria_info.id,' + ' oknardia_seria_info.sName,' + ' oknardia_seria_info.sSeriaDescription,' + ' oknardia_seria_info.kRoot_id,' + ' oknardia_seria_info.kParent_id ' + 'FROM oknardia_seria_info ' + 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' + 'ORDER BY oknardia_seria_info.sName;') + error_seria = True + for count_seria in q_seria: + if count_seria.id == int(seria_id): + error_seria = False + break + if error_seria: + # Ошибочный seria_id. Такой базовой серии нет и надо ее найти. + try: + query = Seria_Info.objects.get(id=int(seria_id)) + if query.kRoot_id is not None: + # базовая серия прописана в kRoot_id + seria_id = query.kRoot_id + else: + # == корневой нет + # == ищем методом наименьших расстояний" + min_min = 100000000 + min_id = seria_id + for count_seria in q_seria: + if math.fabs(int(seria_id) - count_seria.id) < min_min: + min_min = math.fabs(int(seria_id) - count_seria.id) + min_id = count_seria.id + seria_id = min_id + except ObjectDoesNotExist: + seria_id = q_seria[0].id + # print(f"-->{seria_id}<--") + return all_seria_nav(seria_id, q_seria) + + +def all_seria_nav(seria_id: int, q_seria) -> (int, dict): + seria_nav_dim = [] + this_return = {} + for count_seria in q_seria: + one_seria = {} + one_seria.update({"SERIA_R": count_seria.sName, "ID2URL": count_seria.id}) + if count_seria.id == seria_id: + this_return.update({"THIS_SERIA_NAME": count_seria.sName, + "THIS_SERIA_DESCRIPTION": count_seria.sSeriaDescription}) + one_seria.update({"SERIA_L": ""}) + else: + one_seria.update({"SERIA_L": pytils.translit.slugify(count_seria.sName)}) + seria_nav_dim.append(one_seria) + this_return.update({"SERIA_NAV_DIM": seria_nav_dim}) + return seria_id, this_return + + +def seria_info_year(seria_id: int = 12) -> dict: + """ Возвращает данные для графика распределения сдачи серии в эксплуатацию + + :param seria_id: int -- id серии для которой нужно получить данные + :return: dict -- данные для графика распределения сдачи серии в эксплуатацию типа: + {"DATA4GRAPH": [{'YEAR': 1997, 'NUMS': 1, 'CLRS': '99'}, + {'YEAR': 1998, 'NUMS': 15, 'CLRS': 'сс'}, + {'YEAR': 1998, 'NUMS': 10, 'CLRS': 'a9'} + ] + } + """ + seria_in_years = [] + query = Seria_Info.objects.raw( + f"SELECT oknardia_building_info.iCommissioning_year as id," + f" COUNT(oknardia_building_info.iCommissioning_year) AS NumInYear " + f"FROM oknardia_building_info" + f" INNER JOIN oknardia_seria_info" + f" ON oknardia_building_info.kSeria_Link_id = oknardia_seria_info.id " + f"WHERE oknardia_seria_info.kRoot_id = {seria_id} " + f"GROUP BY oknardia_building_info.iCommissioning_year;" + ) + max_per_year = 0 + graph_color_light = 0xCC # самый светлый цвет на графике (максимальное значение) + graph_color_dark = 0x99 # самый темный цвет на графике (минимальное значение) + for YearCount in query: + if int(YearCount.NumInYear) > max_per_year: + max_per_year = int(YearCount.NumInYear) + # print("max", MaxPerYear) + for YearCount in query: + data_of_year = {} + try: + data_of_year.update({ + "YEAR": int(YearCount.id), + "NUMS": YearCount.NumInYear, + "CLRS": str(hex(int(graph_color_dark + YearCount.NumInYear * ( + graph_color_light - graph_color_dark) / max_per_year)))[2:] + }) + except ValueError: + continue + seria_in_years.append(data_of_year) + # print(seria_in_years) + return {"DATA4GRAPH": seria_in_years} + + +def seria_info_geo_code(seria_id: str = '12') -> dict: + """ Возвращает массив геокоординат зданий одной серии + + :param seria_id: str -- id серии для которой нужно получить данные + :return: dict -- массив геокоординат зданий серии + """ + data_return = {} + seria_to_geo = [] + municipal_m2 = 0 # муниципальный фонд (кв.м) + residential_m2 = 0 # жилой фонд (кв.м) + government_m2 = 0 # государственные учреждения занимают (кв.м.) + residents = 0 # количество жильцов + apartments = 0 # число квартиры + accounts = 0 # количество лицевых счетов + condition_max = 0 # максимальное значение показателя состояния здания + condition_min = 1000000 # минимальное значение показателя состояния здания + query = Building_Info.objects.raw( + f"SELECT" + f" oknardia_building_info.id," + f" oknardia_seria_info.kRoot_id as SerId," + f" oknardia_building_info.sAddress," + f" oknardia_building_info.fResidential_Area," + f" oknardia_building_info.fMunicipal_Area," + f" oknardia_building_info.fGovernment_Area," + f" oknardia_building_info.iNum_Residents," + f" oknardia_building_info.iNum_Apartments," + f" oknardia_building_info.iNum_Accounts," + f" oknardia_building_info.fCondition_House," + f" oknardia_building_info.fGeoCode_Latitude," + f" oknardia_building_info.fGeoCode_Longitude " + f"FROM oknardia_building_info" + f" INNER JOIN oknardia_seria_info" + f" ON oknardia_building_info.kSeria_Link_id = oknardia_seria_info.id " + f"WHERE oknardia_seria_info.kRoot_id IN ({seria_id});" + ) + for count in query: + if int(count.fGeoCode_Latitude) != 0 and int(count.fGeoCode_Longitude) != 0: + seria_to_geo.append({"LATITUDE": count.fGeoCode_Latitude, + "LONGITUDE": count.fGeoCode_Longitude, + "ADDR_ID": count.id, + "ADDR_LAT": pytils.translit.slugify(count.sAddress), + "ADDR_RUS": count.sAddress, + "SER_ID": count.SerId + }) + if count.fMunicipal_Area > 0: + municipal_m2 += count.fMunicipal_Area + if count.fResidential_Area > 0: + residential_m2 += count.fResidential_Area + if count.fGovernment_Area > 0: + government_m2 += count.fGovernment_Area + if count.iNum_Residents > 0: + residents += count.iNum_Residents + if count.iNum_Residents > 0: + residents += count.iNum_Residents + if count.iNum_Apartments > 0: + apartments += count.iNum_Apartments + if count.iNum_Accounts > 0: + accounts += count.iNum_Accounts + if count.fCondition_House > 0: + if count.fCondition_House > condition_max: + condition_max = count.fCondition_House + if count.fCondition_House < condition_min: + condition_min = count.fCondition_House + data_return.update({"DATA4GEO": seria_to_geo, + "MUNICIPAL_M2": municipal_m2, + "RESIDENTIAL_M2": residential_m2, + "GOVERNMENT_M2": government_m2, + "RESIDENTS": residents, + "APARTMENTS": apartments, + "ACCOUNTS": accounts, + "CONDITION_MAX": condition_max, + "CONDITION_MIN": condition_min}) + # print(seria_to_geo) + return data_return diff --git a/oknardia/web/diagrams.py b/oknardia/web/diagrams.py index b52e153..26e00f0 100644 --- a/oknardia/web/diagrams.py +++ b/oknardia/web/diagrams.py @@ -3,14 +3,15 @@ from django.shortcuts import render from django.http import HttpRequest, HttpResponse from django.core.exceptions import ObjectDoesNotExist from time import time -from oknardia.models import Seria_Info from oknardia.settings import * +from oknardia.models import Seria_Info +# from oknardia.catalog import all_seria_nav import math import os import pytils # вместо Rus2Lat(smth) --> pytils.translit.slugify(smth).lower() -# возвращает корректный SeriaID и кортеж для построения навигации по сериям дома +# возвращает корректный seria_id и кортеж для построения навигации по сериям дома def seria_nav(i_seria_id: int = 12) -> (int, dict): query_seria = Seria_Info.objects.raw( 'SELECT oknardia_seria_info.id,' @@ -27,7 +28,7 @@ def seria_nav(i_seria_id: int = 12) -> (int, dict): error_seria = False break if error_seria: - # Ошибочный SeriaID. Такой базовой серии нет и надо ее найти. + # Ошибочный seria_id. Такой базовой серии нет и надо ее найти. try: query = Seria_Info.objects.get(id=int(i_seria_id)) if query.kRoot_id is None: @@ -45,21 +46,8 @@ def seria_nav(i_seria_id: int = 12) -> (int, dict): i_seria_id = min_id except ObjectDoesNotExist: i_seria_id = query_seria[0].id - # print("-->", SeriaID, "<--") - seria_nav_dim = [] - this_return = {} - for count_seria in query_seria: - one_seria = {} - one_seria.update({"SERIA_R": count_seria.sName, "ID2URL": count_seria.id}) - if count_seria.id == i_seria_id: - this_return.update({"THIS_SERIA_NAME": count_seria.sName, - "THIS_SERIA_DESCRIPTION": count_seria.sSeriaDescription}) - one_seria.update({"SERIA_L": ""}) - else: - one_seria.update({"SERIA_L": pytils.translit.slugify(count_seria.sName)}) - seria_nav_dim.append(one_seria) - this_return.update({"SERIA_NAV_DIM": seria_nav_dim}) - return i_seria_id, this_return + # print(f"-->{seria_id}<--") + return all_seria_nav(i_seria_id, query_seria) def statistic_menu(request: HttpRequest) -> HttpResponse: