209 lines
5.9 KiB
Python
209 lines
5.9 KiB
Python
"""
|
|
Author: Michel Peltriaux
|
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
Created on: 16.12.21
|
|
|
|
"""
|
|
from django.db import models, transaction
|
|
|
|
from konova.models import UuidModel
|
|
|
|
|
|
class AdministrativeSpatialReference(models.Model):
|
|
key = models.CharField(
|
|
max_length=255,
|
|
help_text="Represents Kreisschlüssel",
|
|
null=True,
|
|
blank=True
|
|
)
|
|
name = models.CharField(
|
|
max_length=1000,
|
|
help_text="Kreis",
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.key})"
|
|
|
|
@property
|
|
def table_str(self):
|
|
return f"{self.name} ({self.key})"
|
|
|
|
|
|
class District(UuidModel, AdministrativeSpatialReference):
|
|
""" The model District refers to "Kreis"
|
|
|
|
"""
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=[
|
|
"key",
|
|
"name",
|
|
],
|
|
name="Unique district constraint"
|
|
)
|
|
]
|
|
|
|
|
|
class Municipal(UuidModel, AdministrativeSpatialReference):
|
|
""" The model Municipal refers to "Gemeinde"
|
|
|
|
"""
|
|
district = models.ForeignKey(
|
|
District,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=[
|
|
"key",
|
|
"name",
|
|
"district",
|
|
],
|
|
name="Unique municipal constraint"
|
|
)
|
|
]
|
|
|
|
|
|
class ParcelGroup(UuidModel, AdministrativeSpatialReference):
|
|
""" The model ParcelGroup refers to "Gemarkung", which is defined as a loose group of parcels
|
|
|
|
"""
|
|
municipal = models.ForeignKey(
|
|
Municipal,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=[
|
|
"key",
|
|
"name",
|
|
"municipal",
|
|
],
|
|
name="Unique parcel group constraint"
|
|
)
|
|
]
|
|
|
|
|
|
class Parcel(UuidModel):
|
|
""" The Parcel model holds administrative data on covered properties.
|
|
|
|
Due to the unique but relevant naming of the administrative data, we have to use these namings as field
|
|
names in german. Any try to translate them to English result in strange or insufficient translations.
|
|
|
|
All fields have to be CharFields as well, since there are e.g. Flurstücksnummer holding e.g. '123____' which
|
|
can not be realized using numerical fields.
|
|
|
|
To avoid conflicts due to german Umlaute, the field names are shortened and vocals are dropped.
|
|
|
|
"""
|
|
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")
|
|
municipal = models.ForeignKey("konova.Municipal", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
|
parcel_group = models.ForeignKey(
|
|
"konova.ParcelGroup",
|
|
on_delete=models.SET_NULL,
|
|
help_text="Gemarkung",
|
|
null=True,
|
|
blank=True,
|
|
related_name="parcels"
|
|
)
|
|
flr = models.IntegerField(
|
|
help_text="Flur",
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
flrstck_nnr = models.IntegerField(
|
|
help_text="Flurstücksnenner",
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
flrstck_zhlr = models.IntegerField(
|
|
help_text="Flurstückszähler",
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
updated_on = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=[
|
|
"district",
|
|
"municipal",
|
|
"parcel_group",
|
|
"flr",
|
|
"flrstck_nnr",
|
|
"flrstck_zhlr",
|
|
],
|
|
name="Unique parcel constraint"
|
|
)
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
|
|
|
@classmethod
|
|
def make_unique(cls, **kwargs):
|
|
""" Checks for duplicates of a Parcel, choose a (now) unique one,
|
|
repairs relations for ParcelIntersection and removes duplicates.
|
|
|
|
Args:
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
unique_true (Parcel): The new unique 'true one'
|
|
"""
|
|
parcel_objs = Parcel.objects.filter(**kwargs)
|
|
|
|
if not parcel_objs.exists():
|
|
return None
|
|
|
|
# Get one of the found parcels and use it as new 'true one'
|
|
unique_parcel = parcel_objs.first()
|
|
# separate it from the rest
|
|
parcel_objs = parcel_objs.exclude(id=unique_parcel.id)
|
|
|
|
if not parcel_objs.exists():
|
|
# There are no duplicates - all good, just return
|
|
return unique_parcel
|
|
|
|
# Fetch existing intersections, which still point on the duplicated parcels
|
|
intersection_objs = ParcelIntersection.objects.filter(
|
|
parcel__in=parcel_objs
|
|
)
|
|
|
|
# Change each intersection, so they point on the 'true one' parcel from now on
|
|
for intersection in intersection_objs:
|
|
intersection.parcel = unique_parcel
|
|
intersection.save()
|
|
|
|
# Remove the duplicated parcels
|
|
parcel_objs.delete()
|
|
|
|
return unique_parcel
|
|
|
|
|
|
class ParcelIntersection(UuidModel):
|
|
"""
|
|
ParcelIntersection is an intermediary model, which is used to add extras to the
|
|
M2M relation between Parcel and Geometry.
|
|
"""
|
|
parcel = models.ForeignKey(Parcel, on_delete=models.CASCADE)
|
|
geometry = models.ForeignKey("konova.Geometry", on_delete=models.CASCADE)
|