From 96dbe27ea6ff4aa86ac4f1fa7e2e47b1f0f70747 Mon Sep 17 00:00:00 2001 From: erjemin Date: Wed, 2 Sep 2026 23:30:39 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20=D0=B3=D0=B0=D0=BB=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=8F=20(6)=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20"=D0=9E=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B5=D0=BD=D0=BE=20=D0=9C=D0=BE=D0=B7=D0=B3=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=BC=20=D0=A1=D0=BB=D0=B8=D0=B7=D0=BD=D0=B5=D0=BC?= =?UTF-8?q?"=20=D0=B8=20"=D0=93=D0=BB=D1=83=D0=B1=D0=BE=D0=BA=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=82=D1=80=D0=B0=D0=BD=D1=81"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hypn0/hypn0_site/tests.py | 63 ++++++++++++++++++++++++++++++++++---- hypn0/hypn0_site/views.py | 54 ++++++++++++++++++++++++++++++-- hypn0/templates/index.html | 49 ++++++++++++----------------- 3 files changed, 129 insertions(+), 37 deletions(-) diff --git a/hypn0/hypn0_site/tests.py b/hypn0/hypn0_site/tests.py index 46fd3fd..beb873d 100644 --- a/hypn0/hypn0_site/tests.py +++ b/hypn0/hypn0_site/tests.py @@ -719,9 +719,9 @@ class GalleryFreshFloorTests(BaseMediaTestCase): self.assertContains(response, f"gallery-card-{item.s_hash_id}") def test_gallery_floor_fresh_pagination(self): - # Создаем 10 картин + # Создаем 20 картин svg_bytes = b'' - for i in range(10): + for i in range(20): item = TbHypn0Item( s_title=f"Кандидат #{i}", file_svg=ContentFile(svg_bytes, name=f"cand_{i}.svg"), @@ -731,21 +731,72 @@ class GalleryFreshFloorTests(BaseMediaTestCase): ) item.save(visitor_uuid_or_fp=self.vid) - # Страница 1 (должно быть 8 штук) + # Страница 1 (должно быть 16 штук) url = reverse("hypn0_site:gallery_floor", kwargs={"floor_slug": "fresh"}) response_p1 = self.client.get(url) self.assertEqual(response_p1.status_code, 200) self.assertContains(response_p1, "Плеск бессознательного") self.assertContains(response_p1, "FRESH STREAM") - self.assertEqual(len(response_p1.context["page_obj"]), 8) + self.assertEqual(len(response_p1.context["page_obj"]), 16) self.assertContains(response_p1, "Фаза 1 из 2") - # Страница 2 (должно быть 2 штуки) + # Страница 2 (должно быть 4 штуки) response_p2 = self.client.get(url, data={"page": 2}) self.assertEqual(response_p2.status_code, 200) - self.assertEqual(len(response_p2.context["page_obj"]), 2) + self.assertEqual(len(response_p2.context["page_obj"]), 4) self.assertContains(response_p2, "Фаза 2 из 2") + def test_gallery_floors_curated_and_top(self): + from hypn0_site.views import get_floor_curated, get_floor_top + + svg_bytes = b'' + item_curated = TbHypn0Item( + s_title="Одобренная картина", + file_svg=ContentFile(svg_bytes, name="cur.svg"), + i_level=TbHypn0Item.Level.LEVEL_2, + f_score=42.5, + is_public=True, + ) + item_curated.save(visitor_uuid_or_fp=self.vid) + + item_top = TbHypn0Item( + s_title="Шедевр золотого фонда", + file_svg=ContentFile(svg_bytes, name="top.svg"), + i_level=TbHypn0Item.Level.IMMORTAL, + i_likes_count=150, + f_score=99.0, + is_public=True, + ) + item_top.save(visitor_uuid_or_fp=self.vid) + + # Проверяем выборки + curated_res = list(get_floor_curated()) + self.assertEqual(len(curated_res), 1) + self.assertEqual(curated_res[0].pk, item_curated.pk) + + top_res = list(get_floor_top()) + self.assertEqual(len(top_res), 1) + self.assertEqual(top_res[0].pk, item_top.pk) + + # Проверяем рендеринг страниц этажей + url_curated = reverse("hypn0_site:gallery_floor", kwargs={"floor_slug": "curated"}) + resp_c = self.client.get(url_curated) + self.assertEqual(resp_c.status_code, 200) + self.assertContains(resp_c, "Одобрено Мозговым Слизнем") + self.assertContains(resp_c, "CURATED") + + url_top = reverse("hypn0_site:gallery_floor", kwargs={"floor_slug": "top"}) + resp_t = self.client.get(url_top) + self.assertEqual(resp_t.status_code, 200) + self.assertContains(resp_t, "Глубокий транс") + self.assertContains(resp_t, "TOP TIER") + + # Проверяем отображение на главной странице + resp_idx = self.client.get(reverse("hypn0_site:index")) + self.assertEqual(resp_idx.status_code, 200) + self.assertContains(resp_idx, f"gallery-card-{item_curated.s_hash_id}") + self.assertContains(resp_idx, f"gallery-card-{item_top.s_hash_id}") + def test_gallery_floor_unknown_404(self): url = reverse("hypn0_site:gallery_floor", kwargs={"floor_slug": "non_existent"}) response = self.client.get(url) diff --git a/hypn0/hypn0_site/views.py b/hypn0/hypn0_site/views.py index f230948..c60cd32 100644 --- a/hypn0/hypn0_site/views.py +++ b/hypn0/hypn0_site/views.py @@ -36,9 +36,41 @@ def get_floor_fresh(limit: int | None = 8, offset: int = 0): return qs +def get_floor_curated(limit: int | None = 8, offset: int = 0): + """ + 2-й ЭТАЖ: «Одобрено Мозговым Слизнем» (Кураторский отбор / Тренды). + Выборка: прошедшие первичный отбор (Level.LEVEL_2). + Сортировка: по гравитационному рейтингу -f_score (актуальные тренды) и свежести -d_created_at. + """ + qs = TbHypn0Item.objects.filter( + i_level=TbHypn0Item.Level.LEVEL_2, + is_public=True, + ).order_by("-f_score", "-d_created_at") + + if limit is not None: + return qs[offset : offset + limit] + return qs + + +def get_floor_top(limit: int | None = 8, offset: int = 0): + """ + 3-й ЭТАЖ: «Глубокий транс» (Золотой фонд / Высшая лига). + Выборка: бессмертные шедевры сообщества (Level.IMMORTAL). + Сортировка: по числу признания -i_likes_count, -f_score и свежести -d_created_at. + """ + qs = TbHypn0Item.objects.filter( + i_level=TbHypn0Item.Level.IMMORTAL, + is_public=True, + ).order_by("-i_likes_count", "-f_score", "-d_created_at") + + if limit is not None: + return qs[offset : offset + limit] + return qs + + def gallery_floor(request: HttpRequest, floor_slug: str) -> HttpResponse: """ - Страница полного просмотра конкретного этажа галереи с пагинацией (по 8 карточек). + Страница полного просмотра конкретного этажа галереи с пагинацией (по 16 карточек). """ floors_config = { "fresh": { @@ -48,6 +80,20 @@ def gallery_floor(request: HttpRequest, floor_slug: str) -> HttpResponse: "subtitle": "Свежие галлюцинации из инкубатора • Первичная оценка сообщества", "getter": get_floor_fresh, }, + "curated": { + "title": "Одобрено Мозговым Слизнем", + "badge": "CURATED", + "badge_color": "cyan", + "subtitle": "Проверено контролем качества • Высокий психоделический резонанс", + "getter": get_floor_curated, + }, + "top": { + "title": "Глубокий транс", + "badge": "TOP TIER", + "badge_color": "emerald", + "subtitle": "Золотой фонд гипноза • Абсолютное подчинение воли", + "getter": get_floor_top, + }, } if floor_slug not in floors_config: @@ -56,7 +102,7 @@ def gallery_floor(request: HttpRequest, floor_slug: str) -> HttpResponse: cfg = floors_config[floor_slug] items_qs = cfg["getter"](limit=None) - paginator = Paginator(items_qs, 8) + paginator = Paginator(items_qs, 16) page_number = request.GET.get("page", 1) page_obj = paginator.get_page(page_number) @@ -74,8 +120,12 @@ def gallery_floor(request: HttpRequest, floor_slug: str) -> HttpResponse: @ensure_csrf_cookie def index(request: HttpRequest | None) -> HttpResponse: fresh_items = get_floor_fresh(limit=8) + curated_items = get_floor_curated(limit=8) + top_items = get_floor_top(limit=8) return render(request, "index.html", { "fresh_items": fresh_items, + "curated_items": curated_items, + "top_items": top_items, }) diff --git a/hypn0/templates/index.html b/hypn0/templates/index.html index b8a4369..2e196b0 100644 --- a/hypn0/templates/index.html +++ b/hypn0/templates/index.html @@ -509,7 +509,7 @@
-
+
@@ -520,28 +520,25 @@

Проверено контролем качества • Высокий психоделический резонанс

- 100–999 лайков + + Смотреть все одобренные → +
- + {% endfor %}
-
+
Favicon
@@ -552,25 +549,19 @@

Золотой фонд гипноза • Абсолютное подчинение воли

- 1000+ лайков + + Смотреть весь золотой фонд → +
- + {% endfor %}