86_User_suggestions_and_feedback #111
@ -133,7 +133,9 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.parcels.values_list(
|
parcels = value.parcels.filter(
|
||||||
|
parcelintersection__calculated_on__isnull=False,
|
||||||
|
).values_list(
|
||||||
"gmrkng",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
@ -294,7 +296,9 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.parcels.values_list(
|
parcels = value.parcels.filter(
|
||||||
|
parcelintersection__calculated_on__isnull=False,
|
||||||
|
).values_list(
|
||||||
"gmrkng",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
|
@ -103,7 +103,9 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.parcels.values_list(
|
parcels = value.parcels.filter(
|
||||||
|
parcelintersection__calculated_on__isnull=False,
|
||||||
|
).values_list(
|
||||||
"gmrkng",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
|
@ -130,7 +130,9 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.parcels.values_list(
|
parcels = value.parcels.filter(
|
||||||
|
parcelintersection__calculated_on__isnull=False,
|
||||||
|
).values_list(
|
||||||
"gmrkng",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
|
54
konova/migrations/0003_auto_20220208_1801.py
Normal file
54
konova/migrations/0003_auto_20220208_1801.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-02-08 17:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_parcels(apps, schema_editor):
|
||||||
|
Geometry = apps.get_model('konova', 'Geometry')
|
||||||
|
SpatialIntersection = apps.get_model('konova', 'SpatialIntersection')
|
||||||
|
|
||||||
|
all_geoms = Geometry.objects.all()
|
||||||
|
for geom in all_geoms:
|
||||||
|
SpatialIntersection.objects.bulk_create([
|
||||||
|
SpatialIntersection(geometry=geom, parcel=parcel)
|
||||||
|
for parcel in geom.parcels.all()
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0002_auto_20220114_0936'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SpatialIntersection',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('calculated_on', models.DateTimeField(auto_now_add=True, null=True)),
|
||||||
|
('geometry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='konova.geometry')),
|
||||||
|
('parcel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='konova.parcel')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_parcels),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='geometries_tmp',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='parcels', through='konova.SpatialIntersection', to='konova.Geometry'),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='geometries',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='parcel',
|
||||||
|
old_name='geometries_tmp',
|
||||||
|
new_name='geometries',
|
||||||
|
),
|
||||||
|
]
|
17
konova/migrations/0004_auto_20220209_0839.py
Normal file
17
konova/migrations/0004_auto_20220209_0839.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-02-09 07:39
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0003_auto_20220208_1801'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='SpatialIntersection',
|
||||||
|
new_name='ParcelIntersection',
|
||||||
|
),
|
||||||
|
]
|
@ -99,7 +99,7 @@ class Geometry(BaseResource):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.models import Parcel, District
|
from konova.models import Parcel, District, ParcelIntersection
|
||||||
parcel_fetcher = ParcelWFSFetcher(
|
parcel_fetcher = ParcelWFSFetcher(
|
||||||
geometry_id=self.id,
|
geometry_id=self.id,
|
||||||
)
|
)
|
||||||
@ -107,6 +107,7 @@ class Geometry(BaseResource):
|
|||||||
fetched_parcels = parcel_fetcher.get_features(
|
fetched_parcels = parcel_fetcher.get_features(
|
||||||
typename
|
typename
|
||||||
)
|
)
|
||||||
|
_now = timezone.now()
|
||||||
underlying_parcels = []
|
underlying_parcels = []
|
||||||
for result in fetched_parcels:
|
for result in fetched_parcels:
|
||||||
fetched_parcel = result[typename]
|
fetched_parcel = result[typename]
|
||||||
@ -125,19 +126,35 @@ class Geometry(BaseResource):
|
|||||||
krs=fetched_parcel["ave:kreis"],
|
krs=fetched_parcel["ave:kreis"],
|
||||||
)[0]
|
)[0]
|
||||||
parcel_obj.district = district
|
parcel_obj.district = district
|
||||||
parcel_obj.updated_on = timezone.now()
|
parcel_obj.updated_on = _now
|
||||||
parcel_obj.save()
|
parcel_obj.save()
|
||||||
underlying_parcels.append(parcel_obj)
|
underlying_parcels.append(parcel_obj)
|
||||||
|
|
||||||
|
# Update the linked parcels
|
||||||
self.parcels.set(underlying_parcels)
|
self.parcels.set(underlying_parcels)
|
||||||
|
|
||||||
|
# Set the calculated_on intermediate field, so this related data will be found on lookups
|
||||||
|
intersections_without_ts = self.parcelintersection_set.filter(
|
||||||
|
parcel__in=self.parcels.all(),
|
||||||
|
calculated_on__isnull=True,
|
||||||
|
)
|
||||||
|
for entry in intersections_without_ts:
|
||||||
|
entry.calculated_on = _now
|
||||||
|
ParcelIntersection.objects.bulk_update(
|
||||||
|
intersections_without_ts,
|
||||||
|
["calculated_on"]
|
||||||
|
)
|
||||||
|
|
||||||
def get_underlying_parcels(self):
|
def get_underlying_parcels(self):
|
||||||
""" Getter for related parcels and their districts
|
""" Getter for related parcels and their districts
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
parcels (QuerySet): The related parcels as queryset
|
parcels (QuerySet): The related parcels as queryset
|
||||||
"""
|
"""
|
||||||
parcels = self.parcels.all().prefetch_related(
|
|
||||||
|
parcels = self.parcels.filter(
|
||||||
|
parcelintersection__calculated_on__isnull=False,
|
||||||
|
).prefetch_related(
|
||||||
"district"
|
"district"
|
||||||
).order_by(
|
).order_by(
|
||||||
"gmrkng",
|
"gmrkng",
|
||||||
|
@ -22,7 +22,7 @@ class Parcel(UuidModel):
|
|||||||
To avoid conflicts due to german Umlaute, the field names are shortened and vocals are dropped.
|
To avoid conflicts due to german Umlaute, the field names are shortened and vocals are dropped.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
geometries = models.ManyToManyField("konova.Geometry", related_name="parcels", blank=True)
|
geometries = models.ManyToManyField("konova.Geometry", blank=True, related_name="parcels", through='ParcelIntersection')
|
||||||
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
||||||
gmrkng = models.CharField(
|
gmrkng = models.CharField(
|
||||||
max_length=1000,
|
max_length=1000,
|
||||||
@ -77,3 +77,22 @@ class District(UuidModel):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.gmnd} | {self.krs}"
|
return f"{self.gmnd} | {self.krs}"
|
||||||
|
|
||||||
|
|
||||||
|
class ParcelIntersection(UuidModel):
|
||||||
|
""" ParcelIntersection is an intermediary model, which is used to configure the
|
||||||
|
M2M relation between Parcel and Geometry.
|
||||||
|
|
||||||
|
Based on uuids, we will not have (practically) any problems on outrunning primary keys
|
||||||
|
and extending the model with calculated_on timestamp, we can 'hide' entries while they
|
||||||
|
are being recalculated and keep track on the last time they have been calculated this
|
||||||
|
way.
|
||||||
|
|
||||||
|
Please note: The calculated_on describes when the relation between the Parcel and the Geometry
|
||||||
|
has been established. The updated_on field of Parcel describes when this Parcel has been
|
||||||
|
changed the last time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
parcel = models.ForeignKey(Parcel, on_delete=models.CASCADE)
|
||||||
|
geometry = models.ForeignKey("konova.Geometry", on_delete=models.CASCADE)
|
||||||
|
calculated_on = models.DateTimeField(auto_now_add=True, null=True, blank=True)
|
||||||
|
@ -4,13 +4,19 @@ from celery import shared_task
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def celery_update_parcels(geometry_id: str, recheck: bool = True):
|
def celery_update_parcels(geometry_id: str, recheck: bool = True):
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry, ParcelIntersection
|
||||||
try:
|
try:
|
||||||
geom = Geometry.objects.get(id=geometry_id)
|
geom = Geometry.objects.get(id=geometry_id)
|
||||||
geom.parcels.clear()
|
objs = geom.parcelintersection_set.all()
|
||||||
|
for obj in objs:
|
||||||
|
obj.calculated_on = None
|
||||||
|
ParcelIntersection.objects.bulk_update(
|
||||||
|
objs,
|
||||||
|
["calculated_on"]
|
||||||
|
)
|
||||||
|
|
||||||
geom.update_parcels()
|
geom.update_parcels()
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
if recheck:
|
if recheck:
|
||||||
|
Loading…
Reference in New Issue
Block a user