From 9bcbc73602b16950e89f0a54296fd233a43d973e Mon Sep 17 00:00:00 2001 From: erjemin Date: Fri, 30 Dec 2022 19:52:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=B0=D0=B4=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B5=D0=B5=20=D0=BE=D0=BA=D0=BD=D0=BE=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=B0?= =?UTF-8?q?=D0=B4=D1=80=D0=B5=D1=81=D0=B0=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 | 2 + oknardia/oknardia/urls.py | 3 + oknardia/templates/popup_1.html | 71 ++++++ .../templates/popup_incorrect_address.html | 26 +++ .../templates/report/build_info_in_table.html | 83 +++++++ oknardia/web/add_func.py | 194 ++++++++++------- oknardia/web/views.py | 203 +++++++++++++++++- 7 files changed, 495 insertions(+), 87 deletions(-) create mode 100755 oknardia/templates/popup_1.html create mode 100755 oknardia/templates/popup_incorrect_address.html create mode 100755 oknardia/templates/report/build_info_in_table.html diff --git a/oknardia/oknardia/settings.py b/oknardia/oknardia/settings.py index a912571..f5e60a7 100644 --- a/oknardia/oknardia/settings.py +++ b/oknardia/oknardia/settings.py @@ -265,3 +265,5 @@ CATALOG_RECORD_FOR_PROFILE_MODEL = 1 CATALOG_RECORD_FOR_PROFILE_MANUFACTURER = 100 CATALOG_SORTER_MAGIC_NUMBER_ADV = 5 CATALOG_SORTER_MAGIC_NUMBER_TIZER = 1 + +YANDEX_MAPS_API_KEY = MY_YANDEX_MAPS_API_KEY diff --git a/oknardia/oknardia/urls.py b/oknardia/oknardia/urls.py index b9e6f02..4ee5484 100644 --- a/oknardia/oknardia/urls.py +++ b/oknardia/oknardia/urls.py @@ -28,6 +28,9 @@ urlpatterns = [ re_path(r'^$', views.main_init), # обработчик автокомлита (подсказки во время ввода адреса на главной странице) re_path(r'^autocomplete_addr$', autocomplete_addr.autocomplete_addr), + # обработка адреса введеного в форме поиска + re_path(r'^get_address$', views.get_address), + # ОБРАБОТЧИКИ АВТОРИЗАЦИИ # Вызов шаблона подгружаем captcha re_path(r'^captcha', user_manager.captcha), diff --git a/oknardia/templates/popup_1.html b/oknardia/templates/popup_1.html new file mode 100755 index 0000000..3586310 --- /dev/null +++ b/oknardia/templates/popup_1.html @@ -0,0 +1,71 @@ +{% load static %}{% load filters %} + +
+ + Здание по адресу: {{ addr }} (id:{{ ADDRESS_ID }}) +
+ {% if LIST_APART %}

Укажите типовую планировку вашей квартиры:

+
{% for I_APART in LIST_APART %} + {% endfor %} +
+ {% else %} + +

Нет данных по этому дому

+
+

К сожалению база «Окнардии» только формируется. Пока у нас нет данных по типовым квартирам для дома по этому адресу. Ближащие адреса, по которым есть информация:

+ +

С небольшой погрешностью (±6%) мы можем дать ценовые предложения под ваши требования, но потребуется самостоятельный замер проёмов.

+ +

Сделав замер, и сообщив его нам, вы поможете наполнению базы «Окнардии», вашим соседям и сотням людей проживающих в домах таких же серий или проектов. В благодарность за содействие мы подготовим и вышлем самые актуальные предложения по email, а также предоставим купон, предоставляющий скидку на сумму 500 рублей при установке окон любым из наших партнёров. Если согласны перейдите по ссылке:
.

+
{% endif %} + + Подробная информация по зданию +{% include "report/build_info_in_table.html" %} diff --git a/oknardia/templates/popup_incorrect_address.html b/oknardia/templates/popup_incorrect_address.html new file mode 100755 index 0000000..48d9fbd --- /dev/null +++ b/oknardia/templates/popup_incorrect_address.html @@ -0,0 +1,26 @@ +{% load static %} + + +

Ошибка адреса

+
+

Это могло произойти по следующим причинам:

+ +
TICKs: {{ ticks|floatformat:4 }}
+
+ + + diff --git a/oknardia/templates/report/build_info_in_table.html b/oknardia/templates/report/build_info_in_table.html new file mode 100755 index 0000000..37530f3 --- /dev/null +++ b/oknardia/templates/report/build_info_in_table.html @@ -0,0 +1,83 @@ +{% load filters %} + + {% if ADDRESS %} + + + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Дом по адресу: {{ ADDRESS }}
Проект:{{ SERIA|safe }}   Площадь участка, м²:{{ LAND|capfirst|default:"Нет данных"|price_format }}   Кадастровый номер участка:{{ CADASTRE_NUM|capfirst }}
Типовая серия:{{ SERIA_BASE|safe|default:"Нет данных" }}Общаяя площадь помещений, м²:{{ TOTAL_AREA|capfirst|default:"Нет данных"|price_format }}Инвентарный номер:{{ INVENTORY_NUM|capfirst|default:"Нет данных" }}
Число квартир:{{ NUM_APARTMENTS|capfirst|default:"Нет данных" }}из них:Тип здания:{{ TYPE_BUILDING|capfirst|default:"Нет данных" }}
Этажей:{{ STOREYS|capfirst|default:"Нет данных" }}• помещений общего пользования, м²:{{ COMMON_AREA|default:"Нет данных"|price_format }}Класс энергоэффективности:{{ ENERGY_EFFICIENCY|capfirst|default:"Нет данных" }}
Подъездов:{{ NUM_ENTERANCES|capfirst|default:"Нет данных" }}• нежилых помещений, м²:{{ UNINHABITED_AREA|capfirst|default:"Нет данных"|price_format }}Число счетов:{{ NUM_ACCOUNTS|capfirst|default:"Нет данных"|price_format }}
Лифтов:{{ NUM_ELEVATORS|capfirst|default:"Нет данных" }}• жилых помещений, м²:{{ RESIDENTIAL_AREA|capfirst|default:"Нет данных"|price_format }}Число жителей:{{ NUM_RESIDENTS|capfirst|default:"Нет данных"|price_format }}
Грузовых лифтов:Нет данных• приватизированных помещений, м²:{{ PRIVATE_AREA|capfirst|default:"Нет данных"|price_format }}Управляющая компания:{{ MANAGEMENT_CO|safe|default:"Нет данных" }}
Год постройки:{{ COMMISSIONING_YEAR|capfirst|default:"Нет данных" }}• государственных помещений, м²:{{ GOVERNMENT_AREA|capfirst|default:"Нет данных"|price_format }}
Cтепень износа:{{ CONDITION_HOUSE|capfirst|default:"Нет данных" }}• мунициальных помещений, м²:{{ MUNICIPAL_AREA|capfirst|default:"Нет данных"|price_format }}
\ No newline at end of file diff --git a/oknardia/web/add_func.py b/oknardia/web/add_func.py index 84706e7..4206d7c 100644 --- a/oknardia/web/add_func.py +++ b/oknardia/web/add_func.py @@ -1,12 +1,12 @@ # -*- 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 +import urllib3 +import xml.dom.minidom def safe_html_spec_symbols(s: str) -> str: @@ -37,6 +37,7 @@ def safe_html_spec_symbols(s: str) -> str: result = result.replace('
', ' ') return result + # def Rus2Lat(RusString): # return translit(re.sub( # r'<[\s\S]*?>', '', re.sub(r'&[\S]*?;', '-', RusString) @@ -76,11 +77,13 @@ def get_rating_set_for_stars(rating: float = 0.) -> list: # return [] rating_set = [] for CountStar in range(RARING_STAR): - if RARING_SET_MIN+CountStar * (RARING_SET_MAX - RARING_SET_MIN) / RARING_STAR + 1. <= rating: + if RARING_SET_MIN + CountStar * (RARING_SET_MAX - RARING_SET_MIN) / RARING_STAR + 1. <= rating: rating_set.append(1) else: rating_set.append(0) return rating_set + + # # # # рассчитывает дистанцию в км. между двумя геокоординатами @@ -109,9 +112,9 @@ def get_flaps_for_big_pictures(query_set) -> dict: :return: dict: dict -- словарь с размерами картинок для больших картинок """ result = {} - mount_max_h = 0 # максимальная высота оконного проема в квартире - door_h = 0 # высота двери - for i in query_set: # найдём максимальную высоту проёма окна и двери + 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: @@ -120,21 +123,21 @@ def get_flaps_for_big_pictures(query_set) -> dict: 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 # размещение дверной перемычки + 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 если это дверь + img_file_name += u"D" # добавляем букву D если это дверь else: - img_file_name += u"W" # добавляем букву W если это окно + img_file_name += u"W" # добавляем букву W если это окно if i.bIsNearDoor: - img_file_name += u"N" # добавляем букву N если это окно рядом с дверью - mount_bulk = i.iWinHight # размещение дверной перемычки + img_file_name += u"N" # добавляем букву N если это окно рядом с дверью + mount_bulk = i.iWinHight # размещение дверной перемычки else: - img_file_name += u"-" # добавляем символ отдельное окно (не рядом с дверью) + img_file_name += u"-" # добавляем символ отдельное окно (не рядом с дверью) # маскируем символы схемы открывания, которые не допустимы в названии файлов img_file_name += i.sFlapConfig img_file_name = img_file_name.replace(">", "G") @@ -163,7 +166,7 @@ def get_flaps_for_big_pictures(query_set) -> dict: 'sDescription': i.sDescripion, 'id': i.id, 'qStr': q_local, - 'W': int((i.iWinWidth*250) / mount_max_h) + 'W': int((i.iWinWidth * 250) / mount_max_h) }) result.update({'FLAP_DIM': flaps_dim, 'WIN_DIM': query_set}) @@ -193,10 +196,10 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, 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: # это дверь. Выравнивание по нижней кромке + 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: # это не дверь... Выравниваем по верхней кромке + else: # это не дверь... Выравниваем по верхней кромке if height < height_door: top = bottom - (height_door * PICT_H / height_max) bottom = top + (height * PICT_H / height_max) @@ -205,21 +208,21 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, 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) + 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 = 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 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 @@ -227,12 +230,12 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, 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"]: # москитная сетка + 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: # Это дверь. Выравнивание по нижней кромке + if is_door: # Это дверь. Выравнивание по нижней кромке top = bottom - (height * PICT_H / height_max) # рисуем серединную перегородку = draw.line((left, top + (height_mount_bulk * PICT_H / height_max) - 8, @@ -242,40 +245,40 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, 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"]: # вертикальная перегородка | + 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"]: # горизонтальная перегородка = + 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 + 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"]: # поворотное влево > + 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"]: # поворотное вправо < + 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"]: # глухое окно + + "+" 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"]: # РАСШИРИТЕЛЬ (спейсер) + 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) @@ -296,15 +299,15 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, # второй проход -- рисуем тоненькую рамочку внутри толстых линий local_top = top local_bottom = top - for i in dim_flap: # цикл по рядам створок + for i in dim_flap: # цикл по рядам створок local_bottom += i["vRatio"] * flap_h / v_ratio_max - h_ratio_max = 0 # число всех долей (частей) для построения пропорций по горизонтали + 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 + 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 @@ -317,11 +320,11 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, fill=(255, 255, 255, 0), outline=None) # для окон ниже чем дверь рисуем прямоугольник над проемом draw.rectangle((left, 0, right - 1, top), fill=(255, 255, 255, 0), outline=None) - if is_door: # Это дверь. Надо нарисовать сверху прямоугольник, на случай если есть очень высокое окно + 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)) + draw.rectangle((left, top, right - 1, bottom - 1), fill=None, outline=(0, 0, 0)) del draw # сохраняем картинку # img.info = {"Comment": "123456"} @@ -331,20 +334,20 @@ def make_big_img_win_flap(img_file_name_with_path: str, width: int, height: int, def flap_analiz(flap_config: str) -> list: # анализ схем открывания. - dim_flap = [] # массив для хранения полной схемы открывания + dim_flap = [] # массив для хранения полной схемы открывания i_end = len(flap_config) - j = -1 # счётчик горизонтальных рядов (формула, оконный блок) - k = -1 # счётчик вертикальных рядов внутри горизонтальных (створки) - in_flap = False # признак, где идёт разбор: внутри ли сворки или снаружи (схемы открывания или описания рядов) + 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({}) # надо создать новый ряд (фрамуги или ряд створок) + dim_flap.append({}) # надо создать новый ряд (фрамуги или ряд створок) j += 1 k = -1 - dim_flap[j].update({"row": []}) # добавляем в ряд пустой список створок - dim_flap[j].update({"vRatio": 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 @@ -364,35 +367,35 @@ def flap_analiz(flap_config: str) -> list: in_flap = False continue # символ увеличения пропорции - if in_flap and (flap_config[i] == "-" or flap_config[i] == "_"): # для управления пропорциями створок + 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])}) + 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] == "х" # ^ - or flap_config[i] == "Х" # ^ - 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"): # ^ + 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] == "х" # ^ + or flap_config[i] == "Х" # ^ + 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 @@ -407,8 +410,8 @@ def make_flap_mini_pictures(path_to_img_file: str, str_flap_config: str) -> None """ # print(path_to_img_file, str_flap_config, is_door) dim_flap = flap_analiz(str_flap_config) - v_ratio_max = 0 # число всех долей (частей) для построения пропорций по вертикали - h_ratio_max = 0 # число всех долей (частей) для построения пропорций по горизонтали + v_ratio_max = 0 # число всех долей (частей) для построения пропорций по вертикали + h_ratio_max = 0 # число всех долей (частей) для построения пропорций по горизонтали for i in dim_flap: local_h_ratio_max = 0 v_ratio_max += i["vRatio"] @@ -425,47 +428,47 @@ def make_flap_mini_pictures(path_to_img_file: str, str_flap_config: str) -> None draw = ImageDraw.Draw(img) draw.rectangle((left, top, right, bottom), fill=(200, 200, 200, 50), outline=None) # рисуем внешнюю рамку - draw.line((left, bottom-1, right, bottom-1), fill=(125, 125, 125), width=5) # нижняя - draw.line((left, top, right, top), fill=(125, 125, 125), width=5) # верхняя - draw.line((left, top, left, bottom), fill=(125, 125, 125), width=5) # левая - draw.line((right-1, top, right-1, bottom), fill=(125, 125, 125), width=5) # правая + draw.line((left, bottom - 1, right, bottom - 1), fill=(125, 125, 125), width=5) # нижняя + draw.line((left, top, right, top), fill=(125, 125, 125), width=5) # верхняя + draw.line((left, top, left, bottom), fill=(125, 125, 125), width=5) # левая + draw.line((right - 1, top, right - 1, bottom), fill=(125, 125, 125), width=5) # правая ############################################################ # НАЧИНАЕМ ОТРИСОВКУ СТВОРОК ОКНА НА КРТИНКУ ############################################################ local_top = top local_bottom = top - for i in dim_flap: # цикл по рядам створок - local_bottom += i["vRatio"]*flap_h/v_ratio_max + for i in dim_flap: # цикл по рядам створок + local_bottom += i["vRatio"] * flap_h / v_ratio_max local_right = 0 local_left = 0 for j in i["row"]: - local_right += j["hRatio"]*right/h_ratio_max + local_right += j["hRatio"] * right / h_ratio_max # отрисовка схему открывания створки 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 - 5, (local_bottom + local_top) / 2, - (local_right + local_left) / 2 + 5, (local_bottom + local_top)/2), + (local_right + local_left) / 2 + 5, (local_bottom + local_top) / 2), fill=(105, 105, 225), width=1) draw.line(((local_right + local_left) / 2, (local_bottom + local_top) / 2 - 5, (local_right + local_left) / 2, (local_bottom + local_top) / 2 + 5), fill=(105, 105, 225), width=1) - if "V" in j["flap"]: # откидное открывание + if "V" in j["flap"]: # откидное открывание draw.line((local_right - 3, local_bottom - 4, (local_right + local_left) / 2, local_top + 3), fill=(125, 225, 125), width=1) draw.line((local_left + 3, local_bottom - 4, (local_right + local_left) / 2, local_top + 3), fill=(125, 225, 125), width=1) - if ">" in j["flap"] or "G" in j["flap"]: # поворотное влево + if ">" in j["flap"] or "G" in j["flap"]: # поворотное влево draw.line((local_left + 3, local_top + 3, local_right - 3, (local_bottom + local_top) / 2), fill=(225, 125, 125), width=1) draw.line((local_left + 3, local_bottom - 3, local_right - 3, (local_bottom + local_top) / 2), fill=(225, 125, 125), width=1) - if "<" in j["flap"] or "L" in j["flap"]: # поворотное вправо + if "<" in j["flap"] or "L" in j["flap"]: # поворотное вправо draw.line((local_right - 3, local_bottom - 3, local_left + 3, (local_bottom + local_top) / 2), fill=(225, 125, 125), width=1) - draw.line((local_right - 3, local_top + 3, local_left + 3, (local_bottom+local_top) / 2), + draw.line((local_right - 3, local_top + 3, local_left + 3, (local_bottom + local_top) / 2), fill=(225, 125, 125), width=1) - if "Z" in j["flap"] or "S" in j["flap"] or "z" in j["flap"] or "s" in j["flap"]: # расширитель (спейсер) + if "Z" in j["flap"] or "S" in j["flap"] or "z" 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=1) @@ -525,3 +528,30 @@ def get_geo_distance(lon1: float, lat1: float, lat2: float, lon2: float) -> floa 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 + + +def get_yandex_geocode_by_address(address_string: str) -> list: + """ Функция получает от Яндекс-Карт геокоординаты соответсвующее адресу. + + :param address_string: str -- строка с адресом (utf-8) + :return: geocode: list -- [Долгота (longitude), Широта (latitude)] + """ + geocode = [0, 0] + http = urllib3.PoolManager() + response_api = http.request('GET', f"http://geocode-maps.yandex.ru/1.x/?apikey={YANDEX_MAPS_API_KEY}" + f"&geocode={address_string}") + # print(response_api.data.decode('utf-8')) + try: + data = xml.dom.minidom.parseString(response_api.data.decode('utf-8')) + data = data.getElementsByTagName('pos')[0] + data = data.childNodes[0].data + data = tuple(data.split()) + geocode[0] = float(data[0]) # Долгота (longitude): Восточная + (E) // Западная - (W) + geocode[1] = float(data[1]) # Широта (latitude): Северная + (N) // Южная - (S) + # print(geocode) + return geocode + except: + # Тут может быть много разных типов ошибок urllub3 связанных с получением данных от Яндекс-Карт + # Перечень исключений: https://urllib3.readthedocs.io/en/stable/reference/urllib3.exceptions.html + # Возвращаем нулевые координаты, как признак, что данные не получены. + return [0, 0] diff --git a/oknardia/web/views.py b/oknardia/web/views.py index 01cf124..5136352 100644 --- a/oknardia/web/views.py +++ b/oknardia/web/views.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- -from django.shortcuts import render +from django.shortcuts import render, redirect from django.http import HttpRequest, HttpResponse from django.core.mail import send_mail from smtplib import SMTPException +from oknardia.models import Seria_Info, Building_Info, Apartment_Type +from web.add_func import get_yandex_geocode_by_address, get_geo_distance import json import datetime +import time +import pytils # from django.core.context_processors import csrf @@ -17,7 +21,6 @@ def main_init(request: HttpRequest) -> HttpResponse: """ to_template = {} # словарь, для передачи шаблону num_viz = 0 # как будто первый визит - template = "index.html" # шаблон # проверяем куки числа визита if "NumVisit" in request.COOKIES: # стоят куки, и это не первый визит @@ -41,7 +44,7 @@ def main_init(request: HttpRequest) -> HttpResponse: to_template.update({'META_DOCUMENT_STATE': u"Static"}) # Эта страничка статичная (в шаблон) to_template.update({'NV': num_viz}) # to_template.update(csrf(request)) # токен, для метода POST и GET - response = render(request, template, to_template) + response = render(request, "index.html", to_template) response.set_cookie("NumVisit", num_viz, max_age=604800) # ставим или перезаписываем куки (неделя) return response @@ -53,7 +56,6 @@ def tariff(request: HttpRequest) -> HttpResponse: :return response: исходящий http-ответ """ to_template = {} # для передачи в шаблон - template = "tariff.html" # шаблон if request.method == 'POST': # print request.POST if 'tariff' in request.POST and 'email_' in request.POST \ @@ -91,7 +93,7 @@ def tariff(request: HttpRequest) -> HttpResponse: # Что-то пошло не так и почта не отправилась. Надо подумать что в этим делать to_template.update({'SENDER': "Error!"}) pass - return render(request, template, to_template) + return render(request, "tariff.html", to_template) def contact(request: HttpRequest) -> HttpResponse: @@ -101,3 +103,194 @@ def contact(request: HttpRequest) -> HttpResponse: :return response: исходящий http-ответ """ return render(request, "contact.html", {}) + + +def get_address(request: HttpRequest) -> HttpResponse: + """ Вызывается после ввода пользователем адреса. Получает строку с адресом методом POST + + ВНИМАНИЕ ТЕХНИЧЕСКИЙ ДОЛГ: Заменить GET на POST + + :param request: request + :return: response + ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░""" + time_start = time.time() + if request.method != 'POST': + return redirect("/") + if 'address' not in request.POST: + return redirect("/") + addr = request.POST['address'] + to_template = {} + try: + q = Building_Info.objects.get(sAddress=addr) + # Если QuerySet не содержит GeoCode (такое бывает, что в Яндекс-Картах не было каких-то данных), + # то пробуем получить GeoCode повторно (вдруг, у Яндекс-Карт расширилась база адресов) + if int(q.fGeoCode_Longitude) != 0 and int(q.fGeoCode_Latitude != 0): + # print("координаты не ноль") + to_template.update({'LATITUDE': str(q.fGeoCode_Latitude).replace(",", "."), + 'LONGITUDE': str(q.fGeoCode_Longitude).replace(",", ".")}) + geocode = [q.fGeoCode_Latitude, q.fGeoCode_Longitude] + else: + # print("координаты ноль") + geocode = get_yandex_geocode_by_address(addr) + # print("получен геокод", geocode) + to_template.update({'LATITUDE': str(geocode[0]).replace(",", "."), + 'LONGITUDE': str(geocode[1]).replace(",", ".")}) + except: + # print("тут") + geocode = get_yandex_geocode_by_address(addr) + # print(geocode) + to_template.update({'LATITUDE': str(geocode[0]).replace(",", ".")}) + to_template.update({'LONGITUDE': str(geocode[1]).replace(",", ".")}) + q = Building_Info.objects.raw( + f"SELECT oknardia_building_info.*, " + f"ABS({geocode[0]} - oknardia_building_info.fGeoCode_Latitude) + " + f"ABS({geocode[1]} - oknardia_building_info.fGeoCode_Longitude) AS R2 " + f"FROM oknardia_building_info " + f"ORDER BY R2 " + f"LIMIT 1;")[0] + if q.R2 > 0.67: # Если расстояние между точками больше 670 метров, то не показываем результат + to_template.update({'ticks': float(time.time()-time_start)}) + to_template.update({'addr': addr}) + return render(request, "popup_incorrect_address.html", to_template) + addr = q.sAddress + print("addr", addr) + to_template.update({'ADDRESS_ID': q.id, + 'SERIA': q.sSerias_Project}) + if q.fTotal_Area < 0: + to_template.update({'TOTAL_AREA': "Нет данных"}) + else: + to_template.update({'TOTAL_AREA': f"{q.fTotal_Area: .1f}"}) + to_template.update({'CADASTRE_NUM': q.sCadastre_Num_Area}) + if q.fLand_Area < 0: + to_template.update({'LAND': "Нет данных"}) + else: + to_template.update({'LAND': f"{q.fLand_Area: .1f}"}) + to_template.update({'INVENTORY_NUM': q.sInventory_Num}) + if q.iNum_Apartments < 0: + to_template.update({'NUM_APARTMENTS': "Нет данных"}) + else: + to_template.update({'NUM_APARTMENTS': q.iNum_Apartments}) + to_template.update({'TYPE_BUILDING': q.sType}) + if q.iNum_Apartments < 0: + to_template.update({'STOREYS': "Нет данных"}) + else: + to_template.update({'STOREYS': q.iStoreys}) + if q.fCommon_Area < 0: + to_template.update({'COMMON_AREA': "Нет данных"}) + else: + to_template.update({'COMMON_AREA': f"{q.fCommon_Area: .1f}"}) + to_template.update({'ENERGY_EFFICIENCY': q.sEnergy_Efficiency}) + if q.iEntrances_Porchs < 0: + to_template.update({'NUM_ENTERANCES': "Нет"}) + else: + to_template.update({'NUM_ENTERANCES': q.iEntrances_Porchs}) + if q.fUninhabited_Area < 0: + to_template.update({'UNINHABITED_AREA': "Нет данных"}) + else: + to_template.update({'UNINHABITED_AREA': f"{q.fUninhabited_Area: .1f}"}) + if q.sManagement_Co == u"N/A": + to_template.update({'MANAGEMENT_CO': "Нет данных"}) + else: + to_template.update({'MANAGEMENT_CO': q.sManagement_Co}) + if q.iElevators < 0: + to_template.update({'NUM_ELEVATORS': "Нет данных"}) + else: + to_template.update({'NUM_ELEVATORS': q.iElevators}) + if q.fResidential_Area < 0: + to_template.update({'RESIDENTIAL_AREA': "Нет данных"}) + else: + to_template.update({'RESIDENTIAL_AREA': f"{q.fResidential_Area: .1f}"}) + if q.iNum_Residents < 0: + to_template.update({'NUM_RESIDENTS': "Нет данных"}) + else: + to_template.update({'NUM_RESIDENTS': q.iNum_Residents}) + if q.fPrivate_Area < 0: + to_template.update({'PRIVATE_AREA': "Нет данных"}) + else: + to_template.update({'PRIVATE_AREA': f"{q.fPrivate_Area:.1f}"}) + if q.iNum_Accounts < 0: + to_template.update({'NUM_ACCOUNTS': "Нет данных"}) + else: + to_template.update({'NUM_ACCOUNTS': q.iNum_Accounts}) + if q.iCommissioning_year == "N/A": + to_template.update({'COMMISSIONING_YEAR': "Нет данных"}) + else: + to_template.update({'COMMISSIONING_YEAR': q.iCommissioning_year}) + if q.fGovernment_Area < 0: + to_template.update({'GOVERNMENT_AREA': "Нет данных"}) + else: + to_template.update({'GOVERNMENT_AREA': f"{q.fGovernment_Area: .1f}"}) + if q.fCondition_House < 0: + to_template.update({'CONDITION_HOUSE': "Нет данных"}) + else: + to_template.update({'CONDITION_HOUSE': f"{q.fCondition_House: .0f}%"}) + if q.fCondition_Foundation < 0: + to_template.update({'CONDITION_FOUNDATION': "Нет данных"}) + else: + to_template.update({'CONDITION_FOUNDATION': f"{q.fCondition_Foundation: .0f}%"}) + if q.fCondition_Walls < 0: + to_template.update({'CONDITION_WALL': u"Нет данных"}) + else: + to_template.update({'CONDITION_WALL': f"{q.fCondition_Walls: .0f}%"}) + if q.fCondition_Overlap < 0: + to_template.update({'CONDITION_OVERLAP': "Нет данных"}) + else: + to_template.update({'CONDITION_OVERLAP': f"{q.fCondition_Overlap: .0f}%"}) + if q.fMunicipal_Area < 0: + to_template.update({'MUNICIPAL_AREA': "Нет данных"}) + else: + to_template.update({'MUNICIPAL_AREA': f"{q.fMunicipal_Area: .1f}"}) + to_template.update({'URL2REFOEMAGKH': q.sURL}) + # Пробуем получить базовую серию дома. Для этого рекурсивно раскручиваем записи в таблице Seria_Info + idd = q.kSeria_Link_id + all_apartment_in_seria = False + while idd is not None: + # рекурсивно движемся по дерву потомок→предок серий домов. + q1 = Seria_Info.objects.get(id=idd) + # получаем список типовых квартир для серии дома с id == idd + all_apartment_in_seria = Apartment_Type.objects.filter(kSeria_id=idd).order_by("iSort") + # проверяем есть-ли что-то в списке типовых квартир. + if bool(all_apartment_in_seria): + # список типовых квартир не нулевой + to_template.update({'LIST_APART': all_apartment_in_seria}) + break + idd = q1.kParent_id + # проверяем, был ли получен список квартир + if not bool(all_apartment_in_seria): + # Если списка квартир нет, нужно получить список ближайших адресов, для которых есть цены. + q = Building_Info.objects.raw( + f"SELECT" + f" oknardia_building_info.sAddress, oknardia_building_info.id," + f" oknardia_building_info.fGeoCode_Longitude, oknardia_building_info.fGeoCode_Latitude," + f" oknardia_seria_info.kRoot_id, oknardia_seria_info.sName," + f" COUNT(oknardia_apartment_type.sNameApartment) AS NumApart," + f" ABS({geocode[0]} - oknardia_building_info.fGeoCode_Latitude)" + f" + ABS({geocode[1]} - oknardia_building_info.fGeoCode_Longitude) AS R2 " + f"FROM oknardia_building_info" + f" INNER JOIN oknardia_seria_info" + f" ON oknardia_building_info.kSeria_Link_id = oknardia_seria_info.id" + f" INNER JOIN oknardia_apartment_type" + f" ON oknardia_seria_info.kRoot_id = oknardia_apartment_type.kSeria_id " + f"WHERE oknardia_building_info.fGeoCode_Longitude <> 0.0" + f" AND oknardia_building_info.fGeoCode_Latitude <> 0.0 " + f"GROUP BY oknardia_seria_info.sName," + f" oknardia_seria_info.kRoot_id," + f" oknardia_building_info.id," + f" oknardia_building_info.sAddress," + f" oknardia_building_info.fGeoCode_Longitude," + f" oknardia_building_info.fGeoCode_Latitude " + f"ORDER BY R2 " + f"LIMIT 5;") + q = list(q) + for i in q: + i.R2 = get_geo_distance(i.fGeoCode_Longitude, i.fGeoCode_Latitude, geocode[0], geocode[1]) + # print i.id, i.sAddress, i.sName, i.R2 + # сортируем список по R2 (дистанция от текущего адреса, до домов по которым данные известны) + sorted(q, key=lambda item: item.R2) + to_template.update({'NEAR_KNOWN_ADDRESS': q}) + # print q + to_template.update({'SERIA_BASE': q1.sName, + 'addr': addr, + 'addr_T': pytils.translit.slugify(addr), + 'ticks': float(time.time()-time_start)}) + return render(request, "popup_1.html", to_template)