From 7e339972605ddf03761ca7a192316279125c9d27 Mon Sep 17 00:00:00 2001 From: erjemin Date: Wed, 18 Feb 2026 17:38:35 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C.=20mod:=20=D1=81=D0=BE=D0=B2=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B9=20=D1=81=D1=82=D0=B8=D0=BB=D1=8C=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B2=D1=8C=D1=8E=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dicquo/dicquo/urls.py | 7 +- dicquo/web/views.py | 239 +++++++++++++++++++++++++++++------------- public/robots.txt | 21 +++- 3 files changed, 192 insertions(+), 75 deletions(-) diff --git a/dicquo/dicquo/urls.py b/dicquo/dicquo/urls.py index 1fb1637..45537be 100644 --- a/dicquo/dicquo/urls.py +++ b/dicquo/dicquo/urls.py @@ -29,9 +29,10 @@ sitemaps = { urlpatterns = [ path('admin/', admin.site.urls), - re_path(r'^$', views.index), - re_path(r'^(?P\d{1,12})_\S*$', views.by_id), + re_path(r'^$', views.IndexView.as_view()), + re_path(r'^(?P\d{1,12})_\S*$', views.DictumDetailView.as_view()), path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), ] -urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/dicquo/web/views.py b/dicquo/web/views.py index 6ea3b2c..1c1bf76 100644 --- a/dicquo/web/views.py +++ b/dicquo/web/views.py @@ -3,92 +3,189 @@ __author__ = "Sergei Erjemin" __copyright__ = "Copyright 2020-2026, Sergei Erjemin" __credits__ = ["Sergei Erjemin", ] __license__ = "GPL" -__version__ = "0.3.9" +__version__ = "0.3.0" __maintainer__ = "Sergei Erjemin" __email__ = "erjemin@gmail.com" __status__ = "in progress" -from django.shortcuts import render from django.core.exceptions import ObjectDoesNotExist +from django.views.generic import DetailView, TemplateView import time import hashlib import random import pytils -from taggit.models import Tag -from web.models import TbOrigin, TbDictumAndQuotes, TbImages, TbAuthor +from web.models import TbDictumAndQuotes, TbImages, TbAuthor # Create your views here. -def for_dq(dq): - to_template = {} - num = int(hashlib.blake2s(dq.szContent.encode("utf-8"), digest_size=1).hexdigest(), 16) - clr = sorted([num / 2, num / 3, num / 5, num / 7, num / 11, num / 1.5], key=lambda A: random.random()) - to_template.update({'CLR': clr}) - to_template.update({'DQ': dq}) - try: - au = TbAuthor.objects.get(id=dq.kAuthor_id) - to_template.update({'AUTHOR': au}) - tags = au.tags.names() - except ObjectDoesNotExist: - tags = dq.tags.names() - tag_and_slug = [] - for i in tags: - tag_and_slug.append({"name": i, "slug": pytils.translit.slugify(i.lower())[:120]}) - to_template.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])}) # tag_and_slug - if dq.kImages_id is None: - if len(tags) != 0: - try: - # tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first() - tagged_image = TbImages.objects.filter(tags__name__in=tags) - random.shuffle(list(tagged_image)) - to_template.update({'IMAGE': tagged_image[0].imFile}) - except IndexError: - pass - else: - to_template.update({'IMAGE': dq.kImages.imFile}) - dq.iViewCounter += 1 - dq.save() - # dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id).order_by('?').first() - dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id) - random.shuffle(list(dq_next)) - to_template.update({"NEXT": dq_next[0].id}) - to_template.update({"NEXT_TXT": pytils.translit.slugify(dq_next[0].szContent.lower()[:120])}) - return to_template + +class CommonContextMixin: + """ + Общий миксин для представлений: + - Логика "одной цитаты" (получение контекста цитаты) + - Общий контекст (куки, тайминги) + """ + def dispatch(self, request, *args, **kwargs): + # Засекаем время в самом начале обработки запроса + self.t_start = time.process_time() + return super().dispatch(request, *args, **kwargs) + + def get_filtered_queryset(self): + """ + Возвращает (queryset, tag_slug) на основе GET-параметров запроса. + Если тега нет или он не найден, возвращает (None, None). + """ + tag_slug = self.request.GET.get('tag') + if not tag_slug: + return None, None + + dq_qs = TbDictumAndQuotes.objects.all() + + # 1. Пробуем найти цитаты, где АВТОР имеет этот тег + author_tag_qs = dq_qs.filter(kAuthor__tags__slug__in=[tag_slug]) + if author_tag_qs.exists(): + return author_tag_qs, tag_slug + + # 2. Если авторов нет, ищем цитаты с этим тегом + quote_tag_qs = dq_qs.filter(tags__slug__in=[tag_slug]) + if quote_tag_qs.exists(): + return quote_tag_qs, tag_slug + + return None, None + + def get_dictum_context(self, request, dq, queryset=None): + """ + Получение контекста для цитаты dq. Если queryset передан, используется для логики "следующей цитаты" + и фильтрации по тегу. + """ + context = {} + + # Если queryset не передан, используем все объекты + if queryset is None: + queryset = TbDictumAndQuotes.objects.all() + + # --- 1. ЛОГИКА ИСТОРИИ СЕССИИ (Предотвращение петель) --- + seen_ids = request.session.get('seen_ids', []) + + if dq.id not in seen_ids: + seen_ids.append(dq.id) + if len(seen_ids) > 100: + seen_ids.pop(0) + request.session['seen_ids'] = seen_ids + + # --- 2. ГЕНЕРАЦИЯ ЦВЕТОВ --- + num = int(hashlib.blake2s(dq.szContent.encode("utf-8"), digest_size=1).hexdigest(), 16) + clr = sorted([num / 2, num / 3, num / 5, num / 7, num / 11, num / 1.5], key=lambda A: random.random()) + context.update({'CLR': clr}) + context.update({'DQ': dq}) + + # --- 3. АВТОР И ТЕГИ --- + try: + au = TbAuthor.objects.get(id=dq.kAuthor_id) + context.update({'AUTHOR': au}) + tags = au.tags.names() + except ObjectDoesNotExist: + tags = dq.tags.names() + + tag_and_slug = [] + for i in tags: + tag_and_slug.append({"name": i, "slug": pytils.translit.slugify(i.lower())[:120]}) + context.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])}) + + # --- 4. ВЫБОР КАРТИНКИ --- + if dq.kImages_id is None: + if len(tags) != 0: + tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first() + if tagged_image: + context.update({'IMAGE': tagged_image.imFile}) + else: + context.update({'IMAGE': dq.kImages.imFile}) + + # --- 5. СЧЕТЧИК --- + dq.iViewCounter += 1 + dq.save(update_fields=['iViewCounter']) + + # --- 6. ВЫБОР СЛЕДУЮЩЕЙ ЦИТАТЫ --- + dq_next = queryset.exclude(id__in=seen_ids).order_by('?').first() + + if dq_next is None: + request.session['seen_ids'] = [] + dq_next = queryset.exclude(id=dq.id).order_by('?').first() + + if dq_next: + context.update({"NEXT": dq_next.id}) + context.update({"NEXT_TXT": pytils.translit.slugify(dq_next.szContent.lower()[:120])}) + + # Если мы в режиме фильтрации (tag), передаем текущий тег в контекст + if request.GET.get('tag'): + context.update({"CURRENT_TAG": request.GET.get('tag')}) + + return context + + def finalize_context(self, context): + """ + Добавляет общие данные: проверки куки и время выполнения. + """ + if self.request.COOKIES.get('cookie_accept'): + context['cookie_accept'] = 1 + + # Считаем время от self.t_start, заданного в dispatch + total_time = 0.0 + if hasattr(self, 't_start'): + total_time = float(time.process_time() - self.t_start) + context['ticks'] = total_time + return context -def by_id(request, dq_id): - t_start = time.process_time() - template = "index.html" # шаблон - dq = TbDictumAndQuotes.objects.get(id=dq_id) - to_template = for_dq(dq) - # пероверка, что посетитель согласился со сбором даных через cookies - if request.COOKIES.get('cookie_accept'): - to_template.update({'cookie_accept': 1}) - to_template.update({'ticks': float(time.process_time() - t_start)}) - response = render(request, template, to_template) - return response +class DictumDetailView(CommonContextMixin, DetailView): + model = TbDictumAndQuotes + template_name = "index.html" + pk_url_kwarg = 'dq_id' + context_object_name = 'DQ' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # Определяем контекст фильтрации (если есть тег в URL) + active_qs, _ = self.get_filtered_queryset() + + # Используем миксин логики цитаты с учетом фильтра + extras = self.get_dictum_context(self.request, self.object, queryset=active_qs) + context.update(extras) + + # Финализируем контекст (куки, тайминги) + return self.finalize_context(context) -def index(request): - t_start = time.process_time() - # проверка на аутентификацию - # if not request.user.is_authenticated(): - # return HttpResponseRedirect("/access") - template = "index.html" # шаблон - dq_ = TbDictumAndQuotes.objects - if request.GET.get('tag'): - dq = dq_.filter(kAuthor__tags__slug__in=[request.GET['tag']]).order_by('?').first() +class IndexView(CommonContextMixin, TemplateView): + template_name = "index.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + active_qs, _ = self.get_filtered_queryset() + + dq = None + if active_qs is not None: + dq = active_qs.order_by('?').first() + if dq is None: - dq = dq_.filter(tags__slug__in=[request.GET['tag']]).order_by('?').first() - if dq is None: - dq = dq_.order_by('?').first() - else: - dq = dq_.first() - to_template = for_dq(dq) - # пероверка, что посетитель согласился со сбором даных через cookies - if request.COOKIES.get('cookie_accept'): - to_template.update({'cookie_accept': 1}) - to_template.update({'ticks': float(time.process_time() - t_start)}) - response = render(request, template, to_template) - return response + # Если тег не задан, или по тегу ничего не нашлось совсем + # Сбрасываем active_qs на "все", так как специфический контекст пуст + active_qs = TbDictumAndQuotes.objects.all() + # Случайная цитата (с учетом истории, чтобы главная страница тоже не зацикливалась) + seen_ids = self.request.session.get('seen_ids', []) + dq = active_qs.exclude(id__in=seen_ids).order_by('?').first() + + if dq is None: + self.request.session['seen_ids'] = [] + dq = active_qs.order_by('?').first() + + if dq: + # Используем миксин, ОБЯЗАТЕЛЬНО передаем active_qs + extras = self.get_dictum_context(self.request, dq, queryset=active_qs) + context.update(extras) + + # Финализируем контекст (куки, тайминги) + return self.finalize_context(context) diff --git a/public/robots.txt b/public/robots.txt index 37b5f3b..b95652a 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,7 +1,26 @@ # DicQuo User-Agent: * Allow: / -Disallow: +Disallow: /admin/ +Disallow: /*?tag= +Disallow: /*? + +# Optimize for Yandex +Clean-param: tag / + +# AI and LLM bots settings +# OpenAI GPT +# User-agent: GPTBot +# Disallow: + +# Common Crawl (used by many AI models) +# User-agent: CCBot +# Disallow: + +# Google Bard/Gemini +# User-agent: Google-Extended +# Disallow: + Host: dq.cube2.ru Sitemap: https://dq.cube2.ru/sitemap.xml