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 max-line-length = 120
exclude = fixtures/* exclude = fixtures/*
max-complexity = 10 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 django.contrib import admin
from service.models import PlacementPoint, AO, Rayon 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(AO)
admin.site.register(Rayon) admin.site.register(Rayon)
admin.site.register(Rivals)
admin.site.register(PointDist)
class PlacementPointAdmin(admin.ModelAdmin): 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 # Generated by Django 3.2 on 2023-03-08 11:48
from django.db import migrations, models 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 * from service.signals import *
class PlacementPoint(models.Model): class PlacementPoint(models.Model):
location_id = models.IntegerField(null=False, blank=False,unique=True)
address = models.TextField(null=True, blank=True, verbose_name='Адрес') address = models.TextField(null=True, blank=True, verbose_name='Адрес')
name = 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='Категория') category = models.TextField(null=True, blank=True, verbose_name='Категория')
@ -32,8 +31,8 @@ class PlacementPoint(models.Model):
yndxfood_cnt = models.IntegerField(null=True, blank=True) yndxfood_cnt = models.IntegerField(null=True, blank=True)
yndxfood_sum = models.IntegerField(null=True, blank=True) yndxfood_sum = models.IntegerField(null=True, blank=True)
yndxfood_cnt_cst = 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) okrug = models.ForeignKey('AO', on_delete=models.CASCADE, null=True, blank=True)
rayon = models.ForeignKey("Rayon", 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='Координаты') geometry = gis_models.PointField(srid=4326, null=True, verbose_name='Координаты')
@ -41,7 +40,21 @@ class AO(models.Model):
name = models.TextField(null=True, blank=True, verbose_name='Округ') 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): class Rayon(models.Model):
name = models.TextField(null=True, blank=True, verbose_name='Район') name = models.TextField(null=True, blank=True, verbose_name='Район')
AO = models.ForeignKey("AO",related_name='rayons', on_delete=models.CASCADE) AO = models.ForeignKey('AO', related_name='rayons', on_delete=models.CASCADE)
polygon = gis_models.MultiPolygonField(null=True, srid=4326) 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'), 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'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_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'), 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(?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'), re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),

@ -1,8 +1,6 @@
import geojson import geojson
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import sqlalchemy
from django.conf import settings
from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.geos import GEOSGeometry
from geojson import MultiPolygon from geojson import MultiPolygon
from tqdm import tqdm from tqdm import tqdm
@ -10,62 +8,6 @@ from tqdm import tqdm
from service import models 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): def load_data(filepath: str):
models.PlacementPoint.objects.all().delete() models.PlacementPoint.objects.all().delete()
df = pd.read_csv(filepath) df = pd.read_csv(filepath)
@ -79,8 +21,10 @@ def load_data(filepath: str):
models.PlacementPoint.objects.create(**data) models.PlacementPoint.objects.create(**data)
def load_ao_and_rayons(ao_filepath: str, def load_ao_and_rayons(
rayons_filepath: str): ao_filepath: str,
rayons_filepath: str,
):
models.AO.objects.all().delete() models.AO.objects.all().delete()
models.Rayon.objects.all().delete() models.Rayon.objects.all().delete()
gj = geojson.load(ao_filepath) gj = geojson.load(ao_filepath)
@ -98,3 +42,21 @@ def load_ao_and_rayons(ao_filepath: str,
ao = models.AO.objects.get(name=okr) ao = models.AO.objects.get(name=okr)
models.Rayon.objects.create(**{'name': name, 'polygon': GEOSGeometry(str(MultiPolygon(coords))), 'AO': ao}) models.Rayon.objects.create(**{'name': name, 'polygon': GEOSGeometry(str(MultiPolygon(coords))), 'AO': ao})
print('AO and Rayons loaded') 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 import pandas as pd
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse, JsonResponse 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
from rest_framework import status as http_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.decorators import action, api_view, permission_classes
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet 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 import utils
from service.permissions import UserPermission from service.permissions import UserPermission
from service.utils import load_data from service.utils import load_data
@ -48,7 +54,7 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
aos = self.request.GET.get('ao[]') aos = self.request.GET.get('ao[]')
if location_ids: if location_ids:
location_ids = list(location_ids.split(',')) location_ids = list(location_ids.split(','))
qs = qs.filter(location_id__in=location_ids) qs = qs.filter(pk__in=location_ids)
if prediction_first: if prediction_first:
prediction_first = list(prediction_first.split(',')) prediction_first = list(prediction_first.split(','))
qs = qs.filter(prediction_first__range=prediction_first) qs = qs.filter(prediction_first__range=prediction_first)
@ -84,11 +90,10 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
qs = qs.filter(okrug_id__in=aos) qs = qs.filter(okrug_id__in=aos)
if excluded: if excluded:
excluded = list(excluded.split(',')) excluded = list(excluded.split(','))
qs = qs.filter(~Q(location_id__in=excluded)) qs = qs.filter(~Q(pk__in=excluded))
if included: if included:
inclded = list(included.split(',')) inclded = list(included.split(','))
qs2 = models.PlacementPoint.objects.filter( qs2 = models.PlacementPoint.objects.filter(pk__in=inclded).all()
location_id__in=inclded).all()
qs = (qs | qs2).distinct() qs = (qs | qs2).distinct()
return qs return qs
@ -225,7 +230,7 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
fact = request.GET.get('fact') fact = request.GET.get('fact')
if not point_id or not fact or not fact.isdigit(): if not point_id or not fact or not fact.isdigit():
return Response(status=http_status.HTTP_400_BAD_REQUEST) 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: if not qs:
return Response(status=http_status.HTTP_404_NOT_FOUND) return Response(status=http_status.HTTP_404_NOT_FOUND)
qs.update(**{'fact': fact}) qs.update(**{'fact': fact})
@ -243,10 +248,10 @@ class PlacementPointViewSet(ReadOnlyModelViewSet):
data.to_excel(writer, sheet_name="Placement Points", data.to_excel(writer, sheet_name="Placement Points",
index=False) index=False)
filename = "placement_points.xlsx" filename = 'placement_points.xlsx'
res = HttpResponse( res = HttpResponse(
b.getvalue(), 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}' res['Content-Disposition'] = f'attachment; filename={filename}'
return res return res
@ -284,6 +289,22 @@ class load_ao_and_rayons(APIView):
return Response(status=http_status.HTTP_200_OK) 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']) @api_view(['GET'])
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
def get_current_user(request): def get_current_user(request):

@ -71,6 +71,18 @@
<input type="file" name="file" accept=".zip"> <input type="file" name="file" accept=".zip">
<input type="submit" value="Отправить"> <input type="submit" value="Отправить">
</form> </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> </div>
<style> <style>

Loading…
Cancel
Save