add_new_models+remove_location_id

dev
AlexP077 3 years ago committed by Dmitry Titov
parent 00a47b3672
commit 36a7831bad

@ -2,3 +2,6 @@
max-line-length = 120
exclude = fixtures/*
max-complexity = 10
per-file-ignores =
service/migrations/*:E501
service/views.py:C901

File diff suppressed because one or more lines are too long

@ -1,9 +1,17 @@
from django.contrib import admin
from service.models import PlacementPoint, AO, Rayon
from service.models import AO
from service.models import PlacementPoint
from service.models import PointDist
from service.models import Rayon
from service.models import Rivals
admin.site.register(AO)
admin.site.register(Rayon)
admin.site.register(Rivals)
admin.site.register(PointDist)
class PlacementPointAdmin(admin.ModelAdmin):

@ -1,3 +1,7 @@
# Generated by Django 3.2 on 2023-03-12 16:03
import django.db.models.deletion
from django.db import migrations
from django.db import models
# Generated by Django 3.2 on 2023-03-08 11:48
from django.db import migrations, models

@ -0,0 +1,34 @@
# Generated by Django 3.2 on 2023-03-12 16:23
import django.contrib.gis.db.models.fields
import django.db.models.deletion
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
dependencies = [
('service', '0008_alter_rayon_ao'),
]
operations = [
migrations.CreateModel(
name='Rivals',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('wkt', django.contrib.gis.db.models.fields.PointField(null=True, srid=4326)),
('info', models.TextField(blank=True, null=True)),
('type', models.TextField(blank=True, null=True)),
('source', models.TextField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='PointDist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('distance', models.FloatField()),
('id1', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='placement_point_id1', to='service.placementpoint')),
('id2', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='placement_point_id2', to='service.placementpoint')),
],
),
]

@ -0,0 +1,17 @@
# Generated by Django 3.2 on 2023-03-12 16:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('service', '0009_pointdist_rivals'),
]
operations = [
migrations.RenameField(
model_name='rivals',
old_name='wkt',
new_name='WKT',
),
]

@ -0,0 +1,16 @@
# Generated by Django 3.2 on 2023-03-12 17:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('service', '0010_rename_wkt_rivals_wkt'),
]
operations = [
migrations.RemoveField(
model_name='placementpoint',
name='location_id',
),
]

@ -3,7 +3,6 @@ from django.db import models
from service.signals import *
class PlacementPoint(models.Model):
location_id = models.IntegerField(null=False, blank=False,unique=True)
address = models.TextField(null=True, blank=True, verbose_name='Адрес')
name = models.TextField(null=True, blank=True, verbose_name='Название')
category = models.TextField(null=True, blank=True, verbose_name='Категория')
@ -32,16 +31,30 @@ class PlacementPoint(models.Model):
yndxfood_cnt = models.IntegerField(null=True, blank=True)
yndxfood_sum = models.IntegerField(null=True, blank=True)
yndxfood_cnt_cst = models.IntegerField(null=True, blank=True)
okrug = models.ForeignKey("AO", on_delete=models.CASCADE, null=True, blank=True)
rayon = models.ForeignKey("Rayon", on_delete=models.CASCADE, null=True, blank=True)
okrug = models.ForeignKey('AO', on_delete=models.CASCADE, null=True, blank=True)
rayon = models.ForeignKey('Rayon', on_delete=models.CASCADE, null=True, blank=True)
geometry = gis_models.PointField(srid=4326, null=True, verbose_name='Координаты')
class AO(models.Model):
name = models.TextField(null=True, blank=True, verbose_name='Округ')
polygon = gis_models.MultiPolygonField(null=True,srid=4326)
polygon = gis_models.MultiPolygonField(null=True, srid=4326)
class Rayon(models.Model):
name = models.TextField(null=True, blank=True, verbose_name='Район')
AO = models.ForeignKey("AO",related_name='rayons', on_delete=models.CASCADE)
polygon = gis_models.MultiPolygonField(null=True,srid=4326)
AO = models.ForeignKey('AO', related_name='rayons', on_delete=models.CASCADE)
polygon = gis_models.MultiPolygonField(null=True, srid=4326)
class Rivals(models.Model):
WKT = gis_models.PointField(srid=4326, null=True)
info = models.TextField(null=True, blank=True)
type = models.TextField(null=True, blank=True)
source = models.TextField(null=True, blank=True)
class PointDist(models.Model):
id1 = models.ForeignKey('PlacementPoint', on_delete=models.CASCADE, null=False, related_name='placement_point_id1')
id2 = models.ForeignKey('PlacementPoint', on_delete=models.CASCADE, null=False, related_name='placement_point_id2')
distance = models.FloatField(null=False)

@ -31,6 +31,8 @@ urlpatterns = [
path('ao_rayons', views.AOViewSet.as_view({'get': 'list'}), name='ao_and_rayons'),
url(r'load_csv/', views.refresh_placement_points.as_view(), name='upload_placement_points'),
url(r'upload_ao_and_rayons/', views.load_ao_and_rayons.as_view(), name='upload_ao_and_rayons'),
url(r'upload_rivals/', views.upload_rivals, name='upload_rivals'),
url(r'upload_dist/', views.upload_dist, name='upload_dist'),
url(r'me/', views.get_current_user, name='me'),
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),

@ -1,8 +1,6 @@
import geojson
import numpy as np
import pandas as pd
import sqlalchemy
from django.conf import settings
from django.contrib.gis.geos import GEOSGeometry
from geojson import MultiPolygon
from tqdm import tqdm
@ -10,62 +8,6 @@ from tqdm import tqdm
from service import models
def raschet_real(df, koefs):
koef_summ = sum(koefs.values())
df['rate'] = df[list(koefs.keys())].apply(lambda x: sum([x[k] * koefs[k] for k in list(koefs.keys())]), axis=1)
df['rate'] = df['rate'] / koef_summ
return df
def raschet(tables, filters, koefs, method):
conn = sqlalchemy.create_engine(settings.DB_URL, connect_args={'options': '-csearch_path=public'})
msk_ao = filters.get('msk_ao')
msk_rayon = filters.get('msk_rayon')
rate_from = filters.get('rate_from', None)
rate_to = filters.get('rate_to', None)
category = filters.get('category', [])
categories = ','.join([f"'{c}'" for c in category])
points_df = None
nets_df = None
for table in tables:
if 'point' in table:
if msk_ao is not None:
query = f"select * from {table} where msk_ao={msk_ao} and category in ({categories});" if len(
categories) > 0 else f"select * from {table} where msk_ao={msk_ao};"
elif msk_rayon is not None:
query = f"select * from {table} where msk_rayon={msk_rayon} and category in ({categories});" if len(
categories) > 0 else f"select * from {table} where msk_rayon={msk_rayon};"
else:
query = f"select * from {table} where category in ({categories});" if len(
categories) > 0 else f"select * from {table};"
points = pd.read_sql(query, conn)
if method == 'rate':
points_df = raschet_real(points, koefs)
if rate_from is not None and rate_to is not None:
points_df = points_df[(points_df['rate'] >= rate_from) & (points_df['rate'] <= rate_to)]
else:
points_df = points[(points['model'] >= rate_from) & (points['model'] <= rate_to)]
if 'net' in table:
if msk_ao is not None:
query = f"select * from {table} where msk_ao={msk_ao};"
elif msk_rayon is not None:
query = f"select * from {table} where msk_rayon={msk_rayon};"
else:
query = f"select * from {table};"
nets = pd.read_sql(query, conn)
if method == 'rate':
nets_df = raschet_real(nets, koefs)
if rate_from is not None and rate_to is not None:
nets_df = nets_df[(nets_df['rate'] >= rate_from) & (nets_df['rate'] <= rate_to)]
else:
nets_df = nets[(nets['model'] >= rate_from) & (nets['model'] <= rate_to)]
return points_df, nets_df
# ==============
def load_data(filepath: str):
models.PlacementPoint.objects.all().delete()
df = pd.read_csv(filepath)
@ -79,8 +21,10 @@ def load_data(filepath: str):
models.PlacementPoint.objects.create(**data)
def load_ao_and_rayons(ao_filepath: str,
rayons_filepath: str):
def load_ao_and_rayons(
ao_filepath: str,
rayons_filepath: str,
):
models.AO.objects.all().delete()
models.Rayon.objects.all().delete()
gj = geojson.load(ao_filepath)
@ -98,3 +42,21 @@ def load_ao_and_rayons(ao_filepath: str,
ao = models.AO.objects.get(name=okr)
models.Rayon.objects.create(**{'name': name, 'polygon': GEOSGeometry(str(MultiPolygon(coords))), 'AO': ao})
print('AO and Rayons loaded')
def load_rivals(filepath: str):
models.Rivals.objects.all().delete()
df = pd.read_csv(filepath)
df = df.replace(np.nan, None)
df = df.replace('NaT', None)
for row in tqdm(df.to_dict('records'), desc='Loading data...'):
models.Rivals.objects.create(**row)
def load_dist(filepath: str):
models.PointDist.objects.all().delete()
df = pd.read_csv(filepath)
for row in tqdm(df.to_dict('records'), desc='Loading data...'):
row['id1'] = models.PlacementPoint.objects.get(pk=row.get('id1'))
row['id2'] = models.PlacementPoint.objects.get(pk=row.get('id2'))
models.PointDist.objects.create(**row)

@ -4,15 +4,21 @@ from io import BytesIO
import pandas as pd
from django.db.models import Q
from django.http import HttpResponse, JsonResponse
from django.http import HttpResponse
from django.http import JsonResponse
from rest_framework import status
from rest_framework import status as http_status
from rest_framework.decorators import action
from rest_framework.decorators import api_view
from rest_framework.decorators import action, api_view, 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 service import serializers, models, pagination
from service import models
from service import pagination
from service import serializers
from service import utils
from service.permissions import UserPermission
from service.utils import load_data
@ -48,7 +54,7 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
aos = self.request.GET.get('ao[]')
if location_ids:
location_ids = list(location_ids.split(','))
qs = qs.filter(location_id__in=location_ids)
qs = qs.filter(pk__in=location_ids)
if prediction_first:
prediction_first = list(prediction_first.split(','))
qs = qs.filter(prediction_first__range=prediction_first)
@ -84,11 +90,10 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
qs = qs.filter(okrug_id__in=aos)
if excluded:
excluded = list(excluded.split(','))
qs = qs.filter(~Q(location_id__in=excluded))
qs = qs.filter(~Q(pk__in=excluded))
if included:
inclded = list(included.split(','))
qs2 = models.PlacementPoint.objects.filter(
location_id__in=inclded).all()
qs2 = models.PlacementPoint.objects.filter(pk__in=inclded).all()
qs = (qs | qs2).distinct()
return qs
@ -225,7 +230,7 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
fact = request.GET.get('fact')
if not point_id or not fact or not fact.isdigit():
return Response(status=http_status.HTTP_400_BAD_REQUEST)
qs = models.PlacementPoint.objects.filter(location_id=point_id)
qs = models.PlacementPoint.objects.filter(pk_id=point_id)
if not qs:
return Response(status=http_status.HTTP_404_NOT_FOUND)
qs.update(**{'fact': fact})
@ -243,10 +248,10 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
data.to_excel(writer, sheet_name="Placement Points",
index=False)
filename = "placement_points.xlsx"
filename = 'placement_points.xlsx'
res = HttpResponse(
b.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
)
res['Content-Disposition'] = f'attachment; filename={filename}'
return res
@ -284,6 +289,22 @@ class load_ao_and_rayons(APIView):
return Response(status=http_status.HTTP_200_OK)
@api_view(['POST'])
def upload_rivals(request):
warnings.filterwarnings('ignore')
file_rivals = request.FILES['file_rivals']
utils.load_rivals(file_rivals)
return JsonResponse({'message': 'OK'})
@api_view(['POST'])
def upload_dist(request):
warnings.filterwarnings('ignore')
file_dist = request.FILES['file_dist']
utils.load_dist(file_dist)
return JsonResponse({'message': 'OK'})
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_current_user(request):

@ -71,6 +71,18 @@
<input type="file" name="file" accept=".zip">
<input type="submit" value="Отправить">
</form>
<h3>Загрузить файл конкурентов</h3>
<form method="post" action="/api/upload_rivals/" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file_rivals" accept=".csv">
<input type="submit" value="Отправить">
</form>
<h3>Загрузить файл расстояний</h3>
<form method="post" action="/api/upload_dist/" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file_dist" accept=".csv">
<input type="submit" value="Отправить">
</form>
</div>
<style>

Loading…
Cancel
Save