1- import random
2- import typing
1+ from random import shuffle
2+ from typing import Iterable
3+
4+ from rest_framework .request import Request
5+ from rest_framework .views import APIView
36
47from feed import constants
8+ from feed .constants import (
9+ SupportedModel ,
10+ SupportedQuerySet ,
11+ LIMIT_PAGINATION_CONSTANT
12+ )
13+ from feed .pagination import FeedPagination
514from feed .serializers import FeedItemSerializer
15+ from news .models import News
616from projects .models import Project
717
18+ from django .db .models import Count
819
9- def collect_feed (models_list : typing .List , num ) -> list [dict ]:
10- get_model_data = {
11- model .__name__ : collect_querysets (model , num ) for model in models_list
12- }
20+ from vacancy .models import Vacancy
21+
22+
23+ def add_pagination (results : list [SupportedQuerySet ], count : int ) -> dict :
24+ return {"count" : count , "previous" : None , "next" : None , "results" : results }
25+
26+
27+ def paginate_serialize_feed (
28+ model_data : dict [SupportedQuerySet ],
29+ paginator : FeedPagination ,
30+ request : Request ,
31+ view : APIView ,
32+ ) -> tuple [list [SupportedQuerySet ], int ]:
1333 result = []
14- for model in get_model_data :
15- result . extend ( to_feed_items ( model , get_model_data [ model ]))
16- random . shuffle ( result )
17- return result
34+ pages_count = 0
35+
36+ if len ( model_data ) == 0 :
37+ return [], 0
1838
39+ offset = request .query_params .get ("offset" , 0 )
40+ request .query_params ._mutable = True
1941
20- def collect_querysets (model , num ):
21- if model .__name__ == Project .__class__ .__name__ :
22- return set (get_n_random_projects (num ) + get_n_latest_created_projects (num ))
42+ if isinstance (offset , str ) and offset .isdigit ():
43+ offset = int (offset )
2344 else :
24- return list (model .objects .order_by ("-datetime_created" )[:num ])
45+ offset = 0
46+
47+ request .query_params ["offset" ] = offset
48+
49+ models_counts = {
50+ model_name : model_data [model_name ].count () for model_name in model_data .keys ()
51+ }
52+ offset_numbers = offset_distribution (offset , models_counts )
53+
54+ for model_name in model_data .keys ():
55+ request .query_params ["offset" ] = offset_numbers [model_name ]
56+
57+ paginated_part : dict = paginate_serialize_feed_queryset (
58+ model_data , paginator , request , model_name , models_counts [model_name ], view
59+ )
60+
61+ result += paginated_part ["paginated_data" ]
62+ pages_count += paginated_part ["page_count" ]
63+
64+ limit = request .query_params .get ("limit" , LIMIT_PAGINATION_CONSTANT )
65+
66+ if limit == "" :
67+ limit = LIMIT_PAGINATION_CONSTANT
68+ else :
69+ limit = int (limit )
70+
71+ shuffle (result )
72+ return result [:limit ], pages_count
2573
2674
27- def to_feed_items (type_ : constants .FeedItemType , items : typing .Iterable ) -> list [dict ]:
75+ def offset_distribution (offset : int , models_counts : dict ) -> dict :
76+ common_key_list = list (models_counts .keys ())
77+ quantity_of_models = len (list (models_counts .keys ()))
78+
79+ full_division = offset // quantity_of_models
80+ extra_items = offset % quantity_of_models
81+
82+ distributed_not_ready = {model_name : full_division for model_name in common_key_list }
83+ distributed = dict (
84+ sorted (distributed_not_ready .items (), key = lambda item : models_counts [item [0 ]])
85+ )
86+
87+ last_key = common_key_list [- 1 ]
88+ distributed [last_key ] += extra_items
89+
90+ new_keys_list = list (distributed .keys ())
91+ for i , key in enumerate (
92+ new_keys_list
93+ ): # распределяем переполненные значения от маленьких моделей к большим
94+ offset_value = distributed [key ]
95+ model_count = models_counts [key ]
96+ if offset_value > model_count :
97+ diff = offset_value - model_count
98+ distributed [key ] = model_count
99+ if i + 1 < len (new_keys_list ):
100+ next_key = new_keys_list [i + 1 ]
101+ distributed [next_key ] += diff
102+
103+ return distributed
104+
105+
106+ def paginate_serialize_feed_queryset (
107+ model_data : dict [SupportedQuerySet ],
108+ paginator : FeedPagination ,
109+ request : Request ,
110+ model : SupportedModel ,
111+ count : int ,
112+ view : APIView ,
113+ ) -> dict :
114+ paginated_info = paginator .custom_paginate_queryset (
115+ model_data [model ], request , count , view = view
116+ )
117+ paginated_data = paginated_info ["queryset_ready" ]
118+ num_pages = paginated_info ["count" ]
119+ return {
120+ "paginated_data" : to_feed_items (model , paginated_data ),
121+ "page_count" : num_pages ,
122+ }
123+
124+
125+ def collect_querysets (model : SupportedModel ) -> SupportedQuerySet :
126+ if model == Project :
127+ queryset = model .objects .select_related ("leader" , "industry" ).filter (draft = False )
128+ elif model == Vacancy :
129+ queryset = model .objects .select_related ("project" )
130+ elif model == News :
131+ queryset = (
132+ model .objects .select_related ("content_type" )
133+ .prefetch_related ("content_object" , "files" )
134+ .annotate (likes_count = Count ("likes" ), views_count = Count ("views" ))
135+ )
136+
137+ return queryset .order_by ("-datetime_created" )
138+
139+
140+ def to_feed_items (type_ : constants .FeedItemType , items : Iterable ) -> list [dict ]:
28141 feed_items = []
29142 for item in items :
30143 serializer = to_feed_item (type_ , item )
@@ -33,14 +146,6 @@ def to_feed_items(type_: constants.FeedItemType, items: typing.Iterable) -> list
33146 return feed_items
34147
35148
36- def get_n_random_projects (num : int ) -> list [Project ]:
37- return list (Project .objects .filter (draft = False ).order_by ("?" ).distinct ()[:num ])
38-
39-
40- def get_n_latest_created_projects (num : int ) -> list [Project ]:
41- return list (Project .objects .filter (draft = False ).order_by ("-datetime_created" )[:num ])
42-
43-
44149def to_feed_item (type_ : constants .FeedItemType , data ):
45150 serializer = constants .FEED_SERIALIZER_MAPPING [type_ ](data )
46- return FeedItemSerializer (data = {"type " : type_ , "content" : serializer .data })
151+ return FeedItemSerializer (data = {"type_model " : type_ , "content" : serializer .data })
0 commit comments