From c1f498e3ecc57aa93c4c4b1f3c1eb5d0fec74d83 Mon Sep 17 00:00:00 2001 From: Aleksandr Popov Date: Tue, 1 Aug 2023 13:29:55 +0000 Subject: [PATCH] layers --- service/admin.py | 20 ++- service/migrations/0023_auto_20230720_2042.py | 63 +++++++++ service/migrations/0024_auto_20230721_1933.py | 82 ++++++++++++ .../0025_rename_param1_otherobjects_param.py | 18 +++ service/migrations/0026_auto_20230721_2347.py | 28 ++++ service/migrations/0027_auto_20230722_2310.py | 87 +++++++++++++ .../0028_placementpointpvzdistance.py | 23 ++++ ...ntpointpvzdistance_pvz_postamates_group.py | 18 +++ service/migrations/0030_taskstatus.py | 21 +++ service/migrations/0031_auto_20230729_1705.py | 27 ++++ service/migrations/0032_auto_20230731_1745.py | 58 +++++++++ service/models.py | 123 +++++++++++++++++- service/tasks.py | 31 ++++- service/urls.py | 3 +- service/utils.py | 39 +++++- service/views.py | 27 +++- templates/admin/index.html | 12 +- 17 files changed, 657 insertions(+), 23 deletions(-) create mode 100644 service/migrations/0023_auto_20230720_2042.py create mode 100644 service/migrations/0024_auto_20230721_1933.py create mode 100644 service/migrations/0025_rename_param1_otherobjects_param.py create mode 100644 service/migrations/0026_auto_20230721_2347.py create mode 100644 service/migrations/0027_auto_20230722_2310.py create mode 100644 service/migrations/0028_placementpointpvzdistance.py create mode 100644 service/migrations/0029_rename_pvz_postomates_group_placementpointpvzdistance_pvz_postamates_group.py create mode 100644 service/migrations/0030_taskstatus.py create mode 100644 service/migrations/0031_auto_20230729_1705.py create mode 100644 service/migrations/0032_auto_20230731_1745.py diff --git a/service/admin.py b/service/admin.py index c3a0cfc..3e6118b 100644 --- a/service/admin.py +++ b/service/admin.py @@ -8,7 +8,9 @@ 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 +from service.models import Post_and_pvz, Post_and_pvzCategory, Post_and_pvzGroup, OtherObjects, OtherObjectsGroup, \ + OtherObjectsCategory +from service.models import PlacementPointPVZDistance, TaskStatus class MyAdminSite(AdminSite): @@ -23,8 +25,20 @@ my_admin_site = MyAdminSite(name='myadmin') my_admin_site.register(AO) my_admin_site.register(Rayon) -my_admin_site.register(Rivals) -my_admin_site.register(PointDist) +my_admin_site.register(Post_and_pvz) +my_admin_site.register(Post_and_pvzCategory) +my_admin_site.register(Post_and_pvzGroup) +my_admin_site.register(OtherObjects) +my_admin_site.register(OtherObjectsGroup) +my_admin_site.register(OtherObjectsCategory) +my_admin_site.register(PlacementPointPVZDistance) + + +class TaskStatusAdmin(admin.ModelAdmin): + list_display = ('task_name', 'status') + + +my_admin_site.register(TaskStatus, TaskStatusAdmin) class PlacementPointAdmin(admin.ModelAdmin): diff --git a/service/migrations/0023_auto_20230720_2042.py b/service/migrations/0023_auto_20230720_2042.py new file mode 100644 index 0000000..b268790 --- /dev/null +++ b/service/migrations/0023_auto_20230720_2042.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2 on 2023-07-20 17:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0022_lastmlcall'), + ] + + operations = [ + migrations.CreateModel( + name='RivalsCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название слоя')), + ('image', models.ImageField(blank=True, default=None, null=True, upload_to='rivals_category_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ], + options={ + 'verbose_name': 'Категории постаматов и ПВЗ', + }, + ), + migrations.RemoveField( + model_name='rivals', + name='info', + ), + migrations.RemoveField( + model_name='rivals', + name='source', + ), + migrations.RemoveField( + model_name='rivals', + name='type', + ), + migrations.AddField( + model_name='rivals', + name='visible', + field=models.BooleanField(default=True), + ), + migrations.CreateModel( + name='RivalsGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название группы')), + ('image', models.ImageField(blank=True, null=True, upload_to='rivals_group_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='rivalsgroup', to='service.rivalscategory')), + ], + ), + migrations.AddField( + model_name='rivals', + name='category', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='rivals', to='service.rivalscategory'), + ), + migrations.AddField( + model_name='rivals', + name='group', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='rivals', to='service.rivalsgroup'), + ), + ] diff --git a/service/migrations/0024_auto_20230721_1933.py b/service/migrations/0024_auto_20230721_1933.py new file mode 100644 index 0000000..5cc565f --- /dev/null +++ b/service/migrations/0024_auto_20230721_1933.py @@ -0,0 +1,82 @@ +# Generated by Django 3.2 on 2023-07-21 16:33 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0023_auto_20230720_2042'), + ] + + operations = [ + migrations.CreateModel( + name='OtherObjectsCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название слоя')), + ('image', models.ImageField(blank=True, default=None, null=True, upload_to='other_objects_category_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ], + options={ + 'verbose_name_plural': 'Категории прочих объектов', + }, + ), + migrations.AlterModelOptions( + name='ao', + options={'verbose_name_plural': 'АО'}, + ), + migrations.AlterModelOptions( + name='placementpoint', + options={'verbose_name_plural': 'Точки'}, + ), + migrations.AlterModelOptions( + name='pointdist', + options={'verbose_name_plural': 'Расстояния между точками'}, + ), + migrations.AlterModelOptions( + name='rayon', + options={'verbose_name_plural': 'Районы'}, + ), + migrations.AlterModelOptions( + name='rivals', + options={'verbose_name_plural': 'Постаматы и ПВЗ'}, + ), + migrations.AlterModelOptions( + name='rivalscategory', + options={'verbose_name_plural': 'Категории постаматов и ПВЗ'}, + ), + migrations.AlterModelOptions( + name='rivalsgroup', + options={'verbose_name_plural': 'Группы постаматов и ПВЗ'}, + ), + migrations.CreateModel( + name='OtherObjectsGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название группы')), + ('image', models.ImageField(blank=True, null=True, upload_to='other_objects_group_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='otherobjectsgroup', to='service.otherobjectscategory')), + ], + options={ + 'verbose_name_plural': 'Группы прочих объектов', + }, + ), + migrations.CreateModel( + name='OtherObjects', + 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)), + ('param1', models.FloatField(blank=True, null=True)), + ('visible', models.BooleanField(default=True)), + ('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='other_objects', to='service.otherobjectscategory')), + ('group', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='other_objects', to='service.otherobjectsgroup')), + ], + options={ + 'verbose_name_plural': 'Прочие объекты', + }, + ), + ] diff --git a/service/migrations/0025_rename_param1_otherobjects_param.py b/service/migrations/0025_rename_param1_otherobjects_param.py new file mode 100644 index 0000000..7c6de9b --- /dev/null +++ b/service/migrations/0025_rename_param1_otherobjects_param.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2023-07-21 17:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0024_auto_20230721_1933'), + ] + + operations = [ + migrations.RenameField( + model_name='otherobjects', + old_name='param1', + new_name='param', + ), + ] diff --git a/service/migrations/0026_auto_20230721_2347.py b/service/migrations/0026_auto_20230721_2347.py new file mode 100644 index 0000000..4026219 --- /dev/null +++ b/service/migrations/0026_auto_20230721_2347.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2 on 2023-07-21 20:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0025_rename_param1_otherobjects_param'), + ] + + operations = [ + migrations.AddField( + model_name='rivals', + name='inlude_in_ml', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='rivalscategory', + name='inlude_in_ml', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='rivalsgroup', + name='inlude_in_ml', + field=models.BooleanField(default=True), + ), + ] diff --git a/service/migrations/0027_auto_20230722_2310.py b/service/migrations/0027_auto_20230722_2310.py new file mode 100644 index 0000000..919369e --- /dev/null +++ b/service/migrations/0027_auto_20230722_2310.py @@ -0,0 +1,87 @@ +# Generated by Django 3.2 on 2023-07-22 20:10 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0026_auto_20230721_2347'), + ] + + operations = [ + migrations.CreateModel( + name='Post_and_pvz', + 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)), + ('visible', models.BooleanField(default=True)), + ('inlude_in_ml', models.BooleanField(default=True)), + ], + options={ + 'verbose_name_plural': 'Постаматы и ПВЗ', + }, + ), + migrations.CreateModel( + name='Post_and_pvzCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название слоя')), + ('image', models.ImageField(blank=True, default=None, null=True, upload_to='post_and_pvz_category_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ('inlude_in_ml', models.BooleanField(default=True)), + ], + options={ + 'verbose_name_plural': 'Категории постаматов и ПВЗ', + }, + ), + migrations.CreateModel( + name='Post_and_pvzGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Название группы')), + ('image', models.ImageField(blank=True, null=True, upload_to='post_and_pvz_group_images/', verbose_name='Картинка')), + ('visible', models.BooleanField(default=True)), + ('inlude_in_ml', models.BooleanField(default=True)), + ('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='post_and_pvz_group', to='service.post_and_pvzcategory')), + ], + options={ + 'verbose_name_plural': 'Группы постаматов и ПВЗ', + }, + ), + migrations.RemoveField( + model_name='rivalsgroup', + name='category', + ), + migrations.RenameField( + model_name='otherobjects', + old_name='param', + new_name='param1', + ), + migrations.AddField( + model_name='otherobjects', + name='param2', + field=models.TextField(blank=True, null=True), + ), + migrations.DeleteModel( + name='Rivals', + ), + migrations.DeleteModel( + name='RivalsCategory', + ), + migrations.DeleteModel( + name='RivalsGroup', + ), + migrations.AddField( + model_name='post_and_pvz', + name='category', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='post_and_pvz', to='service.post_and_pvzcategory'), + ), + migrations.AddField( + model_name='post_and_pvz', + name='group', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='post_and_pvz', to='service.post_and_pvzgroup'), + ), + ] diff --git a/service/migrations/0028_placementpointpvzdistance.py b/service/migrations/0028_placementpointpvzdistance.py new file mode 100644 index 0000000..24faae3 --- /dev/null +++ b/service/migrations/0028_placementpointpvzdistance.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2 on 2023-07-29 11:09 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0027_auto_20230722_2310'), + ] + + operations = [ + migrations.CreateModel( + name='PlacementPointPVZDistance', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('dist', models.FloatField(blank=True, default=None, null=True)), + ('placement_point', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='service.placementpoint')), + ('pvz_postomates_group', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='service.post_and_pvzgroup')), + ], + ), + ] diff --git a/service/migrations/0029_rename_pvz_postomates_group_placementpointpvzdistance_pvz_postamates_group.py b/service/migrations/0029_rename_pvz_postomates_group_placementpointpvzdistance_pvz_postamates_group.py new file mode 100644 index 0000000..7e510d3 --- /dev/null +++ b/service/migrations/0029_rename_pvz_postomates_group_placementpointpvzdistance_pvz_postamates_group.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2023-07-29 12:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0028_placementpointpvzdistance'), + ] + + operations = [ + migrations.RenameField( + model_name='placementpointpvzdistance', + old_name='pvz_postomates_group', + new_name='pvz_postamates_group', + ), + ] diff --git a/service/migrations/0030_taskstatus.py b/service/migrations/0030_taskstatus.py new file mode 100644 index 0000000..bdc1b28 --- /dev/null +++ b/service/migrations/0030_taskstatus.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2 on 2023-07-29 13:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0029_rename_pvz_postomates_group_placementpointpvzdistance_pvz_postamates_group'), + ] + + operations = [ + migrations.CreateModel( + name='TaskStatus', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('task_name', models.TextField(unique=True)), + ('status', models.IntegerField(blank=True, null=True)), + ], + ), + ] diff --git a/service/migrations/0031_auto_20230729_1705.py b/service/migrations/0031_auto_20230729_1705.py new file mode 100644 index 0000000..e93a793 --- /dev/null +++ b/service/migrations/0031_auto_20230729_1705.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2 on 2023-07-29 14:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0030_taskstatus'), + ] + + operations = [ + migrations.AlterModelOptions( + name='taskstatus', + options={'verbose_name_plural': 'Статус фоновых задач'}, + ), + migrations.AlterField( + model_name='taskstatus', + name='status', + field=models.IntegerField(blank=True, null=True, verbose_name='Процент выполнения'), + ), + migrations.AlterField( + model_name='taskstatus', + name='task_name', + field=models.TextField(unique=True, verbose_name='Название задачи'), + ), + ] diff --git a/service/migrations/0032_auto_20230731_1745.py b/service/migrations/0032_auto_20230731_1745.py new file mode 100644 index 0000000..d97e88a --- /dev/null +++ b/service/migrations/0032_auto_20230731_1745.py @@ -0,0 +1,58 @@ +# Generated by Django 3.2 on 2023-07-31 14:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('service', '0031_auto_20230729_1705'), + ] + + operations = [ + migrations.AlterModelOptions( + name='ao', + options={'verbose_name': 'АО', 'verbose_name_plural': 'АО'}, + ), + migrations.AlterModelOptions( + name='otherobjects', + options={'verbose_name': 'Прочий объект', 'verbose_name_plural': 'Прочие объекты'}, + ), + migrations.AlterModelOptions( + name='otherobjectscategory', + options={'verbose_name': 'категория прочих объектов', 'verbose_name_plural': 'Категории прочих объектов'}, + ), + migrations.AlterModelOptions( + name='otherobjectsgroup', + options={'verbose_name': 'группа прочих объектов', 'verbose_name_plural': 'Группы прочих объектов'}, + ), + migrations.AlterModelOptions( + name='placementpoint', + options={'verbose_name': 'Точка', 'verbose_name_plural': 'Точки'}, + ), + migrations.AlterModelOptions( + name='post_and_pvz', + options={'verbose_name': 'Постамат или ПВЗ', 'verbose_name_plural': 'Постаматы и ПВЗ'}, + ), + migrations.AlterModelOptions( + name='post_and_pvzcategory', + options={'verbose_name': 'категория постаматов и ПВЗ', 'verbose_name_plural': 'Категории постаматов и ПВЗ'}, + ), + migrations.AlterModelOptions( + name='post_and_pvzgroup', + options={'verbose_name': 'группа постаматов и ПВЗ', 'verbose_name_plural': 'Группы постаматов и ПВЗ'}, + ), + migrations.AlterModelOptions( + name='rayon', + options={'verbose_name': 'Район', 'verbose_name_plural': 'Районы'}, + ), + migrations.AlterModelOptions( + name='taskstatus', + options={'verbose_name': 'Статус фоновых задач', 'verbose_name_plural': 'Статус фоновых задач'}, + ), + migrations.AlterField( + model_name='taskstatus', + name='status', + field=models.TextField(blank=True, null=True, verbose_name='Статус выполнения'), + ), + ] diff --git a/service/models.py b/service/models.py index c9d2ded..1a26403 100644 --- a/service/models.py +++ b/service/models.py @@ -10,6 +10,10 @@ User._meta.get_field('email')._unique = True class PlacementPoint(models.Model): + class Meta: + verbose_name = 'Точка' + verbose_name_plural = 'Точки' + STATUS_CHOICES = [(tag.name, tag.value) for tag in PointStatus] address = models.TextField(null=True, blank=True, verbose_name='Адрес') name = models.TextField(null=True, blank=True, verbose_name='Название') @@ -83,28 +87,137 @@ class PlacementPoint(models.Model): class AO(models.Model): + class Meta: + verbose_name = 'АО' + verbose_name_plural = 'АО' + name = models.TextField(null=True, blank=True, verbose_name='Округ') polygon = gis_models.MultiPolygonField(null=True, srid=SRID) class Rayon(models.Model): + class Meta: + verbose_name = 'Район' + verbose_name_plural = 'Районы' + 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=SRID) -class Rivals(models.Model): +class Post_and_pvz(models.Model): + class Meta: + verbose_name = 'Постамат или ПВЗ' + verbose_name_plural = 'Постаматы и ПВЗ' + + wkt = gis_models.PointField(srid=SRID, null=True) + category = models.ForeignKey('Post_and_pvzCategory', default=None, related_name='post_and_pvz', + on_delete=models.CASCADE) + group = models.ForeignKey('Post_and_pvzGroup', default=None, related_name='post_and_pvz', on_delete=models.CASCADE) + visible = models.BooleanField(default=True) + inlude_in_ml = models.BooleanField(default=True) + + +class OtherObjects(models.Model): + class Meta: + verbose_name = 'Прочий объект' + verbose_name_plural = 'Прочие объекты' + wkt = gis_models.PointField(srid=SRID, null=True) - info = models.TextField(null=True, blank=True) - type = models.TextField(null=True, blank=True) - source = models.TextField(null=True, blank=True) + category = models.ForeignKey('OtherObjectsCategory', default=None, related_name='other_objects', + on_delete=models.CASCADE) + group = models.ForeignKey('OtherObjectsGroup', default=None, related_name='other_objects', on_delete=models.CASCADE) + param1 = models.FloatField(blank=True, null=True) + param2 = models.TextField(blank=True, null=True) + visible = models.BooleanField(default=True) class PointDist(models.Model): + class Meta: + verbose_name_plural = 'Расстояния между точками' + 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) +class Post_and_pvzCategory(models.Model): + class Meta: + verbose_name = 'категория постаматов и ПВЗ' + verbose_name_plural = 'Категории постаматов и ПВЗ' + + def __str__(self): + return self.name + + name = models.TextField(null=False, blank=False, verbose_name='Название слоя') + image = models.ImageField(blank=True, null=True, default=None, upload_to='post_and_pvz_category_images/', + verbose_name='Картинка') + visible = models.BooleanField(default=True) + inlude_in_ml = models.BooleanField(default=True) + + +class Post_and_pvzGroup(models.Model): + class Meta: + verbose_name = 'группа постаматов и ПВЗ' + verbose_name_plural = 'Группы постаматов и ПВЗ' + + def __str__(self): + return self.category.name + ' ' + self.name + + name = models.TextField(null=False, blank=False, verbose_name='Название группы') + category = models.ForeignKey('Post_and_pvzCategory', default=None, related_name='post_and_pvz_group', + on_delete=models.CASCADE) + image = models.ImageField(blank=True, null=True, upload_to='post_and_pvz_group_images/', verbose_name='Картинка') + visible = models.BooleanField(default=True) + inlude_in_ml = models.BooleanField(default=True) + + +class OtherObjectsCategory(models.Model): + class Meta: + verbose_name = 'категория прочих объектов' + verbose_name_plural = 'Категории прочих объектов' + + def __str__(self): + return self.name + + name = models.TextField(null=False, blank=False, verbose_name='Название слоя') + image = models.ImageField(blank=True, null=True, default=None, upload_to='other_objects_category_images/', + verbose_name='Картинка') + visible = models.BooleanField(default=True) + + +class OtherObjectsGroup(models.Model): + class Meta: + verbose_name = 'группа прочих объектов' + verbose_name_plural = 'Группы прочих объектов' + + def __str__(self): + return self.category.name + ' ' + self.name + + name = models.TextField(null=False, blank=False, verbose_name='Название группы') + category = models.ForeignKey('OtherObjectsCategory', default=None, related_name='otherobjectsgroup', + on_delete=models.CASCADE) + image = models.ImageField(blank=True, null=True, upload_to='other_objects_group_images/', verbose_name='Картинка') + visible = models.BooleanField(default=True) + + +class PlacementPointPVZDistance(models.Model): + placement_point = models.ForeignKey('PlacementPoint', default=None, on_delete=models.CASCADE) + pvz_postamates_group = models.ForeignKey('Post_and_pvzGroup', default=None, on_delete=models.CASCADE) + dist = models.FloatField(blank=True, null=True, default=None) + + +class TaskStatus(models.Model): + class Meta: + verbose_name = 'Статус фоновых задач' + verbose_name_plural = 'Статус фоновых задач' + + def __str__(self): + return self.task_name + + task_name = models.TextField(blank=False, unique=True, verbose_name='Название задачи') + status = models.TextField(blank=True, null=True, verbose_name='Статус выполнения') + + class LastMLCall(models.Model): - dt = models.DateTimeField(auto_now_add=True) \ No newline at end of file + dt = models.DateTimeField(auto_now_add=True) diff --git a/service/tasks.py b/service/tasks.py index afdf778..0d7bd22 100644 --- a/service/tasks.py +++ b/service/tasks.py @@ -14,12 +14,13 @@ from shapely import wkb from sklearn import metrics from sklearn import model_selection as ms from sqlalchemy import text - +from django.contrib.gis.db.models.functions import Distance import requests - +from io import StringIO from postamates.settings import AGE_DAY_LIMIT from postamates.settings import DB_URL from service.models import PlacementPoint, LastMLCall +from service import models def log_to_telegram(msg): @@ -312,6 +313,32 @@ def raschet(): log_to_telegram('end raschet') +@shared_task +def calculate_group_distance(groups: list): + status, _ = models.TaskStatus.objects.get_or_create(task_name='Расчет ближайшего расстояния') + points = models.PlacementPoint.objects.all() + num_points = points.count() + total = len(groups) * num_points + for _i, gr in enumerate(groups): + group = models.Post_and_pvzGroup.objects.get(name=gr['group'], category__name=gr['category']) + for _j, point in enumerate(points): + status.status = "Подсчет расстояний: " + str(int((num_points * _i + _j) / total * 100)) + "%" + status.save() + post_object = models.Post_and_pvz.objects.filter(group__name=group.name).annotate( + distance=Distance("wkt", point.geometry)).order_by('distance').first() + d = models.PlacementPointPVZDistance.objects.filter(placement_point=point, + pvz_postamates_group=group).first() + if d: + if d.dist > post_object.distance.m: + d.dist = post_object.distance.m + d.save() + else: + models.PlacementPointPVZDistance.objects.create(placement_point=point, pvz_postamates_group=group, + dist=post_object.distance.m) + status.status = "Подсчет расстояний завершен" + status.save() + + @shared_task() def add_age_day(): qs = PlacementPoint.objects diff --git a/service/urls.py b/service/urls.py index 4736770..d0873c1 100644 --- a/service/urls.py +++ b/service/urls.py @@ -32,7 +32,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_post_and_pvz/', views.upload_post_and_pvz, name='upload_post_and_pvz'), + url(r'upload_other_objects/', views.upload_other_objects, name='upload_other_objects'), url(r'upload_dist/', views.upload_dist, name='upload_dist'), url(r'me/', views.get_current_user, name='me'), re_path(r'^swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), diff --git a/service/utils.py b/service/utils.py index 2baaa24..246c6c4 100644 --- a/service/utils.py +++ b/service/utils.py @@ -4,6 +4,7 @@ import pandas as pd from django.contrib.gis.geos import GEOSGeometry from geojson import MultiPolygon from tqdm import tqdm +from service.tasks import calculate_group_distance from service import models @@ -44,13 +45,45 @@ def load_ao_and_rayons( def load_rivals(filepath: str): - models.Rivals.objects.all().delete() - df = pd.read_csv(filepath) + df = pd.read_excel(filepath) + df = df.replace(np.nan, None) + df = df.replace('NaT', None) + df.columns = df.columns.str.lower() + for row in tqdm(df.to_dict('records'), desc='Loading data...'): + category = row.get('category') + group = row.get('group') + if category: + cat, _ = models.Post_and_pvzCategory.objects.get_or_create(name=category) + if group: + gr, _ = models.Post_and_pvzGroup.objects.get_or_create(name=group, category=cat) + row['category'] = cat + row['group'] = gr + lon = str(row.pop('lon')) + lat = str(row.pop("lat")) + row['wkt'] = "POINT(" + lon + " " + lat + ")" + models.Post_and_pvz.objects.get_or_create(**row) + new_groups = df[['group', 'category']].drop_duplicates().to_dict(orient='records') + calculate_group_distance.delay(new_groups) + + +def load_other_objects(filepath: str): + df = pd.read_excel(filepath) df = df.replace(np.nan, None) df = df.replace('NaT', None) df.columns = df.columns.str.lower() for row in tqdm(df.to_dict('records'), desc='Loading data...'): - models.Rivals.objects.create(**row) + category = row.get('category') + group = row.get('group') + if category: + cat, _ = models.OtherObjectsCategory.objects.get_or_create(name=category) + if group: + gr, _ = models.OtherObjectsGroup.objects.get_or_create(name=group, category=cat) + row['category'] = cat + row['group'] = gr + lon = str(row.pop('lon')) + lat = str(row.pop("lat")) + row['wkt'] = "POINT(" + lon + " " + lat + ")" + models.OtherObjects.objects.get_or_create(**row) def load_dist(filepath: str): diff --git a/service/views.py b/service/views.py index 6a591eb..4c8ecf1 100644 --- a/service/views.py +++ b/service/views.py @@ -25,6 +25,8 @@ from service.service import PointService from service.tasks import raschet from service.utils import load_data from rest_framework.permissions import AllowAny +from django.shortcuts import redirect +from django.contrib import messages class AOViewSet(ReadOnlyModelViewSet): @@ -231,7 +233,8 @@ class refresh_placement_points(APIView): warnings.filterwarnings('ignore') file = request.FILES['file'] load_data(file) - return Response(status=HTTPStatus.OK) + messages.success(request, 'Файл точек успешно загружен') + return redirect('/admin') class load_ao_and_rayons(APIView): @@ -241,15 +244,26 @@ class load_ao_and_rayons(APIView): file_ao = request.FILES['file_ao'] file_rayon = request.FILES['file_rayon'] utils.load_ao_and_rayons(file_ao, file_rayon) - return Response(status=HTTPStatus.OK) + messages.success(request, 'Файл АО и Районов успешно загружен') + return redirect('/admin') @api_view(['POST']) -def upload_rivals(request): +def upload_post_and_pvz(request): warnings.filterwarnings('ignore') - file_rivals = request.FILES['file_rivals'] + file_rivals = request.FILES['file_post_and_pvz'] utils.load_rivals(file_rivals) - return JsonResponse({'message': 'OK'}) + messages.success(request, 'Файл ПВЗ и Постаматов успешно загружен') + return redirect('/admin') + + +@api_view(['POST']) +def upload_other_objects(request): + warnings.filterwarnings('ignore') + file_rivals = request.FILES['file_other_objects'] + utils.load_other_objects(file_rivals) + messages.success(request, 'Файл прочих объектов успешно загружен') + return redirect('/admin') @api_view(['POST']) @@ -257,7 +271,8 @@ def upload_dist(request): warnings.filterwarnings('ignore') file_dist = request.FILES['file_dist'] utils.load_dist(file_dist) - return JsonResponse({'message': 'OK'}) + messages.success(request, 'Файл расстояний успешно загружен') + return redirect('/admin') @api_view(['GET']) diff --git a/templates/admin/index.html b/templates/admin/index.html index 49190a9..ecae4a9 100644 --- a/templates/admin/index.html +++ b/templates/admin/index.html @@ -71,10 +71,16 @@ -

Загрузить файл конкурентов

-
+

Загрузить файл ПВЗ и Постаматов

+ {% csrf_token %} - + + +
+

Загрузить файл прочих объектов

+
+ {% csrf_token %} +

Загрузить файл расстояний