import json import warnings from io import BytesIO import pandas as pd from django.core.cache import cache from django.db.models import Q from django.http import HttpResponse from rest_framework import permissions, status from rest_framework import status as http_status from rest_framework.decorators import action from rest_framework.generics import GenericAPIView from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import ReadOnlyModelViewSet from service import serializers, models, pagination from service import utils from service.utils import raschet as raschet_alg, load_data def rename_result_dataset(dataframe, rename_dict, reverse=False): if reverse: return dataframe.rename(columns={v: k for k, v in rename_dict.items()}) return dataframe.rename(columns=rename_dict) class ao_and_rayons(GenericAPIView): permission_classes = [permissions.AllowAny] def get(self, request, format=None): d = cache.get('ao_and_rayons') if d is None: data = json.loads(open('ao_and_rayons.json', 'r').read()) cache.set('ao_and_rayons', data, 60 * 60 * 24) d = data return Response(d) class raschet(GenericAPIView): permission_classes = [permissions.AllowAny] def post(self, request, format=None): POINTS_RENAME_DICT = { "point_id": "ID точки (для всех наборов одинаковые)", "category": "Тип объекта", "msk_ao": "Адм. округ", "msk_rayon": "Адм. район", "addr": "Адрес", "name": "Название", "model": "Модель на ML", "rate": "Стандартная модель", } NET_RENAME_DICT = { "cell_id": "ID ячейки (для всех трёх сеток - разные, нумерация сквозная)", "msk_ao": "Адм. округ", "msk_rayon": "Адм. район", "vuz": "ВУЗы и техникумы", "model": "Модель на ML", "rate": "Стандартная модель", } TIPES_DICT = { "kiosk": "городской киоск", "mfc": "МФЦ", "library": "библиотека", "dk": "дом культуры и клуб", "sport": "спортивный объект", } RAYONS_RENAME_DICT = {1: 'район Филёвский Парк', 2: 'район Зюзино', 3: 'район Внуково', 4: 'Алтуфьевский район', 5: 'Дмитровский район', 6: 'Можайский район', 7: 'район Ново-Переделкино', 8: 'район Щукино', 9: 'Молжаниновский район', 10: 'район Митино', 11: 'район Куркино', 12: 'район Аэропорт', 13: 'район Строгино', 14: 'район Крылатское', 15: 'район Тёплый Стан', 16: 'район Солнцево', 57: 'Бутырский район', 17: 'район Южное Тушино', 18: 'район Северное Тушино', 19: 'район Покровское-Стрешнево', 20: 'район Хорошёво-Мнёвники', 21: 'район Очаково-Матвеевское', 22: 'район Тропарёво-Никулино', 23: 'район Левобережный', 24: 'район Фили-Давыдково', 25: 'район Ховрино', 26: 'Головинский район', 27: 'район Раменки', 28: 'Войковский район', 29: 'район Западное Дегунино', 30: 'район Сокол', 31: 'район Проспект Вернадского', 32: 'район Южное Бутово', 33: 'Обручевский район', 34: 'район Ясенево', 35: 'район Дорогомилово', 36: 'район Коньково', 37: 'Хорошёвский район', 38: 'район Северный', 39: 'район Коптево', 40: 'Ломоносовский район', 58: 'район Марфино', 59: 'Тверской район', 41: 'Пресненский район', 74: 'район Нагатино-Садовники', 42: 'Бескудниковский район', 43: 'Гагаринский район', 44: 'район Черёмушки', 45: 'Тимирязевский район', 46: 'район Беговой', 47: 'район Хамовники', 48: 'район Северное Бутово', 49: 'район Лианозово', 50: 'район Восточное Дегунино', 51: 'Савёловский район', 52: 'Академический район', 53: 'район Чертаново Центральное', 54: 'район Отрадное', 55: 'район Арбат', 56: 'район Чертаново Южное', 60: 'район Чертаново Северное', 61: 'район Якиманка', 62: 'район Котловка', 63: 'Останкинский район', 64: 'Донской район', 65: 'район Бибирево', 66: 'район Марьина Роща', 67: 'Нагорный район', 68: 'Даниловский район', 73: 'район Северное Медведково', 69: 'Мещанский район', 70: 'район Бирюлёво Западное', 71: 'район Южное Медведково', 72: 'район Замоскворечье', 75: 'район Царицыно', 76: 'район Москворечье-Сабурово', 77: 'район Свиблово', 78: 'район Нагатинский Затон', 82: 'Таганский район', 79: 'Басманный район', 80: 'Красносельский район', 81: 'район Ростокино', 83: 'Алексеевский район', 84: 'район Восточный', 87: 'Бабушкинский район', 85: 'район Бирюлёво Восточное', 86: 'Южнопортовый район', 88: 'район Сокольники', 89: 'Ярославский район', 90: 'район Богородское', 91: 'район Метрогородок', 92: 'район Лефортово', 93: 'район Орехово-Борисово Северное', 94: 'район Печатники', 95: 'Лосиноостровский район', 98: 'район Новогиреево', 96: 'Нижегородский район', 99: 'район Марьино', 97: 'район Орехово-Борисово Южное', 100: 'район Преображенское', 105: 'район Братеево', 101: 'район Соколиная Гора', 102: 'район Перово', 103: 'район Люблино', 104: 'район Текстильщики', 106: 'район Зябликово', 107: 'Рязанский район', 108: 'район Измайлово', 109: 'район Северное Измайлово', 113: 'район Капотня', 110: 'район Кузьминки', 111: 'район Гольяново', 112: 'район Вешняки', 114: 'район Выхино-Жулебино', 115: 'район Восточное Измайлово', 118: 'район Новокосино', 116: 'район Ивановское', 117: 'район Косино-Ухтомский', 119: 'район Некрасовка', 120: 'район Кунцево'} AO_RENAME_DICT = {1: 'Западный административный округ', 2: 'Южный административный округ', 3: 'Северный административный округ', 4: 'Юго-Западный административный округ', 5: 'Северо-Западный административный округ', 6: 'Центральный административный округ', 7: 'Северо-Восточный административный округ', 8: 'Юго-Восточный административный округ', 9: 'Восточный административный округ'} EXCLUDED_COLS = ['id', 'people', 'people2025', 'stops_ot', 'routes_ot', 'in_metro', 'out_metro', 'tc', 'empls', 'walkers', 'schools', 'parking', 'pvz', 'gov_place', 'bike_park', 'products', 'nonprod', 'service', 'vuz'] df_points_, df_nets_ = raschet_alg(**request.data) df_points_['category'] = df_points_['category'].map(TIPES_DICT) df_points_['msk_ao'] = df_points_['msk_ao'].map(AO_RENAME_DICT) df_points_['msk_rayon'] = df_points_['msk_rayon'].map(RAYONS_RENAME_DICT) df_nets_['msk_ao'] = df_nets_['msk_ao'].map(AO_RENAME_DICT) df_nets_['msk_rayon'] = df_nets_['msk_rayon'].map(RAYONS_RENAME_DICT) df_points_ = df_points_.drop(columns=EXCLUDED_COLS) df_nets_ = df_nets_.drop(columns=EXCLUDED_COLS) df_points = rename_result_dataset(df_points_, POINTS_RENAME_DICT) if "Стандартная модель" in df_points.columns: df_points = df_points[ ["ID точки (для всех наборов одинаковые)", "Адм. округ", "Адм. район", "Адрес", "Название", "Тип объекта", "geom", "Модель на ML", "Стандартная модель"]] else: df_points = df_points[ ["ID точки (для всех наборов одинаковые)", "Адм. округ", "Адм. район", "Адрес", "Название", "Тип объекта", "geom", "Модель на ML", ]] df_nets = rename_result_dataset(df_nets_, NET_RENAME_DICT) if "Стандартная модель" in df_nets.columns: df_nets = df_nets[ ["ID ячейки (для всех трёх сеток - разные, нумерация сквозная)", "Адм. округ", "Адм. район", "geom", "Модель на ML", "Стандартная модель"]] else: df_nets = df_nets[ ["ID ячейки (для всех трёх сеток - разные, нумерация сквозная)", "Адм. округ", "Адм. район", "geom", "Модель на ML"]] with BytesIO() as b: # Use the StringIO object as the filehandle. writer = pd.ExcelWriter(b, engine='xlsxwriter') if df_points is not None: df_points.to_excel(writer, sheet_name='Точки', index=False) worksheet = writer.sheets['Точки'] # pull worksheet object for idx, col in enumerate(df_points): # loop through all columns if col == 'geom': continue series = df_points[col] max_len = max(( series.astype(str).map(len).max(), # len of largest item len(str(series.name)) # len of column name/header )) + 1 # adding a little extra space worksheet.set_column(idx, idx, max_len) if df_nets is not None: df_nets.to_excel(writer, sheet_name='Полигоны', index=False) worksheet = writer.sheets['Полигоны'] # pull worksheet object for idx, col in enumerate(df_nets): # loop through all columns if col == 'geom': continue series = df_nets[col] max_len = max(( series.astype(str).map(len).max(), # len of largest item len(str(series.name)) # len of column name/header )) + 1 # adding a little extra space worksheet.set_column(idx, idx, max_len) writer.save() # Set up the Http response. filename = f'Выгрузка.xlsx' response = HttpResponse( b.getvalue(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) response['Content-Disposition'] = 'attachment; filename=%s' % filename return response class PlacementPointViewSet(ReadOnlyModelViewSet): serializer_class = serializers.PlacementPointSerializer queryset = models.PlacementPoint.objects pagination_class = pagination.MyPagination def get_queryset(self): qs = self.queryset.all() location_ids = self.request.GET.get('location_ids[]') prediction = self.request.GET.get('prediction[]') categories = self.request.GET.get('categories[]') status = self.request.GET.get('status[]') delta = self.request.GET.get('delta[]') fact = self.request.GET.get('fact[]') age = self.request.GET.get('age[]') included = self.request.GET.get('included[]') excluded = self.request.GET.get('excluded[]') if location_ids: location_ids = list(location_ids.split(',')) qs = qs.filter(location_id__in=location_ids) if prediction: prediction = list(prediction.split(',')) qs = qs.filter(prediction__range=prediction) 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 age: age = list(age.split(',')) qs = qs.filter(age__range=age) if excluded: excluded = list(excluded.split(',')) qs = qs.filter(~Q(location_id__in=excluded)) if included: inclded = list(included.split(',')) qs2 = models.PlacementPoint.objects.filter(location_id__in=inclded).all() qs = (qs | qs2).distinct() return qs @action(detail=False, methods=['get']) def filters(self, request): qs = self.get_queryset() age_day = [x for x in list(set(qs.values_list('age_day', flat=True))) if x is not None] prediction_first = [x for x in list(set(qs.values_list('prediction_first', flat=True))) if x is not None] prediction_current = [x for x in list(set(qs.values_list('prediction_current', flat=True))) if x is not None] plan_first = [x for x in list(set(qs.values_list('plan_first', flat=True))) if x is not None] plan_current = [x for x in list(set(qs.values_list('plan_current', flat=True))) if x is not None] fact = [x for x in list(set(qs.values_list('fact', flat=True))) if x is not None] delta_first = [x for x in list(set(qs.values_list('delta_first', flat=True))) if x is not None] delta_current = [x for x in list(set(qs.values_list('delta_current', flat=True))) if x is not None] flat_cnt = [x for x in list(set(qs.values_list('flat_cnt', flat=True))) if x is not None] year_bld = [x for x in list(set(qs.values_list('year_bld', flat=True))) if x is not None] levels = [x for x in list(set(qs.values_list('levels', flat=True))) if x is not None] doors = [x for x in list(set(qs.values_list('doors', flat=True))) if x is not None] flats_cnt = [x for x in list(set(qs.values_list('flats_cnt', flat=True))) if x is not None] popul_home = [x for x in list(set(qs.values_list('popul_home', flat=True))) if x is not None] popul_job = [x for x in list(set(qs.values_list('popul_job', flat=True))) if x is not None] other_post_cnt = [x for x in list(set(qs.values_list('other_post_cnt', flat=True))) if x is not None] target_post_cnt = [x for x in list(set(qs.values_list('target_post_cnt', flat=True))) if x is not None] yndxfood_cnt = [x for x in list(set(qs.values_list('yndxfood_cnt', flat=True))) if x is not None] yndxfood_sum = [x for x in list(set(qs.values_list('yndxfood_sum', flat=True))) if x is not None] yndxfood_cnt_cst = [x for x in list(set(qs.values_list('yndxfood_cnt_cst', flat=True))) if x is not None] data = { "age_day": [min(age_day), max(age_day)] if age_day else [0, 2030], "prediction_first": [min(prediction_first), max(prediction_first)] if prediction_first else [-22, 22], "prediction_current": [min(prediction_current), max(prediction_current)] if prediction_current else [-22, 22], "plan_first": [min(plan_first), max(plan_first)] if plan_first else [0, 100], "plan_current": [min(plan_current), max(plan_current)] if plan_current else [0, 100], "fact": [min(fact), max(fact)] if fact else [0, 100], "delta_first": [min(delta_first), max(delta_first)] if delta_first else [0, 100], "delta_current": [min(delta_current), max(delta_current)] if delta_current else [0, 100], "flat_cnt": [min(flat_cnt), max(flat_cnt)] if flat_cnt else [0, 100], "year_bld": [min(year_bld), max(year_bld)] if year_bld else [0, 2030], "levels": [min(levels), max(levels)] if levels else [0, 100], "doors ": [min(doors), max(doors)] if doors else [0, 100], "flats_cnt": [min(flats_cnt), max(flats_cnt)] if flats_cnt else [0, 100], "popul_home": [min(popul_home), max(popul_home)] if popul_home else [0, 100], "popul_job": [min(popul_job), max(popul_job)] if popul_job else [0, 100], "other_post_cnt": [min(other_post_cnt), max(other_post_cnt)] if other_post_cnt else [0, 100], "target_post_cnt": [min(target_post_cnt), max(target_post_cnt)] if target_post_cnt else [0, 100], "yndxfood_cnt": [min(yndxfood_cnt), max(yndxfood_cnt)] if yndxfood_cnt else [0, 100], "yndxfood_sum": [min(yndxfood_sum), max(yndxfood_sum)] if yndxfood_sum else [0, 100], "yndxfood_cnt_cst": [min(yndxfood_cnt_cst), max(yndxfood_cnt_cst)] if yndxfood_cnt_cst else [0, 100], } return Response(data, status=status.HTTP_200_OK) class refresh_placement_points(APIView): @staticmethod def post(request): warnings.filterwarnings('ignore') file = request.FILES['file'] load_data(file) return Response(status=http_status.HTTP_200_OK) 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) return Response(status=http_status.HTTP_200_OK) class update_status(APIView): def put(self, request): qs = models.PlacementPoint.objects.all() new_status = self.request.GET.get('status') location_ids = self.request.GET.get('location_ids[]') prediction = self.request.GET.get('prediction[]') categories = self.request.GET.get('categories[]') status = self.request.GET.get('status[]') delta = self.request.GET.get('delta[]') fact = self.request.GET.get('fact[]') age = self.request.GET.get('age[]') included = self.request.GET.get('included[]') excluded = self.request.GET.get('excluded[]') if not new_status: return Response({'message': 'No status provided'}, status=http_status.HTTP_400_BAD_REQUEST) if not any([location_ids, prediction, categories, status, delta, fact, age]): qs = models.PlacementPoint.objects.none() if location_ids: location_ids = list(location_ids.split(',')) qs = qs.filter(location_id__in=location_ids) if prediction: prediction = list(prediction.split(',')) qs = qs.filter(prediction__range=prediction) 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 age: age = list(age.split(',')) qs = qs.filter(age__range=age) if excluded: excluded = list(excluded.split(',')) qs = qs.filter(~Q(location_id__in=excluded)) if included: inclded = list(included.split(',')) qs2 = models.PlacementPoint.objects.filter(location_id__in=inclded).all() qs = (qs | qs2).distinct() if not any([location_ids, prediction, categories, status, delta, fact, age, excluded, included]): return Response({'message': 'Empty queryset'}, status=http_status.HTTP_200_OK) qs.update(**{'status': new_status}) return Response({'message': 'status updated'}, status=http_status.HTTP_200_OK)