feat: add rescue org create and change form

This commit is contained in:
2026-01-06 17:15:52 +01:00
parent 9a3cbffa42
commit f3f7131912
8 changed files with 106 additions and 9 deletions

View File

@@ -0,0 +1,19 @@
Tierschutzorganisation hinzufügen
=================================
Notfellchen führt eine Liste von Tierheime und anderer Tierschutzorganisationen. Die meisten Tierheime wurden von
OpenStreetMap importiert.
Ausnahmen stellen nicht-ortsgebunden Organisationen wie die Rattenhilfe Süd dar. Sie existieren nicht auf der Karte und
können dort auch nicht sinnvoll verzeichnet werden, daher werden sie nur in Notfellchen geführt.
.. warning::
Generell ist es besser eine Tierschutzorganisation in OpenStreetMap anzulegen, anstatt bei Notfellchen. Das
ermöglichtes anderen die Daten ebenfalls zu nutzen und sie werden ggf. durch die OpenStreetMap Community aktuell gehalten.
Hier erklären wir aber trotzdem wie Tierheime direkt in Notfellchen hinzugefügt werden können, damit es schneller geht
diese Anzulegen und ihnen Vermittlungen zuzuweisen.
Organisationen in Notfellchen hinzufügen
----------------------------------------

View File

@@ -201,3 +201,14 @@ class CloseAdoptionNoticeForm(forms.ModelForm):
class Meta: class Meta:
model = AdoptionNotice model = AdoptionNotice
fields = ('adoption_notice_status',) fields = ('adoption_notice_status',)
class RescueOrgForm(forms.ModelForm):
template_name = "fellchensammlung/forms/form_snippets.html"
class Meta:
model = RescueOrganization
fields = ('name', 'allows_using_materials', 'location_string', "email", "phone_number", "website", "instagram",
"facebook", "fediverse_profile", "internal_comment", "description", "external_source_identifier",
"external_object_identifier",
"parent_org")

View File

