страница "Статистика"
This commit is contained in:
@@ -123,6 +123,8 @@ if DEBUG: # DEBUG: заменяем настройки прода, на на
|
||||
STATICFILES_DIRS = [
|
||||
MY_STATIC_ROOT_DEV1 if socket.gethostname() == MY_HOST_HOME1 else MY_STATIC_ROOT_DEV2,
|
||||
]
|
||||
# путь к каталогу static (в эту переменную использовать для указания пути где будут делаться кэш-блоки для шаблонов)
|
||||
STATIC_BASE_PATH = MY_STATIC_BASE_PATH_DEV1 if socket.gethostname() == MY_HOST_HOME1 else MY_STATIC_BASE_PATH_DEV2
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': "django.db.backends.mysql",
|
||||
@@ -137,8 +139,10 @@ if DEBUG: # DEBUG: заменяем настройки прода, на на
|
||||
TOUCH_RELOAD = MY_TOUCH_RELOAD_PROD
|
||||
else:
|
||||
MEDIA_ROOT = MY_MEDIA_ROOT_PROD
|
||||
STATIC_ROOT = MY_STATIC_ROOT_PROD
|
||||
# STATICFILES_DIRS = [MY_STATIC_ROOT_PROD1, ]
|
||||
STATIC_ROOT = MY_STATIC_ROOT_PROD
|
||||
# путь к каталогу static (в эту переменную использовать для указания пути где будут делаться кэш-блоки для шаблонов)
|
||||
STATIC_BASE_PATH = MY_STATIC_BASE_PATH_PROD
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': "django.db.backends.mysql",
|
||||
|
||||
0
oknardia/oknardia/templatetags/__init__.py
Executable file
0
oknardia/oknardia/templatetags/__init__.py
Executable file
@@ -18,7 +18,7 @@ from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.conf.urls.static import static
|
||||
from oknardia.settings import *
|
||||
from web import views, autocomplete_addr, user_manager, blog
|
||||
from web import views, autocomplete_addr, user_manager, blog, diagrams
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
@@ -47,6 +47,7 @@ urlpatterns = [
|
||||
# САТИЧЕСКИЕ СТРАНИЦЫ
|
||||
re_path(r'^tariff$', views.tariff),
|
||||
re_path(r'^contact', views.contact),
|
||||
re_path(r'^stat_all$', diagrams.statistic_menu),
|
||||
|
||||
]
|
||||
|
||||
|
||||
189
oknardia/templates/seria_info/all_stat.html
Executable file
189
oknardia/templates/seria_info/all_stat.html
Executable file
@@ -0,0 +1,189 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load filters %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block Title %} Статистика типового строительства СССР и России.{% endblock %}
|
||||
|
||||
{% block Add_Body_Attribute %} style="padding-top:70px;"{% endblock %}
|
||||
|
||||
{# block Date4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock #}
|
||||
|
||||
{# block Last4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock #}
|
||||
|
||||
{% block Description %}Статистика типового строительства СССР и России. Географи, график ввода в эксплуатацтяю, метраж. Здания проектов серии: {% for CountSeria in SERIA_NAV_DIM %}{{ CountSeria.SERIA_R }}{% if not forloop.last %}, {% endif %}{% endfor %}.{% endblock %}
|
||||
|
||||
{% block Keywords %}типовые проекты зданий, панельное строительство, {% for CountSeria in SERIA_NAV_DIM %}серия {{ CountSeria.SERIA_R }}, {{ CountSeria.SERIA_R }}, {% endfor %}, года простойки, регионы построки, распространенность{% endblock %}
|
||||
|
||||
{% block Top_JS1%}
|
||||
<script type="text/javascript">
|
||||
$(window).load(function(){let images = $('.half');images.each(function(i){$(this).width($(this).width()/2);});});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block Main_Content %}<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-9"><h1>Статистика</h1></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10"><a name="s_graph"></a>
|
||||
<h2 class="header">Распределение типовых серии в жилом фонде<sup><small style="color: darkred;">*</small></sup></h2>
|
||||
</div>
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
<script type="text/javascript">
|
||||
step = Math.round( Math.pow({{ SERIA_NAV_DIM|length }}, 1./3.)-1);
|
||||
step_tone = Math.floor(0xF0/step)-1;
|
||||
DimColor = [];
|
||||
for (i1=0; i1<=step; i1++ )
|
||||
for (i2=step; i2>=0; i2-- )
|
||||
for (i3=0; i3<=step; i3++ ) {
|
||||
//document.write(" <span style='color:#"
|
||||
// + ("00"+(i1*step_tone).toString(16)).substr(-2)
|
||||
// + ("00"+(i2*step_tone).toString(16)).substr(-2)
|
||||
// + ("00"+(i3*step_tone).toString(16)).substr(-2)
|
||||
// + ";'>█</span> -- ");
|
||||
//document.write( "#"
|
||||
// + ("00"+(i1*step_tone).toString(16)).substr(-2)
|
||||
// + ("00"+(i2*step_tone).toString(16)).substr(-2)
|
||||
// + ("00"+(i3*step_tone).toString(16)).substr(-2) + "<br>");
|
||||
DimColor.push("#"
|
||||
+ ("00"+(i1*step_tone).toString(16)).substr(-2)
|
||||
+ ("00"+(i2*step_tone).toString(16)).substr(-2)
|
||||
+ ("00"+(i3*step_tone).toString(16)).substr(-2)
|
||||
);
|
||||
}{% for CountSeria in SERIA_NAV_DIM %}
|
||||
c{{ CountSeria.ID2URL }} = DimColor[{{ forloop.counter0 }}]; s{{ CountSeria.ID2URL }} = "{{ CountSeria.SERIA_R }}"; {% endfor %} sNone = "нет данных";
|
||||
|
||||
google.load("visualization", "1", {packages:["corechart"]});
|
||||
google.setOnLoadCallback(drawChart);
|
||||
function drawChart() {
|
||||
|
||||
let dataNumBuildingPerSeria = google.visualization.arrayToDataTable([
|
||||
[ 'Проект серии', 'Число зданий (шт.)' ],{% for CountSeria in DATA2PIE %}
|
||||
[ 'Серия ' + s{{ CountSeria.ID }}, {{ CountSeria.NUM_BUILDING }} ]{% if not forloop.last %},{% endif %}{% endfor %}
|
||||
]);
|
||||
let dataM2PerSeria = google.visualization.arrayToDataTable([
|
||||
[ 'Проект серии', 'Общая площадь помещений в данно серии (м2)' ],{% for CountSeria in DATA2PIE %}
|
||||
[ 'Серия ' + s{{ CountSeria.ID }}, {{ CountSeria.AREA_M2|stringformat:"f" }} ]{% if not forloop.last %},{% endif %}{% endfor %}
|
||||
]);
|
||||
let TotalKnown = {% for CountSeria in DATA2PIE %}{{ CountSeria.NUM_BUILDING }}{% if not forloop.last %}+{% endif %}{% endfor %};
|
||||
let TotalAddress = 114808;
|
||||
let dataTotal = google.visualization.arrayToDataTable([
|
||||
[ 'База «Окнардии»', 'Количество адресов' ],
|
||||
[ 'Не обработано или нет данных', TotalAddress-TotalKnown ],
|
||||
[ 'Оцифрованые типовые серии', TotalKnown ]
|
||||
|
||||
]);
|
||||
|
||||
let options4Pie1 = {
|
||||
pieHole: 0.386,
|
||||
//backgroundColor:'#f1fff1',
|
||||
legend: {position: "none"},
|
||||
//chartArea: {left:20,top:10,width:'100%',height:'100%'},
|
||||
chartArea: {width: '95%', height: '95%'},
|
||||
slices: { {% for CountSeria in DATA2PIE %}
|
||||
{{ forloop.counter0 }}: { color: c{{ CountSeria.ID }} }{% if not forloop.last %},{% endif %}{% endfor %}
|
||||
},
|
||||
//colors: [ {% for CountSeria in DATA2PIE %} c{{ CountSeria.ID }} {% if not forloop.last %},{% endif %}{% endfor %} ],
|
||||
pieStartAngle: 30,
|
||||
//is3D: true,
|
||||
pieSliceTextStyle: {fontSize: 12},
|
||||
tooltip: {textStyle: {fontSize: 14, color: 'black', opacity: 0.8}, showColorCode: true}
|
||||
};
|
||||
let options4Pie2 = {
|
||||
pieHole: 0.16,
|
||||
legend: {position: "none"},
|
||||
chartArea: {width: '90%', height: '95%', top: 8, left:8},
|
||||
pieStartAngle: 115,
|
||||
slices: { 1: {offset: 0.04}},
|
||||
pieSliceTextStyle: {fontSize: 12},
|
||||
tooltip: {textStyle: {fontSize: 10, color: 'black', opacity: 0.8}, showColorCode: true}
|
||||
};
|
||||
|
||||
let chartNumBuildingPerSeria = new google.visualization.PieChart(document.getElementById('donutchart1'));
|
||||
let chartM2PerSeria = new google.visualization.PieChart(document.getElementById('donutchart2'));
|
||||
let chartTotsl = new google.visualization.PieChart(document.getElementById('donutchart3'));
|
||||
chartNumBuildingPerSeria.draw(dataNumBuildingPerSeria, options4Pie1);
|
||||
chartM2PerSeria.draw(dataM2PerSeria, options4Pie1);
|
||||
chartTotsl.draw(dataTotal, options4Pie2);
|
||||
}
|
||||
$(window).load(function(){ {% for CountSeria in SERIA_NAV_DIM %}$(".cc{{ CountSeria.ID2URL }}").css("color",c{{ CountSeria.ID2URL }}); {% endfor %}});
|
||||
</script>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h4 style="text-align:center;">Количество зданий типовых проектов<sup><small style="color: darkred;">*</small></sup></h4>
|
||||
<div id="donutchart1" style="height:350px;width:100%;"></div>
|
||||
<div style="font-size: xx-small;float: right">© 2015-{% now "Y" %}, данные: oknardia.ru</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 style="text-align:center;">Общая площадь (м²) в типовых сериях<sup><small style="color: darkred;">*</small></sup></h4>
|
||||
<div id="donutchart2" style="height:350px;width:100%;"></div>
|
||||
<div style="font-size: xx-small;float: right">© 2015-{% now "Y" %}, данные: oknardia.ru</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-md-offset-1">
|
||||
<h4>Адресная база «Окнардия»<sup><small style="color: darkred;">*</small></sup></h4>
|
||||
<div id="donutchart3" style="height:180px;width:100%;"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4>Обозначения:</h4>
|
||||
<diV style="font-size: small;">{% for CountSeria in SERIA_NAV_DIM %}
|
||||
<span style="display:inline-flex;width: 22.5%;"><nobr><span class="cc{{ CountSeria.ID2URL }}">█</span> <a href="/catalog/seria/{{ CountSeria.SERIA_L }}/all{{ CountSeria.ID2URL }}/">{{ CountSeria.SERIA_R }}</a></nobr></span>{% endfor %}
|
||||
</diV>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>
|
||||
<script src="{% static 'js/ymaps-pie-chart-clusterer.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static '' %}{{ MAP_JS }}" charset="utf-8" type="text/javascript"></script>
|
||||
<div class="row">
|
||||
<div class="col-md-12"><a name="s_map"></a>
|
||||
<h2 class="header">Здания типовых проектов на карте:</h2></div>
|
||||
<div class="col-xs-1" style="font-size: x-small;">
|
||||
<h6>Обозначения:</h6>
|
||||
<p style="margin-left:1ex;">{% for CountSeria in SERIA_NAV_DIM %}
|
||||
<nobr><span class="cc{{ CountSeria.ID2URL }}">█</span> <a href="/catalog/seria/{{ CountSeria.SERIA_L }}/all{{ CountSeria.ID2URL }}/">{{ CountSeria.SERIA_R }}</a></nobr><br/>{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-11">
|
||||
<div id="SeriaMap" style="height:650px;width:100%;">
|
||||
<p style="font-size:small;padding-top: 1ex;">Чтобы посмотреть цены на установку и замену окон от партнёров «Окнардия» в своей квартире: найдите дом на карте; кликните на него; перейдите по ссылке «Смотреть цены на установку окон». При необходимости смените типовую планировку квартиры (на странице ценовой выдачи, справа от изображения типовых проёмов и схем открывания).</p>
|
||||
<noscript><p>Для отображения картографических данных с помощью «Яндекс.Карт» нужно включить поддержку JavaScript.</p></noscript>
|
||||
</div>
|
||||
<div style="font-size: xx-small;float: right">© 2015-{% now "Y" %}, данные: oknardia.ru</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- div class="row col-md-12">
|
||||
<div class="col-md-8 header"><a name="s_graph"></a>
|
||||
<h2>Возведение зданий разных серий по годам</h2>
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-1" style="height:300px;font-size:large;">
|
||||
{% now "Y" %}<br>
|
||||
{#% include 'SeriaInfo/yaer_graph.html' %#}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-md-12">
|
||||
<div class="col-md-8 header"><a name="s_graph"></a>
|
||||
<h2>Метраж в эксплуатации по годам</h2>
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-1" style="height:300px;font-size:large;">
|
||||
{#% include 'SeriaInfo/yaer_graph.html' %#}
|
||||
</div>
|
||||
</div -->
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-md-offset-10" style="margin-top: 4em;"><p style="font-size:xx-small;">время исполнения скрипта: {{ ticks|floatformat:4 }}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% comment %}
|
||||
{% block Top_Nav_Bar %}
|
||||
{# ОТЛАДКА, ГАСИМ ВЕРХНЕЕ МЕНЮ #}
|
||||
{% endblock %}
|
||||
{% endcomment %}
|
||||
|
||||
110
oknardia/web/diagrams.py
Normal file
110
oknardia/web/diagrams.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from time import time
|
||||
from oknardia.models import Seria_Info
|
||||
from oknardia.settings import *
|
||||
import math
|
||||
import os
|
||||
import pytils # вместо Rus2Lat(smth) --> pytils.translit.slugify(smth).lower()
|
||||
|
||||
|
||||
# возвращает корректный SeriaID и кортеж для построения навигации по сериям дома
|
||||
def seria_nav(i_seria_id: int = 12) -> (int, dict):
|
||||
query_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 query_seria:
|
||||
if count_seria.id == int(i_seria_id):
|
||||
error_seria = False
|
||||
break
|
||||
if error_seria:
|
||||
# Ошибочный SeriaID. Такой базовой серии нет и надо ее найти.
|
||||
try:
|
||||
query = Seria_Info.objects.get(id=int(i_seria_id))
|
||||
if query.kRoot_id is None:
|
||||
# базовая серия прописана в kRoot_id
|
||||
i_seria_id = query.kRoot_id
|
||||
else:
|
||||
# == корневой нет
|
||||
# == ищем методом наименьших расстояний
|
||||
min_min = 100000000
|
||||
min_id = i_seria_id
|
||||
for count_seria in query_seria:
|
||||
if math.fabs(int(i_seria_id) - count_seria.id) < min_min:
|
||||
min_min = math.fabs(int(i_seria_id) - count_seria.id)
|
||||
min_id = count_seria.id
|
||||
i_seria_id = min_id
|
||||
except ObjectDoesNotExist:
|
||||
i_seria_id = query_seria[0].id
|
||||
# print("-->", SeriaID, "<--")
|
||||
seria_nav_dim = []
|
||||
this_return = {}
|
||||
for count_seria in query_seria:
|
||||
one_seria = {}
|
||||
one_seria.update({"SERIA_R": count_seria.sName, "ID2URL": count_seria.id})
|
||||
if count_seria.id == i_seria_id:
|
||||
this_return.update({"THIS_SERIA_NAME": count_seria.sName,
|
||||
"THIS_SERIA_DESCRIPTION": count_seria.sSeriaDescription})
|
||||
one_seria.update({"SERIA_L": ""})
|
||||
else:
|
||||
one_seria.update({"SERIA_L": pytils.translit.slugify(count_seria.sName)})
|
||||
seria_nav_dim.append(one_seria)
|
||||
this_return.update({"SERIA_NAV_DIM": seria_nav_dim})
|
||||
return i_seria_id, this_return
|
||||
|
||||
|
||||
def statistic_menu(request: HttpRequest) -> HttpResponse:
|
||||
""" Страница "Статистика" в главном меню
|
||||
|
||||
ВНИМАНИЕ: ТЕХНИЧЕСКИЙ ДОЛГ -- выводятся данные только по сериям зданий. Этого маловато.
|
||||
Можно добавить данные по проемам, предложениям, график распределения цен и т.п.
|
||||
|
||||
:param request: HttpRequest -- входящий http-запрос
|
||||
:return: HttpResponse -- исходящий http-ответ
|
||||
"""
|
||||
time_start = time()
|
||||
template = "seria_info/all_stat.html"
|
||||
to_template = {}
|
||||
seria_id, for_seria_nav = seria_nav(0)
|
||||
to_template.update(for_seria_nav)
|
||||
# проверяем какой JS с картами и PieCharts: упакованные или нет.
|
||||
path_name = f"{STATIC_BASE_PATH}/{PATH_FOR_JS_MAP}"
|
||||
print(path_name)
|
||||
if os.path.isfile(f"{path_name}/_ALL{SUFFIX_FOR_MINI_JS_MAP}"):
|
||||
to_template.update({'MAP_JS': f"{PATH_FOR_JS_MAP}/_ALL{SUFFIX_FOR_MINI_JS_MAP}"})
|
||||
else:
|
||||
to_template.update({'MAP_JS': f"{PATH_FOR_JS_MAP}/_ALL{SUFFIX_FOR_JS_MAP}"})
|
||||
|
||||
# строим диаграмму сколько каких серий и каковы их площади...
|
||||
q_seria_pie = Seria_Info.objects.raw(
|
||||
"SELECT"
|
||||
" oknardia_seria_info.kRoot_id as id,"
|
||||
" COUNT(oknardia_building_info.id) AS num_building,"
|
||||
" SUM(oknardia_building_info.fTotal_Area) AS areaM2 "
|
||||
"FROM oknardia_building_info"
|
||||
" INNER JOIN oknardia_seria_info"
|
||||
" ON oknardia_building_info.kSeria_Link_id = oknardia_seria_info.id "
|
||||
"WHERE oknardia_seria_info.kRoot_id IS NOT NULL "
|
||||
"GROUP BY oknardia_seria_info.kRoot_id "
|
||||
"ORDER BY num_building DESC;")
|
||||
data2pie = []
|
||||
for count in q_seria_pie:
|
||||
data2pie.append({
|
||||
"ID": count.id,
|
||||
"AREA_M2": count.areaM2,
|
||||
"NUM_BUILDING": count.num_building
|
||||
})
|
||||
# print data2pie
|
||||
to_template.update({'DATA2PIE': data2pie})
|
||||
|
||||
to_template.update({'ticks': float(time()-time_start)})
|
||||
return render(request, template, to_template)
|
||||
Reference in New Issue
Block a user