diff --git a/README.md b/README.md index 9e8f0de..c458d3b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ * Проект получает настройки и секреты через переменные окружения (`.env`) вместо `my_secret*.py`. * Рефакторинг создания `sitemap.xml`: raw ⟶ ORM, создание через Django-команду `generate_sitemaps` в медиа-файлы. * Рефакторинг URL `/catalog/profil/`: raw SQL ⟶ ORM, убран `last_update`, измененs SEO `description` и `keywords`. +* Распилен `oknardia/web/catalog.py` на тематические модули (`catalog_companies.py`, `catalog_profiles.py`, `catalog_series.py`, `catalog_openings.py`) с вынесением общей логики в `catalog_utils.py`; маршруты обновлены без изменения внешних URL. * Рефакторинг `catalog_profile_model` (`/catalog/profile/...`): raw SQL ⟶ ORM, упрощена логика, вынесены helper-функции, сокращено дублирование расчёта цветов рейтинга, нормализована подготовка `LIST_OTHER`/`MERCHANTS`/`PROFILES`/`PROFILE_DETAIL`, сохранена совместимость шаблонов. +* Рефакторинг `catalog_profile_manufacture` (`/catalog/profile/-`): упрощена валидация URL, убран дублирующий код маппинга для `PROFILES` и `MERCHANTS` через общие хелперы, стандартизирован хвост контекста (`LAST_VISIT`, `LOG_VISIT`, `ticks`) через `_append_visit_context`. #### Планы, задачи, маркеры и идеи на будущее: diff --git a/oknardia/oknardia/urls.py b/oknardia/oknardia/urls.py index cb058f5..da344b5 100644 --- a/oknardia/oknardia/urls.py +++ b/oknardia/oknardia/urls.py @@ -19,7 +19,8 @@ from django.urls import include, path, re_path from django.conf.urls.static import static from oknardia.settings import * from web import views, autocomplete_addr, user_manager, blog, diagrams, report1, report2, catalog, prices, service, \ - catalog_profiles + catalog_profiles, catalog_series, catalog_openings, catalog_companies + urlpatterns = [ path('admin/', admin.site.urls), @@ -55,23 +56,26 @@ urlpatterns = [ re_path(r'^stat/series/geo[/*]$', diagrams.statistic_menu), # дубль для старых ссылок re_path(r'^stat/rating[/*]$', report2.ratings), re_path(r'^stat/rating/profiles_rank[/*]$', report2.profiles_rating), - # --- Каталог + # --- КАТАЛОГ re_path(r'^catalog[/*]$', catalog.catalog_root), - # --- --- Каталог профилей + # --- --- КАТАЛОГ ПРОФИЛЕЙ re_path(r'^catalog/profile[/*]$', catalog_profiles.catalog_profile), re_path(r'^catalog/profile/(?P\d+)-(?P\S*)' - r'/(?P\d+)-(?P\S*)[/*]$', catalog_profiles.catalog_profile_model), + r'/(?P\d+)-(?P\S*)[/*]$', + catalog_profiles.catalog_profile_model), re_path(r'^catalog/profile/(?P\d+)-(?P\S*)[/*]$', catalog_profiles.catalog_profile_manufacture), # --- --- Каталог серий типового строительства - re_path(r'^catalog/seria[/*]$', catalog.catalog_seria), - re_path(r'^catalog/seria/(?P[^/]*)/all(?P\d+)[/*]$', catalog.catalog_seria_info), + re_path(r'^catalog/seria[/*]$', catalog_series.catalog_seria), + re_path(r'^catalog/seria/(?P[^/]*)/all(?P\d+)[/*]$', + catalog_series.catalog_seria_info), re_path(r'^seria_[^/]*/all(?P\d+)/\S*$', catalog.report_all_info_seria_redirect), # для старых ссылок # --- --- Каталог стандартных проёмов и схем открывания длч типовых серий строительства - re_path(r'^catalog/standard_opening[/*]$', catalog.standard_opening), + re_path(r'^catalog/standard_opening[/*]$', catalog_openings.standard_opening), # --- --- Каталог производителей окон - re_path(r'^catalog/company[/*]$', catalog.catalog_company), - re_path(r'^catalog/company/(?P\d+)-(?P\S*)[/*]$', catalog.catalog_company_detail), + re_path(r'^catalog/company[/*]$', catalog_companies.catalog_company), + re_path(r'^catalog/company/(?P\d+)-(?P\S*)[/*]$', + catalog_companies.catalog_company_detail), # ЦЕНОВЫЕ ПРЕДЛОЖЕНИЯ # --- Одиночное окно re_path(r'^tsena-odnogo-okna/(?P\d+)x(?P\d+)mm/tip(?P\d+)[/*]$', diff --git a/oknardia/web/catalog.py b/oknardia/web/catalog.py index 5408425..526943a 100644 --- a/oknardia/web/catalog.py +++ b/oknardia/web/catalog.py @@ -1,32 +1,9 @@ # -*- coding: utf-8 -*- -from datetime import datetime -from django.core.exceptions import ObjectDoesNotExist -from django.db.models import Count 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 ( - Catalog2Profile, - MerchantBrand, - PVCprofiles, - PriceOffer, - Seria_Info, - Win_MountDim, - Building_Info, -) +from oknardia.models import Seria_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, get_flaps_for_big_pictures,\ - get_flaps_for_mini_pictures, touch_reload_wsgi -import django.utils.dateformat import time -import json -import random -import re -import os -import math -import pytils def catalog_root(request: HttpRequest) -> HttpResponse: @@ -46,611 +23,6 @@ def catalog_root(request: HttpRequest) -> HttpResponse: response = render(request, "catalog/catalog_root.html", to_template) return response -# Каталог типовый серий зданий (пока переадресация) -def catalog_seria(request: HttpRequest) -> HttpResponse: - """ - КАТАЛОГ ТИПОВЫЙ СЕРИЙ: страница со всеми сериями зданий в базе окнардии - - :param request: HttpRequest -- входящий http-запрос - :return response: HttpResponse -- исходящий http-ответ - """ - time_start = time.time() - try: - q_seria = Seria_Info.objects.raw('SELECT' - ' oknardia_seria_info.id,' - ' oknardia_seria_info.sURL2IMG,' - ' oknardia_seria_info.sName ' - 'FROM oknardia_seria_info ' - 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' - 'ORDER BY oknardia_seria_info.sName;') - list_seria = [] - for i in q_seria: - list_seria.append({ - "ID": i.id, - "URL": i.sURL2IMG, - "NAME": i.sName, - "NAME_T": pytils.translit.slugify(i.sName) - }) - to_template = {'SERIAS': list_seria} - except (ObjectDoesNotExist,): - to_template = {} - 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, "catalog/catalog_seria.html", to_template) - - -def catalog_seria_info(request: HttpRequest, seria_name_translit: None, seria_id: int = 843) -> 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) - # 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() - touch_reload_wsgi(light_template_w_path) - 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": ""}) - one_seria.update({"SERIA_L": pytils.translit.slugify(count_seria.sName)}) - 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 - - -def standard_opening(request: HttpRequest) -> HttpResponse: - time_start = time.time() - to_template = {} # словарь, для передачи шаблону - q_seria = Seria_Info.objects.raw('SELECT oknardia_seria_info.id, oknardia_seria_info.sName ' - 'FROM oknardia_seria_info ' - 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' - 'ORDER BY oknardia_seria_info.sName;') - to_template.update({'SERIAS': list(q_seria)}) - q_win_opening = Win_MountDim.objects.raw( - 'SELECT oknardia_win_mountdim.*,' - ' oknardia_seria_info.sName,' - ' oknardia_seria_info.id AS ID_Seria ' - 'FROM oknardia_win_mountdim' - ' INNER JOIN oknardia_mountdim2apartment' - ' ON oknardia_win_mountdim.id = oknardia_mountdim2apartment.kMountDim_id' - ' RIGHT OUTER JOIN oknardia_apartment_type' - ' ON oknardia_apartment_type.id = oknardia_mountdim2apartment.kApartment_id' - ' RIGHT OUTER JOIN oknardia_seria_info' - ' ON oknardia_apartment_type.kSeria_id = oknardia_seria_info.id ' - 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' - 'GROUP BY oknardia_win_mountdim.iWinWidth, oknardia_win_mountdim.iWinHight,' - ' oknardia_win_mountdim.bIsDoor, oknardia_win_mountdim.bIsNearDoor,' - ' oknardia_win_mountdim.sFlapConfig, oknardia_win_mountdim.id,' - ' oknardia_seria_info.sName, oknardia_seria_info.id ' - 'ORDER BY oknardia_win_mountdim.iWinWidth DESC,' - ' oknardia_win_mountdim.iWinHight DESC,' - ' oknardia_win_mountdim.bIsNearDoor,' - ' oknardia_win_mountdim.bIsDoor,' - ' oknardia_win_mountdim.id,' - ' oknardia_seria_info.sName;') - list_windows_opening = [] - tmp_id = 0 - for i in q_win_opening: - if tmp_id != i.id: - tmp_id = i.id - image_file_name = get_flaps_for_mini_pictures(i.sFlapConfig) - list_windows_opening.append({ - "ID": i.id, - "INCLUDING_IN_SERIA": [{ - "ID": i.ID_Seria, - "NAME_T": pytils.translit.slugify(i.sName), - "NAME": i.sName - }], - "INCLUDING_IN_SERIA_ID": [], - "URL2IMG": image_file_name, - "FLAP_CONFIG": i.sFlapConfig, - "DESCRIPTION": i.sDescripion.split(" для")[0].split(" (")[0], - "DESCRIPTION_L": i.sDescripion, - "IS_DOOR": i.bIsDoor, - "IS_NEAR_DOOR": i.bIsNearDoor, - "H": i.iWinHight * 10, - "W": i.iWinWidth * 10 - }) - else: - list_windows_opening[-1]["INCLUDING_IN_SERIA"].append({ - "ID": i.ID_Seria, - "NAME_T": pytils.translit.slugify(i.sName), - "NAME": i.sName - }) - to_template.update({ - 'LIST_WIN_OPENING': list_windows_opening, - # получаем последние визиты клиента через куки - '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, "catalog/catalog_standard_opening.html", to_template) - - -def catalog_company(request: HttpRequest) -> HttpResponse: - time_start = time.time() - to_template = {} # словарь, для передачи шаблону - q_company = MerchantBrand.objects.raw('SELECT' - ' oknardia_merchantbrand.id,' - ' oknardia_merchantbrand.sMerchantName,' - ' oknardia_merchantbrand.pMerchantLogo,' - ' oknardia_merchantbrand.sMerchantMainURL,' - ' COUNT(oknardia_priceoffer.id) AS NumOffers,' - ' AVG(oknardia_priceoffer.fOfferPrice) AS PriceAVG,' - ' MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate,' - ' Q.NumSets,' - ' Q.RatingAVG,' - ' 1 AS STARS ' - 'FROM (SELECT' - ' COUNT(oknardia_setkit.sSetName) AS NumSets,' - ' oknardia_merchantoffice.kMerchantName_id AS Q_ID,' - ' AVG(oknardia_setkit.fSetRating) AS RatingAVG' - ' FROM oknardia_merchantoffice' - ' INNER JOIN oknardia_ouruser' - ' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id' - ' INNER JOIN oknardia_setkit' - ' ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id' - ' GROUP BY oknardia_merchantoffice.id,' - ' oknardia_merchantoffice.kMerchantName_id) AS Q,' - ' oknardia_ouruser' - ' INNER JOIN oknardia_merchantoffice' - ' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id' - ' INNER JOIN oknardia_priceoffer' - ' ON oknardia_priceoffer.kOfferFromUser_id = oknardia_ouruser.id' - ' INNER JOIN oknardia_merchantbrand' - ' ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id' - ' WHERE Q_ID = oknardia_merchantoffice.kMerchantName_id ' - 'GROUP BY oknardia_merchantoffice.kMerchantName_id ' - 'ORDER BY Q.RatingAVG DESC;') - list_company = list(q_company) - for i in list_company: - i.STARS = get_rating_set_for_stars(i.RatingAVG) - i.NumSets = pytils.numeral.get_plural(i.NumSets, u"оконный набор, оконных набора, оконных наборов") - i.NumOffers = pytils.numeral.get_plural(i.NumOffers, u"вариант, варианта, вариантов") - i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U'))) - i.sMerchantMainURL = pytils.translit.slugify(i.sMerchantName) - # print("NAME:", i.sMerchantName, "\tNumSets:", i.NumSets, "\tNumOffers:", i.NumOffers, - # "\t:AverageRating:", i.RatingAVG, "\tAveragePrice:", i.PriceAVG, "\tSTARS:", i.STARS) - to_template.update({ - 'COMPANIES': list_company, - # получаем последние визиты клиента через куки - '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, "catalog/catalog_company.html", to_template) - - -def catalog_company_detail(request: HttpRequest, company_id: str, company_name_slug: str) -> HttpResponse: - time_start = time.time() - to_template = {} # словарь, для передачи шаблону - company_id = int(company_id) - q_by_id = MerchantBrand.objects.get(id=company_id) - if pytils.translit.slugify(q_by_id.sMerchantName) != company_name_slug: - return redirect('/catalog/company/%d-%s' % (company_id, pytils.translit.slugify(q_by_id.sMerchantName))) - to_template.update({'COMPANY': q_by_id.sMerchantName}) - to_template.update({'COMPANY_ID': company_id}) - to_template.update({'COMPANY_T': company_name_slug}) - list_not = [u"нет", u"—", ""] - to_template.update({'LIST_NOT': list_not}) - q_sets = MerchantBrand.objects.raw(f"SELECT" - f" COUNT(oknardia_priceoffer.id) AS NumOffers," - f" AVG(oknardia_priceoffer.fOfferPrice) AS priceAVG," - f" MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate," - f" MIN(oknardia_priceoffer.dOfferCreate) AS earlyCreation," - f" oknardia_merchantbrand.*," - f" oknardia_merchantoffice.*," - f" oknardia_merchantoffice.id AS idMERCH," - f" oknardia_setkit.*," - f" oknardia_setkit.id AS idSET," - f" oknardia_pvcprofiles.*," - f" oknardia_pvcprofiles.id AS idPVC," - f" oknardia_glazing.*, " - f" oknardia_glazing.id AS idGLAZ " - f"FROM oknardia_ouruser" - f" INNER JOIN oknardia_merchantoffice" - f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id" - f" INNER JOIN oknardia_merchantbrand" - f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id" - f" INNER JOIN oknardia_setkit" - f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id" - f" INNER JOIN oknardia_priceoffer" - f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id" - f" INNER JOIN oknardia_pvcprofiles" - f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id" - f" INNER JOIN oknardia_glazing" - f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id " - f"WHERE oknardia_merchantbrand.id = {company_id} " - f"AND oknardia_priceoffer.sOfferActive = TRUE " - f"GROUP BY oknardia_merchantoffice.id," - f" oknardia_setkit.id," - f" oknardia_setkit.fSetRating " - f"ORDER BY oknardia_setkit.fSetRating DESC;") - list_sets = list(q_sets) - for i in list_sets: - i.sMerchantMainURL = {"URL": i.sMerchantMainURL, - "URL_VIEW": re.sub(r"(?:^http://|^https://|/$|www\.)", "", i.sMerchantMainURL)} - k = random.randint(1, int(len(i.sOfficeEmails)/2) - 1) - i.sOfficeEmails = [i.sOfficeEmails[0:k], i.sOfficeEmails[k:-k], i.sOfficeEmails[-k:]] - to_template.update({'IMG_FOR_BLOG': i.pMerchantLogo}) - i.fSetRating = {"RATING": i.fSetRating, - "STARS": get_rating_set_for_stars(i.fSetRating)} - i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U'))) - i.earlyCreation = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.earlyCreation, 'U'))) - i.sProfileName = {"NAME": i.sProfileName, - "NAME_T": pytils.translit.slugify(i.sProfileName)} - i.sProfileManufacturer = {"NAME": i.sProfileManufacturer, - "NAME_T": pytils.translit.slugify(i.sProfileManufacturer)} - i.fProfileSeals = pytils.numeral.sum_string(i.fProfileSeals, pytils.numeral.MALE, u"контур, контура, контуров") - if i.sSetImplementCatch.lower() in list_not: - i.sSetImplementCatch = "" - if i.sSetClimateControl.lower() in list_not: - i.sSetClimateControl = "" - if len(i.sProfileReinforcement) > 0: - i.sProfileReinforcement = i.sProfileReinforcement[0].lower()+i.sProfileReinforcement[1:] - if len(i.sSetSill) > 0: - i.sSetSill = i.sSetSill[0].lower()+i.sSetSill[1:] - if len(i.sSetPanes) > 0: - i.sSetPanes = i.sSetPanes[0].lower()+i.sSetPanes[1:] - if len(i.sSetSlope) > 0: - i.sSetSlope = i.sSetSlope[0].lower()+i.sSetSlope[1:] - if len(i.sSetUninstallInstall) > 0: - i.sSetUninstallInstall = i.sSetUninstallInstall[0].lower()+i.sSetUninstallInstall[1:] - if len(i.sSetDelivery) > 0: - i.sSetDelivery = i.sSetDelivery[0].lower()+i.sSetDelivery[1:] - if len(i.sSetOtherConditions) > 0: - i.sSetOtherConditions = i.sSetOtherConditions[0].lower()+i.sSetOtherConditions[1:] - to_template.update({ - 'SETS': list_sets, - # получаем последние визиты клиента через куки - '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, "catalog/catalog_company_detail.html", to_template) - def report_all_info_seria_redirect(request: HttpRequest, seria_id: str = "12") -> HttpResponse: """ Переадресация старых URL, т.к. их сколько-то есть (было) во внешних ссылках diff --git a/oknardia/web/catalog_companies.py b/oknardia/web/catalog_companies.py new file mode 100644 index 0000000..3871ed1 --- /dev/null +++ b/oknardia/web/catalog_companies.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +from django.shortcuts import render, redirect +from django.http import HttpRequest, HttpResponse +from django.utils import timezone +from oknardia.models import ( + MerchantBrand, +) +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 get_rating_set_for_stars +import django.utils.dateformat +import time +import random +import re +import pytils + + +def catalog_company(request: HttpRequest) -> HttpResponse: + time_start = time.time() + to_template = {} # словарь, для передачи шаблону + q_company = MerchantBrand.objects.raw('SELECT' + ' oknardia_merchantbrand.id,' + ' oknardia_merchantbrand.sMerchantName,' + ' oknardia_merchantbrand.pMerchantLogo,' + ' oknardia_merchantbrand.sMerchantMainURL,' + ' COUNT(oknardia_priceoffer.id) AS NumOffers,' + ' AVG(oknardia_priceoffer.fOfferPrice) AS PriceAVG,' + ' MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate,' + ' Q.NumSets,' + ' Q.RatingAVG,' + ' 1 AS STARS ' + 'FROM (SELECT' + ' COUNT(oknardia_setkit.sSetName) AS NumSets,' + ' oknardia_merchantoffice.kMerchantName_id AS Q_ID,' + ' AVG(oknardia_setkit.fSetRating) AS RatingAVG' + ' FROM oknardia_merchantoffice' + ' INNER JOIN oknardia_ouruser' + ' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id' + ' INNER JOIN oknardia_setkit' + ' ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id' + ' GROUP BY oknardia_merchantoffice.id,' + ' oknardia_merchantoffice.kMerchantName_id) AS Q,' + ' oknardia_ouruser' + ' INNER JOIN oknardia_merchantoffice' + ' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id' + ' INNER JOIN oknardia_priceoffer' + ' ON oknardia_priceoffer.kOfferFromUser_id = oknardia_ouruser.id' + ' INNER JOIN oknardia_merchantbrand' + ' ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id' + ' WHERE Q_ID = oknardia_merchantoffice.kMerchantName_id ' + 'GROUP BY oknardia_merchantoffice.kMerchantName_id ' + 'ORDER BY Q.RatingAVG DESC;') + list_company = list(q_company) + for i in list_company: + i.STARS = get_rating_set_for_stars(i.RatingAVG) + i.NumSets = pytils.numeral.get_plural(i.NumSets, u"оконный набор, оконных набора, оконных наборов") + i.NumOffers = pytils.numeral.get_plural(i.NumOffers, u"вариант, варианта, вариантов") + i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U'))) + i.sMerchantMainURL = pytils.translit.slugify(i.sMerchantName) + # print("NAME:", i.sMerchantName, "\tNumSets:", i.NumSets, "\tNumOffers:", i.NumOffers, + # "\t:AverageRating:", i.RatingAVG, "\tAveragePrice:", i.PriceAVG, "\tSTARS:", i.STARS) + to_template.update({ + 'COMPANIES': list_company, + # получаем последние визиты клиента через куки + '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, "catalog/catalog_company.html", to_template) + + +def catalog_company_detail(request: HttpRequest, company_id: str, company_name_slug: str) -> HttpResponse: + time_start = time.time() + to_template = {} # словарь, для передачи шаблону + company_id = int(company_id) + q_by_id = MerchantBrand.objects.get(id=company_id) + if pytils.translit.slugify(q_by_id.sMerchantName) != company_name_slug: + return redirect('/catalog/company/%d-%s' % (company_id, pytils.translit.slugify(q_by_id.sMerchantName))) + to_template.update({'COMPANY': q_by_id.sMerchantName}) + to_template.update({'COMPANY_ID': company_id}) + to_template.update({'COMPANY_T': company_name_slug}) + list_not = [u"нет", u"—", ""] + to_template.update({'LIST_NOT': list_not}) + q_sets = MerchantBrand.objects.raw(f"SELECT" + f" COUNT(oknardia_priceoffer.id) AS NumOffers," + f" AVG(oknardia_priceoffer.fOfferPrice) AS priceAVG," + f" MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate," + f" MIN(oknardia_priceoffer.dOfferCreate) AS earlyCreation," + f" oknardia_merchantbrand.*," + f" oknardia_merchantoffice.*," + f" oknardia_merchantoffice.id AS idMERCH," + f" oknardia_setkit.*," + f" oknardia_setkit.id AS idSET," + f" oknardia_pvcprofiles.*," + f" oknardia_pvcprofiles.id AS idPVC," + f" oknardia_glazing.*, " + f" oknardia_glazing.id AS idGLAZ " + f"FROM oknardia_ouruser" + f" INNER JOIN oknardia_merchantoffice" + f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id" + f" INNER JOIN oknardia_merchantbrand" + f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id" + f" INNER JOIN oknardia_setkit" + f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id" + f" INNER JOIN oknardia_priceoffer" + f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id" + f" INNER JOIN oknardia_pvcprofiles" + f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id" + f" INNER JOIN oknardia_glazing" + f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id " + f"WHERE oknardia_merchantbrand.id = {company_id} " + f"AND oknardia_priceoffer.sOfferActive = TRUE " + f"GROUP BY oknardia_merchantoffice.id," + f" oknardia_setkit.id," + f" oknardia_setkit.fSetRating " + f"ORDER BY oknardia_setkit.fSetRating DESC;") + list_sets = list(q_sets) + for i in list_sets: + i.sMerchantMainURL = {"URL": i.sMerchantMainURL, + "URL_VIEW": re.sub(r"(?:^http://|^https://|/$|www\.)", "", i.sMerchantMainURL)} + k = random.randint(1, int(len(i.sOfficeEmails)/2) - 1) + i.sOfficeEmails = [i.sOfficeEmails[0:k], i.sOfficeEmails[k:-k], i.sOfficeEmails[-k:]] + to_template.update({'IMG_FOR_BLOG': i.pMerchantLogo}) + i.fSetRating = {"RATING": i.fSetRating, + "STARS": get_rating_set_for_stars(i.fSetRating)} + i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U'))) + i.earlyCreation = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.earlyCreation, 'U'))) + i.sProfileName = {"NAME": i.sProfileName, + "NAME_T": pytils.translit.slugify(i.sProfileName)} + i.sProfileManufacturer = {"NAME": i.sProfileManufacturer, + "NAME_T": pytils.translit.slugify(i.sProfileManufacturer)} + i.fProfileSeals = pytils.numeral.sum_string(i.fProfileSeals, pytils.numeral.MALE, u"контур, контура, контуров") + if i.sSetImplementCatch.lower() in list_not: + i.sSetImplementCatch = "" + if i.sSetClimateControl.lower() in list_not: + i.sSetClimateControl = "" + if len(i.sProfileReinforcement) > 0: + i.sProfileReinforcement = i.sProfileReinforcement[0].lower()+i.sProfileReinforcement[1:] + if len(i.sSetSill) > 0: + i.sSetSill = i.sSetSill[0].lower()+i.sSetSill[1:] + if len(i.sSetPanes) > 0: + i.sSetPanes = i.sSetPanes[0].lower()+i.sSetPanes[1:] + if len(i.sSetSlope) > 0: + i.sSetSlope = i.sSetSlope[0].lower()+i.sSetSlope[1:] + if len(i.sSetUninstallInstall) > 0: + i.sSetUninstallInstall = i.sSetUninstallInstall[0].lower()+i.sSetUninstallInstall[1:] + if len(i.sSetDelivery) > 0: + i.sSetDelivery = i.sSetDelivery[0].lower()+i.sSetDelivery[1:] + if len(i.sSetOtherConditions) > 0: + i.sSetOtherConditions = i.sSetOtherConditions[0].lower()+i.sSetOtherConditions[1:] + to_template.update({ + 'SETS': list_sets, + # получаем последние визиты клиента через куки + '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, "catalog/catalog_company_detail.html", to_template) diff --git a/oknardia/web/catalog_openings.py b/oknardia/web/catalog_openings.py new file mode 100644 index 0000000..5fa7405 --- /dev/null +++ b/oknardia/web/catalog_openings.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +from django.shortcuts import render +from django.http import HttpRequest, HttpResponse +from oknardia.models import ( + Seria_Info, + Win_MountDim, +) +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 get_flaps_for_mini_pictures +import time +import pytils + +def standard_opening(request: HttpRequest) -> HttpResponse: + time_start = time.time() + to_template = {} # словарь, для передачи шаблону + q_seria = Seria_Info.objects.raw('SELECT oknardia_seria_info.id, oknardia_seria_info.sName ' + 'FROM oknardia_seria_info ' + 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' + 'ORDER BY oknardia_seria_info.sName;') + to_template.update({'SERIAS': list(q_seria)}) + q_win_opening = Win_MountDim.objects.raw( + 'SELECT oknardia_win_mountdim.*,' + ' oknardia_seria_info.sName,' + ' oknardia_seria_info.id AS ID_Seria ' + 'FROM oknardia_win_mountdim' + ' INNER JOIN oknardia_mountdim2apartment' + ' ON oknardia_win_mountdim.id = oknardia_mountdim2apartment.kMountDim_id' + ' RIGHT OUTER JOIN oknardia_apartment_type' + ' ON oknardia_apartment_type.id = oknardia_mountdim2apartment.kApartment_id' + ' RIGHT OUTER JOIN oknardia_seria_info' + ' ON oknardia_apartment_type.kSeria_id = oknardia_seria_info.id ' + 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' + 'GROUP BY oknardia_win_mountdim.iWinWidth, oknardia_win_mountdim.iWinHight,' + ' oknardia_win_mountdim.bIsDoor, oknardia_win_mountdim.bIsNearDoor,' + ' oknardia_win_mountdim.sFlapConfig, oknardia_win_mountdim.id,' + ' oknardia_seria_info.sName, oknardia_seria_info.id ' + 'ORDER BY oknardia_win_mountdim.iWinWidth DESC,' + ' oknardia_win_mountdim.iWinHight DESC,' + ' oknardia_win_mountdim.bIsNearDoor,' + ' oknardia_win_mountdim.bIsDoor,' + ' oknardia_win_mountdim.id,' + ' oknardia_seria_info.sName;') + list_windows_opening = [] + tmp_id = 0 + for i in q_win_opening: + if tmp_id != i.id: + tmp_id = i.id + image_file_name = get_flaps_for_mini_pictures(i.sFlapConfig) + list_windows_opening.append({ + "ID": i.id, + "INCLUDING_IN_SERIA": [{ + "ID": i.ID_Seria, + "NAME_T": pytils.translit.slugify(i.sName), + "NAME": i.sName + }], + "INCLUDING_IN_SERIA_ID": [], + "URL2IMG": image_file_name, + "FLAP_CONFIG": i.sFlapConfig, + "DESCRIPTION": i.sDescripion.split(" для")[0].split(" (")[0], + "DESCRIPTION_L": i.sDescripion, + "IS_DOOR": i.bIsDoor, + "IS_NEAR_DOOR": i.bIsNearDoor, + "H": i.iWinHight * 10, + "W": i.iWinWidth * 10 + }) + else: + list_windows_opening[-1]["INCLUDING_IN_SERIA"].append({ + "ID": i.ID_Seria, + "NAME_T": pytils.translit.slugify(i.sName), + "NAME": i.sName + }) + to_template.update({ + 'LIST_WIN_OPENING': list_windows_opening, + # получаем последние визиты клиента через куки + '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, "catalog/catalog_standard_opening.html", to_template) + diff --git a/oknardia/web/catalog_profiles.py b/oknardia/web/catalog_profiles.py index afa384a..9335f15 100644 --- a/oknardia/web/catalog_profiles.py +++ b/oknardia/web/catalog_profiles.py @@ -6,7 +6,7 @@ from django.db.models import Count from django.shortcuts import render, redirect from django.http import HttpRequest, HttpResponse from oknardia.settings import * -from oknardia.models import Catalog2Profile, PVCprofiles, PriceOffer +from oknardia.models import Catalog2Profile, PVCprofiles, PriceOffer 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 import time @@ -14,6 +14,50 @@ import json import re import pytils +# --------------------------------------------------------------------------- +# Модульные хелперы, общие для всех вьюх этого файла +# --------------------------------------------------------------------------- + +def make_slug(value: str) -> str: + """Транслитерирует строку в slug (pytils).""" + return pytils.translit.slugify(value).lower() + + +def _merchant_row_to_dict(row: dict) -> dict: + """Преобразует ORM-строку с данными партнёра в словарь для шаблона.""" + merchant_name = row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__sMerchantName"] + return { + "MERCHANT_ID": row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__id"], + "MERCHANT_NAME": merchant_name, + "MERCHANT_NAME_T": make_slug(merchant_name), + "MERCHANT_LOGO_URL": row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__pMerchantLogo"], + "MERCHANT_OFFERS": row["offers_by_merchant"], + } + + +def _profile_row_to_dict(profile: dict) -> dict: + """Преобразует ORM-строку профиля в словарь для шаблона.""" + return { + "PROFILE_NAME": profile["sProfileBriefDescription"], + "PROFILE_ID": profile["id"], + "PROFILE_URL": make_slug(profile["sProfileName"]), + "PROFILE_RATING": profile["fProfileRating"], + "PROFILE_RATING_STARS": get_rating_set_for_stars(profile["fProfileRating"]), + } + + +def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None: + """Дописывает в контекст стандартный хвост: визиты и время выполнения.""" + to_template.update({ + 'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]), + 'LOG_VISIT': get_last_all_user_visit_list(), + 'ticks': float(time.time() - time_start), + }) + + +# --------------------------------------------------------------------------- + + def catalog_profile(request: HttpRequest) -> HttpResponse: """ КАТАЛОГ ПРОФИЛЕЙ: страница со списком производителей и моделей (марками) профилей @@ -37,9 +81,6 @@ def catalog_profile(request: HttpRequest) -> HttpResponse: to_template: dict[str, object] = { 'CATALOG_PROFILE_NUM': pytils.numeral.get_plural(profile_count, "профиль,профиля,профилей") } - # Локальный помощник: slug нужен несколько раз, а повторять одну и ту же строку не хочется. - def make_slug(value: str) -> str: - return pytils.translit.slugify(value).lower() list_profile_manufactures = [] tmp_profile_manufacture = "" @@ -76,10 +117,8 @@ def catalog_profile(request: HttpRequest) -> HttpResponse: pytils.numeral.sum_string(len(list_profile_manufactures), pytils.numeral.MALE, ("производитель", "производителя", "производителей")), - 'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]), - 'LOG_VISIT': get_last_all_user_visit_list(), - 'ticks': float(time.time() - time_start), }) + _append_visit_context(to_template, request, time_start) return render(request, "catalog/catalog_of_profiles.html", to_template) @@ -108,9 +147,6 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture f"{model_id}-{model_slug}") # Локальные помощники держат вьюху короче и не размазывают однотипную логику по коду. - def make_slug(value: str) -> str: - return pytils.translit.slugify(value).lower() - def build_other_list(value: str) -> list[str]: # Убираем пустые куски, чтобы не плодить «пустые» характеристики в шаблоне. result = [] @@ -142,27 +178,6 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture else: to_template[template_key] = f"{color},255,{color}" - def merchant_row_to_dict(row: dict) -> dict: - # Один маппер для строки с партнёром: ключи шаблона остаются как были. - merchant_name = row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__sMerchantName"] - return { - "MERCHANT_ID": row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__id"], - "MERCHANT_NAME": merchant_name, - "MERCHANT_NAME_T": make_slug(merchant_name), - "MERCHANT_LOGO_URL": row["kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__pMerchantLogo"], - "MERCHANT_OFFERS": row["offers_by_merchant"], - } - - def profile_row_to_dict(profile: dict) -> dict: - # И то же самое для списка соседних профилей производителя. - return { - "PROFILE_NAME": profile["sProfileBriefDescription"], - "PROFILE_ID": profile["id"], - "PROFILE_URL": make_slug(profile["sProfileName"]), - "PROFILE_RATING": profile["fProfileRating"], - "PROFILE_RATING_STARS": get_rating_set_for_stars(profile["fProfileRating"]), - } - to_template: dict[str, object] = {"CATALOG_MODEL": q_pvc_by_id, "CATALOG_MAN2URL": manufacture_name, "CATALOG_URL": f"{manufacture_id}-{manufacture_name}", @@ -208,7 +223,7 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture .annotate(offers_by_merchant=Count("id")) .order_by("-offers_by_merchant", "kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__sMerchantName") ) - to_template.update({'MERCHANTS': [merchant_row_to_dict(row) for row in q_merchant]}) + to_template.update({'MERCHANTS': [_merchant_row_to_dict(row) for row in q_merchant]}) # Близкие профили этого же производителя нужны для быстрых переходов по карточкам. q_profiles = ( PVCprofiles.objects.filter(sProfileManufacturer=q_pvc_by_id.sProfileManufacturer) @@ -216,7 +231,7 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture .values("id", "fProfileRating", "sProfileBriefDescription", "sProfileName") .order_by("fProfileRating") ) - to_template.update({'PROFILES': [profile_row_to_dict(profile) for profile in q_profiles]}) + to_template.update({'PROFILES': [_profile_row_to_dict(profile) for profile in q_profiles]}) # Описание профиля берём через связку каталог -> блог: это один ORM-запрос вместо сырого SQL. q_profiles_detail = ( Catalog2Profile.objects.filter( @@ -241,13 +256,7 @@ def catalog_profile_model(request: HttpRequest, manufacture_id: int, manufacture profile_blog_dat: datetime | None = max((post.dPostDataModify for post in profile_blog_posts), default=pub_dat) pub_dat = update_pub_dat(pub_dat, profile_blog_dat) or pub_dat to_template['PUB_DAT'] = pub_dat - to_template.update( - { - 'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]), - 'LOG_VISIT': get_last_all_user_visit_list(), - 'ticks': float(time.time() - time_start), - } - ) + _append_visit_context(to_template, request, time_start) return render(request, "catalog/catalog_of_profiles_model.html", to_template) @@ -272,120 +281,76 @@ def catalog_profile_manufacture(request: HttpRequest, manufacture_id: int, manuf if q_pvc_by_id.id != manufacture_id: return redirect(f'/catalog/profile/{q_pvc_by_id.id}-' f'{pytils.translit.slugify(q_pvc_by_id.sProfileManufacturer)}') - to_template = {'CATALOG_MANUFACT': q_pvc_by_id.sProfileManufacturer, - 'CATALOG_MAN2URL': manufacture_name, - 'CATALOG_URL': f"{manufacture_id}-{manufacture_name}"} + to_template: dict[str, object] = {'CATALOG_MANUFACT': q_pvc_by_id.sProfileManufacturer, + 'CATALOG_MAN2URL': manufacture_name, + 'CATALOG_URL': f"{manufacture_id}-{manufacture_name}"} try: - # получаем информацию о производителе (статью из блога) - manufacture_description = list(PVCprofiles.objects.raw( - f"SELECT " - f" oknardia_blogposts.* " - f"FROM oknardia_catalog2profile" - f" RIGHT OUTER JOIN oknardia_pvcprofiles" - f" ON oknardia_catalog2profile.kProfile_id = oknardia_pvcprofiles.id" - f" LEFT OUTER JOIN oknardia_blogposts" - f" ON oknardia_catalog2profile.kBlogCatalog_id = oknardia_blogposts.id " - f"WHERE oknardia_catalog2profile.sCatalogCardType = {CATALOG_RECORD_FOR_PROFILE_MANUFACTURER} " - f" AND oknardia_pvcprofiles.sProfileManufacturer = '{q_pvc_by_id.sProfileManufacturer}'" - f" AND oknardia_blogposts.bCatalog IS TRUE " - f"GROUP BY oknardia_blogposts.bCatalog " - f"LIMIT 1;" - ))[0] - to_template.update({'PUB_DAT': manufacture_description.dPostDataModify}) - if PATH_FOR_IMG_BLOG in manufacture_description.sImgForBlogSocial: - to_template.update({'IMG_FOR_BLOG': manufacture_description.sImgForBlogSocial}) - to_template.update({'HEADER': manufacture_description.sPostHeader, - 'CONTENT': re.sub(r'', '', manufacture_description.sPostContent, - 0, re.IGNORECASE)}) - to_template.update({'TIZER': re.sub(r'||', - '', to_template["CONTENT"], 0, re.IGNORECASE)}) - except (ObjectDoesNotExist, IndexError, TypeError, KeyError,): - pass - q_profiles = PVCprofiles.objects.raw( - f"SELECT oknardia_pvcprofiles.id," - f" oknardia_pvcprofiles.fProfileRating," - f" oknardia_pvcprofiles.sProfileBriefDescription," - f" oknardia_pvcprofiles.sProfileName " - f"FROM oknardia_pvcprofiles " - f"WHERE oknardia_pvcprofiles.sProfileManufacturer = '{q_pvc_by_id.sProfileManufacturer}' " - f"ORDER BY oknardia_pvcprofiles.fProfileRating;" - ) - list_profiles = [] - for i in q_profiles: - list_profiles.append({ - "PROFILE_NAME": i.sProfileBriefDescription, - "PROFILE_ID": i.id, - "PROFILE_URL": pytils.translit.slugify(i.sProfileName).lower(), - "PROFILE_RATING": i.fProfileRating, - "PROFILE_RATING_STARS": get_rating_set_for_stars(i.fProfileRating), - }) - to_template.update({'PROFILES': list_profiles}) - try: - q_share_of_offers = list(PVCprofiles.objects.raw( - f"SELECT" - f" 1 AS id," - f" SUM(Q1.offers_by_model) AS offers_by_maufacture," - f" Q2.tatal_offers-SUM(Q1.offers_by_model) AS offers_other " - f"FROM (SELECT COUNT(oknardia_priceoffer.id) AS offers_by_model" - f" FROM oknardia_priceoffer" - f" LEFT OUTER JOIN oknardia_setkit" - f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id" - f" RIGHT OUTER JOIN oknardia_pvcprofiles" - f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id" - f" WHERE oknardia_pvcprofiles.sProfileManufacturer = '{q_pvc_by_id.sProfileManufacturer}') Q1," - f" (SELECT COUNT(oknardia_priceoffer.id) AS tatal_offers" - f" FROM oknardia_priceoffer) AS Q2 " - f"LIMIT 1;" - ))[0] - 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) - }) - if q_share_of_offers is not None and q_share_of_offers.offers_by_maufacture != 0: - q_merchant = PVCprofiles.objects.raw( - f"SELECT" - f" COUNT(oknardia_priceoffer.id) AS offers_by_merchant," - f" oknardia_merchantbrand.sMerchantName," - f" oknardia_merchantbrand.pMerchantLogo," - f" oknardia_merchantbrand.id " - f"FROM oknardia_priceoffer" - f" INNER JOIN oknardia_setkit" - f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id" - f" INNER JOIN oknardia_pvcprofiles" - f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.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_merchantbrand" - f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id " - f"WHERE oknardia_pvcprofiles.sProfileManufacturer = '{q_pvc_by_id.sProfileManufacturer}' " - f"GROUP BY oknardia_merchantbrand.sMerchantName," - f" oknardia_merchantbrand.pMerchantLogo," - f" oknardia_merchantbrand.id " - f"ORDER BY offers_by_merchant DESC;" + # Получаем статью-описание производителя через Catalog2Profile → BlogPosts. + # GROUP BY из оригинального SQL здесь не нужен: нас устраивает любая первая запись. + catalog_entry = ( + Catalog2Profile.objects.filter( + kProfile__sProfileManufacturer=q_pvc_by_id.sProfileManufacturer, + sCatalogCardType=CATALOG_RECORD_FOR_PROFILE_MANUFACTURER, + kBlogCatalog__bCatalog=True, ) - list_merchant = [] - for i in q_merchant: - list_merchant.append({ - "MERCHANT_ID": i.id, - "MERCHANT_NAME": i.sMerchantName, - "MERCHANT_NAME_T": pytils.translit.slugify(i.sMerchantName), - "MERCHANT_LOGO_URL": i.pMerchantLogo, - "MERCHANT_OFFERS": i.offers_by_merchant - }) - to_template.update({'MERCHANTS': list_merchant}) - 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) - }) + .select_related("kBlogCatalog") + .first() + ) + if catalog_entry is None or catalog_entry.kBlogCatalog is None: + raise ObjectDoesNotExist + manufacture_description = catalog_entry.kBlogCatalog + to_template.update({'PUB_DAT': manufacture_description.dPostDataModify}) + if PATH_FOR_IMG_BLOG in (manufacture_description.sImgForBlogSocial or ""): + to_template.update({'IMG_FOR_BLOG': manufacture_description.sImgForBlogSocial}) + content = re.sub(r'', '', manufacture_description.sPostContent, 0, re.IGNORECASE) + to_template.update({'HEADER': manufacture_description.sPostHeader, 'CONTENT': content}) + to_template.update({'TIZER': re.sub( + r'||', + '', content, 0, re.IGNORECASE, + )}) + except (ObjectDoesNotExist, IndexError, TypeError, KeyError): + pass + + # Список всех профилей этого производителя для навигации по карточкам. + q_profiles = ( + PVCprofiles.objects.filter(sProfileManufacturer=q_pvc_by_id.sProfileManufacturer) + .values("id", "fProfileRating", "sProfileBriefDescription", "sProfileName") + .order_by("fProfileRating") + ) + to_template.update({'PROFILES': [_profile_row_to_dict(p) for p in q_profiles]}) + + try: + # Доля предложений этого производителя относительно всех предложений в базе. + offers_by_manufacture = PriceOffer.objects.filter( + kOffer2SetKit__kSet2PVCprofiles__sProfileManufacturer=q_pvc_by_id.sProfileManufacturer, + ).count() + total_offers = PriceOffer.objects.count() + offers_other = total_offers - offers_by_manufacture + to_template.update({ + 'OFFERS_BY_MAUFACTURE': offers_by_manufacture, + 'OFFERS_OTHER': offers_other, + 'OFFERS_ANGLE': 90 + 180 * normalize(offers_by_manufacture, total_offers), + }) + if offers_by_manufacture > 0: + # Партнёры, у которых есть предложения с профилями этого производителя. + q_merchant = ( + PriceOffer.objects.filter( + kOffer2SetKit__kSet2PVCprofiles__sProfileManufacturer=q_pvc_by_id.sProfileManufacturer, + ) + .values( + "kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__id", + "kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__sMerchantName", + "kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__pMerchantLogo", + ) + .annotate(offers_by_merchant=Count("id")) + .order_by( + "-offers_by_merchant", + "kOffer2SetKit__kSet2User__kMerchantOffice__kMerchantName__sMerchantName", + ) + ) + to_template.update({'MERCHANTS': [_merchant_row_to_dict(row) for row in q_merchant]}) + except (ObjectDoesNotExist, IndexError, TypeError): + pass + _append_visit_context(to_template, request, time_start) return render(request, "catalog/catalog_of_profiles_manufacture.html", to_template) diff --git a/oknardia/web/catalog_series.py b/oknardia/web/catalog_series.py new file mode 100644 index 0000000..930ca62 --- /dev/null +++ b/oknardia/web/catalog_series.py @@ -0,0 +1,404 @@ +# -*- coding: utf-8 -*- +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 oknardia.settings import * +from oknardia.models import ( + 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 get_flaps_for_big_pictures, touch_reload_wsgi +import time +import os +import math +import pytils + +# Каталог типовых серий зданий (пока переадресация) +def catalog_seria(request: HttpRequest) -> HttpResponse: + """ + КАТАЛОГ ТИПОВЫЙ СЕРИЙ: страница со всеми сериями зданий в базе окнардии + + :param request: HttpRequest -- входящий http-запрос + :return response: HttpResponse -- исходящий http-ответ + """ + time_start = time.time() + try: + q_seria = Seria_Info.objects.raw('SELECT' + ' oknardia_seria_info.id,' + ' oknardia_seria_info.sURL2IMG,' + ' oknardia_seria_info.sName ' + 'FROM oknardia_seria_info ' + 'WHERE oknardia_seria_info.id = oknardia_seria_info.kRoot_id ' + 'ORDER BY oknardia_seria_info.sName;') + list_seria = [] + for i in q_seria: + list_seria.append({ + "ID": i.id, + "URL": i.sURL2IMG, + "NAME": i.sName, + "NAME_T": pytils.translit.slugify(i.sName) + }) + to_template = {'SERIAS': list_seria} + except (ObjectDoesNotExist,): + to_template = {} + 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, "catalog/catalog_seria.html", to_template) + + +def catalog_seria_info(request: HttpRequest, seria_name_translit: None, seria_id: int = 843) -> 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) + # 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() + touch_reload_wsgi(light_template_w_path) + 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": ""}) + one_seria.update({"SERIA_L": pytils.translit.slugify(count_seria.sName)}) + 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/catalog_utils.py b/oknardia/web/catalog_utils.py new file mode 100644 index 0000000..e69de29 diff --git a/oknardia/web/diagrams.py b/oknardia/web/diagrams.py index d7e9cdf..dcce5c0 100644 --- a/oknardia/web/diagrams.py +++ b/oknardia/web/diagrams.py @@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist from time import time from oknardia.settings import * from oknardia.models import Seria_Info -from web.catalog import all_seria_nav +from web.catalog_series import all_seria_nav # from oknardia.catalog import all_seria_nav import math import os