diff --git a/README.md b/README.md index 80617b8c..c67caf2a 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Für die App *BEMAS* kann optional ein Cronjob eingerichtet werden, der folgende python manage.py deletepersons -Dieser Befehl führt dazu, dass alle Personen gelöscht werden, die nicht als Ansprechpartner:innen mit Organisationen verknüpft sind und die als Beschwerdeführer:innen nur noch mit Beschwerden verknüpft sind, die seit `BEMAS_STATUS_CHANGE_DEADLINE_DAYS` (siehe `secrets.template`) abgeschlossen sind. +Dieser Befehl führt dazu, dass alle Personen gelöscht werden, die nicht als Ansprechpartner:innen mit Organisationen verknüpft sind, nicht als Betreiber:innen mit Verursachern verknüpft sind und die als Beschwerdeführer:innen nur noch mit Beschwerden verknüpft sind, die seit `BEMAS_STATUS_CHANGE_DEADLINE_DAYS` (siehe `secrets.template`) abgeschlossen sind. ## Entwicklung diff --git a/bemas/management/commands/deletepersons.py b/bemas/management/commands/deletepersons.py index 8d42e180..7f04be2c 100644 --- a/bemas/management/commands/deletepersons.py +++ b/bemas/management/commands/deletepersons.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand -from bemas.models import Complaint, Contact, Person +from bemas.models import Complaint, Contact, Originator, Person from bemas.utils import get_orphaned_persons from bemas.views.functions import create_log_entry @@ -9,8 +9,8 @@ class Command(BaseCommand): def handle(self, *args, **options): # get persons to be deleted - # (i.e. persons not connected to contacts and active complaints) - persons_delete = get_orphaned_persons(Complaint, Contact, Person) + # (i.e. persons not connected to any contacts and any originators and any active complaints) + persons_delete = get_orphaned_persons(Complaint, Contact, Originator, Person) num_deleted = 0 for person_delete in persons_delete: object_pk, content = person_delete.pk, str(person_delete) diff --git a/bemas/migrations/0001_initial.py b/bemas/migrations/0001_initial.py index fe463634..1ed3a61c 100644 --- a/bemas/migrations/0001_initial.py +++ b/bemas/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.4 on 2023-08-18 10:37 +# Generated by Django 4.2.4 on 2023-08-31 10:24 import datenmanagement.models.fields import datetime @@ -72,7 +72,7 @@ class Migration(migrations.Migration): ('search_content', models.CharField(blank=True, editable=False, max_length=255, null=True)), ('name', models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$')], verbose_name='Name')), ('address_street', models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$')], verbose_name='Straße')), - ('address_house_number', models.CharField(blank=True, max_length=4, null=True, validators=[django.core.validators.RegexValidator(message='Die Hausnummer muss eine Zahl zwischen 1 und 999 sein, optional gefolgt von einem Kleinbuchstaben.', regex='^[1-9][0-9]{0,2}[a-z]?$')], verbose_name='Hausnummer')), + ('address_house_number', models.CharField(blank=True, max_length=4, null=True, validators=[django.core.validators.RegexValidator(message='Die Hausnummer muss eine Zahl zwischen 1 und 999 sein, optional direkt gefolgt von einem Kleinbuchstaben.', regex='^[1-9][0-9]{0,2}[a-z]?$')], verbose_name='Hausnummer')), ('address_postal_code', models.CharField(blank=True, max_length=5, null=True, validators=[django.core.validators.RegexValidator(message='Die Postleitzahl muss aus genau fünf Ziffern bestehen.', regex='^[0-9]{5}$')], verbose_name='Postleitzahl')), ('address_place', models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$')], verbose_name='Ort')), ('email_addresses', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.EmailValidator(message='E-Mail-Adressen müssen syntaktisch korrekt sein und daher folgendes Format aufweisen (Beispiele): abc@def.xyz oder 1cba_mno.asff@xy.a23c.zy')], verbose_name='E-Mail-Adresse(n)'), blank=True, null=True, size=None, verbose_name='E-Mail-Adresse(n)')), @@ -98,7 +98,7 @@ class Migration(migrations.Migration): ('first_name', models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$'), django.core.validators.RegexValidator(message='Im Text darf nach einem Bindestrich kein Leerzeichen stehen.', regex='^(?!.*- ).*$'), django.core.validators.RegexValidator(message='Im Text darf vor einem Bindestrich kein Leerzeichen stehen.', regex='^(?!.* -).*$')], verbose_name='Vorname')), ('last_name', models.CharField(max_length=255, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$'), django.core.validators.RegexValidator(message='Im Text darf nach einem Bindestrich kein Leerzeichen stehen.', regex='^(?!.*- ).*$'), django.core.validators.RegexValidator(message='Im Text darf vor einem Bindestrich kein Leerzeichen stehen.', regex='^(?!.* -).*$')], verbose_name='Nachname')), ('address_street', models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$')], verbose_name='Straße')), - ('address_house_number', models.CharField(blank=True, max_length=4, null=True, validators=[django.core.validators.RegexValidator(message='Die Hausnummer muss eine Zahl zwischen 1 und 999 sein, optional gefolgt von einem Kleinbuchstaben.', regex='^[1-9][0-9]{0,2}[a-z]?$')], verbose_name='Hausnummer')), + ('address_house_number', models.CharField(blank=True, max_length=4, null=True, validators=[django.core.validators.RegexValidator(message='Die Hausnummer muss eine Zahl zwischen 1 und 999 sein, optional direkt gefolgt von einem Kleinbuchstaben.', regex='^[1-9][0-9]{0,2}[a-z]?$')], verbose_name='Hausnummer')), ('address_postal_code', models.CharField(blank=True, max_length=5, null=True, validators=[django.core.validators.RegexValidator(message='Die Postleitzahl muss aus genau fünf Ziffern bestehen.', regex='^[0-9]{5}$')], verbose_name='Postleitzahl')), ('address_place', models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.RegexValidator(message='Texte dürfen keine Akute (´) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*´).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Schreibmaschinensatz-Anführungszeichen (") enthalten. Stattdessen müssen die typographisch korrekten Anführungszeichen („“) verwendet werden.', regex='^(?!.*\\").*$'), django.core.validators.RegexValidator(message="Texte dürfen keine einfachen Schreibmaschinensatz-Anführungszeichen (') enthalten. Stattdessen muss der typographisch korrekte Apostroph(’) verwendet werden.", regex="^(?!.*\\').*$"), django.core.validators.RegexValidator(message='Texte dürfen keine doppelten Leerzeichen und/oder Zeilenumbrüche enthalten.', regex='^(?!.* ).*$'), django.core.validators.RegexValidator(message='Texte dürfen keine Gravis (`) enthalten. Stattdessen muss der typographisch korrekte Apostroph (’) verwendet werden.', regex='^(?!.*`).*$')], verbose_name='Ort')), ('email_addresses', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=255, null=True, validators=[django.core.validators.EmailValidator(message='E-Mail-Adressen müssen syntaktisch korrekt sein und daher folgendes Format aufweisen (Beispiele): abc@def.xyz oder 1cba_mno.asff@xy.a23c.zy')], verbose_name='E-Mail-Adresse(n)'), blank=True, null=True, size=None, verbose_name='E-Mail-Adresse(n)')), @@ -197,14 +197,15 @@ class Migration(migrations.Migration): ('emission_point', django.contrib.gis.db.models.fields.PointField(srid=4326, verbose_name='Emissionsort')), ('address', models.CharField(blank=True, max_length=255, null=True, verbose_name='Adresse')), ('dms_link', models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(message='Der d.3-Vorgang muss folgendes Format aufweisen (Beispiel): 552.6#04-004/008', regex='^[0-9]{3}\\.[0-9]#[0-9]{2}-[0-9]{3}\\/[0-9]{3}$')], verbose_name=' d.3')), - ('operator', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bemas.organization', verbose_name='Betreiberin')), + ('operator_organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='bemas.organization', verbose_name='Organisation als Betreiberin')), + ('operator_person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='bemas.person', verbose_name='Person als Betreiber:in')), ('sector', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='bemas.sector', verbose_name='Branche')), ], options={ 'verbose_name': 'Verursacher', 'verbose_name_plural': 'Verursacher', 'db_table': 'originator', - 'ordering': ['sector__title', 'operator__name', 'description'], + 'ordering': ['sector__title', 'operator_organization__name', 'operator_person__last_name', 'description'], 'get_latest_by': 'updated_at', 'abstract': False, }, diff --git a/bemas/models/models_objectclass.py b/bemas/models/models_objectclass.py index 580db71c..aef562aa 100644 --- a/bemas/models/models_objectclass.py +++ b/bemas/models/models_objectclass.py @@ -375,10 +375,19 @@ class Originator(GeometryObjectclass): verbose_name='Branche', on_delete=PROTECT ) - operator = ForeignKey( + operator_organization = ForeignKey( Organization, - verbose_name='Betreiberin', - on_delete=PROTECT + verbose_name='Organisation als Betreiberin', + on_delete=PROTECT, + blank=True, + null=True + ) + operator_person = ForeignKey( + Person, + verbose_name='Person als Betreiber:in', + on_delete=PROTECT, + blank=True, + null=True ) description = TextField( 'Beschreibung', @@ -408,7 +417,12 @@ class Originator(GeometryObjectclass): class Meta(GeometryObjectclass.Meta): db_table = 'originator' - ordering = ['sector__title', 'operator__name', 'description'] + ordering = [ + 'sector__title', + 'operator_organization__name', + 'operator_person__last_name', + 'description' + ] verbose_name = 'Verursacher' verbose_name_plural = 'Verursacher' @@ -421,11 +435,20 @@ class BasemodelMeta(GeometryObjectclass.BasemodelMeta): new = 'neuen' def __str__(self): - return str(self.sector) + ' mit der Betreiberin ' + str(self.operator) + \ - ' (' + shorten_string(self.description) + ')' + operator = '' + if self.operator_organization: + operator = ' mit der Betreiberin ' + str(self.operator_organization) + elif self.operator_person: + operator = ' mit der/dem Betreiber:in ' + str(self.operator_person) + return str(self.sector) + operator + ' (' + shorten_string(self.description) + ')' def sector_and_operator(self): - return str(self.sector) + ' (Betreiberin: ' + str(self.operator) + ')' + operator = '' + if self.operator_organization: + operator = ' (Betreiberin: ' + str(self.operator_organization) + ')' + elif self.operator_person: + operator = ' (Betreiber:in: ' + str(self.operator_person) + ')' + return str(self.sector) + operator def save(self, force_insert=False, force_update=False, using=None, update_fields=None): # store search content in designated field diff --git a/bemas/templates/bemas/generic-objectclass-form.html b/bemas/templates/bemas/generic-objectclass-form.html index f62b4a73..35f52bad 100644 --- a/bemas/templates/bemas/generic-objectclass-form.html +++ b/bemas/templates/bemas/generic-objectclass-form.html @@ -18,6 +18,7 @@ {% else %} {% endif %} + {% endblock %} {% block scripts %} @@ -33,6 +34,8 @@ {% endif %} + + {% endblock %} {% block content %} @@ -246,10 +249,19 @@

