mod: данные о последних визитах пользователя полностью перенесены с сервера на клиент (в JS). Отключен из контекста всех шаблонов LAST_VISIT и связанные с ним функции в вьюшках
This commit is contained in:
@@ -75,11 +75,7 @@
|
|||||||
<button type="submit" class="btn btn-primary btn-add">Найти</button>
|
<button type="submit" class="btn btn-primary btn-add">Найти</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% if LAST_VISIT %}<div><h5>Ваши последние просмотры:</h5>
|
{% include 'report/report_last_user_visit.html' with background_color="None" %}
|
||||||
<ul style="font-size:small">{% for ITEM in LAST_VISIT %}
|
|
||||||
<li><a href="{{ ITEM.LastURL }}">{{ ITEM.LastApart }} <small>({{ ITEM.LastAddress }})</small></a> <small style="font-size: xx-small;">{{ ITEM.Time }}</small></li>{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>{% endif %}
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
<p></p>{% endwith %}
|
<p></p>{% endwith %}
|
||||||
@@ -107,7 +107,8 @@
|
|||||||
<meta property="article:modified_time" content="{{ META_DATA_PUBLISH|date:'Y-m-d' }}" />
|
<meta property="article:modified_time" content="{{ META_DATA_PUBLISH|date:'Y-m-d' }}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block Top_JS3%}<script type="text/javascript">
|
{% block Top_JS3%}<script type="text/javascript" src="{% static 'js/track_user_visit.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
function show_phone_num( id ){ // колапсатор для отображения контатной информации постафшика окон
|
function show_phone_num( id ){ // колапсатор для отображения контатной информации постафшика окон
|
||||||
$('#tel'+id).collapse('show');
|
$('#tel'+id).collapse('show');
|
||||||
$('#hid'+id).collapse('hide');
|
$('#hid'+id).collapse('hide');
|
||||||
@@ -278,6 +279,13 @@ $(function () { // инициализация и обработка попове
|
|||||||
<p id="shadow_buffer"></p>
|
<p id="shadow_buffer"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# Скрытый элемент для отслеживания визитов пользователя (передача данных в JS track_user_visit.js) #}
|
||||||
|
<div id="tracking-data"
|
||||||
|
data-current-url="{{ request.path }}"
|
||||||
|
data-address="{{ ADDRESS }}"
|
||||||
|
data-apart="{{ APART }}"
|
||||||
|
style="display: none;"></div>
|
||||||
|
|
||||||
{# модальное окно #}
|
{# модальное окно #}
|
||||||
<div class="modal fade bs-example-modal-sm" id="modal-exclamation" tabindex="-1" role="dialog">
|
<div class="modal fade bs-example-modal-sm" id="modal-exclamation" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog modal-sm">
|
<div class="modal-dialog modal-sm">
|
||||||
|
|||||||
@@ -1,8 +1,71 @@
|
|||||||
<!--- Информация об адресах просмотренных текущим пользователем --->{% load filters %}
|
<!-- Информация об адресах, просмотренных текущим пользователем (читается из браузерных кук) -->
|
||||||
{% if LAST_VISIT and LAST_VISIT|length >= 1 %}<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="col-md-11 col-xs-12 last_user_visit"><h5>Цены на окна просмотренные вами:</h5>
|
<div class="col-md-11 col-xs-12{% if background_color != "None" %} last_user_visit{% endif %}" id="last_user_visit_container" style="display:none;">
|
||||||
<ul>{% for ITEM in LAST_VISIT %}
|
<h5>Цены на окна просмотренные вами {{ background_color }}:</h5>
|
||||||
<li><a href="{{ ITEM.LastURL }}">Цены на окна для серии {{ ITEM.LastApart }} <small>({{ ITEM.LastAddress }})</small></a> <small>{{ ITEM.Time }}</small></li>{% endfor %}
|
<ul id="last_visits_list"></ul>
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>{% endif %}
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
/**
|
||||||
|
* Отслеживание последних визитов пользователя из браузерных кук.
|
||||||
|
* Читает куку 'LastVisit', парсит JSON и выводит список ссылок на уже просмотренные ценовые отчёты.
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Функция для получения значения куки по имени
|
||||||
|
function getCookieValue(name) {
|
||||||
|
try {
|
||||||
|
if (document.cookie) {
|
||||||
|
const cookies = document.cookie.split('; ');
|
||||||
|
for (let cookie of cookies) {
|
||||||
|
const [cookieName, cookieValue] = cookie.split('=');
|
||||||
|
if (cookieName === name) {
|
||||||
|
return decodeURIComponent(cookieValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Ошибка при чтении куки LastVisit:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем куку с визитами
|
||||||
|
const cookieValue = getCookieValue('LastVisit');
|
||||||
|
if (cookieValue) {
|
||||||
|
try {
|
||||||
|
const visits = JSON.parse(cookieValue);
|
||||||
|
|
||||||
|
// Проверяем, есть ли визиты
|
||||||
|
if (visits && visits.length > 0) {
|
||||||
|
const listContainer = document.getElementById('last_visits_list');
|
||||||
|
const lastUserVisitContainer = document.getElementById('last_user_visit_container');
|
||||||
|
|
||||||
|
// Очищаем список перед заполнением
|
||||||
|
listContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// При перезагрузке страницы текущий визит уже записан, поэтому пропускаем первый
|
||||||
|
const visitsToShow = visits.slice(1);
|
||||||
|
|
||||||
|
// Выводим предыдущие визиты (не текущий)
|
||||||
|
for (let i = 0; i < visitsToShow.length; i++) {
|
||||||
|
const item = visitsToShow[i];
|
||||||
|
const li = document.createElement('li');
|
||||||
|
|
||||||
|
// Форматируем текст ссылки: адрес (тип квартиры)
|
||||||
|
const linkText = `Цены на окна для серии ${item.LastApart} <small>(${item.LastAddress})</small>`;
|
||||||
|
|
||||||
|
li.innerHTML = `<a href="${item.LastURL}">${linkText}</a>`;
|
||||||
|
listContainer.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если есть данные для отображения, показываем блок
|
||||||
|
if (visitsToShow.length > 0) {
|
||||||
|
lastUserVisitContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Ошибка при разборе JSON из кук LastVisit:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.shortcuts import render, redirect
|
|||||||
|
|
||||||
from oknardia.models import Seria_Info, SetKit
|
from oknardia.models import Seria_Info, SetKit
|
||||||
from web.add_func import get_rating_set_for_stars
|
from web.add_func import get_rating_set_for_stars
|
||||||
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
|
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||||
|
|
||||||
|
|
||||||
def catalog_root(request: HttpRequest) -> HttpResponse:
|
def catalog_root(request: HttpRequest) -> HttpResponse:
|
||||||
@@ -21,7 +21,6 @@ def catalog_root(request: HttpRequest) -> HttpResponse:
|
|||||||
time_start = time.perf_counter()
|
time_start = time.perf_counter()
|
||||||
# получаем из cookies последние визиты клиента
|
# получаем из cookies последние визиты клиента
|
||||||
to_template: dict[str, object] = {
|
to_template: dict[str, object] = {
|
||||||
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
|
|
||||||
'LOG_VISIT': get_last_all_user_visit_list(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start)}
|
'ticks': float(time.perf_counter() - time_start)}
|
||||||
response = render(request, "catalog/catalog_root.html", to_template)
|
response = render(request, "catalog/catalog_root.html", to_template)
|
||||||
@@ -83,7 +82,6 @@ def catalog_sets(request: HttpRequest) -> HttpResponse:
|
|||||||
|
|
||||||
to_template: dict[str, object] = {
|
to_template: dict[str, object] = {
|
||||||
'SET_LIST': kits,
|
'SET_LIST': kits,
|
||||||
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
|
|
||||||
'LOG_VISIT': get_last_all_user_visit_list(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ from oknardia.models import (
|
|||||||
SetKit,
|
SetKit,
|
||||||
PriceOffer,
|
PriceOffer,
|
||||||
)
|
)
|
||||||
from web.report1 import (
|
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||||
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
|
from web.add_func import get_rating_set_for_stars
|
||||||
import django.utils.dateformat
|
import django.utils.dateformat
|
||||||
import time
|
import time
|
||||||
@@ -179,7 +175,6 @@ def catalog_company(request: HttpRequest) -> HttpResponse:
|
|||||||
|
|
||||||
Контекст шаблона:
|
Контекст шаблона:
|
||||||
- COMPANIES (list): Список компаний с статистикой
|
- COMPANIES (list): Список компаний с статистикой
|
||||||
- LAST_VISIT (list): Последние визиты текущего пользователя
|
|
||||||
- LOG_VISIT (list): Последние визиты всех пользователей
|
- LOG_VISIT (list): Последние визиты всех пользователей
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -200,9 +195,6 @@ def catalog_company(request: HttpRequest) -> HttpResponse:
|
|||||||
# Получаем информацию о посещениях для персонализации
|
# Получаем информацию о посещениях для персонализации
|
||||||
to_template: dict[str, object] = {
|
to_template: dict[str, object] = {
|
||||||
'COMPANIES': formatted_companies,
|
'COMPANIES': formatted_companies,
|
||||||
'LAST_VISIT': get_last_user_visit_list(
|
|
||||||
get_last_user_visit_cookies(request)[:3]
|
|
||||||
),
|
|
||||||
'LOG_VISIT': get_last_all_user_visit_list(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +461,6 @@ def catalog_company_detail(
|
|||||||
- SETS (list): Список оконных наборов с их полной информацией
|
- SETS (list): Список оконных наборов с их полной информацией
|
||||||
- IMG_FOR_BLOG (str): Логотип компании
|
- IMG_FOR_BLOG (str): Логотип компании
|
||||||
- LIST_NOT (list): Стандартные маркеры "пусто"
|
- LIST_NOT (list): Стандартные маркеры "пусто"
|
||||||
- LAST_VISIT (list): Последние визиты текущего пользователя
|
|
||||||
- LOG_VISIT (list): Последние визиты всех пользователей
|
- LOG_VISIT (list): Последние визиты всех пользователей
|
||||||
- ticks (float): Время выполнения представления (в секундах)
|
- ticks (float): Время выполнения представления (в секундах)
|
||||||
|
|
||||||
@@ -518,9 +509,6 @@ def catalog_company_detail(
|
|||||||
'META_KEYWORDS': company.sMerchantName,
|
'META_KEYWORDS': company.sMerchantName,
|
||||||
'IMG_FOR_BLOG': company.pMerchantLogo,
|
'IMG_FOR_BLOG': company.pMerchantLogo,
|
||||||
'LIST_NOT': empty_values,
|
'LIST_NOT': empty_values,
|
||||||
'LAST_VISIT': get_last_user_visit_list(
|
|
||||||
get_last_user_visit_cookies(request)[:3]
|
|
||||||
),
|
|
||||||
'LOG_VISIT': get_last_all_user_visit_list(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from django.db.models import F
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from oknardia.models import MountDim2Apartment
|
from oknardia.models import MountDim2Apartment
|
||||||
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
|
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||||
from web.add_func import get_flaps_for_mini_pictures
|
from web.add_func import get_flaps_for_mini_pictures
|
||||||
import time
|
import time
|
||||||
import pytils
|
import pytils
|
||||||
@@ -20,8 +20,6 @@ def _make_slug(value: str) -> str:
|
|||||||
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
||||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||||
to_template.update({
|
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(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.shortcuts import render, redirect
|
|||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from oknardia.settings import *
|
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.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||||
from web.add_func import normalize, get_rating_set_for_stars
|
from web.add_func import normalize, get_rating_set_for_stars
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
@@ -49,7 +49,6 @@ def _profile_row_to_dict(profile: dict) -> dict:
|
|||||||
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
||||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||||
to_template.update({
|
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(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from oknardia.models import (
|
|||||||
Win_MountDim,
|
Win_MountDim,
|
||||||
Building_Info,
|
Building_Info,
|
||||||
)
|
)
|
||||||
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
|
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||||
from web.add_func import get_flaps_for_big_pictures
|
from web.add_func import get_flaps_for_big_pictures
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
@@ -29,7 +29,6 @@ def _make_slug(value: str) -> str:
|
|||||||
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
def _append_visit_context(to_template: dict, request: HttpRequest, time_start: float) -> None:
|
||||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||||
to_template.update({
|
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(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from oknardia.models import (
|
|||||||
MountDim2Apartment,
|
MountDim2Apartment,
|
||||||
)
|
)
|
||||||
from oknardia.settings import *
|
from oknardia.settings import *
|
||||||
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
|
from web.report1 import get_last_all_user_visit_list, 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, \
|
from web.add_func import normalize, get_rating_set_for_stars, get_flaps_for_big_pictures, get_flaps_for_mini_pictures, \
|
||||||
get_geo_distance
|
get_geo_distance
|
||||||
import django.utils.dateformat
|
import django.utils.dateformat
|
||||||
@@ -57,11 +57,8 @@ def _append_visit_context(
|
|||||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||||
if log_visit is None:
|
if log_visit is None:
|
||||||
log_visit = get_last_all_user_visit_list()
|
log_visit = get_last_all_user_visit_list()
|
||||||
if last_visit_cookie is None:
|
|
||||||
last_visit_cookie = get_last_user_visit_cookies(request)
|
|
||||||
|
|
||||||
to_template.update({
|
to_template.update({
|
||||||
'LAST_VISIT': get_last_user_visit_list(last_visit_cookie[:3]),
|
|
||||||
'LOG_VISIT': log_visit,
|
'LOG_VISIT': log_visit,
|
||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
})
|
})
|
||||||
@@ -956,29 +953,14 @@ def report_price(request: HttpRequest, build_id: str = "22427", apart_id: str =
|
|||||||
)
|
)
|
||||||
log_entry.save() # INSERT
|
log_entry.save() # INSERT
|
||||||
|
|
||||||
# получаем последние визиты клиента через куки
|
# Вызываем контекст без параметра last_visit_cookie (получит из кук автоматически)
|
||||||
last_visit = get_last_user_visit_cookies(request)
|
|
||||||
# Для блока LAST_VISIT показываем историю до текущего захода.
|
|
||||||
last_visit_for_context = list(last_visit)
|
|
||||||
# подготавливаем данные о текущем посещении для помещения в cookie
|
|
||||||
Item = {
|
|
||||||
"LastURL": new_url,
|
|
||||||
"LastAddress": to_template["ADDRESS"],
|
|
||||||
"LastApart": to_template["APART"],
|
|
||||||
"Time": time.perf_counter()}
|
|
||||||
last_visit.insert(0, Item) # Добавляем текущий Item в начало
|
|
||||||
last_visit = json.dumps(last_visit[:3]) # упаковываем json без пробелов (три записи)
|
|
||||||
# print u"сейчас запишем вот эту куку:", LastVisit
|
|
||||||
_append_visit_context(
|
_append_visit_context(
|
||||||
to_template=to_template,
|
to_template=to_template,
|
||||||
request=request,
|
request=request,
|
||||||
time_start=time_start,
|
time_start=time_start,
|
||||||
log_visit=log_visit,
|
log_visit=log_visit,
|
||||||
last_visit_cookie=last_visit_for_context,
|
|
||||||
)
|
)
|
||||||
response = render(request, "price/price_list.html", to_template)
|
return render(request, "price/price_list.html", to_template)
|
||||||
response.set_cookie("LastVisit", last_visit, max_age=7862400) # ставим или перезаписываем куки (91 день)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def next_price_frame(request: HttpRequest, apart_id: str = "1", mount_dim_per_offer: str = "1",
|
def next_price_frame(request: HttpRequest, apart_id: str = "1", mount_dim_per_offer: str = "1",
|
||||||
|
|||||||
@@ -95,21 +95,6 @@ def _bounds(items: list, field: str, threshold=None) -> tuple[float, float]:
|
|||||||
return min(vals), max(vals)
|
return min(vals), max(vals)
|
||||||
|
|
||||||
|
|
||||||
def get_last_user_visit_cookies(request: HttpRequest) -> list:
|
|
||||||
""" Служебная функция: проверяет есть ли куки о последних посещениях пользователя, и если есть возвращает их
|
|
||||||
|
|
||||||
:param request: HttpRequest -- входящий http-запрос
|
|
||||||
:return LastVisit: json -- загруженный json-объект из куки LastVisit
|
|
||||||
"""
|
|
||||||
if "LastVisit" in request.COOKIES:
|
|
||||||
try:
|
|
||||||
return json.loads(request.COOKIES["LastVisit"])
|
|
||||||
except (json.decoder.JSONDecodeError, TypeError, ValueError, KeyError, AttributeError):
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def get_last_user_visit_list(list_visit: list) -> list:
|
def get_last_user_visit_list(list_visit: list) -> list:
|
||||||
""" Служебная функция: получает список с посещенных страниц с ценовой выдачей (ListVisit), меняет в нем даты
|
""" Служебная функция: получает список с посещенных страниц с ценовой выдачей (ListVisit), меняет в нем даты
|
||||||
на описание типа "три недели назад" и возвращает обратно.
|
на описание типа "три недели назад" и возвращает обратно.
|
||||||
@@ -417,10 +402,7 @@ def compare_offers(request: HttpRequest, to_compare: str = "1,2") -> HttpRespons
|
|||||||
except SetKit.DoesNotExist:
|
except SetKit.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
to_template.update({
|
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(),
|
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||||
'ticks': float(time.perf_counter() - time_start)
|
'ticks': float(time.perf_counter() - time_start)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -158,9 +158,6 @@ class ReportOneWinPriceTests(TestCase):
|
|||||||
sOfferActive=False,
|
sOfferActive=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch("web.prices.get_last_all_user_visit_list", return_value=[])
|
|
||||||
@patch("web.prices.get_last_user_visit_list", return_value=[])
|
|
||||||
@patch("web.prices.get_last_user_visit_cookies", return_value=[])
|
|
||||||
@patch("web.prices.get_flaps_for_mini_pictures", return_value="img/test-mini.png")
|
@patch("web.prices.get_flaps_for_mini_pictures", return_value="img/test-mini.png")
|
||||||
@patch(
|
@patch(
|
||||||
"web.prices.get_flaps_for_big_pictures",
|
"web.prices.get_flaps_for_big_pictures",
|
||||||
@@ -178,9 +175,6 @@ class ReportOneWinPriceTests(TestCase):
|
|||||||
self,
|
self,
|
||||||
mocked_big_pictures,
|
mocked_big_pictures,
|
||||||
mocked_mini_pictures,
|
mocked_mini_pictures,
|
||||||
mocked_cookies,
|
|
||||||
mocked_last_visits,
|
|
||||||
mocked_all_visits,
|
|
||||||
):
|
):
|
||||||
"""Вьюха должна собирать тот же ключевой контекст, но уже без raw SQL."""
|
"""Вьюха должна собирать тот же ключевой контекст, но уже без raw SQL."""
|
||||||
request = self.factory.get(
|
request = self.factory.get(
|
||||||
@@ -216,9 +210,6 @@ class ReportOneWinPriceTests(TestCase):
|
|||||||
self.assertIn("META_DATA_PUBLISH", context)
|
self.assertIn("META_DATA_PUBLISH", context)
|
||||||
self.assertTrue(mocked_big_pictures.called)
|
self.assertTrue(mocked_big_pictures.called)
|
||||||
self.assertTrue(mocked_mini_pictures.called)
|
self.assertTrue(mocked_mini_pictures.called)
|
||||||
self.assertTrue(mocked_cookies.called)
|
|
||||||
self.assertTrue(mocked_last_visits.called)
|
|
||||||
self.assertTrue(mocked_all_visits.called)
|
|
||||||
|
|
||||||
def test_report_one_win_price_redirects_to_canonical_dimensions(self):
|
def test_report_one_win_price_redirects_to_canonical_dimensions(self):
|
||||||
"""Если SEO-размеры в URL неверные, вьюха должна редиректить на канонический URL."""
|
"""Если SEO-размеры в URL неверные, вьюха должна редиректить на канонический URL."""
|
||||||
|
|||||||
@@ -156,14 +156,8 @@ class CatalogProfileViewTests(TestCase):
|
|||||||
|
|
||||||
return profile, sibling, brand, blog
|
return profile, sibling, brand, blog
|
||||||
|
|
||||||
@patch("web.catalog.get_last_all_user_visit_list", return_value=["all-visits"])
|
|
||||||
@patch("web.catalog.get_last_user_visit_list", return_value=["last-visits"])
|
|
||||||
@patch("web.catalog.get_last_user_visit_cookies", return_value=["cookie-1", "cookie-2", "cookie-3"])
|
|
||||||
def test_catalog_profile_handles_empty_catalog(
|
def test_catalog_profile_handles_empty_catalog(
|
||||||
self,
|
self,
|
||||||
mocked_cookies,
|
|
||||||
mocked_last_visits,
|
|
||||||
mocked_all_visits,
|
|
||||||
):
|
):
|
||||||
"""Пустой каталог не должен падать и должен отдавать ожидаемый контекст."""
|
"""Пустой каталог не должен падать и должен отдавать ожидаемый контекст."""
|
||||||
with self.assertNumQueries(1):
|
with self.assertNumQueries(1):
|
||||||
@@ -174,20 +168,10 @@ class CatalogProfileViewTests(TestCase):
|
|||||||
self.assertEqual(context["CATALOG_PROFILE_NUM"], "0 профилей")
|
self.assertEqual(context["CATALOG_PROFILE_NUM"], "0 профилей")
|
||||||
self.assertEqual(context["CATALOG_MANUFACT_NUM"], 0)
|
self.assertEqual(context["CATALOG_MANUFACT_NUM"], 0)
|
||||||
self.assertEqual(context["CATALOG_PROFILE_MAN1_NAME2"], [])
|
self.assertEqual(context["CATALOG_PROFILE_MAN1_NAME2"], [])
|
||||||
self.assertEqual(context["LAST_VISIT"], ["last-visits"])
|
|
||||||
self.assertEqual(context["LOG_VISIT"], ["all-visits"])
|
|
||||||
self.assertTrue(mocked_cookies.called)
|
|
||||||
self.assertTrue(mocked_last_visits.called)
|
|
||||||
self.assertTrue(mocked_all_visits.called)
|
|
||||||
|
|
||||||
@patch("web.catalog.get_last_all_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_cookies", return_value=[])
|
|
||||||
def test_catalog_profile_groups_and_sorts_profiles(
|
def test_catalog_profile_groups_and_sorts_profiles(
|
||||||
self,
|
self,
|
||||||
mocked_cookies,
|
|
||||||
mocked_last_visits,
|
|
||||||
mocked_all_visits,
|
|
||||||
):
|
):
|
||||||
"""Каталог должен группировать профили по производителю и сохранять сортировку."""
|
"""Каталог должен группировать профили по производителю и сохранять сортировку."""
|
||||||
self._create_profile(name="Alpha Basic", brief="Альфа База", manufacturer="Альфа", days_ago=5)
|
self._create_profile(name="Alpha Basic", brief="Альфа База", manufacturer="Альфа", days_ago=5)
|
||||||
@@ -224,20 +208,9 @@ class CatalogProfileViewTests(TestCase):
|
|||||||
# Проверяем итоговые счетчики и структуру контекста.
|
# Проверяем итоговые счетчики и структуру контекста.
|
||||||
self.assertEqual(context["CATALOG_MANUFACT_NUM"], 2)
|
self.assertEqual(context["CATALOG_MANUFACT_NUM"], 2)
|
||||||
self.assertEqual(context["CATALOG_PROFILE_NUM"], "4 профиля")
|
self.assertEqual(context["CATALOG_PROFILE_NUM"], "4 профиля")
|
||||||
self.assertEqual(context["LAST_VISIT"], [])
|
|
||||||
self.assertEqual(context["LOG_VISIT"], [])
|
|
||||||
self.assertTrue(mocked_cookies.called)
|
|
||||||
self.assertTrue(mocked_last_visits.called)
|
|
||||||
self.assertTrue(mocked_all_visits.called)
|
|
||||||
|
|
||||||
@patch("web.catalog.get_last_all_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_cookies", return_value=[])
|
|
||||||
def test_catalog_profile_model_redirects_to_canonical_url(
|
def test_catalog_profile_model_redirects_to_canonical_url(
|
||||||
self,
|
self,
|
||||||
mocked_cookies,
|
|
||||||
mocked_last_visits,
|
|
||||||
mocked_all_visits,
|
|
||||||
):
|
):
|
||||||
"""При неверных slug страница должна отправлять на канонический URL."""
|
"""При неверных slug страница должна отправлять на канонический URL."""
|
||||||
profile = self._create_profile(name="Alpha Basic", brief="Альфа База", manufacturer="Альфа", days_ago=5)
|
profile = self._create_profile(name="Alpha Basic", brief="Альфа База", manufacturer="Альфа", days_ago=5)
|
||||||
@@ -248,14 +221,8 @@ class CatalogProfileViewTests(TestCase):
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], f"/catalog/profile/{profile.id}-alfa/{profile.id}-alpha-basic")
|
self.assertEqual(response["Location"], f"/catalog/profile/{profile.id}-alfa/{profile.id}-alpha-basic")
|
||||||
|
|
||||||
@patch("web.catalog.get_last_all_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_list", return_value=[])
|
|
||||||
@patch("web.catalog.get_last_user_visit_cookies", return_value=[])
|
|
||||||
def test_catalog_profile_model_renders_related_data(
|
def test_catalog_profile_model_renders_related_data(
|
||||||
self,
|
self,
|
||||||
mocked_cookies,
|
|
||||||
mocked_last_visits,
|
|
||||||
mocked_all_visits,
|
|
||||||
):
|
):
|
||||||
"""Карточка профиля должна собираться через ORM и отдавать все ключевые блоки."""
|
"""Карточка профиля должна собираться через ORM и отдавать все ключевые блоки."""
|
||||||
profile, sibling, brand, blog = self._create_catalog_profile_model_fixture()
|
profile, sibling, brand, blog = self._create_catalog_profile_model_fixture()
|
||||||
@@ -287,7 +254,4 @@ class CatalogProfileViewTests(TestCase):
|
|||||||
self.assertEqual(context["IMG_FOR_BLOG"], blog.sImgForBlogSocial)
|
self.assertEqual(context["IMG_FOR_BLOG"], blog.sImgForBlogSocial)
|
||||||
self.assertEqual(context["PUB_DAT"].date(), blog.dPostDataModify.date())
|
self.assertEqual(context["PUB_DAT"].date(), blog.dPostDataModify.date())
|
||||||
self.assertEqual(context["LIST_OTHER"], ["<b>Контур:</b>2", "<b>Цвет:</b>Белый"])
|
self.assertEqual(context["LIST_OTHER"], ["<b>Контур:</b>2", "<b>Цвет:</b>Белый"])
|
||||||
self.assertTrue(mocked_cookies.called)
|
|
||||||
self.assertTrue(mocked_last_visits.called)
|
|
||||||
self.assertTrue(mocked_all_visits.called)
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import pytils
|
|||||||
|
|
||||||
|
|
||||||
def main_init(request: HttpRequest) -> HttpResponse:
|
def main_init(request: HttpRequest) -> HttpResponse:
|
||||||
""" Главная страница (статичная, только с проверками куков)
|
""" Главная страница (статичная, только с проверками кук)
|
||||||
|
|
||||||
:param request: входящий http-запрос
|
:param request: входящий http-запрос
|
||||||
:return response: исходящий http-ответ
|
:return response: исходящий http-ответ
|
||||||
@@ -28,22 +28,6 @@ def main_init(request: HttpRequest) -> HttpResponse:
|
|||||||
# стоят куки, и это не первый визит
|
# стоят куки, и это не первый визит
|
||||||
num_viz = request.COOKIES["NumVisit"] # читаем число визитов
|
num_viz = request.COOKIES["NumVisit"] # читаем число визитов
|
||||||
num_viz = int(num_viz) + 1 # увеличиваем порядковый номер визитов
|
num_viz = int(num_viz) + 1 # увеличиваем порядковый номер визитов
|
||||||
# ПРОВЕРЯЧЕМ КУКИ ПРОСМОТРЕ ЦЕНОВЫХ ПРЕДЛОЖЕНИЙ
|
|
||||||
if "LastVisit" in request.COOKIES:
|
|
||||||
# стоят куки
|
|
||||||
last_visit = json.loads(request.COOKIES["LastVisit"])
|
|
||||||
last_visit2 = []
|
|
||||||
for i in last_visit:
|
|
||||||
last_visit2.append({
|
|
||||||
"Time": datetime.datetime.fromtimestamp(i["Time"]),
|
|
||||||
"LastURL": i["LastURL"],
|
|
||||||
"LastAddress": i["LastAddress"],
|
|
||||||
"LastApart": i["LastApart"]
|
|
||||||
})
|
|
||||||
to_template.update({'LAST_VISIT': last_visit2[:3]})
|
|
||||||
else:
|
|
||||||
to_template.update({'LAST_VISIT': None})
|
|
||||||
to_template.update({'META_DOCUMENT_STATE': u"Static"}) # Эта страничка статичная (в шаблон)
|
|
||||||
to_template.update({'NV': num_viz})
|
to_template.update({'NV': num_viz})
|
||||||
# to_template.update(csrf(request)) # токен, для метода POST и GET
|
# to_template.update(csrf(request)) # токен, для метода POST и GET
|
||||||
response = render(request, "index.html", to_template)
|
response = render(request, "index.html", to_template)
|
||||||
|
|||||||
98
public/static/js/track_user_visit.js
Normal file
98
public/static/js/track_user_visit.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* Логика записи визитов пользователя в cookies.
|
||||||
|
* Отслеживает последние посещения страниц с ценовой выдачей.
|
||||||
|
*
|
||||||
|
* Используемые данные из HTML:
|
||||||
|
* - data-current-url: текущий URL страницы
|
||||||
|
* - data-address: адрес здания
|
||||||
|
* - data-apart: тип квартиры
|
||||||
|
*
|
||||||
|
* Сохраняет в куку 'LastVisit' максимум 3 последних визита в формате JSON.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function trackUserVisit(currentUrl, address, apart) {
|
||||||
|
// Функция для получения значения куки по имени
|
||||||
|
function getCookieValue(name) {
|
||||||
|
try {
|
||||||
|
if (document.cookie) {
|
||||||
|
const cookies = document.cookie.split('; ');
|
||||||
|
for (let cookie of cookies) {
|
||||||
|
const [cookieName, cookieValue] = cookie.split('=');
|
||||||
|
if (cookieName === name) {
|
||||||
|
return decodeURIComponent(cookieValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Ошибка при чтении куки:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для установки куки с заданным сроком жизни
|
||||||
|
function setCookie(name, value, maxAge) {
|
||||||
|
try {
|
||||||
|
const cookieValue = encodeURIComponent(value);
|
||||||
|
let cookieString = `${name}=${cookieValue}; path=/`;
|
||||||
|
if (maxAge) {
|
||||||
|
cookieString += `; max-age=${maxAge}`;
|
||||||
|
}
|
||||||
|
document.cookie = cookieString;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Ошибка при установке куки:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем последние визиты из куки (если есть)
|
||||||
|
let lastVisits = [];
|
||||||
|
const cookieValue = getCookieValue('LastVisit');
|
||||||
|
if (cookieValue) {
|
||||||
|
try {
|
||||||
|
lastVisits = JSON.parse(cookieValue);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Ошибка при разборе JSON из куки LastVisit:', e);
|
||||||
|
lastVisits = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём новый item посещения с текущей информацией
|
||||||
|
const newItem = {
|
||||||
|
LastURL: currentUrl,
|
||||||
|
LastAddress: address,
|
||||||
|
LastApart: apart,
|
||||||
|
Time: performance.now() // используем performance.now() как аналог time.perf_counter() в Python
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавляем новый item в начало списка
|
||||||
|
lastVisits.unshift(newItem);
|
||||||
|
|
||||||
|
// Оставляем максимум 3 последних записи
|
||||||
|
lastVisits = lastVisits.slice(0, 4);
|
||||||
|
|
||||||
|
// Упаковываем в JSON (JSON.stringify без пробелов для компактности)
|
||||||
|
const jsonData = JSON.stringify(lastVisits);
|
||||||
|
|
||||||
|
// Устанавливаем куки на 91 день (7862400 секунд)
|
||||||
|
setCookie('LastVisit', jsonData, 7862400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация отслеживания при загрузке документа.
|
||||||
|
* Ищет элемент с атрибутами data-current-url, data-address, data-apart
|
||||||
|
* и вызывает trackUserVisit с полученными значениями.
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Ищем элемент со встроенными данными (например, скрытый div в шаблоне)
|
||||||
|
const trackingElement = document.querySelector('[data-current-url]');
|
||||||
|
|
||||||
|
if (trackingElement) {
|
||||||
|
const currentUrl = trackingElement.getAttribute('data-current-url');
|
||||||
|
const address = trackingElement.getAttribute('data-address');
|
||||||
|
const apart = trackingElement.getAttribute('data-apart');
|
||||||
|
|
||||||
|
if (currentUrl && address && apart) {
|
||||||
|
trackUserVisit(currentUrl, address, apart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user