From 1852c627ef07c8db4dd45c097dd05d513d93e928 Mon Sep 17 00:00:00 2001
From: Charlotte <47758554+charludo@users.noreply.github.com>
Date: Fri, 17 Jan 2025 08:32:39 +0100
Subject: [PATCH] Prevent orphaned contact cards (#3282)
---
integreat_cms/cms/fixtures/test_data.json | 89 +++++++++++++++++-
integreat_cms/cms/models/contact/contact.py | 8 +-
.../cms/templates/contacts/contact_card.html | 6 +-
.../cms/templates/contacts/contact_form.html | 6 +-
.../cms/views/contacts/contact_actions.py | 54 +++++++----
.../views/contacts/contact_bulk_actions.py | 45 ++++++++-
integreat_cms/locale/de/LC_MESSAGES/django.po | 40 ++++++++
integreat_cms/static/src/css/contact_card.css | 4 +
tests/api/api_config.py | 4 +-
.../augsburg_de_children.json | 21 +++++
...burg_de_children_archived_descendants.json | 4 +-
.../augsburg_de_children_depth_2.json | 21 +++++
.../expected-outputs/augsburg_de_pages.json | 30 +++++-
tests/cms/models/contacts/test_contacts.py | 16 ++--
tests/cms/test_duplicate_regions.py | 10 +-
.../views/contacts/test_contact_actions.py | 65 ++++++-------
tests/cms/views/poi/test_poi_form.py | 2 +-
.../Integreat - Deutsch - Augsburg.pdf | Bin 100586 -> 101694 bytes
tests/pdf/test_pdf_export.py | 4 +-
.../expected-sitemaps/sitemap-augsburg-de.xml | 2 +-
tests/sitemap/sitemap_config.py | 2 +-
21 files changed, 336 insertions(+), 97 deletions(-)
rename tests/pdf/files/{429b3fbce4 => 89219cf973}/Integreat - Deutsch - Augsburg.pdf (81%)
diff --git a/integreat_cms/cms/fixtures/test_data.json b/integreat_cms/cms/fixtures/test_data.json
index 134fcf6288..50c144af44 100644
--- a/integreat_cms/cms/fixtures/test_data.json
+++ b/integreat_cms/cms/fixtures/test_data.json
@@ -1034,6 +1034,21 @@
"created_date": "2024-08-06T13:23:45.256Z"
}
},
+ {
+ "model": "cms.contact",
+ "pk": 5,
+ "fields": {
+ "point_of_contact_for": "Testkontakt",
+ "name": "referred to in a page",
+ "location": 6,
+ "email": "",
+ "phone_number": "",
+ "website": "",
+ "archived": false,
+ "last_updated": "2025-01-08T13:53:31.813Z",
+ "created_date": "2025-01-08T13:53:31.806Z"
+ }
+ },
{
"model": "cms.recurrencerule",
"pk": 1,
@@ -2304,6 +2319,22 @@
"redirect_to": ""
}
},
+ {
+ "model": "linkcheck.url",
+ "pk": 21,
+ "fields": {
+ "url": "/augsburg/contact/5/",
+ "last_checked": "2025-01-08T13:54:42.851Z",
+ "anchor_status": null,
+ "ssl_status": null,
+ "status": true,
+ "status_code": 200,
+ "redirect_status_code": null,
+ "message": "",
+ "error_message": "",
+ "redirect_to": ""
+ }
+ },
{
"model": "linkcheck.link",
"pk": 1,
@@ -2760,6 +2791,18 @@
"ignore": false
}
},
+ {
+ "model": "linkcheck.link",
+ "pk": 40,
+ "fields": {
+ "content_type": ["cms", "pagetranslation"],
+ "object_id": 101,
+ "field": "content",
+ "url": 21,
+ "text": "Contact",
+ "ignore": false
+ }
+ },
{
"model": "cms.page",
"pk": 1,
@@ -3450,6 +3493,29 @@
"embedded_offers": []
}
},
+ {
+ "model": "cms.page",
+ "pk": 31,
+ "fields": {
+ "created_date": "2025-01-11T07:07:46.585Z",
+ "lft": 1,
+ "rgt": 2,
+ "tree_id": 11,
+ "depth": 1,
+ "parent": null,
+ "region": 1,
+ "explicitly_archived": false,
+ "icon": null,
+ "mirrored_page": null,
+ "mirrored_page_first": true,
+ "organization": null,
+ "api_token": "",
+ "hix_ignore": false,
+ "authors": [],
+ "editors": [],
+ "embedded_offers": []
+ }
+ },
{ "model": "cms.regionfeedback", "pk": 1, "fields": { "feedback_ptr": 1 } },
{ "model": "cms.regionfeedback", "pk": 4, "fields": { "feedback_ptr": 4 } },
{ "model": "cms.regionfeedback", "pk": 7, "fields": { "feedback_ptr": 7 } },
@@ -4262,7 +4328,7 @@
"title": "Gesundheitsamt",
"slug": "gesundheitsamt",
"status": "PUBLIC",
- "content": "
Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
+ "content": "Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
"language": 1,
"currently_in_translation": false,
"machine_translated": false,
@@ -5557,6 +5623,27 @@
"hix_feedback": null
}
},
+ {
+ "model": "cms.pagetranslation",
+ "pk": 101,
+ "fields": {
+ "title": "Wichtige Kontakte",
+ "slug": "wichtige-kontakte",
+ "status": "PUBLIC",
+ "content": "",
+ "language": 1,
+ "currently_in_translation": false,
+ "machine_translated": false,
+ "version": 1,
+ "minor_edit": false,
+ "last_updated": "2025-01-11T07:07:46.642Z",
+ "creator": ["root"],
+ "automatic_translation": false,
+ "page": 31,
+ "hix_score": null,
+ "hix_feedback": null
+ }
+ },
{
"model": "cms.imprintpagetranslation",
"pk": 1,
diff --git a/integreat_cms/cms/models/contact/contact.py b/integreat_cms/cms/models/contact/contact.py
index 6053d529c6..d21450c22d 100644
--- a/integreat_cms/cms/models/contact/contact.py
+++ b/integreat_cms/cms/models/contact/contact.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Generator, TYPE_CHECKING
+from typing import List, TYPE_CHECKING
from django.conf import settings
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
@@ -225,16 +225,16 @@ def referring_event_translations(self) -> QuerySet[EventTranslation]:
)
@cached_property
- def referring_objects(self) -> Generator[AbstractContentTranslation]:
+ def referring_objects(self) -> List[AbstractContentTranslation]:
"""
Returns a list of all objects linking to this contact.
:return: all objects referring to this contact
"""
- return (
+ return [
link.content_object
for link in Link.objects.filter(url__url=self.absolute_url)
- )
+ ]
def archive(self) -> None:
"""
diff --git a/integreat_cms/cms/templates/contacts/contact_card.html b/integreat_cms/cms/templates/contacts/contact_card.html
index 191bce298f..afe0de11f0 100644
--- a/integreat_cms/cms/templates/contacts/contact_card.html
+++ b/integreat_cms/cms/templates/contacts/contact_card.html
@@ -26,12 +26,11 @@
{% endif %}
- {{ contact.location.short_address }}
+ {{ contact.location.short_address }}
{% if contact.email %}
-
{{ contact.email }}
{% endif %}
@@ -41,13 +40,12 @@
-
{{ contact.website }}
{% endif %}
diff --git a/integreat_cms/cms/templates/contacts/contact_form.html b/integreat_cms/cms/templates/contacts/contact_form.html
index 7c72d77aa4..c00fb3f6ce 100644
--- a/integreat_cms/cms/templates/contacts/contact_form.html
+++ b/integreat_cms/cms/templates/contacts/contact_form.html
@@ -100,7 +100,8 @@
data-confirmation-title="{{ archive_dialog_title }}"
data-confirmation-text="{{ archive_dialog_text }}"
data-confirmation-subject="{{ contact_form.instance.name }}"
- data-action="{% url 'archive_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}">
+ data-action="{% url 'archive_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}"
+ {% if contact_form.instance.referring_objects %}disabled{% endif %}>
{% translate "Archive this contact" %}
@@ -122,7 +123,8 @@
data-confirmation-title="{{ delete_dialog_title }}"
data-confirmation-text="{{ delete_dialog_text }}"
data-confirmation-subject="{{ contact_form.instance.name }}"
- data-action="{% url 'delete_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}">
+ data-action="{% url 'delete_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}"
+ {% if contact_form.instance.referring_objects %}disabled{% endif %}>
{% translate "Delete contact" %}
diff --git a/integreat_cms/cms/views/contacts/contact_actions.py b/integreat_cms/cms/views/contacts/contact_actions.py
index 65f63cd4c1..0d4469c74a 100644
--- a/integreat_cms/cms/views/contacts/contact_actions.py
+++ b/integreat_cms/cms/views/contacts/contact_actions.py
@@ -28,18 +28,25 @@ def archive_contact(
to_be_archived_contact = get_object_or_404(
Contact, id=contact_id, location__region=request.region
)
- to_be_archived_contact.archive()
-
- messages.success(
+ if not to_be_archived_contact.referring_objects:
+ to_be_archived_contact.archive()
+ messages.success(
+ request,
+ _("Contact {0} was successfully archived").format(to_be_archived_contact),
+ )
+ return redirect(
+ "contacts",
+ **{
+ "region_slug": region_slug,
+ },
+ )
+ messages.error(
request,
- _("Contact {0} was successfully archived").format(to_be_archived_contact),
- )
- return redirect(
- "contacts",
- **{
- "region_slug": region_slug,
- },
+ _('Cannot archive contact "{0}" while content objects refer to it.').format(
+ to_be_archived_contact,
+ ),
)
+ return redirect("edit_contact", region_slug=region_slug, contact_id=contact_id)
@permission_required("cms.delete_contact")
@@ -57,16 +64,25 @@ def delete_contact(
to_be_deleted_contact = get_object_or_404(
Contact, id=contact_id, location__region=request.region
)
- to_be_deleted_contact.delete()
- messages.success(
- request, _("Contact {0} was successfully deleted").format(to_be_deleted_contact)
- )
- return redirect(
- "contacts",
- **{
- "region_slug": region_slug,
- },
+ if not to_be_deleted_contact.referring_objects:
+ to_be_deleted_contact.delete()
+ messages.success(
+ request,
+ _("Contact {0} was successfully deleted").format(to_be_deleted_contact),
+ )
+ return redirect(
+ "contacts",
+ **{
+ "region_slug": region_slug,
+ },
+ )
+ messages.error(
+ request,
+ _('Cannot delete contact "{0}" while content objects refer to it.').format(
+ to_be_deleted_contact,
+ ),
)
+ return redirect("edit_contact", region_slug=region_slug, contact_id=contact_id)
@permission_required("cms.change_contact")
diff --git a/integreat_cms/cms/views/contacts/contact_bulk_actions.py b/integreat_cms/cms/views/contacts/contact_bulk_actions.py
index 76d6b6364a..da0822c5c3 100644
--- a/integreat_cms/cms/views/contacts/contact_bulk_actions.py
+++ b/integreat_cms/cms/views/contacts/contact_bulk_actions.py
@@ -50,10 +50,14 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
"""
archive_successful = []
+ archive_failure = []
for content_object in self.get_queryset():
- content_object.archive()
- archive_successful.append(content_object)
+ if not content_object.referring_objects:
+ content_object.archive()
+ archive_successful.append(content_object)
+ else:
+ archive_failure.append(content_object)
if archive_successful:
messages.success(
@@ -69,6 +73,20 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
),
)
+ if archive_failure:
+ messages.error(
+ request,
+ ngettext_lazy(
+ "{model_name} {object_names} cannot be archived while content objects refer to it.",
+ "The following {model_name_plural} could be archived: {object_names}",
+ ).format(
+ len(archive_failure),
+ model_name=self.model._meta.verbose_name.title(),
+ model_name_plural=self.model._meta.verbose_name_plural,
+ object_names=iter_to_string(archive_failure),
+ ),
+ )
+
return super().post(request, *args, **kwargs)
@@ -131,9 +149,14 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
:return: The redirect
"""
delete_sucessful = []
+ delete_failure = []
+
for content_object in self.get_queryset():
- content_object.delete()
- delete_sucessful.append(content_object)
+ if not content_object.referring_objects:
+ content_object.delete()
+ delete_sucessful.append(content_object)
+ else:
+ delete_failure.append(content_object)
if delete_sucessful:
messages.success(
@@ -149,4 +172,18 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
),
)
+ if delete_failure:
+ messages.error(
+ request,
+ ngettext_lazy(
+ "{model_name} {object_names} cannot be deleted while content objects refer to it.",
+ "The following {model_name_plural} could be deleted: {object_names}",
+ len(delete_failure),
+ ).format(
+ model_name=self.model._meta.verbose_name.title(),
+ model_name_plural=self.model._meta.verbose_name_plural,
+ object_names=iter_to_string(delete_failure),
+ ),
+ )
+
return super().post(request, *args, **kwargs)
diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po
index a48f125b6b..88ad2f3981 100644
--- a/integreat_cms/locale/de/LC_MESSAGES/django.po
+++ b/integreat_cms/locale/de/LC_MESSAGES/django.po
@@ -9170,11 +9170,23 @@ msgstr[1] ""
msgid "Contact {0} was successfully archived"
msgstr "Kontakt {0} wurde erfolgreich archiviert"
+#: cms/views/contacts/contact_actions.py
+#, python-brace-format
+msgid "Cannot archive contact \"{0}\" while content objects refer to it."
+msgstr ""
+"Kontakt {0} kann nicht archiviert werden solange Inhalte auf ihn verweisen."
+
#: cms/views/contacts/contact_actions.py
#, python-brace-format
msgid "Contact {0} was successfully deleted"
msgstr "Kontakt {0} wurde erfolgreich gelöscht."
+#: cms/views/contacts/contact_actions.py
+#, python-brace-format
+msgid "Cannot delete contact \"{0}\" while content objects refer to it."
+msgstr ""
+"Kontakt {0} kann nicht gelöscht werden solange Inhalte auf ihn verweisen."
+
#: cms/views/contacts/contact_actions.py
#, python-brace-format
msgid "Contact {0} was successfully restored"
@@ -9185,6 +9197,20 @@ msgstr "Kontakt {0} wurde erfolgreich wiederhergestellt"
msgid "Contact {0} was successfully copied"
msgstr "Kontakt {0} wurde erfolgreich kopiert"
+#: cms/views/contacts/contact_bulk_actions.py
+#, python-brace-format
+msgid ""
+"{model_name} {object_names} cannot be archived while content objects refer "
+"to it."
+msgid_plural ""
+"The following {model_name_plural} could be archived: {object_names}"
+msgstr[0] ""
+"{model_name} {object_names} kann nicht archiviert werden solange Inhalte auf "
+"ihn verweisen."
+msgstr[1] ""
+"Die folgenden {model_name_plural} konnten nicht archiviert werden: "
+"{object_names}"
+
#: cms/views/contacts/contact_bulk_actions.py
#: cms/views/organizations/organization_bulk_actions.py
#, python-brace-format
@@ -9195,6 +9221,20 @@ msgstr[0] "{model_name} {object_names} wurde erfolgreich gelöscht."
msgstr[1] ""
"Die folgenden {model_name_plural} wurden erfolgreich gelöscht: {object_names}"
+#: cms/views/contacts/contact_bulk_actions.py
+#, python-brace-format
+msgid ""
+"{model_name} {object_names} cannot be deleted while content objects refer to "
+"it."
+msgid_plural ""
+"The following {model_name_plural} could be deleted: {object_names}"
+msgstr[0] ""
+"{model_name} {object_names} kann nicht gelöscht werden solange Inhalte auf "
+"ihn verweisen."
+msgstr[1] ""
+"Die folgenden {model_name_plural} konnten nicht gelöscht werden: "
+"{object_names}"
+
#: cms/views/contacts/contact_context_mixin.py
msgid "Please confirm that you really want to archive this contact"
msgstr "Bitte bestätigen Sie, dass dieser Kontakt archiviert werden soll."
diff --git a/integreat_cms/static/src/css/contact_card.css b/integreat_cms/static/src/css/contact_card.css
index 3cc240d3a3..2895818895 100644
--- a/integreat_cms/static/src/css/contact_card.css
+++ b/integreat_cms/static/src/css/contact_card.css
@@ -22,4 +22,8 @@
h4 {
margin: 0 0 12px 0;
}
+
+ img {
+ margin: 0 8px 0 0;
+ }
}
diff --git a/tests/api/api_config.py b/tests/api/api_config.py
index 6ed030bbd5..65ccb9da8e 100644
--- a/tests/api/api_config.py
+++ b/tests/api/api_config.py
@@ -121,7 +121,7 @@
"/augsburg/de/wp-json/extensions/v3/children/",
"tests/api/expected-outputs/augsburg_de_children.json",
200,
- 14,
+ 15,
),
(
"/api/v3/augsburg/de/children/?depth=3&url=/augsburg/de/behörden-und-beratung/behörden/",
@@ -142,7 +142,7 @@
"/augsburg/de/wp-json/extensions/v3/children/?depth=2",
"tests/api/expected-outputs/augsburg_de_children_depth_2.json",
200,
- 14,
+ 15,
),
(
"/api/v3/augsburg/de/events/",
diff --git a/tests/api/expected-outputs/augsburg_de_children.json b/tests/api/expected-outputs/augsburg_de_children.json
index 993b27c487..1d98b869cd 100644
--- a/tests/api/expected-outputs/augsburg_de_children.json
+++ b/tests/api/expected-outputs/augsburg_de_children.json
@@ -229,5 +229,26 @@
"organization": null,
"embedded_offers": [],
"hash": null
+ },
+ {
+ "id": 101,
+ "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/",
+ "path": "/augsburg/de/wichtige-kontakte/",
+ "title": "Wichtige Kontakte",
+ "modified_gmt": "2025-01-11T07:07:46.642Z",
+ "last_updated": "2025-01-11T08:07:46.642+01:00",
+ "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n",
+ "content": "",
+ "parent": {
+ "id": 0,
+ "url": null,
+ "path": null
+ },
+ "order": 11,
+ "available_languages": {},
+ "thumbnail": null,
+ "organization": null,
+ "hash": null,
+ "embedded_offers": []
}
]
diff --git a/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json b/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json
index 342d58df90..700ac077c0 100644
--- a/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json
+++ b/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json
@@ -88,8 +88,8 @@
"title": "Gesundheitsamt",
"modified_gmt": "2022-01-16T16:41:33.158Z",
"last_updated": "2022-01-16T17:41:33.158+01:00",
- "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n",
- "content": "Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
+ "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n",
+ "content": "Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
"parent": {
"id": 15,
"url": "http://localhost:8000/augsburg/de/behörden-und-beratung/behörden/",
diff --git a/tests/api/expected-outputs/augsburg_de_children_depth_2.json b/tests/api/expected-outputs/augsburg_de_children_depth_2.json
index 0b4f3763e9..ad49854a56 100644
--- a/tests/api/expected-outputs/augsburg_de_children_depth_2.json
+++ b/tests/api/expected-outputs/augsburg_de_children_depth_2.json
@@ -565,5 +565,26 @@
"organization": null,
"embedded_offers": [],
"hash": null
+ },
+ {
+ "id": 101,
+ "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/",
+ "path": "/augsburg/de/wichtige-kontakte/",
+ "title": "Wichtige Kontakte",
+ "modified_gmt": "2025-01-11T07:07:46.642Z",
+ "last_updated": "2025-01-11T08:07:46.642+01:00",
+ "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n",
+ "content": "",
+ "parent": {
+ "id": 0,
+ "url": null,
+ "path": null
+ },
+ "order": 11,
+ "available_languages": {},
+ "thumbnail": null,
+ "organization": null,
+ "hash": null,
+ "embedded_offers": []
}
]
diff --git a/tests/api/expected-outputs/augsburg_de_pages.json b/tests/api/expected-outputs/augsburg_de_pages.json
index f2e9a0f2f9..0fb614890e 100644
--- a/tests/api/expected-outputs/augsburg_de_pages.json
+++ b/tests/api/expected-outputs/augsburg_de_pages.json
@@ -311,7 +311,10 @@
"alias": "zammad-formular",
"url": "",
"thumbnail": "",
- "post": { "form-id": "example", "zammad-url": "https://zammad.example.com" }
+ "post": {
+ "form-id": "example",
+ "zammad-url": "https://zammad.example.com"
+ }
}
],
"hash": null
@@ -323,8 +326,8 @@
"title": "Gesundheitsamt",
"modified_gmt": "2022-01-16T16:41:33.158Z",
"last_updated": "2022-01-16T17:41:33.158+01:00",
- "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n",
- "content": "Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
+ "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n",
+ "content": "Leistungen des Gesundheitsamtes:
\r\n
\r\n- Beratung zur Gesundheitsfragen
\r\n- Beratung für Menschen, die an einer psychischen Krankheit leiden
\r\n- Kontaktstelle für Selbsthilfegruppen Infos auch unter:
www.augsburg.de/selbsthilfe-schwaben \r\n- Beratung für Schwangere
\r\n- Impfberatung
\r\n- Belehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)
\r\n- Untersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)
\r\n- Erstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF
\r\n- Erstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl
\r\n- Meldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)
\r\n- Tuberkulosefürsorge
\r\n- Gesundheitsberatung und Untersuchung für Prostituierte
\r\n- anonyme AIDS-Beratung / HIV-Test
\r\n
\r\n
",
"parent": {
"id": 15,
"url": "http://localhost:8000/augsburg/de/behörden-und-beratung/behörden/",
@@ -647,5 +650,26 @@
"organization": null,
"embedded_offers": [],
"hash": null
+ },
+ {
+ "id": 101,
+ "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/",
+ "path": "/augsburg/de/wichtige-kontakte/",
+ "title": "Wichtige Kontakte",
+ "modified_gmt": "2025-01-11T07:07:46.642Z",
+ "last_updated": "2025-01-11T08:07:46.642+01:00",
+ "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n",
+ "content": "",
+ "parent": {
+ "id": 0,
+ "url": null,
+ "path": null
+ },
+ "order": 11,
+ "available_languages": {},
+ "thumbnail": null,
+ "organization": null,
+ "hash": null,
+ "embedded_offers": []
}
]
diff --git a/tests/cms/models/contacts/test_contacts.py b/tests/cms/models/contacts/test_contacts.py
index 1793d314d6..d4e26cf784 100644
--- a/tests/cms/models/contacts/test_contacts.py
+++ b/tests/cms/models/contacts/test_contacts.py
@@ -40,37 +40,37 @@ def test_contact_string(
def test_copying_contact_works(
load_test_data: None,
) -> None:
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
contact = Contact.objects.get(id=1)
contact.copy()
- assert Contact.objects.all().count() == 5
+ assert Contact.objects.all().count() == 6
@pytest.mark.django_db
def test_deleting_contact_works(
load_test_data: None,
) -> None:
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
contact = Contact.objects.get(id=1)
contact.delete()
- assert Contact.objects.all().count() == 3
+ assert Contact.objects.all().count() == 4
@pytest.mark.django_db
def test_archiving_contact_works(
load_test_data: None,
) -> None:
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
contact = Contact.objects.get(id=1)
assert contact.archived is False
contact.archive()
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
assert contact.archived is True
@@ -78,11 +78,11 @@ def test_archiving_contact_works(
def test_restoring_contact_works(
load_test_data: None,
) -> None:
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
contact = Contact.objects.get(id=2)
assert contact.archived is True
contact.restore()
- assert Contact.objects.all().count() == 4
+ assert Contact.objects.all().count() == 5
assert contact.archived is False
diff --git a/tests/cms/test_duplicate_regions.py b/tests/cms/test_duplicate_regions.py
index d9707b929e..77133266ee 100644
--- a/tests/cms/test_duplicate_regions.py
+++ b/tests/cms/test_duplicate_regions.py
@@ -118,23 +118,23 @@ def test_duplicate_regions(
# Check if links exist
assert get_url_count(source_region.slug) == {
- "number_all_urls": 17,
+ "number_all_urls": 18,
"number_email_urls": 0,
"number_ignored_urls": 1,
"number_invalid_urls": 4,
"number_phone_urls": 0,
"number_unchecked_urls": 0,
- "number_valid_urls": 12,
+ "number_valid_urls": 13,
}, "Links should exist in the source region"
# Check if links have been cloned (except ignored ones)
assert get_url_count(target_region.slug) == {
- "number_all_urls": 17,
+ "number_all_urls": 20,
"number_email_urls": 0,
"number_ignored_urls": 0,
- "number_invalid_urls": 9,
+ "number_invalid_urls": 10,
"number_phone_urls": 0,
"number_unchecked_urls": 0,
- "number_valid_urls": 8,
+ "number_valid_urls": 10,
}, "Links should be cloned into the new region"
# Check if internal links have been cloned
diff --git a/tests/cms/views/contacts/test_contact_actions.py b/tests/cms/views/contacts/test_contact_actions.py
index 8197201678..c77bddc8bf 100644
--- a/tests/cms/views/contacts/test_contact_actions.py
+++ b/tests/cms/views/contacts/test_contact_actions.py
@@ -18,11 +18,12 @@
# Use the region Augsburg, as it has some contacts in the test data
REGION_SLUG = "augsburg"
+USED_CONTACT_ID = 5
NOT_USED_CONTACT_ID = 3
ARCHIVED_CONTACT_ID = 2
-test_archive_parameters = [(NOT_USED_CONTACT_ID, True)]
+test_archive_parameters = [(NOT_USED_CONTACT_ID, True), (USED_CONTACT_ID, False)]
@pytest.mark.django_db
@@ -68,14 +69,14 @@ def test_archive_contact(
).content.decode("utf-8")
assert Contact.objects.filter(id=contact_id).first().archived
else:
- # To be adjusted after #3282
assert_message_in_log(
- "ERROR Contact couldn't be archived as it's used in a content",
+ f'ERROR Cannot archive contact "{contact_string}" while content objects refer to it.',
caplog,
)
- assert (
- "Contact couldn't be archived as it's used in a content"
- in client.get(redirect_url).content.decode("utf-8")
+ assert f"Cannot archive contact "{contact_string}" while content objects refer to it." in client.get(
+ redirect_url
+ ).content.decode(
+ "utf-8"
)
assert not Contact.objects.filter(id=contact_id).first().archived
elif role == ANONYMOUS:
@@ -88,7 +89,7 @@ def test_archive_contact(
assert response.status_code == 403
-test_delete_parameters = [(NOT_USED_CONTACT_ID, True)]
+test_delete_parameters = [(NOT_USED_CONTACT_ID, True), (USED_CONTACT_ID, False)]
@pytest.mark.django_db
@@ -134,14 +135,14 @@ def test_delete_contact(
).content.decode("utf-8")
assert not Contact.objects.filter(id=contact_id).first()
else:
- # To be adjusted after #3282
assert_message_in_log(
- "ERROR Contact couldn't be archived as it's used in a content",
+ f'ERROR Cannot delete contact "{contact_string}" while content objects refer to it.',
caplog,
)
- assert (
- "Contact couldn't be archived as it's used in a content"
- in client.get(redirect_url).content.decode("utf-8")
+ assert f"Cannot delete contact "{contact_string}" while content objects refer to it." in client.get(
+ redirect_url
+ ).content.decode(
+ "utf-8"
)
assert Contact.objects.filter(id=contact_id).first()
elif role == ANONYMOUS:
@@ -184,7 +185,7 @@ def test_restore_contact(
response = client.post(restore_contact)
if role in HIGH_PRIV_STAFF_ROLES:
- response.status_code == 302
+ assert response.status_code == 302
redirect_url = response.headers.get("location")
assert_message_in_log(
f"SUCCESS Contact {contact_string} was successfully restored",
@@ -205,7 +206,7 @@ def test_restore_contact(
assert response.status_code == 403
-BULK_ARCHIVE_SELECTED_IDS = [NOT_USED_CONTACT_ID]
+BULK_ARCHIVE_SELECTED_IDS = [NOT_USED_CONTACT_ID, USED_CONTACT_ID]
@pytest.mark.django_db
@@ -226,6 +227,7 @@ def test_bulk_archive_contacts(
not_used_contact_string = str(
Contact.objects.filter(id=NOT_USED_CONTACT_ID).first()
)
+ used_contact_string = str(Contact.objects.filter(id=USED_CONTACT_ID).first())
bulk_archive_contacts = reverse(
"bulk_archive_contacts",
@@ -239,25 +241,18 @@ def test_bulk_archive_contacts(
)
if role in HIGH_PRIV_STAFF_ROLES:
- response.status_code == 302
+ assert response.status_code == 302
redirect_url = response.headers.get("location")
redirect_page = client.get(redirect_url).content.decode("utf-8")
- # To be adjusted after #3282
- """
assert_message_in_log(
- "ERROR could not be archived",
+ f'ERROR Contact "{used_contact_string}" cannot be archived while content objects refer to it.',
caplog,
)
assert (
- " could not be archived"
+ f"Contact "{used_contact_string}" cannot be archived while content objects refer to it."
in redirect_page
)
- assert (
- not Contact.objects.filter(id=USED_CONTACT_ID)
- .first()
- .archived
- )
- """
+ assert not Contact.objects.filter(id=USED_CONTACT_ID).first().archived
assert_message_in_log(
f'SUCCESS Contact "{not_used_contact_string}" was successfully archived.',
caplog,
@@ -277,7 +272,7 @@ def test_bulk_archive_contacts(
assert response.status_code == 403
-BULK_DELETE_SELECTED_IDS = [NOT_USED_CONTACT_ID]
+BULK_DELETE_SELECTED_IDS = [NOT_USED_CONTACT_ID, USED_CONTACT_ID]
@pytest.mark.django_db
@@ -298,6 +293,7 @@ def test_bulk_delete_contacts(
not_used_contact_string = str(
Contact.objects.filter(id=NOT_USED_CONTACT_ID).first()
)
+ used_contact_string = str(Contact.objects.filter(id=USED_CONTACT_ID).first())
bulk_delete_contacts = reverse(
"bulk_delete_contacts",
@@ -311,25 +307,18 @@ def test_bulk_delete_contacts(
)
if role in HIGH_PRIV_STAFF_ROLES:
- response.status_code == 302
+ assert response.status_code == 302
redirect_url = response.headers.get("location")
redirect_page = client.get(redirect_url).content.decode("utf-8")
- # To be adjusted after #3282
- """
assert_message_in_log(
- "ERROR could not be archived",
+ f'ERROR Contact "{used_contact_string}" cannot be deleted while content objects refer to it.',
caplog,
)
assert (
- " could not be archived"
+ f"Contact "{used_contact_string}" cannot be deleted while content objects refer to it."
in redirect_page
)
- assert (
- not Contact.objects.filter(id=USED_CONTACT_ID)
- .first()
- .archived
- )
- """
+ assert Contact.objects.filter(id=USED_CONTACT_ID).first()
assert_message_in_log(
f'SUCCESS Contact "{not_used_contact_string}" was successfully deleted.',
caplog,
@@ -381,7 +370,7 @@ def test_bulk_restore_contacts(
)
if role in HIGH_PRIV_STAFF_ROLES:
- response.status_code == 302
+ assert response.status_code == 302
redirect_url = response.headers.get("location")
redirect_page = client.get(redirect_url).content.decode("utf-8")
diff --git a/tests/cms/views/poi/test_poi_form.py b/tests/cms/views/poi/test_poi_form.py
index c0e7bf6e4e..8b28995b95 100644
--- a/tests/cms/views/poi/test_poi_form.py
+++ b/tests/cms/views/poi/test_poi_form.py
@@ -227,7 +227,7 @@ def test_poi_form_shows_associated_contacts(
settings: SettingsWrapper,
) -> None:
"""
- POI "Draft location" (id=6) has three related contacts. Test whether they are shown in the POI form.
+ POI "Draft location" (id=6) has four related contacts. Test whether they are shown in the POI form.
"""
client, role = login_role_user
diff --git a/tests/pdf/files/429b3fbce4/Integreat - Deutsch - Augsburg.pdf b/tests/pdf/files/89219cf973/Integreat - Deutsch - Augsburg.pdf
similarity index 81%
rename from tests/pdf/files/429b3fbce4/Integreat - Deutsch - Augsburg.pdf
rename to tests/pdf/files/89219cf973/Integreat - Deutsch - Augsburg.pdf
index e0f1afde2c6ce386bebee7f045da1060422f6a16..62974e6db919a53c8ac41da3a4c0ff3784f74cc8 100644
GIT binary patch
delta 16544
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
diff --git a/tests/pdf/test_pdf_export.py b/tests/pdf/test_pdf_export.py
index 080e923207..faeb5d8e55 100644
--- a/tests/pdf/test_pdf_export.py
+++ b/tests/pdf/test_pdf_export.py
@@ -23,9 +23,9 @@
),
(
"de",
- [1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 26, 27, 28],
+ [1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 26, 27, 28, 31],
"",
- "429b3fbce4/Integreat - Deutsch - Augsburg.pdf",
+ "89219cf973/Integreat - Deutsch - Augsburg.pdf",
),
(
"en",
diff --git a/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml b/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml
index 99ec896016..cc8405e22c 100644
--- a/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml
+++ b/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml
@@ -1,4 +1,4 @@
-https://integreat.app/augsburg/de/willkommen/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/willkommen-in-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/uber-die-app-integreat-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/wissenswertes-uber-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/stadtplan/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/kontakt-zu-app-team-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/ausländerbehörde/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/gesundheitsamt/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/deutsch-selber-lernen/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sprachkurse/2022-01-16monthly1.0https://integreat.app/augsburg/de/alltag/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sonstige-sprachlernangebote/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/dolmetschen-und-übersetzen/2022-01-16monthly1.0https://integreat.app/augsburg/de/test-hidden-language/2022-11-30monthly1.0https://integreat.app/augsburg/de/test-links/2023-08-18monthly1.0https://integreat.app/augsburg/de/jetzt-google-translate-ist-da/2022-12-30monthly1.0https://integreat.app/augsburg/de/locations/test-ort/2020-01-22monthly0.5https://integreat.app/augsburg/de/offers/ihk-lehrstellenboerse2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/lehrstellen-radar2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/sprungbrett2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/zammad-formular2023-10-09monthly1.0https://integreat.app/augsburg/de/events/test-veranstaltung/2020-01-22daily0.5
+https://integreat.app/augsburg/de/willkommen/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/willkommen-in-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/uber-die-app-integreat-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/wissenswertes-uber-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/stadtplan/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/kontakt-zu-app-team-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/ausländerbehörde/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/gesundheitsamt/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/deutsch-selber-lernen/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sprachkurse/2022-01-16monthly1.0https://integreat.app/augsburg/de/alltag/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sonstige-sprachlernangebote/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/dolmetschen-und-übersetzen/2022-01-16monthly1.0https://integreat.app/augsburg/de/test-hidden-language/2022-11-30monthly1.0https://integreat.app/augsburg/de/test-links/2023-08-18monthly1.0https://integreat.app/augsburg/de/jetzt-google-translate-ist-da/2022-12-30monthly1.0https://integreat.app/augsburg/de/wichtige-kontakte/2025-01-11monthly1.0https://integreat.app/augsburg/de/locations/test-ort/2020-01-22monthly0.5https://integreat.app/augsburg/de/offers/ihk-lehrstellenboerse2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/lehrstellen-radar2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/sprungbrett2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/zammad-formular2023-10-09monthly1.0https://integreat.app/augsburg/de/events/test-veranstaltung/2020-01-22daily0.5
diff --git a/tests/sitemap/sitemap_config.py b/tests/sitemap/sitemap_config.py
index 1be0d263fc..7ee1433a94 100644
--- a/tests/sitemap/sitemap_config.py
+++ b/tests/sitemap/sitemap_config.py
@@ -19,7 +19,7 @@
(
"/augsburg/de/sitemap.xml",
"tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml",
- 147,
+ 152,
),
(
"/augsburg/en/sitemap.xml",