@@ -167,10 +167,12 @@ class RescueOrganization(models.Model):
internal_comment = models.TextField(verbose_name=_("Interner Kommentar"), null=True, blank=True, ) internal_comment = models.TextField(verbose_name=_("Interner Kommentar"), null=True, blank=True, )
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung')) # Markdown allowed description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung')) # Markdown allowed
external_object_identifier = models.CharField(max_length=200, null=True, blank=True, external_object_identifier = models.CharField(max_length=200, null=True, blank=True,
verbose_name=_('External Object Identifier')) verbose_name=_('External Object Identifier'),
help_text=_("Id des Objekts in der externen Datenbank (kann leer gelassen werden)"))
external_source_identifier = models.CharField(max_length=200, null=True, blank=True, external_source_identifier = models.CharField(max_length=200, null=True, blank=True,
choices=ExternalSourceChoices.choices, choices=ExternalSourceChoices.choices,
verbose_name=_('External Source Identifier')) verbose_name=_('External Source Identifier'),
help_text=_("Name der Datenbank aus der die Tierschutzorganisation importiert wurde (kann leer gelassen werden)"))
exclude_from_check = models.BooleanField(default=False, verbose_name=_('Von Prüfung ausschließen'), exclude_from_check = models.BooleanField(default=False, verbose_name=_('Von Prüfung ausschließen'),
help_text=_("Organisation von der manuellen Überprüfung ausschließen, " help_text=_("Organisation von der manuellen Überprüfung ausschließen, "
"z.B. weil Tiere nicht online geführt werden")) "z.B. weil Tiere nicht online geführt werden"))
@@ -183,7 +185,7 @@ class RescueOrganization(models.Model):
ongoing_communication = models.BooleanField(default=False, verbose_name=_('In aktiver Kommunikation'), ongoing_communication = models.BooleanField(default=False, verbose_name=_('In aktiver Kommunikation'),
help_text=_( help_text=_(
"Es findet gerade Kommunikation zwischen Notfellchen und der Organisation statt.")) "Es findet gerade Kommunikation zwischen Notfellchen und der Organisation statt."))
parent_org = models.ForeignKey("RescueOrganization", on_delete=models.PROTECT, blank=True, null=True) parent_org = models.ForeignKey("RescueOrganization", on_delete=models.PROTECT, blank=True, null=True, verbose_name=_("Übergeordnete Organisation"))
# allows to specify if a rescue organization has a specialization for dedicated species # allows to specify if a rescue organization has a specialization for dedicated species
specializations = models.ManyToManyField(Species, blank=True) specializations = models.ManyToManyField(Species, blank=True)
twenty_id = models.UUIDField(verbose_name=_("Twenty-ID"), null=True, blank=True, twenty_id = models.UUIDField(verbose_name=_("Twenty-ID"), null=True, blank=True,

View File

@@ -24,11 +24,20 @@
<div class="block"> <div class="block">
{% include "fellchensammlung/partials/rescue_orgs/partial-rescue-organization-contact.html" %} {% include "fellchensammlung/partials/rescue_orgs/partial-rescue-organization-contact.html" %}
</div> </div>
{% trust_level "MODERATOR" as coordinator_trust_level %}
{% if request.user.trust_level >= coordinator_trust_level %}
<div class="block">
<a class="button is-primary is-fullwidth"
href="{% url 'rescue-organization-edit' rescue_organization_id=org.pk %}">
<i class="fa-solid fa-tools fa-fw"></i> {% translate 'Bearbeiten' %}
</a>
</div>
<div class="block"> <div class="block">
<a class="button is-warning is-fullwidth" href="{% url org_meta|admin_urlname:'change' org.pk %}"> <a class="button is-warning is-fullwidth" href="{% url org_meta|admin_urlname:'change' org.pk %}">
<i class="fa-solid fa-tools fa-fw"></i> Admin interface <i class="fa-solid fa-tools fa-fw"></i> Admin interface
</a> </a>
</div> </div>
{% endif %}
</div> </div>
<div class="column"> <div class="column">
{% include "fellchensammlung/partials/partial-map.html" %} {% include "fellchensammlung/partials/partial-map.html" %}

View File

@@ -102,6 +102,10 @@
<a class="nav-link " href="{% url "modtools" %}"> <a class="nav-link " href="{% url "modtools" %}">
{% translate 'Moderationstools' %} {% translate 'Moderationstools' %}
</a> </a>
<br>
<a class="nav-link " href="{% url "rescue-organization-create" %}">
{% translate 'Tierschutzorganisation hinzufügen' %}
</a>
{% endif %} {% endif %}
<br/> <br/>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}

View File

@@ -0,0 +1,19 @@
{% extends "fellchensammlung/base.html" %}
{% load i18n %}
{% load widget_tweaks %}
{% block content %}
<h1 class="title is-1">
{% if rescue_org %}
{{ rescue_org.name }}
{% else %}
{% translate 'Tierschutzorganisation hinzufügen' %}
{% endif %}
</h1>
<form method="post">
{% csrf_token %}
{{ form }}
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
</form>
{% endblock %}

View File

@@ -47,6 +47,9 @@ urlpatterns = [
path("tierschutzorganisationen/", views.list_rescue_organizations, name="rescue-organizations"), path("tierschutzorganisationen/", views.list_rescue_organizations, name="rescue-organizations"),
path("tierschutzorganisationen/<int:rescue_organization_id>/", views.detail_view_rescue_organization, path("tierschutzorganisationen/<int:rescue_organization_id>/", views.detail_view_rescue_organization,
name="rescue-organization-detail"), name="rescue-organization-detail"),
path("tierschutzorganisationen/add", views.rescue_org_create_or_update, name="rescue-organization-create"),
path("tierschutzorganisationen/<int:rescue_organization_id>/edit", views.rescue_org_create_or_update,
name="rescue-organization-edit"),
path("tierschutzorganisationen/<int:rescue_organization_id>/exkludieren", views.exclude_from_regular_check, path("tierschutzorganisationen/<int:rescue_organization_id>/exkludieren", views.exclude_from_regular_check,
name="rescue-organization-exclude"), name="rescue-organization-exclude"),
path("tierschutzorganisationen/add-exclusion-reason", views.update_exclusion_reason, path("tierschutzorganisationen/add-exclusion-reason", views.update_exclusion_reason,

View File

@@ -25,7 +25,8 @@ from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, Moderatio
ImportantLocation, SpeciesSpecificURL, NotificationTypeChoices, SocialMediaPost ImportantLocation, SpeciesSpecificURL, NotificationTypeChoices, SocialMediaPost
from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, \ from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, \
CommentForm, ReportCommentForm, AnimalForm, AdoptionNoticeFormAutoAnimal, SpeciesURLForm, RescueOrgInternalComment, \ CommentForm, ReportCommentForm, AnimalForm, AdoptionNoticeFormAutoAnimal, SpeciesURLForm, RescueOrgInternalComment, \
UpdateRescueOrgRegularCheckStatus, UserModCommentForm, CloseAdoptionNoticeForm, RescueOrgSearchByNameForm UpdateRescueOrgRegularCheckStatus, UserModCommentForm, CloseAdoptionNoticeForm, RescueOrgSearchByNameForm, \
RescueOrgForm
from .models import Language, Announcement from .models import Language, Announcement
from .tools import i18n, img from .tools import i18n, img
from .tools.fedi import post_an_to_fedi from .tools.fedi import post_an_to_fedi
@@ -1045,3 +1046,32 @@ def adoption_notice_sharepic(request, adoption_notice_id):
adoption_notice = get_object_or_404(AdoptionNotice, pk=adoption_notice_id) adoption_notice = get_object_or_404(AdoptionNotice, pk=adoption_notice_id)
svg_data = img.export_svg(adoption_notice) svg_data = img.export_svg(adoption_notice)
return HttpResponse(svg_data, content_type="image/svg+xml") return HttpResponse(svg_data, content_type="image/svg+xml")
@login_required
def rescue_org_create_or_update(request, rescue_organization_id=None):
"""
Create or update a rescue organization
"""
# Only users that are mods to create or edit it
if not user_is_trust_level_or_above(request.user, TrustLevel.MODERATOR):
return HttpResponseForbidden()
if rescue_organization_id:
rescue_org = get_object_or_404(RescueOrganization, pk=rescue_organization_id)
else:
rescue_org = None
if request.method == 'POST':
form = RescueOrgForm(request.POST, instance=rescue_org)
if form.is_valid():
rescue_org = form.save()
"""Log"""
Log.objects.create(user=request.user, action="add_rescue_org",
text=f"{request.user} hat Tierschutzorganisation {rescue_org.pk} geändert")
return redirect(reverse("rescue-organization-detail", args=[rescue_org.pk], ))
else:
form = RescueOrgForm(instance=rescue_org)
return render(request, 'fellchensammlung/forms/form-rescue-organization.html',
context={"form": form, "rescue_org": rescue_org})