diff --git a/docs/modules/feed.md b/docs/modules/feed.md index 3b7cda7e..06e2ffbe 100644 --- a/docs/modules/feed.md +++ b/docs/modules/feed.md @@ -52,20 +52,12 @@ View выбирает подходящие `news.News`, сериализует ### 2. В ленту попадает обычная новость -Если `news.News` содержит текст и относится к пользователю, проекту или -партнерской программе, лента возвращает ее как новость. +Если `news.News` содержит текст и относится к пользователю или проекту, лента +возвращает ее как новость. Проектная новость с текстом возвращается как `type_model = "news"`, даже если ее `content_object` - проект. -Новость партнерской программы с текстом тоже возвращается как -`type_model = "news"`. Отдельный `type_model = "partner_program"` пока не -вводится. - -Служебные feed-записи партнерских программ сейчас не создаются. Если такой -сценарий понадобится, для него нужно отдельно согласовать `type_model` и -frontend-контракт. - ### 3. В ленту попадает служебная запись Служебные feed-записи создаются через `feed.services.create_news_for_model()`. @@ -91,17 +83,15 @@ frontend-контракт. - `GET /feed/?type=news` - новости пользователей. - `GET /feed/?type=project` - проектные новости и проектные feed-записи. - `GET /feed/?type=vacancy` - служебные feed-записи вакансий. -- `GET /feed/?type=partnerprogram` - новости партнерских программ. -- `GET /feed/?type=project|vacancy|news|partnerprogram` - комбинированная - выдача по нескольким типам. +- `GET /feed/?type=project|vacancy|news` - комбинированная выдача по нескольким + типам. ## Ограничения и правила - Feed читает данные из `news.News`, но не отвечает за создание обычных - project/user/program news. + project/user news. - Служебная feed-запись определяется через пустой `text`. -- Новости партнерских программ с текстом отображаются как обычные новости; - отдельные служебные карточки программ в ленте пока не поддерживаются. +- Новости партнерских программ не отображаются в `/feed/`. - Signals `feed` создают или удаляют служебные feed-записи для проектов и вакансий. Более широкие сценарии публикации проекта остаются в модуле `projects`. @@ -112,8 +102,6 @@ frontend-контракт. - `/feed/?type=news` возвращает пользовательские новости; - `/feed/?type=project` возвращает проектные новости в frontend-формате; -- `/feed/?type=partnerprogram` возвращает новости программ как - `type_model = "news"`; - `/feed/?type=project` возвращает служебную feed-запись проекта как `type_model = "project"`; - `/feed/?type=vacancy` возвращает служебную feed-запись вакансии как diff --git a/docs/modules/partner-programs.md b/docs/modules/partner-programs.md index d31bb234..f185f47e 100644 --- a/docs/modules/partner-programs.md +++ b/docs/modules/partner-programs.md @@ -161,8 +161,6 @@ `PartnerProgramProject`. - `users` - участники и менеджеры программы. - `news` - новости программы создаются и читаются через общий news API. -- `feed` - текстовые новости программы могут попадать в общую ленту как - `type_model = "news"`. - `courses` - курс может быть связан с программой и доступен участникам программы. - `project_rates` - оценки проектов используются в выгрузке результатов. @@ -176,8 +174,6 @@ - Значения дополнительных полей хранятся в `PartnerProgramFieldValue`. - После сдачи проекта в конкурсной программе значения полей редактировать нельзя. -- Служебные feed-карточки программ пока не поддерживаются; новости программ в - feed отображаются как обычные новости. - Основной API-код пока сосредоточен во `views.py`; перед крупным рефакторингом нужно зафиксировать больше regression-тестов. diff --git a/feed/serializers.py b/feed/serializers.py index c412356b..4efead6f 100644 --- a/feed/serializers.py +++ b/feed/serializers.py @@ -6,7 +6,6 @@ from news.mapping import NewsMapping from news.models import News from news.services import is_content_news -from partner_programs.models import PartnerProgram from projects.models import Project from users.models import CustomUser @@ -24,11 +23,6 @@ class FeedNewsContentSerializer(serializers.ModelSerializer): def get_type_model(self, obj) -> str | None: content_model = obj.content_type.model - if content_model == PartnerProgram.__name__.lower(): - # Новости программ сейчас отображаются как обычные новости. - # Отдельная служебная карточка программы в ленте пока не согласована. - return "news" if is_content_news(obj) else None - if is_content_news(obj) and content_model == Project.__name__.lower(): return "news" diff --git a/feed/tests/test_feed_api.py b/feed/tests/test_feed_api.py index 12cbf199..0ab0e664 100644 --- a/feed/tests/test_feed_api.py +++ b/feed/tests/test_feed_api.py @@ -43,18 +43,15 @@ def test_feed_returns_project_news_as_news_content(self): self.assertEqual(item["content"]["id"], news.id) self.assertEqual(item["content"]["text"], "Project feed news") - def test_feed_returns_program_news_as_news_content(self): + def test_feed_ignores_program_news_filter(self): program = create_partner_program(name="Feed program") - news = create_news_for(program, text="Program feed news") + create_news_for(program, text="Program feed news") + create_news_for(program, text="") response = self.client.get("/feed/?type=partnerprogram") self.assertEqual(response.status_code, 200) - item = response.data["results"][0] - self.assertEqual(set(item.keys()), {"type_model", "content"}) - self.assertEqual(item["type_model"], "news") - self.assertEqual(item["content"]["id"], news.id) - self.assertEqual(item["content"]["text"], "Program feed news") + self.assertEqual(response.data["results"], []) def test_feed_returns_project_feed_record_as_project_content(self): project = create_project(name="Feed record project") @@ -80,7 +77,7 @@ def test_feed_returns_vacancy_feed_record_as_vacancy_content(self): self.assertEqual(item["content"]["id"], vacancy.id) self.assertEqual(item["content"]["role"], "Backend developer") - def test_feed_combines_all_supported_filters(self): + def test_feed_combines_supported_filters_and_ignores_program_news(self): project_news = create_news_for( create_project(name="Combined project news"), text="Combined project news", @@ -117,14 +114,11 @@ def test_feed_combines_all_supported_filters(self): items_by_text[project_news.text]["content"]["id"], project_news.id, ) - self.assertEqual( - items_by_text[program_news.text]["content"]["id"], - program_news.id, - ) self.assertEqual( items_by_text[user_news.text]["content"]["id"], user_news.id, ) + self.assertNotIn(program_news.text, items_by_text) self.assertIn(project.id, content_ids_by_type["project"]) self.assertIn(vacancy.id, content_ids_by_type["vacancy"]) diff --git a/feed/views.py b/feed/views.py index 324b8a29..a02f628a 100644 --- a/feed/views.py +++ b/feed/views.py @@ -21,7 +21,11 @@ def _get_filter_data(self) -> list[str]: news_types: list[str] = filter_queries.split("|") if "news" in news_types: news_types.append("customuser") - return news_types + return [ + news_type + for news_type in news_types + if news_type != "partnerprogram" + ] def get_queryset(self) -> QuerySet[News]: filters = self._get_filter_data() diff --git a/news/tests/test_news_user_program_api.py b/news/tests/test_news_user_program_api.py index 2347f9c0..204cff58 100644 --- a/news/tests/test_news_user_program_api.py +++ b/news/tests/test_news_user_program_api.py @@ -97,6 +97,15 @@ def test_program_news_list_orders_pinned_news_first(self): news_ids = [item["id"] for item in response.data["results"]] self.assertEqual(news_ids, [pinned_news.id, regular_news.id]) + def test_program_news_list_returns_program_news(self): + news = create_news_for(self.program, text="Visible program news") + + response = self.client.get(f"/programs/{self.program.id}/news/") + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["results"][0]["id"], news.id) + self.assertEqual(response.data["results"][0]["text"], "Visible program news") + def test_missing_program_context_returns_not_found(self): response = self.client.get("/programs/999999/news/")