You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import warnings
from http import HTTPStatus
from django.db.models import Q
from django.http import HttpResponse
from django.http import JsonResponse
from rest_framework.decorators import action
from rest_framework.decorators import api_view
from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet
from postamates.settings import AGE_DAY_BORDER
from postamates.settings import EXCEL_EXPORT_FILENAME
from postamates.settings import JSON_EXPORT_FILENAME
from service import models
from service import pagination
from service import serializers
from service import utils
from service.enums import PointStatus
from service.permissions import UserPermission
from service.service import PointService
from service.tasks import raschet, load_post_and_pvz, load_other_objects, load_data
from rest_framework.permissions import AllowAny
from django.shortcuts import redirect
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
class AOViewSet(CustomReadOnlyModelViewSet):
serializer_class = serializers.AOSerializer
queryset = models.AO.objects
permission_classes = [AllowAny]
class PostAndPVZCategoryViewSet(CustomReadOnlyModelViewSet):
serializer_class = serializers.PostAndPVZCategorySerializer
queryset = models.Post_and_pvzCategory.objects
class OtherObjectsCategoryViewSet(CustomReadOnlyModelViewSet):
serializer_class = serializers.OtherObjectsCategorySerializer
queryset = models.OtherObjectsCategory.objects
class PlacementPointViewSet(ReadOnlyModelViewSet):
serializer_class = serializers.PlacementPointSerializer
queryset = models.PlacementPoint.objects
pagination_class = pagination.MyPagination
permission_classes = [UserPermission]
filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]
@property
def filterset_fields(self):
model_cls = self.queryset.model
fieldset = {
field.name: [
"exact",
"gt",
"gte",
"lt",
"lte",
"in",
"iexact",
"startswith",
"istartswith",
"endswith",
"iendswith",
"regex",
"iregex",
"isnull",
"contains",
"icontains",
]
for field in [
field
for field in model_cls._meta.get_fields()
if field.get_internal_type()
not in [
"JSONField",
"ForeignKey",
"ManyToManyField",
"OneToOneField",
"PointField"
]
]
}
# filters for model relations
for field in [
field
for field in model_cls._meta.get_fields()
if field.get_internal_type()
in [
"ForeignKey",
"ManyToManyField",
"OneToOneField",
]
]:
fieldset[field.name] = [
"exact",
"gt",
"gte",
"lt",
"lte",
]
return fieldset
def get_queryset(self):
qs = self.queryset.all().order_by('id')
location_ids = self.request.GET.get('location_ids[]')
prediction_first = self.request.GET.get('prediction_first[]')
prediction_current = self.request.GET.get('prediction_current[]')
age_day = self.request.GET.get('age_day[]')
categories = self.request.GET.get('categories[]')
status = self.request.GET.get('status[]')
delta = self.request.GET.get('delta[]')
fact = self.request.GET.get('fact[]')
included = self.request.GET.get('included[]')
excluded = self.request.GET.get('excluded[]')
plan_first = self.request.GET.get('plan_first[]')
plan_current = self.request.GET.get('plan_current[]')
delta_first = self.request.GET.get('delta_first[]')
delta_current = self.request.GET.get('delta_current[]')
rayons = self.request.GET.get('area[]')
aos = self.request.GET.get('district[]')
group_dists_lt = self.request.GET.getlist('dist_to_group__lt')
group_dists_gt = self.request.GET.getlist('dist_to_group__gt')
if location_ids:
location_ids = list(location_ids.split(','))
qs = qs.filter(pk__in=location_ids)
if prediction_first:
prediction_first = list(prediction_first.split(','))
qs = qs.filter(prediction_first__range=prediction_first)
if prediction_current:
prediction_current = list(prediction_current.split(','))
qs = qs.filter(prediction_current__range=prediction_current)
if categories:
categories = list(categories.split(','))
qs = qs.filter(category__in=categories)
if status:
status = list(status.split(','))
qs = qs.filter(status__in=status)
if delta:
delta = list(delta.split(','))
qs = qs.filter(delta__range=delta)
if fact:
fact = list(fact.split(','))
qs = qs.filter(fact__range=fact)
if plan_first:
plan_first = list(plan_first.split(','))
qs = qs.filter(plan_first__range=plan_first)
if plan_current:
plan_current = list(plan_current.split(','))
qs = qs.filter(plan_current__range=plan_current)
if age_day:
age_day = list(age_day.split(','))
qs = qs.filter(age_day__range=age_day)
if delta_current:
delta_current = list(delta_current.split(','))
qs = qs.filter(delta_current__range=delta_current)
if delta_first:
delta_first = list(delta_first.split(','))
qs = qs.filter(delta_first__range=delta_first)
if rayons:
rayons = list(rayons.split(','))
qs = qs.filter(area_id__in=rayons)
if aos:
aos = list(aos.split(','))
qs = qs.filter(district_id__in=aos)
if excluded:
excluded = list(excluded.split(','))
qs = qs.filter(~Q(pk__in=excluded))
if included:
inclded = list(included.split(','))
qs2 = models.PlacementPoint.objects.filter(pk__in=inclded).all()
qs = (qs | qs2).distinct()
if group_dists_lt:
g_d = [list(g.split(',')) for g in group_dists_lt]
for group in g_d:
filtered_points = list(
models.PlacementPointPVZDistance.objects.filter(pvz_postamates_group__id=int(group[0]),
dist__lt=int(group[1])).values_list(
'placement_point__id', flat=True))
qs = qs.filter(id__in=filtered_points)
if group_dists_gt:
g_d = [list(g.split(',')) for g in group_dists_gt]
for group in g_d:
filtered_points = list(
models.PlacementPointPVZDistance.objects.filter(pvz_postamates_group__id=int(group[0]),
dist__gt=int(group[1])).values_list(
'placement_point__id', flat=True))
qs = qs.filter(id__in=filtered_points)
return qs
@action(methods=['get'], detail=False)
def get_filterset_fields(self, request, *args, **kwargs):
return Response(self.filterset_fields, status=HTTPStatus.OK)
@action(detail=False, methods=['get'])
def filters(self, request):
def get_filter_data():
qs = self.get_queryset()
keys = (
'age_day', 'prediction_first', 'prediction_current',
'plan_first', 'plan_current', 'fact', 'delta_first',
'delta_current', 'flat_cnt', 'year_bld', 'levels',
'doors', 'flats_cnt', 'popul_home', 'popul_job',
'other_post_cnt', 'target_post_cnt', 'yndxfood_cnt',
'yndxfood_sum', 'yndxfood_cnt_cst', 'rival_post_cnt', 'rival_pvz_cnt', 'tc_cnt', 'culture_cnt',
'mfc_cnt', 'public_stop_cnt'
, 'supermarket_cnt', 'target_dist', 'metro_dist'
)
temp_data = {
key: [
x for x in list(set(qs.values_list(key, flat=True))) if
x is not None
]
for key in keys
}
data = {
key: [
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 = get_filter_data()
return Response(data, status=HTTPStatus.OK)
@action(detail=False, methods=['get'])
def search_address(self, request):
address = self.request.GET.get('address')
qs = self.get_queryset()
if address:
qs = qs.filter(address__icontains=address)
qs = qs.distinct()
page = self.paginate_queryset(qs)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
@action(detail=False, methods=['put'])
def update_status(self, request):
qs = self.get_queryset()
new_status = self.request.GET.get('status')
if not new_status or new_status not in [tag.name for tag in PointStatus]:
return Response({'message': 'No status'}, HTTPStatus.BAD_REQUEST)
PointService.update_points_in_radius(qs, new_status)
PointService.update_status(qs, new_status)
return Response(
{'message': 'status updated'},
status=HTTPStatus.OK,
)
@action(detail=False, methods=['put'])
def update_fact(self, request):
point_id = request.GET.get('postamat_id')
fact = request.GET.get('fact')
if not point_id or not fact or not fact.isdigit():
return Response(status=HTTPStatus.BAD_REQUEST)
qs = models.PlacementPoint.objects.filter(postamat_id=point_id)
if not qs:
return Response(status=HTTPStatus.NOT_FOUND)
for q in qs:
if q.age_day < AGE_DAY_BORDER:
qs.update(**{'fact': fact, 'fact_raw': fact})
else:
new_fact = fact // (q.age_day / AGE_DAY_BORDER)
qs.update(fact=new_fact, fact_raw=fact)
qs.update(**{'fact_raw': fact})
return Response({'message': 'fact updated'}, status=HTTPStatus.OK)
@action(detail=False, methods=['put'])
def update_postamat_id(self, request):
postamat_id = request.GET.get('postamat_id')
point_id = request.GET.get('id')
if not point_id or not postamat_id:
return Response(status=HTTPStatus.BAD_REQUEST)
qs = PointService.get_point_by_id(point_id)
if not qs:
return Response(status=HTTPStatus.NOT_FOUND)
PointService().update_postamat_id(point_id, postamat_id)
return Response({'message': 'Postamat id updated'}, status=HTTPStatus.OK)
@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(serializer),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
)
res['Content-Disposition'] = f'attachment; filename={filename}'
return res
@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(serializer), content_type='application/json')
response['Content-Disposition'] = f'attachment; filename={filename}'
return response
@action(detail=False, methods=['get'])
def get_10k(self, request):
pred = PointService.get_first_10_k()
return Response({'prediction_current': pred}, status=HTTPStatus.OK)
@action(detail=False, methods=['get'])
def start(self, request):
raschet.delay()
return Response('Sucess', status=HTTPStatus.OK)
@action(detail=False, methods=['get'])
def last_time_ml_run(self, request):
return Response({'last_time': models.LastMLCall.objects.first().dt}, status=HTTPStatus.OK)
class refresh_placement_points(APIView):
@staticmethod
def post(request):
warnings.filterwarnings('ignore')
file = request.FILES['file'].file
file_bytes = file.read()
csv_base64 = base64.b64encode(file_bytes).decode()
obj = models.TempFiles.objects.create(data=csv_base64)
load_data.delay(obj.id)
messages.success(request, 'Файл точек успешно загружен')
return redirect('/admin')
class load_ao_and_rayons(APIView):
@staticmethod
def post(request):
warnings.filterwarnings('ignore')
file_ao = request.FILES['file_ao']
file_rayon = request.FILES['file_rayon']
utils.load_ao_and_rayons(file_ao, file_rayon)
messages.success(request, 'Файл АО и Районов успешно загружен')
return redirect('/admin')
import base64
@api_view(['POST'])
def upload_post_and_pvz(request):
warnings.filterwarnings('ignore')
file_rivals = request.FILES['file_post_and_pvz'].file
file_bytes = file_rivals.read()
excel_base64 = base64.b64encode(file_bytes).decode()
obj = models.TempFiles.objects.create(data=excel_base64)
load_post_and_pvz.delay(obj.id)
messages.success(request, 'Загрузка ПВЗ и Постаматов началась. Отслеживайте выполнение в Статусе фоновых задач')
return redirect('/admin')
@api_view(['POST'])
def upload_other_objects(request):
warnings.filterwarnings('ignore')
file = request.FILES['file_other_objects']
file_bytes = file.read()
excel_base64 = base64.b64encode(file_bytes).decode()
obj = models.TempFiles.objects.create(data=excel_base64)
load_other_objects.delay(obj.id)
messages.success(request, 'Загрузка Прочих объектов началась. Отслеживайте выполнение в Статусе фоновых задач')
return redirect('/admin')
@api_view(['POST'])
def upload_dist(request):
warnings.filterwarnings('ignore')
file_dist = request.FILES['file_dist']
utils.load_dist(file_dist)
messages.success(request, 'Файл расстояний успешно загружен')
return redirect('/admin')
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_current_user(request):
return JsonResponse(
{'groups': [gr.name for gr in request.user.groups.all()]},
)
def download_pvz_template(self):
image_buffer = open('Постоматы и ПВЗ.xlsx', "rb").read()
response = HttpResponse(image_buffer, content_type='xlsx')
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename('pvz_and_postomats.xlsx')
return response
def download_other_template(self):
image_buffer = open('Другие объекты.xlsx', "rb").read()
response = HttpResponse(image_buffer, content_type='xlsx')
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename('other.xlsx')
return response