From bec2c71961ca656dbe92973f2f605c4289665a1a Mon Sep 17 00:00:00 2001 From: Sebastian Gutzeit Date: Thu, 2 May 2024 12:03:11 +0200 Subject: [PATCH] App Datenmanagement: neues Datenthema Mobilfunkantennen --- datenmanagement/models/constants_vars.py | 3 + datenmanagement/models/models_simple.py | 135 ++++++++++- datenmanagement/sql/schema.sql | 44 +++- datenmanagement/tests/test_models_simple.py | 248 +++++++++++++++++++- 4 files changed, 424 insertions(+), 6 deletions(-) diff --git a/datenmanagement/models/constants_vars.py b/datenmanagement/models/constants_vars.py index af64ae37..8cea0eec 100644 --- a/datenmanagement/models/constants_vars.py +++ b/datenmanagement/models/constants_vars.py @@ -78,6 +78,9 @@ linien_linie_message = 'Die Linie muss mit einer ' \ 'Ziffer oder einem Großbuchstaben beginnen, der bzw. ' \ 'dem optional weitere Ziffern und/oder Großbuchstaben folgen können.' +mobilfunkantennen_stob_regex = r'^[0-9]{6,8}$' +mobilfunkantennen_stob_message = 'Die Standortbescheinigungsnummer ' \ + 'muss aus sechs bis acht Ziffern bestehen.' parkscheinautomaten_bewohnerparkgebiet_regex = r'^[A-Z][0-9]$' parkscheinautomaten_bewohnerparkgebiet_message = 'Das Bewohnerparkgebiet' \ ' muss aus genau einem ' \ diff --git a/datenmanagement/models/models_simple.py b/datenmanagement/models/models_simple.py index f3d24e20..a1b514ce 100644 --- a/datenmanagement/models/models_simple.py +++ b/datenmanagement/models/models_simple.py @@ -25,7 +25,8 @@ hydranten_bezeichnung_regex, hydranten_bezeichnung_message, \ ingenieurbauwerke_nummer_asb_regex, ingenieurbauwerke_nummer_asb_message, \ ingenieurbauwerke_baujahr_regex, ingenieurbauwerke_baujahr_message, \ - kleinklaeranlagen_zulassung_regex, kleinklaeranlagen_zulassung_message, poller_nummer_regex, \ + kleinklaeranlagen_zulassung_regex, kleinklaeranlagen_zulassung_message, \ + mobilfunkantennen_stob_regex, mobilfunkantennen_stob_message, poller_nummer_regex, \ poller_nummer_message, trinkwassernotbrunnen_nummer_regex, trinkwassernotbrunnen_nummer_message from .fields import ChoiceArrayField, NullTextField, PositiveSmallIntegerMinField, \ PositiveSmallIntegerRangeField, point_field, line_field, multiline_field, polygon_field, \ @@ -4040,6 +4041,138 @@ def __str__(self): (', Adresse: ' + str(self.adresse) if self.adresse else '') + ']' +class Mobilfunkantennen(SimpleModel): + """ + Mobilfunkantennen + """ + + adresse = ForeignKey( + to=Adressen, + verbose_name='Adresse', + on_delete=SET_NULL, + db_column='adresse', + to_field='uuid', + related_name='%(app_label)s_%(class)s_adressen', + blank=True, + null=True + ) + stob = CharField( + verbose_name='Standortbescheinigungsnummer', + max_length=255, + validators=[ + RegexValidator( + regex=mobilfunkantennen_stob_regex, + message=mobilfunkantennen_stob_message + ) + ] + ) + erteilungsdatum = DateField( + verbose_name='Erteilungsdatum', + default=date.today + ) + techniken = ArrayField( + CharField( + verbose_name='Techniken', + max_length=255, + blank=True, + null=True, + validators=standard_validators + ), + verbose_name='Techniken', + blank=True, + null=True + ) + betreiber = ArrayField( + CharField( + verbose_name='Betreiber', + max_length=255, + blank=True, + null=True, + validators=standard_validators + ), + verbose_name='Betreiber', + blank=True, + null=True + ) + montagehoehe = CharField( + verbose_name='Montagehöhe', + max_length=255, + blank=True, + null=True, + validators=standard_validators + ) + anzahl_gsm = PositiveSmallIntegerMinField( + verbose_name='Anzahl GSM-Einheiten', + min_value=1, + blank=True, + null=True + ) + anzahl_umts = PositiveSmallIntegerMinField( + verbose_name='Anzahl UMTS-Einheiten', + min_value=1, + blank=True, + null=True + ) + anzahl_lte = PositiveSmallIntegerMinField( + verbose_name='Anzahl LTE-Einheiten', + min_value=1, + blank=True, + null=True + ) + anzahl_sonstige = PositiveSmallIntegerMinField( + verbose_name='Anzahl sonstige Einheiten', + min_value=1, + blank=True, + null=True + ) + geometrie = point_field + + class Meta(SimpleModel.Meta): + db_table = 'fachdaten_adressbezug\".\"mobilfunkantennen_hro' + verbose_name = 'Mobilfunkantenne' + verbose_name_plural = 'Mobilfunkantennen' + + class BasemodelMeta(SimpleModel.BasemodelMeta): + description = 'Mobilfunkantennen in der Hanse- und Universitätsstadt Rostock' + as_overlay = True + address_type = 'Adresse' + address_mandatory = False + geometry_type = 'Point' + list_fields = { + 'aktiv': 'aktiv?', + 'adresse': 'Adresse', + 'stob': 'Standortbescheinigungsnummer', + 'erteilungsdatum': 'Erteilungsdatum', + 'techniken': 'Techniken', + 'betreiber': 'Betreiber', + 'montagehoehe': 'Montagehöhe', + 'anzahl_gsm': 'Anzahl GSM-Einheiten', + 'anzahl_umts': 'Anzahl UMTS-Einheiten', + 'anzahl_lte': 'Anzahl LTE-Einheiten', + 'anzahl_sonstige': 'Anzahl sonstige Einheiten' + } + list_fields_with_date = ['erteilungsdatum'] + list_fields_with_foreign_key = { + 'adresse': 'adresse' + } + map_feature_tooltip_fields = ['stob'] + map_filter_fields = { + 'aktiv': 'aktiv?', + 'stob': 'Standortbescheinigungsnummer', + 'erteilungsdatum': 'Erteilungsdatum', + 'techniken': 'Techniken', + 'betreiber': 'Betreiber', + 'montagehoehe': 'Montagehöhe', + 'anzahl_gsm': 'Anzahl GSM-Einheiten', + 'anzahl_umts': 'Anzahl UMTS-Einheiten', + 'anzahl_lte': 'Anzahl LTE-Einheiten', + 'anzahl_sonstige': 'Anzahl sonstige Einheiten' + } + + def __str__(self): + return self.stob + + class Mobilpunkte(SimpleModel): """ Mobilpunkte diff --git a/datenmanagement/sql/schema.sql b/datenmanagement/sql/schema.sql index 4de9f3ca..867524ac 100644 --- a/datenmanagement/sql/schema.sql +++ b/datenmanagement/sql/schema.sql @@ -115,7 +115,7 @@ CREATE FUNCTION fachdaten.foto() RETURNS trigger AS $$ BEGIN IF NEW.foto = '' THEN - NEW.foto := NULL; + NEW.foto := NULL; END IF; RETURN NEW; END; @@ -3430,6 +3430,32 @@ CREATE TABLE fachdaten_adressbezug.meldedienst_punkthaft_hro ( ); +-- +-- Name: mobilfunkantennen_hro; Type: TABLE; Schema: fachdaten_adressbezug; Owner: - +-- + +CREATE TABLE fachdaten_adressbezug.mobilfunkantennen_hro ( + uuid uuid DEFAULT public.uuid_generate_v4() NOT NULL, + aktualisiert date DEFAULT (now())::date NOT NULL, + erstellt date DEFAULT (now())::date NOT NULL, + id_fachsystem character varying(255), + id_zielsystem character varying(255), + aktiv boolean DEFAULT true NOT NULL, + deaktiviert date, + adresse uuid, + stob character varying(8) NOT NULL, + erteilungsdatum date NOT NULL, + techniken character varying(255)[], + betreiber character varying(255)[], + montagehoehe character varying(255), + anzahl_gsm smallint, + anzahl_umts smallint, + anzahl_lte smallint, + anzahl_sonstige smallint, + geometrie public.geometry(Point,25833) NOT NULL +); + + -- -- Name: parkmoeglichkeiten_hro; Type: TABLE; Schema: fachdaten_adressbezug; Owner: - -- @@ -6269,6 +6295,22 @@ ALTER TABLE ONLY fachdaten_adressbezug.meldedienst_punkthaft_hro ADD CONSTRAINT meldedienst_punkthaft_hro_pk PRIMARY KEY (uuid); +-- +-- Name: mobilfunkantennen_hro mobilfunkantennen_hro_pk; Type: CONSTRAINT; Schema: fachdaten_adressbezug; Owner: - +-- + +ALTER TABLE ONLY fachdaten_adressbezug.mobilfunkantennen_hro + ADD CONSTRAINT mobilfunkantennen_hro_pk PRIMARY KEY (uuid); + + +-- +-- Name: mobilfunkantennen_hro mobilfunkantennen_hro_stob_unique; Type: CONSTRAINT; Schema: fachdaten_adressbezug; Owner: - +-- + +ALTER TABLE ONLY fachdaten_adressbezug.mobilfunkantennen_hro + ADD CONSTRAINT mobilfunkantennen_hro_stob_unique UNIQUE (stob); + + -- -- Name: parkmoeglichkeiten_hro parkmoeglichkeiten_hro_pk; Type: CONSTRAINT; Schema: fachdaten_adressbezug; Owner: - -- diff --git a/datenmanagement/tests/test_models_simple.py b/datenmanagement/tests/test_models_simple.py index bd7b9d10..9a8ee2e3 100644 --- a/datenmanagement/tests/test_models_simple.py +++ b/datenmanagement/tests/test_models_simple.py @@ -18,10 +18,10 @@ Hydranten, Ingenieurbauwerke, Kadaverfunde, Kehrbezirke, Kindertagespflegeeinrichtungen, \ Kinder_Jugendbetreuung, Kleinklaeranlagen, Kunst_im_oeffentlichen_Raum, \ Ladestationen_Elektrofahrzeuge, Materialien_Denksteine, Meldedienst_flaechenhaft, \ - Meldedienst_punkthaft, Mobilpunkte, Parkmoeglichkeiten, Pflegeeinrichtungen, Poller, Quartiere, \ - Reinigungsreviere, Reisebusparkplaetze_Terminals, Rettungswachen, Schiffsliegeplaetze, \ - Schlagwoerter_Bildungstraeger, Schlagwoerter_Vereine, Schutzzaeune_Tierseuchen, Sportarten, \ - Sportanlagen, Sporthallen, Stadtteil_Begegnungszentren, \ + Meldedienst_punkthaft, Mobilfunkantennen, Mobilpunkte, Parkmoeglichkeiten, Pflegeeinrichtungen, \ + Poller, Quartiere, Reinigungsreviere, Reisebusparkplaetze_Terminals, Rettungswachen, \ + Schiffsliegeplaetze, Schlagwoerter_Bildungstraeger, Schlagwoerter_Vereine, \ + Schutzzaeune_Tierseuchen, Sportarten, Sportanlagen, Sporthallen, Stadtteil_Begegnungszentren, \ Standortqualitaeten_Geschaeftslagen_Sanierungsgebiet, \ Standortqualitaeten_Wohnlagen_Sanierungsgebiet, Status_Baudenkmale_Denkmalbereiche, \ Status_Poller, Strassen, Thalasso_Kurwege, Tierseuchen, Toiletten, Trinkwassernotbrunnen, \ @@ -7978,6 +7978,246 @@ def test_view_geometry_lat_lng(self): ) +class MobilfunkantennenTest(DefaultSimpleModelTestCase): + """ + Mobilfunkantennen + """ + + model = Mobilfunkantennen + create_test_object_in_classmethod = False + create_test_subset_in_classmethod = False + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + adresse = Adressen.objects.create( + adresse='Adresse' + ) + cls.attributes_values_db_initial = { + 'adresse': adresse, + 'stob': '1234567', + 'erteilungsdatum': VALID_DATE, + 'geometrie': VALID_POINT_DB + } + cls.attributes_values_db_updated = { + 'stob': '2345678' + } + cls.attributes_values_view_initial = { + 'aktiv': True, + 'adresse': str(adresse.pk), + 'stob': '7654321', + 'erteilungsdatum': VALID_DATE, + 'geometrie': VALID_POINT_VIEW + } + cls.attributes_values_view_updated = { + 'aktiv': True, + 'adresse': str(adresse.pk), + 'stob': '8765432', + 'erteilungsdatum': VALID_DATE, + 'geometrie': VALID_POINT_VIEW + } + cls.attributes_values_view_invalid = { + 'montagehoehe': INVALID_STRING + } + cls.test_object = cls.model.objects.create(**cls.attributes_values_db_initial) + cls.test_subset = create_test_subset(cls.model, cls.test_object) + + def setUp(self): + self.init() + + def test_is_simplemodel(self): + self.generic_is_simplemodel_test() + + def test_create(self): + self.generic_create_test(self.model, self.attributes_values_db_initial) + + def test_update(self): + self.generic_update_test(self.model, self.attributes_values_db_updated) + + def test_delete(self): + self.generic_delete_test(self.model) + + def test_view_start(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_start', + {}, + 200, + 'text/html; charset=utf-8', + START_VIEW_STRING + ) + + def test_view_list(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_list', + {}, + 200, + 'text/html; charset=utf-8', + LIST_VIEW_STRING + ) + + def test_view_list_subset(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_list_subset', + {'subset_id': self.test_subset.pk}, + 200, + 'text/html; charset=utf-8', + LIST_VIEW_STRING + ) + + def test_view_data(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_data', + DATA_VIEW_PARAMS, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_data_subset(self): + data_subset_view_params = DATA_VIEW_PARAMS.copy() + data_subset_view_params['subset_id'] = self.test_subset.pk + self.generic_view_test( + self.model, + self.model.__name__ + '_data_subset', + data_subset_view_params, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_map(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_map', + {}, + 200, + 'text/html; charset=utf-8', + MAP_VIEW_STRING + ) + + def test_view_map_subset(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_map_subset', + {'subset_id': self.test_subset.pk}, + 200, + 'text/html; charset=utf-8', + MAP_VIEW_STRING + ) + + def test_view_mapdata(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_mapdata', + {}, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_mapdata_subset(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_mapdata_subset', + {'subset_id': self.test_subset.pk}, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_add_success(self): + self.generic_add_update_view_test( + False, + self.model, + self.attributes_values_view_initial, + 302, + 'text/html; charset=utf-8', + 1 + ) + + def test_view_add_error(self): + self.generic_add_update_view_test( + False, + self.model, + self.attributes_values_view_invalid, + 200, + 'text/html; charset=utf-8', + 0 + ) + + def test_view_change_success(self): + self.generic_add_update_view_test( + True, + self.model, + self.attributes_values_view_updated, + 302, + 'text/html; charset=utf-8', + 1 + ) + + def test_view_change_error(self): + self.generic_add_update_view_test( + True, + self.model, + self.attributes_values_view_invalid, + 200, + 'text/html; charset=utf-8', + 0 + ) + + def test_view_delete(self): + self.generic_delete_view_test( + False, + self.model, + self.attributes_values_db_initial, + 302, + 'text/html; charset=utf-8' + ) + + def test_view_deleteimmediately(self): + self.generic_delete_view_test( + True, + self.model, + self.attributes_values_db_initial, + 204, + 'text/html; charset=utf-8' + ) + + def test_view_geometry(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_geometry', + {}, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_geometry_pk(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_geometry', + {'pk': str(self.test_object.pk)}, + 200, + 'application/json', + str(self.test_object.pk) + ) + + def test_view_geometry_lat_lng(self): + self.generic_view_test( + self.model, + self.model.__name__ + '_geometry', + GEOMETRY_VIEW_PARAMS, + 200, + 'application/json', + str(self.test_object.pk) + ) + + class MobilpunkteTest(DefaultSimpleModelTestCase): """ Mobilpunkte