Compare commits
10 Commits
712c3d32f3
...
9ae64e8cb1
Author | SHA1 | Date | |
---|---|---|---|
9ae64e8cb1 | |||
1b5a0c71e0 | |||
4d4f11c479 | |||
835c89d1d4 | |||
46bf07dd8d | |||
f557672586 | |||
4e27e1be7f | |||
6d390ad21e | |||
2f2543160e | |||
64a9db133e |
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-03-09 08:31
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fellchensammlung', '0037_alter_basenotification_title'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rescueorganization',
|
||||||
|
name='last_checked',
|
||||||
|
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Datum der letzten Prüfung'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-03-09 16:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fellchensammlung', '0038_rescueorganization_last_checked'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rescueorganization',
|
||||||
|
name='last_checked',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, verbose_name='Datum der letzten Prüfung'),
|
||||||
|
),
|
||||||
|
]
|
@ -122,6 +122,7 @@ class RescueOrganization(models.Model):
|
|||||||
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
|
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
last_checked = models.DateTimeField(auto_now_add=True, verbose_name=_('Datum der letzten Prüfung'))
|
||||||
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,
|
||||||
@ -149,7 +150,20 @@ class RescueOrganization(models.Model):
|
|||||||
if self.description is None:
|
if self.description is None:
|
||||||
return ""
|
return ""
|
||||||
if len(self.description) > 200:
|
if len(self.description) > 200:
|
||||||
return self.description[:200] + f" ... [weiterlesen]({self.get_absolute_url()})"
|
return self.description[:200] + _(f" ... [weiterlesen]({self.get_absolute_url()})")
|
||||||
|
|
||||||
|
def set_checked(self):
|
||||||
|
self.last_checked = timezone.now()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_checked_hr(self):
|
||||||
|
time_since_last_checked = timezone.now() - self.last_checked
|
||||||
|
return time_since_as_hr_string(time_since_last_checked)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def species_urls(self):
|
||||||
|
return SpeciesSpecificURL.objects.filter(organization=self)
|
||||||
|
|
||||||
|
|
||||||
# Admins can perform all actions and have the highest trust associated with them
|
# Admins can perform all actions and have the highest trust associated with them
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load custom_tags %}
|
||||||
|
<div class="card">
|
||||||
|
<h1>
|
||||||
|
<a href="{{ rescue_org.get_absolute_url }}">{{ rescue_org.name }}</a>
|
||||||
|
</h1>
|
||||||
|
<i>{% translate 'Zuletzt geprüft:' %} {{ rescue_org.last_checked_hr }}</i>
|
||||||
|
{% if rescue_org.website %}
|
||||||
|
<p>{% translate "Website" %}: {{ rescue_org.website | safe }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<div class="container-edit-buttons">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden"
|
||||||
|
name="rescue_organization_id"
|
||||||
|
value="{{ rescue_org.pk }}">
|
||||||
|
<input type="hidden" name="action" value="checked">
|
||||||
|
<button class="btn" type="submit">{% translate "Organisation geprüft" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,12 @@
|
|||||||
|
{% extends "fellchensammlung/base_generic.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% translate "Aktualitätscheck" %}</h1>
|
||||||
|
<p>{% translate "Überprüfe ob im Tierheim neue Vermittlungen ein Zuhause suchen" %}</p>
|
||||||
|
<div class="container-cards spaced">
|
||||||
|
<h1>{% translate 'Organisation zur Überprüfung' %}</h1>
|
||||||
|
{% for rescue_org in rescue_orgs %}
|
||||||
|
{% include "fellchensammlung/partials/partial-check-rescue-org.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -31,9 +31,11 @@ urlpatterns = [
|
|||||||
# ex: /adoption_notice/7/edit
|
# ex: /adoption_notice/7/edit
|
||||||
path("vermittlung/<int:adoption_notice_id>/edit", views.adoption_notice_edit, name="adoption-notice-edit"),
|
path("vermittlung/<int:adoption_notice_id>/edit", views.adoption_notice_edit, name="adoption-notice-edit"),
|
||||||
# ex: /vermittlung/5/add-photo
|
# ex: /vermittlung/5/add-photo
|
||||||
path("vermittlung/<int:adoption_notice_id>/add-photo", views.add_photo_to_adoption_notice, name="adoption-notice-add-photo"),
|
path("vermittlung/<int:adoption_notice_id>/add-photo", views.add_photo_to_adoption_notice,
|
||||||
|
name="adoption-notice-add-photo"),
|
||||||
# ex: /adoption_notice/2/add-animal
|
# ex: /adoption_notice/2/add-animal
|
||||||
path("vermittlung/<int:adoption_notice_id>/add-animal", views.adoption_notice_add_animal, name="adoption-notice-add-animal"),
|
path("vermittlung/<int:adoption_notice_id>/add-animal", views.adoption_notice_add_animal,
|
||||||
|
name="adoption-notice-add-animal"),
|
||||||
|
|
||||||
path("tierschutzorganisationen/", views.list_rescue_organizations, name="rescue-organizations"),
|
path("tierschutzorganisationen/", views.list_rescue_organizations, name="rescue-organizations"),
|
||||||
path("organisation/<int:rescue_organization_id>/", views.detail_view_rescue_organization,
|
path("organisation/<int:rescue_organization_id>/", views.detail_view_rescue_organization,
|
||||||
@ -57,9 +59,11 @@ urlpatterns = [
|
|||||||
path("meldung/<uuid:report_id>/", views.report_detail, name="report-detail"),
|
path("meldung/<uuid:report_id>/", views.report_detail, name="report-detail"),
|
||||||
path("meldung/<uuid:report_id>/sucess", views.report_detail_success, name="report-detail-success"),
|
path("meldung/<uuid:report_id>/sucess", views.report_detail_success, name="report-detail-success"),
|
||||||
path("modqueue/", views.modqueue, name="modqueue"),
|
path("modqueue/", views.modqueue, name="modqueue"),
|
||||||
|
|
||||||
path("updatequeue/", views.updatequeue, name="updatequeue"),
|
path("updatequeue/", views.updatequeue, name="updatequeue"),
|
||||||
|
|
||||||
|
path("organization-check/", views.rescue_organization_check, name="organization-check"),
|
||||||
|
|
||||||
###########
|
###########
|
||||||
## USERS ##
|
## USERS ##
|
||||||
###########
|
###########
|
||||||
|
@ -639,3 +639,18 @@ def styleguide(request):
|
|||||||
|
|
||||||
context = {"geocoding_api_url": settings.GEOCODING_API_URL, }
|
context = {"geocoding_api_url": settings.GEOCODING_API_URL, }
|
||||||
return render(request, 'fellchensammlung/styleguide.html', context=context)
|
return render(request, 'fellchensammlung/styleguide.html', context=context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def rescue_organization_check(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
rescue_org = RescueOrganization.objects.get(id=request.POST.get("rescue_organization_id"))
|
||||||
|
edit_permission = user_is_trust_level_or_above(request.user, TrustLevel.MODERATOR)
|
||||||
|
if not edit_permission:
|
||||||
|
return render(request, "fellchensammlung/errors/403.html", status=403)
|
||||||
|
action = request.POST.get("action")
|
||||||
|
if action == "checked":
|
||||||
|
rescue_org.set_checked()
|
||||||
|
|
||||||
|
last_checked_rescue_orgs = RescueOrganization.objects.order_by("last_checked")
|
||||||
|
context = {"rescue_orgs": last_checked_rescue_orgs,}
|
||||||
|
return render(request, 'fellchensammlung/rescue-organization-check.html', context=context)
|
||||||
|
@ -89,9 +89,9 @@ CELERY_RESULT_BACKEND = config.get("celery", "backend", fallback="redis://localh
|
|||||||
HEALTHCHECKS_URL = config.get("monitoring", "healthchecks_url", fallback=None)
|
HEALTHCHECKS_URL = config.get("monitoring", "healthchecks_url", fallback=None)
|
||||||
|
|
||||||
""" GEOCODING """
|
""" GEOCODING """
|
||||||
GEOCODING_API_URL = config.get("geocoding", "api_url", fallback="https://nominatim.hyteck.de/search")
|
GEOCODING_API_URL = config.get("geocoding", "api_url", fallback="https://photon.hyteck.de/api")
|
||||||
# GEOCODING_API_FORMAT is allowed to be one of ['nominatim', 'photon']
|
# GEOCODING_API_FORMAT is allowed to be one of ['nominatim', 'photon']
|
||||||
GEOCODING_API_FORMAT = config.get("geocoding", "api_format", fallback="nominatim")
|
GEOCODING_API_FORMAT = config.get("geocoding", "api_format", fallback="photon")
|
||||||
|
|
||||||
""" Tile Server """
|
""" Tile Server """
|
||||||
MAP_TILE_SERVER = config.get("map", "tile_server", fallback="https://tiles.hyteck.de")
|
MAP_TILE_SERVER = config.get("map", "tile_server", fallback="https://tiles.hyteck.de")
|
||||||
|
@ -5,7 +5,8 @@ from django.urls import reverse
|
|||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus, TrustLevel, \
|
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus, TrustLevel, \
|
||||||
Animal, Subscriptions, Comment, CommentNotification
|
Animal, Subscriptions, Comment, CommentNotification, SearchSubscription
|
||||||
|
from fellchensammlung.tools.geo import LocationProxy
|
||||||
from fellchensammlung.views import add_adoption_notice
|
from fellchensammlung.views import add_adoption_notice
|
||||||
|
|
||||||
|
|
||||||
@ -146,6 +147,35 @@ class SearchTest(TestCase):
|
|||||||
self.assertContains(response, "TestAdoption3")
|
self.assertContains(response, "TestAdoption3")
|
||||||
self.assertNotContains(response, "TestAdoption2")
|
self.assertNotContains(response, "TestAdoption2")
|
||||||
|
|
||||||
|
def test_unauthenticated_subscribe(self):
|
||||||
|
response = self.client.post(reverse('search'), {"subscribe_to_search": ""})
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/accounts/login/?next=/suchen/")
|
||||||
|
|
||||||
|
def test_unauthenticated_unsubscribe(self):
|
||||||
|
response = self.client.post(reverse('search'), {"unsubscribe_to_search": 1})
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/accounts/login/?next=/suchen/")
|
||||||
|
|
||||||
|
def test_subscribe(self):
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
response = self.client.post(reverse('search'), {"max_distance": 50, "location_string": "Berlin", "sex": "A",
|
||||||
|
"subscribe_to_search": ""})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTrue(SearchSubscription.objects.filter(owner=User.objects.get(username='testuser0'),
|
||||||
|
max_distance=50).exists())
|
||||||
|
|
||||||
|
def test_unsubscribe(self):
|
||||||
|
user0 = User.objects.get(username='testuser0')
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
location = Location.get_location_from_string("München")
|
||||||
|
subscription = SearchSubscription.objects.create(owner=user0, max_distance=200, location=location, sex="A")
|
||||||
|
response = self.client.post(reverse('search'), {"max_distance": 200, "location_string": "München", "sex": "A",
|
||||||
|
"unsubscribe_to_search": subscription.pk})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertFalse(SearchSubscription.objects.filter(owner=User.objects.get(username='testuser0'),
|
||||||
|
max_distance=200).exists())
|
||||||
|
|
||||||
def test_location_search(self):
|
def test_location_search(self):
|
||||||
response = self.client.post(reverse('search'), {"max_distance": 50, "location_string": "Berlin", "sex": "A"})
|
response = self.client.post(reverse('search'), {"max_distance": 50, "location_string": "Berlin", "sex": "A"})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@ -357,7 +387,8 @@ class AdoptionEditTest(TestCase):
|
|||||||
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
||||||
assert self.client.login(username='testuser0', password='12345')
|
assert self.client.login(username='testuser0', password='12345')
|
||||||
response = self.client.post(reverse("adoption-notice-edit", args=str(an.pk)), data=data, follow=True)
|
response = self.client.post(reverse("adoption-notice-edit", args=str(an.pk)), data=data, follow=True)
|
||||||
self.assertEqual(response.redirect_chain[0][1], 302) # See https://docs.djangoproject.com/en/5.1/topics/testing/tools/
|
self.assertEqual(response.redirect_chain[0][1],
|
||||||
|
302) # See https://docs.djangoproject.com/en/5.1/topics/testing/tools/
|
||||||
self.assertEqual(response.status_code, 200) # Redirects to AN page
|
self.assertEqual(response.status_code, 200) # Redirects to AN page
|
||||||
self.assertContains(response, "Test3")
|
self.assertContains(response, "Test3")
|
||||||
self.assertContains(response, "Mia")
|
self.assertContains(response, "Mia")
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from fellchensammlung.models import User, TrustLevel, AdoptionNotice, Species
|
from docs.conf import language
|
||||||
|
from fellchensammlung.models import User, TrustLevel, AdoptionNotice, Species, Rule, Language, Comment, ReportComment
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +27,19 @@ class BasicViewTest(TestCase):
|
|||||||
for i in range(0, 4):
|
for i in range(0, 4):
|
||||||
AdoptionNotice.objects.get(name=f"TestAdoption{i}").set_active()
|
AdoptionNotice.objects.get(name=f"TestAdoption{i}").set_active()
|
||||||
|
|
||||||
|
rule1 = Rule.objects.create(title="Rule 1", rule_text="Description of r1", rule_identifier="rule1",
|
||||||
|
language=Language.objects.get(name="English"))
|
||||||
|
|
||||||
|
an1 = AdoptionNotice.objects.get(name="TestAdoption0")
|
||||||
|
comment1 = Comment.objects.create(adoption_notice=an1, text="Comment1", user=test_user1)
|
||||||
|
comment2 = Comment.objects.create(adoption_notice=an1, text="Comment2", user=test_user1)
|
||||||
|
comment3 = Comment.objects.create(adoption_notice=an1, text="Comment3", user=test_user1)
|
||||||
|
|
||||||
|
report_comment1 = ReportComment.objects.create(reported_comment=comment1,
|
||||||
|
user_comment="ReportComment1")
|
||||||
|
report_comment1.save()
|
||||||
|
report_comment1.reported_broken_rules.set({rule1,})
|
||||||
|
|
||||||
def test_index_logged_in(self):
|
def test_index_logged_in(self):
|
||||||
self.client.login(username='testuser0', password='12345')
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
|
||||||
@ -41,3 +55,82 @@ class BasicViewTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, "TestAdoption1")
|
self.assertContains(response, "TestAdoption1")
|
||||||
self.assertNotContains(response, "TestAdoption4") # Should not be active, therefore not shown
|
self.assertNotContains(response, "TestAdoption4") # Should not be active, therefore not shown
|
||||||
|
|
||||||
|
def test_about_logged_in(self):
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
response = self.client.get(reverse('about'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Rule 1")
|
||||||
|
|
||||||
|
def test_about_anonymous(self):
|
||||||
|
response = self.client.get(reverse('about'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Rule 1")
|
||||||
|
|
||||||
|
def test_report_adoption_logged_in(self):
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
an = AdoptionNotice.objects.get(name="TestAdoption0")
|
||||||
|
response = self.client.get(reverse('report-adoption-notice', args=str(an.pk)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
data = {"reported_broken_rules": 1, "user_comment": "animal cruelty"}
|
||||||
|
response = self.client.post(reverse('report-adoption-notice', args=str(an.pk)), data=data)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
def test_report_adoption_anonymous(self):
|
||||||
|
an = AdoptionNotice.objects.get(name="TestAdoption0")
|
||||||
|
response = self.client.get(reverse('report-adoption-notice', args=str(an.pk)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
data = {"reported_broken_rules": 1, "user_comment": "animal cruelty"}
|
||||||
|
response = self.client.post(reverse('report-adoption-notice', args=str(an.pk)), data=data)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
def test_report_comment_logged_in(self):
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
c = Comment.objects.get(text="Comment1")
|
||||||
|
response = self.client.get(reverse('report-comment', args=str(c.pk)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
data = {"reported_broken_rules": 1, "user_comment": "animal cruelty"}
|
||||||
|
response = self.client.post(reverse('report-comment', args=str(c.pk)), data=data)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertTrue(ReportComment.objects.filter(reported_comment=c.pk).exists())
|
||||||
|
|
||||||
|
def test_report_comment_anonymous(self):
|
||||||
|
c = Comment.objects.get(text="Comment2")
|
||||||
|
response = self.client.get(reverse('report-comment', args=str(c.pk)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
data = {"reported_broken_rules": 1, "user_comment": "animal cruelty"}
|
||||||
|
response = self.client.post(reverse('report-comment', args=str(c.pk)), data=data)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertTrue(ReportComment.objects.filter(reported_comment=c.pk).exists())
|
||||||
|
|
||||||
|
def test_show_report_details_logged_in(self):
|
||||||
|
self.client.login(username='testuser0', password='12345')
|
||||||
|
report = ReportComment.objects.get(user_comment="ReportComment1")
|
||||||
|
response = self.client.get(reverse('report-detail', args=(report.pk,)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Rule 1")
|
||||||
|
self.assertContains(response, "ReportComment1")
|
||||||
|
self.assertNotContains(response, '<form action="allow" class="">')
|
||||||
|
|
||||||
|
def test_show_report_details_anonymous(self):
|
||||||
|
report = ReportComment.objects.get(user_comment="ReportComment1")
|
||||||
|
response = self.client.get(reverse('report-detail', args=(report.pk,)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Rule 1")
|
||||||
|
self.assertContains(response, "ReportComment1")
|
||||||
|
self.assertNotContains(response, '<form action="allow" class="">')
|
||||||
|
|
||||||
|
def test_show_report_details_admin(self):
|
||||||
|
self.client.login(username='testuser1', password='12345')
|
||||||
|
report = ReportComment.objects.get(user_comment="ReportComment1")
|
||||||
|
response = self.client.get(reverse('report-detail', args=(report.pk,)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Rule 1")
|
||||||
|
self.assertContains(response, "ReportComment1")
|
||||||
|
self.assertContains(response, '<form action="allow" class="">')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user