{{ objectcl $(this).attr('title', sectorExamples[value]); }); - // handle multiple select fields - $('select[multiple]').each(function () { - addEmptyFieldButton($(this)); - }); + // handle certain select fields: convert them to autocomplete fields + $('select#id_organization').select2(); + $('select#id_person').select2(); + $('select#id_operator_organization').select2(); + $('select#id_operator_person').select2(); + $('select#id_originator').select2(); + $('select#id_complainers_organizations').select2(); + $('select#id_complainers_persons').select2(); + + // currently not needed due to conversion of multiple select fields to autocomplete fields + /* $('select[multiple]').each(function () { + // addEmptyFieldButton($(this)); + }); */ // initialize address search (and make its results globally available) window.results = $('div.results'); diff --git a/bemas/templates/bemas/index.html b/bemas/templates/bemas/index.html index 3e0416fe..dccc9019 100644 --- a/bemas/templates/bemas/index.html +++ b/bemas/templates/bemas/index.html @@ -9,8 +9,10 @@ {% if not is_mobile %}
-
diff --git a/bemas/templates/bemas/map.html b/bemas/templates/bemas/map.html index 6d477465..1027bd07 100644 --- a/bemas/templates/bemas/map.html +++ b/bemas/templates/bemas/map.html @@ -104,7 +104,7 @@

- +
- {% for operator in originators_operators %} - - {% endfor %} - - + {% if originators_operators_organizations %} +
+ +
+ + +
-
+ {% endif %} + {% if originators_operators_persons %} +
+ +
+ + +
+
+ {% endif %}
diff --git a/bemas/templates/bemas/orphaned-data.html b/bemas/templates/bemas/orphaned-data.html index 98b16f1f..a3d1b3eb 100644 --- a/bemas/templates/bemas/orphaned-data.html +++ b/bemas/templates/bemas/orphaned-data.html @@ -25,7 +25,9 @@
O