Skip to content
3 changes: 3 additions & 0 deletions feed/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class FeedConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "feed"

def ready(self):
import feed.signals # noqa: F401
35 changes: 0 additions & 35 deletions feed/constants.py

This file was deleted.

151 changes: 0 additions & 151 deletions feed/helpers.py

This file was deleted.

25 changes: 0 additions & 25 deletions feed/pagination.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
from rest_framework import pagination
from rest_framework.request import Request

from feed.constants import SupportedQuerySet


class FeedPagination(pagination.LimitOffsetPagination):
default_limit = 10
limit_query_param = "limit"
offset_query_param = "offset"

def custom_paginate_queryset(
self, queryset: SupportedQuerySet, request: Request, count: int, view=None
) -> dict:
self.limit = self.get_limit(request)
if self.limit is None:
return None

self.count = count
self.offset = self.get_offset(request)
self.request = request
if self.count > self.limit and self.template is not None:
self.display_page_controls = True

if self.count == 0 or self.offset > self.count:
return {"queryset_ready": [], "count": self.count}

queryset_ready = queryset[self.offset : self.offset + self.limit] # noqa: E203
return {
"queryset_ready": queryset_ready,
"count": self.count,
}
17 changes: 17 additions & 0 deletions feed/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
from django.dispatch import receiver

from news.models import News


from vacancy.models import Vacancy


@receiver(post_save, sender=Vacancy)
def create_news_on_save(sender, instance, created, **kwargs):
if created:
content_type = ContentType.objects.filter(model="vacancy").first()
news_instance, created = News.objects.get_or_create(
content_type=content_type, object_id=instance.id
)
5 changes: 3 additions & 2 deletions feed/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.urls import path

from feed.views import FeedList
from feed.views import NewSimpleFeed, DevScript

app_name = "feed"

urlpatterns = [
path("", FeedList.as_view()),
path("", NewSimpleFeed.as_view()),
path("dev-needs-script", DevScript.as_view()),
]
120 changes: 69 additions & 51 deletions feed/views.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,83 @@
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.request import Request
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework.views import APIView

from feed.constants import SupportedModel, SupportedQuerySet, FeedItemType, model_mapping
from feed.helpers import collect_querysets, paginate_serialize_feed, add_pagination
from feed.pagination import FeedPagination

from news.models import News
from news.serializers import NewsFeedListSerializer
from projects.models import Project
from vacancy.models import Vacancy

class FeedList(APIView):
pagination_class = FeedPagination

@swagger_auto_schema(
responses={
200: openapi.Response(
description="List of some news: new projects, vacancies, project, users and program news",
schema=openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
description="Feed item",
properties={
"type": openapi.TYPE_STRING,
"content": openapi.TYPE_OBJECT,
},
),
),
)
}
)
def get(self, request: Request, *args, **kwargs) -> Response:
prepared_data, sum_pages = self.paginate_serialize_data(
self.get_response_data(self.get_request_data())
)
for obj in prepared_data:
obj["type_model"] = obj["type_model"].lower()
return Response(
status=status.HTTP_200_OK,
data=add_pagination(prepared_data, sum_pages),
)
class NewSimpleFeed(APIView):
serializator_class = NewsFeedListSerializer
pagination_class = FeedPagination

def get_request_data(self) -> list[SupportedModel]:
def get_filter_data(self):
filter_queries = self.request.query_params.get("type")
filter_queries = filter_queries if filter_queries else "" # existence check

models = [
model_mapping[model_name]
for model_name in model_mapping.keys()
if model_name.lower() in filter_queries
]
return models
news_types = filter_queries.split("|")
if "news" in news_types:
news_types.append("customuser")
return news_types

def get_response_data(
self, models: list[SupportedModel]
) -> dict[FeedItemType, SupportedQuerySet]:
return {model.__name__: collect_querysets(model) for model in models}
def get_queryset(self):
filters = self.get_filter_data()
queryset = (
News.objects.select_related("content_type")
.prefetch_related("content_object", "files")
.filter(content_type__model__in=filters)
.order_by("-datetime_created")
)
# временное удаление постов для проектов с текстом
return queryset.exclude(~Q(text=""), content_type__model="project")

def paginate_serialize_data(
self, get_model_data: dict[FeedItemType, SupportedQuerySet]
) -> tuple[list[dict], int]:
def get(self, *args, **kwargs):
paginator = self.pagination_class()
return paginate_serialize_feed(get_model_data, paginator, self.request, self)
paginated_data = paginator.paginate_queryset(self.get_queryset(), self.request)
serializer = NewsFeedListSerializer(paginated_data, many=True)

new_data = []
# временная подстройка данных под фронт
for data in serializer.data:
if data["type_model"] in ["project", "vacancy", None]:
fomated_data = {
"type_model": data["type_model"],
"content": data["content_object"],
}
elif data["type_model"] == "news":
del data["type_model"]
fomated_data = {"type_model": "news", "content": data}
new_data.append(fomated_data)

return paginator.get_paginated_response(new_data)


class DevScript(CreateAPIView):
def create(self, request):
content_type = ContentType.objects.filter(model="project").first()
for project in Project.objects.filter(draft=False):
if not News.objects.filter(
content_type=content_type, object_id=project.id
).exists():
News.objects.create(
content_type=content_type,
object_id=project.id,
datetime_created=project.datetime_created,
)

content_type = ContentType.objects.filter(model="vacancy").first()
for vacancy in Vacancy.objects.filter(is_active=True):
if not News.objects.filter(
content_type=content_type, object_id=vacancy.id
).exists():
News.objects.create(
content_type=content_type,
object_id=vacancy.id,
datetime_created=vacancy.datetime_created,
)
return Response({"status": "success"}, status=201)
Loading