From 2bdf9679ddb2f29c759085dfec623b43c151f657 Mon Sep 17 00:00:00 2001 From: AlexP077 Date: Tue, 8 Aug 2023 18:16:26 +0300 Subject: [PATCH] add_distances_to_point_qs --- service/serializers.py | 7 +++++++ service/service.py | 40 ++++++++++++++++++++++++++++++++++++---- service/utils.py | 5 +++++ service/views.py | 13 +++++++++++-- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/service/serializers.py b/service/serializers.py index 39ef0a4..39cfa98 100644 --- a/service/serializers.py +++ b/service/serializers.py @@ -1,6 +1,7 @@ from rest_framework import serializers from service import models +from service.service import PointService class PlacementPointSerializer(serializers.ModelSerializer): @@ -8,6 +9,12 @@ class PlacementPointSerializer(serializers.ModelSerializer): model = models.PlacementPoint fields = '__all__' + def to_representation(self, instance): + representation = super().to_representation(instance) + min_distances = PointService.get_min_distances_to_group(instance.id) + representation['min_distance_to_group'] = min_distances + return representation + class PostAndPVZGroupSerializer(serializers.ModelSerializer): class Meta: diff --git a/service/service.py b/service/service.py index ef56fe9..001a537 100644 --- a/service/service.py +++ b/service/service.py @@ -9,6 +9,7 @@ from postamates.settings import DEFAULT_PLACEMENT_POINT_UPDATE_RADIUS from service import models from service.enums import PointStatus from service.tasks import raschet +from service.utils import create_columns_dist class LayerService: @@ -18,6 +19,7 @@ class LayerService: class PointService: + def update_fact(self, postamat_id: str, fact: int): qs = self.get_point_by_postamat_id(postamat_id) qs.update(**{'fact': fact}) @@ -26,6 +28,12 @@ class PointService: qs = self.get_point_by_id(point_id) qs.update(**{'postamat_id': postamat_id}) + @staticmethod + def get_min_distances_to_group(postamat_id: str): + return {d['pvz_postamates_group']: d['dist'] for d in list( + models.PlacementPointPVZDistance.objects.filter(placement_point=postamat_id).values( + 'pvz_postamates_group', 'dist'))} + @staticmethod def update_points_in_radius(qs: models.PlacementPoint, new_status: str): triggers = False @@ -66,13 +74,25 @@ class PointService: return models.PlacementPoint.objects.filter(postamat_id=postamat_id) @staticmethod - def to_excel(qs: models.PlacementPoint): - data = pd.DataFrame(list(qs.values())) + def to_excel(serializer): + data = pd.DataFrame(serializer.data) if not data.empty: if data['start_date'].any(): data['start_date'] = data.get('start_date').dt.tz_localize(None) data['sample_trn'] = data['sample_trn'].astype(int) data.rename(columns={'district_id': 'district', 'area_id': 'area'}, inplace=True) + data['min_distance_to_group'] = data['min_distance_to_group'].apply(lambda x: list(x.items())) + new_columns = data.apply(create_columns_dist, axis=1) + for ind in new_columns.columns: + expanded = new_columns[ind].apply(pd.Series) + group = models.Post_and_pvzGroup.objects.get(id=int(expanded.loc[0, 0])) + expanded[[f"group_{ind + 1}_name", f"group_{ind + 1}_category"]] = group.name, group.category.name + expanded = expanded.rename(columns={1: f"dist_to_group_{ind + 1}"}) + expanded = expanded.drop(0, axis=1) + new_columns = pd.concat([new_columns, expanded], axis=1) + new_columns = new_columns.drop(ind, axis=1) + data.drop('min_distance_to_group', axis=1, inplace=True) + data = pd.concat([data, new_columns], axis=1) with BytesIO() as b: with pd.ExcelWriter(b) as writer: data.to_excel( @@ -82,13 +102,25 @@ class PointService: return b.getvalue() @staticmethod - def to_json(qs: models.PlacementPoint): - data = pd.DataFrame(list(qs.values())) + def to_json(serializer): + data = pd.DataFrame(serializer.data) data['start_date'] = pd.to_datetime(data['start_date'], errors='coerce') data['start_date'] = data['start_date'].dt.tz_localize(None) data['sample_trn'] = data['sample_trn'].astype(int) data['geometry'] = data['geometry'].apply(lambda x: {'latitude': x[1], 'longtitude': x[0]}) data.rename(columns={'district_id': 'district', 'area_id': 'area'}, inplace=True) + data['min_distance_to_group'] = data['min_distance_to_group'].apply(lambda x: list(x.items())) + new_columns = data.apply(create_columns_dist, axis=1) + for ind in new_columns.columns: + expanded = new_columns[ind].apply(pd.Series) + group = models.Post_and_pvzGroup.objects.get(id=int(expanded.loc[0, 0])) + expanded[[f"group_{ind + 1}_name", f"group_{ind + 1}_category"]] = group.name, group.category.name + expanded = expanded.rename(columns={1: f"dist_to_group_{ind + 1}"}) + expanded = expanded.drop(0, axis=1) + new_columns = pd.concat([new_columns, expanded], axis=1) + new_columns = new_columns.drop(ind, axis=1) + data.drop('min_distance_to_group', axis=1, inplace=True) + data = pd.concat([data, new_columns], axis=1) return data.to_json(orient='records') @staticmethod diff --git a/service/utils.py b/service/utils.py index db0eaec..180cf83 100644 --- a/service/utils.py +++ b/service/utils.py @@ -56,10 +56,15 @@ def cached_func(key, func, timeout=settings.CACHE_TIMEOUT, *args, **kwargs): cache.set(key, d, timeout) return d + class CustomReadOnlyModelViewSet(ReadOnlyModelViewSet): def list(self, request, *args, **kwargs): def f(): return ReadOnlyModelViewSet.list(self, request, *args, **kwargs).data + d = cached_func(self.__class__.__name__, f) return Response(d) + +def create_columns_dist(row): + return pd.Series(row['min_distance_to_group']) diff --git a/service/views.py b/service/views.py index 8ab6247..bb1a4b2 100644 --- a/service/views.py +++ b/service/views.py @@ -29,6 +29,7 @@ from django.contrib import messages from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters from service.utils import CustomReadOnlyModelViewSet +from django.db.models import Min, Max import os @@ -218,7 +219,13 @@ class PlacementPointViewSet(ReadOnlyModelViewSet): min(temp_data[key]), max(temp_data[key]), ] if temp_data[key] else [0, 100] for key in keys } + data['dist_to_groups'] = [{'group_id': d['pvz_postamates_group'], 'dist': [d['min_dist'], d['max_dist']]} + for d in list( + models.PlacementPointPVZDistance.objects.values('pvz_postamates_group').annotate( + min_dist=Min('dist'), max_dist=Max('dist')))] + return data + data = utils.cached_func('get_filter_data', get_filter_data, 120) return Response(data, status=HTTPStatus.OK) @@ -284,9 +291,10 @@ class PlacementPointViewSet(ReadOnlyModelViewSet): @action(detail=False, methods=['get']) def to_excel(self, request): qs = self.get_queryset() + serializer = serializers.PlacementPointSerializer(qs, many=True) filename = EXCEL_EXPORT_FILENAME res = HttpResponse( - PointService.to_excel(qs), + PointService.to_excel(serializer), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ) res['Content-Disposition'] = f'attachment; filename={filename}' @@ -295,8 +303,9 @@ class PlacementPointViewSet(ReadOnlyModelViewSet): @action(detail=False, methods=['get']) def to_json(self, request): qs = self.get_queryset() + serializer = serializers.PlacementPointSerializer(qs, many=True) filename = JSON_EXPORT_FILENAME - response = HttpResponse(PointService.to_json(qs), content_type='application/json') + response = HttpResponse(PointService.to_json(serializer), content_type='application/json') response['Content-Disposition'] = f'attachment; filename={filename}' return response