mod: данные о последних визитах пользователя полностью перенесены с сервера на клиент (в JS). Отключен из контекста всех шаблонов LAST_VISIT и связанные с ним функции в вьюшках
This commit is contained in:
@@ -75,11 +75,7 @@
|
||||
<button type="submit" class="btn btn-primary btn-add">Найти</button>
|
||||
</span>
|
||||
</div>
|
||||
{% if LAST_VISIT %}<div><h5>Ваши последние просмотры:</h5>
|
||||
<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 %}
|
||||
{% include 'report/report_last_user_visit.html' with background_color="None" %}
|
||||
|
||||
</form>
|
||||
<p></p>{% endwith %}
|
||||
@@ -107,7 +107,8 @@
|
||||
<meta property="article:modified_time" content="{{ META_DATA_PUBLISH|date:'Y-m-d' }}" />
|
||||
{% 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 ){ // колапсатор для отображения контатной информации постафшика окон
|
||||
$('#tel'+id).collapse('show');
|
||||
$('#hid'+id).collapse('hide');
|
||||
@@ -278,6 +279,13 @@ $(function () { // инициализация и обработка попове
|
||||
<p id="shadow_buffer"></p>
|
||||
</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-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-md-11 col-xs-12 last_user_visit"><h5>Цены на окна просмотренные вами:</h5>
|
||||
<ul>{% for ITEM in LAST_VISIT %}
|
||||
<li><a href="{{ ITEM.LastURL }}">Цены на окна для серии {{ ITEM.LastApart }} <small>({{ ITEM.LastAddress }})</small></a> <small>{{ ITEM.Time }}</small></li>{% endfor %}
|
||||
</ul>
|
||||
<!-- Информация об адресах, просмотренных текущим пользователем (читается из браузерных кук) -->
|
||||
<div class="col-xs-12">
|
||||
<div class="col-md-11 col-xs-12{% if background_color != "None" %} last_user_visit{% endif %}" id="last_user_visit_container" style="display:none;">
|
||||
<h5>Цены на окна просмотренные вами {{ background_color }}:</h5>
|
||||
<ul id="last_visits_list"></ul>
|
||||
</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 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:
|
||||
@@ -21,7 +21,6 @@ def catalog_root(request: HttpRequest) -> HttpResponse:
|
||||
time_start = time.perf_counter()
|
||||
# получаем из cookies последние визиты клиента
|
||||
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(),
|
||||
'ticks': float(time.perf_counter() - time_start)}
|
||||
response = render(request, "catalog/catalog_root.html", to_template)
|
||||
@@ -83,7 +82,6 @@ def catalog_sets(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
to_template: dict[str, object] = {
|
||||
'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(),
|
||||
'ticks': float(time.perf_counter() - time_start),
|
||||
}
|
||||
|
||||
@@ -17,11 +17,7 @@ from oknardia.models import (
|
||||
SetKit,
|
||||
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 get_rating_set_for_stars
|
||||
import django.utils.dateformat
|
||||
import time
|
||||
@@ -179,7 +175,6 @@ def catalog_company(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
Контекст шаблона:
|
||||
- COMPANIES (list): Список компаний с статистикой
|
||||
- LAST_VISIT (list): Последние визиты текущего пользователя
|
||||
- LOG_VISIT (list): Последние визиты всех пользователей
|
||||
|
||||
Args:
|
||||
@@ -200,9 +195,6 @@ def catalog_company(request: HttpRequest) -> HttpResponse:
|
||||
# Получаем информацию о посещениях для персонализации
|
||||
to_template: dict[str, object] = {
|
||||
'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(),
|
||||
}
|
||||
|
||||
@@ -469,7 +461,6 @@ def catalog_company_detail(
|
||||
- SETS (list): Список оконных наборов с их полной информацией
|
||||
- IMG_FOR_BLOG (str): Логотип компании
|
||||
- LIST_NOT (list): Стандартные маркеры "пусто"
|
||||
- LAST_VISIT (list): Последние визиты текущего пользователя
|
||||
- LOG_VISIT (list): Последние визиты всех пользователей
|
||||
- ticks (float): Время выполнения представления (в секундах)
|
||||
|
||||
@@ -518,9 +509,6 @@ def catalog_company_detail(
|
||||
'META_KEYWORDS': company.sMerchantName,
|
||||
'IMG_FOR_BLOG': company.pMerchantLogo,
|
||||
'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(),
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.db.models import F
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
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
|
||||
import time
|
||||
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:
|
||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||
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.perf_counter() - time_start),
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.shortcuts import render, redirect
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from oknardia.settings import *
|
||||
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
|
||||
import time
|
||||
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:
|
||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||
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.perf_counter() - time_start),
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ from oknardia.models import (
|
||||
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.report1 import get_last_all_user_visit_list, get_last_user_visit_list
|
||||
from web.add_func import get_flaps_for_big_pictures
|
||||
import time
|
||||
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:
|
||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||
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.perf_counter() - time_start),
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ from oknardia.models import (
|
||||
MountDim2Apartment,
|
||||
)
|
||||
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, \
|
||||
get_geo_distance
|
||||
import django.utils.dateformat
|
||||
@@ -57,11 +57,8 @@ def _append_visit_context(
|
||||
"""Дописывает в контекст стандартный хвост: визиты и время выполнения."""
|
||||
if log_visit is None:
|
||||
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({
|
||||
'LAST_VISIT': get_last_user_visit_list(last_visit_cookie[:3]),
|
||||
'LOG_VISIT': log_visit,
|
||||
'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
|
||||
|
||||
# получаем последние визиты клиента через куки
|
||||
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
|
||||
# Вызываем контекст без параметра last_visit_cookie (получит из кук автоматически)
|
||||
_append_visit_context(
|
||||
to_template=to_template,
|
||||
request=request,
|
||||
time_start=time_start,
|
||||
log_visit=log_visit,
|
||||
last_visit_cookie=last_visit_for_context,
|
||||
)
|
||||
response = render(request, "price/price_list.html", to_template)
|
||||
response.set_cookie("LastVisit", last_visit, max_age=7862400) # ставим или перезаписываем куки (91 день)
|
||||
return response
|
||||
return render(request, "price/price_list.html", to_template)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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:
|
||||
""" Служебная функция: получает список с посещенных страниц с ценовой выдачей (ListVisit), меняет в нем даты
|
||||
на описание типа "три недели назад" и возвращает обратно.
|
||||
@@ -417,10 +402,7 @@ def compare_offers(request: HttpRequest, to_compare: str = "1,2") -> HttpRespons
|
||||
except SetKit.DoesNotExist:
|
||||
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.perf_counter() - time_start)
|
||||
})
|
||||
|
||||
@@ -158,9 +158,6 @@ class ReportOneWinPriceTests(TestCase):
|
||||
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_big_pictures",
|
||||
@@ -178,9 +175,6 @@ class ReportOneWinPriceTests(TestCase):
|
||||
self,
|
||||
mocked_big_pictures,
|
||||
mocked_mini_pictures,
|
||||
mocked_cookies,
|
||||
mocked_last_visits,
|
||||
mocked_all_visits,
|
||||
):
|
||||
"""Вьюха должна собирать тот же ключевой контекст, но уже без raw SQL."""
|
||||
request = self.factory.get(
|
||||
@@ -216,9 +210,6 @@ class ReportOneWinPriceTests(TestCase):
|
||||
self.assertIn("META_DATA_PUBLISH", context)
|
||||
self.assertTrue(mocked_big_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):
|
||||
"""Если SEO-размеры в URL неверные, вьюха должна редиректить на канонический URL."""
|
||||
|
||||
@@ -156,14 +156,8 @@ class CatalogProfileViewTests(TestCase):
|
||||
|
||||
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(
|
||||
self,
|
||||
mocked_cookies,
|
||||
mocked_last_visits,
|
||||
mocked_all_visits,
|
||||
):
|
||||
"""Пустой каталог не должен падать и должен отдавать ожидаемый контекст."""
|
||||
with self.assertNumQueries(1):
|
||||
@@ -174,20 +168,10 @@ class CatalogProfileViewTests(TestCase):
|
||||
self.assertEqual(context["CATALOG_PROFILE_NUM"], "0 профилей")
|
||||
self.assertEqual(context["CATALOG_MANUFACT_NUM"], 0)
|
||||
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(
|
||||
self,
|
||||
mocked_cookies,
|
||||
mocked_last_visits,
|
||||
mocked_all_visits,
|
||||
):
|
||||
"""Каталог должен группировать профили по производителю и сохранять сортировку."""
|
||||
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_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(
|
||||
self,
|
||||
mocked_cookies,
|
||||
mocked_last_visits,
|
||||
mocked_all_visits,
|
||||
):
|
||||
"""При неверных slug страница должна отправлять на канонический URL."""
|
||||
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["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(
|
||||
self,
|
||||
mocked_cookies,
|
||||
mocked_last_visits,
|
||||
mocked_all_visits,
|
||||
):
|
||||
"""Карточка профиля должна собираться через ORM и отдавать все ключевые блоки."""
|
||||
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["PUB_DAT"].date(), blog.dPostDataModify.date())
|
||||
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:
|
||||
""" Главная страница (статичная, только с проверками куков)
|
||||
""" Главная страница (статичная, только с проверками кук)
|
||||
|
||||
:param request: входящий http-запрос
|
||||
:return response: исходящий http-ответ
|
||||
@@ -28,22 +28,6 @@ def main_init(request: HttpRequest) -> HttpResponse:
|
||||
# стоят куки, и это не первый визит
|
||||
num_viz = request.COOKIES["NumVisit"] # читаем число визитов
|
||||
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(csrf(request)) # токен, для метода POST и GET
|
||||
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