import logging from datetime import timedelta from django.contrib.auth.views import redirect_to_login from django.core.paginator import Paginator from django.http import HttpResponseRedirect, JsonResponse, HttpResponse from django.http.response import HttpResponseForbidden from django.shortcuts import render, redirect, get_object_or_404 from django.urls import reverse from django.contrib.auth.decorators import login_required from django.utils import translation, timezone from django.core.exceptions import PermissionDenied from django.contrib.auth.decorators import user_passes_test from django.core.serializers import serialize from django.utils.translation import gettext_lazy as _ import json from .mail import notify_mods_new_report from notfellchen import settings from fellchensammlung import logger from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \ User, Location, AdoptionNoticeStatus, Subscriptions, Notification, RescueOrganization, \ Species, Log, Timestamp, TrustLevel, SexChoicesWithAll, SearchSubscription, \ ImportantLocation, SpeciesSpecificURL, NotificationTypeChoices from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, \ CommentForm, ReportCommentForm, AnimalForm, AdoptionNoticeFormAutoAnimal, SpeciesURLForm, RescueOrgInternalComment from .models import Language, Announcement from .tools import i18n from .tools.geo import GeoAPI, zoom_level_for_radius from .tools.metrics import gather_metrics_data from .tools.admin import clean_locations, get_unchecked_adoption_notices, deactivate_unchecked_adoption_notices, \ deactivate_404_adoption_notices, send_test_email from .tasks import post_adoption_notice_save from rest_framework.authtoken.models import Token from .tools.search import Search def user_is_trust_level_or_above(user, trust_level=TrustLevel.MODERATOR): return user.is_authenticated and user.trust_level >= trust_level def user_is_owner_or_trust_level(user, django_object, trust_level=TrustLevel.MODERATOR): return user.is_authenticated and ( user.trust_level == trust_level or django_object.owner == user) def fail_if_user_not_owner_or_trust_level(user, django_object, trust_level=TrustLevel.MODERATOR): if not user_is_owner_or_trust_level(user, django_object, trust_level): raise PermissionDenied def index(request): """View function for home page of site.""" latest_adoption_list = AdoptionNotice.objects.filter( adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE).order_by("-created_at") active_adoptions = [adoption for adoption in latest_adoption_list if adoption.is_active] language_code = translation.get_language() lang = Language.objects.get(languagecode=language_code) active_announcements = Announcement.get_active_announcements(lang) context = {"adoption_notices": active_adoptions[:4], "show_ANs": True, "announcements": active_announcements} Text.get_texts(["how_to", "introduction"], lang, context) return render(request, 'fellchensammlung/index.html', context=context) def change_language(request): if request.method == 'POST': language_code = request.POST.get('language') if language_code: if language_code != settings.LANGUAGE_CODE and language_code in list(zip(*settings.LANGUAGES))[0]: redirect_path = f'/{language_code}/' elif language_code == settings.LANGUAGE_CODE: redirect_path = '/' else: response = HttpResponseRedirect('/') return response translation.activate(language_code) response = HttpResponseRedirect(redirect_path) response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language_code) return response return render(request, "fellchensammlung/errors/403.html", status=403) def adoption_notice_detail(request, adoption_notice_id): adoption_notice = AdoptionNotice.objects.get(id=adoption_notice_id) if request.user.is_authenticated: try: subscription = Subscriptions.objects.get(owner=request.user, adoption_notice=adoption_notice) is_subscribed = True except Subscriptions.DoesNotExist: is_subscribed = False else: is_subscribed = False has_edit_permission = user_is_owner_or_trust_level(request.user, adoption_notice) if request.method == 'POST': action = request.POST.get("action") if request.user.is_authenticated: if action == "comment": comment_form = CommentForm(request.POST) if comment_form.is_valid(): comment_instance = comment_form.save(commit=False) comment_instance.adoption_notice_id = adoption_notice_id comment_instance.user = request.user comment_instance.save() """Log""" Log.objects.create(user=request.user, action="comment", text=f"{request.user} hat Kommentar {comment_instance.pk} zur Vermittlung {adoption_notice_id} hinzugefügt") # Auto-subscribe user to adoption notice subscription, created = Subscriptions.objects.get_or_create(adoption_notice=adoption_notice, owner=request.user) subscription.save() # Notify users that a comment was added for subscription in adoption_notice.get_subscriptions(): # Create a notification but only if the user is not the one that posted the comment if subscription.owner != request.user: notification = Notification(user_to_notify=subscription.owner, adoption_notice=adoption_notice, notification_type=NotificationTypeChoices.NEW_COMMENT, title=f"{adoption_notice.name} - Neuer Kommentar", text=f"{request.user}: {comment_instance.text}", comment=comment_instance) notification.save() else: comment_form = CommentForm(instance=adoption_notice) if action == "subscribe": Subscriptions.objects.create(owner=request.user, adoption_notice=adoption_notice) is_subscribed = True if action == "unsubscribe": subscription.delete() is_subscribed = False elif action == "subscribe": return redirect_to_login(next=request.path) else: return HttpResponseForbidden() else: comment_form = CommentForm(instance=adoption_notice) context = {"adoption_notice": adoption_notice, "comment_form": comment_form, "user": request.user, "has_edit_permission": has_edit_permission, "is_subscribed": is_subscribed} return render(request, 'fellchensammlung/details/detail-adoption-notice.html', context=context) @login_required() def adoption_notice_edit(request, adoption_notice_id): """ Form to update adoption notices """ adoption_notice = AdoptionNotice.objects.get(pk=adoption_notice_id) fail_if_user_not_owner_or_trust_level(request.user, adoption_notice) if request.method == 'POST': form = AdoptionNoticeForm(request.POST, instance=adoption_notice) if form.is_valid(): adoption_notice_instance = form.save() """Search the location given in the location string and add it to the adoption notice""" location = Location.get_location_from_string(adoption_notice_instance.location_string) adoption_notice_instance.location = location adoption_notice_instance.save() """Log""" Log.objects.create(user=request.user, action="adoption_notice_edit", text=f"{request.user} hat Vermittlung {adoption_notice.pk} geändert") return redirect(reverse("adoption-notice-detail", args=[adoption_notice_instance.pk], )) else: form = AdoptionNoticeForm(instance=adoption_notice) return render(request, 'fellchensammlung/forms/form-adoption-notice-basic.html', context={"form": form}) def search_important_locations(request, important_location_slug): i_location = get_object_or_404(ImportantLocation, slug=important_location_slug) search = Search() search.search_from_predefined_i_location(i_location) site_title = _("Ratten in %(location_name)s") % {"location_name": i_location.name} site_description = _("Ratten in Tierheimen und Rattenhilfen in der Nähe von %(location_name)s suchen.") % { "location_name": i_location.name} canonical_url = reverse("search-by-location", args=[i_location.slug]) context = {"adoption_notices": search.get_adoption_notices(), "search_form": search.search_form, "place_not_found": search.place_not_found, "subscribed_search": None, "searched": False, "map_center": search.position, "search_center": search.position, "map_pins": [search], "location": search.location, "search_radius": search.max_distance, "zoom_level": zoom_level_for_radius(search.max_distance), "geocoding_api_url": settings.GEOCODING_API_URL, "show_ANs": True, "site_title": site_title, "site_description": site_description, "canonical_url": canonical_url} return render(request, 'fellchensammlung/search.html', context=context) def search(request, templatename="fellchensammlung/search.html"): # A user just visiting the search site did not search, only upon completing the search form a user has really # searched. This will toggle the "subscribe" button searched = False search = Search() search.search_from_request(request) if request.method == 'POST': searched = True if "subscribe_to_search" in request.POST: # Make sure user is logged in if not request.user.is_authenticated: return redirect_to_login(next=request.path) search.subscribe(request.user) if "unsubscribe_to_search" in request.POST: if not request.user.is_authenticated: return redirect_to_login(next=request.path) search_subscription = SearchSubscription.objects.get(pk=request.POST["unsubscribe_to_search"]) if search_subscription.owner == request.user: search_subscription.delete() else: raise PermissionDenied if request.user.is_authenticated: subscribed_search = search.get_subscription_or_none(request.user) else: subscribed_search = None site_title = _("Suche") site_description = _("Ratten in Tierheimen und Rattenhilfen in der Nähe suchen.") canonical_url = reverse("search") context = {"adoption_notices": search.get_adoption_notices(), "search_form": search.search_form, "place_not_found": search.place_not_found, "subscribed_search": subscribed_search, "searched": searched, "map_center": search.position, "search_center": search.position, "map_pins": [search], "location": search.location, "search_radius": search.max_distance, "zoom_level": zoom_level_for_radius(search.max_distance), "geocoding_api_url": settings.GEOCODING_API_URL, "show_ANs": True, "site_title": site_title, "site_description": site_description, "canonical_url": canonical_url} return render(request, templatename, context=context) @login_required def add_adoption_notice(request): if request.method == 'POST': form = AdoptionNoticeFormAutoAnimal(request.POST) if form.is_valid(): an_instance = form.save(commit=False) an_instance.owner = request.user if request.user.trust_level >= TrustLevel.MODERATOR: an_instance.set_active() else: an_instance.set_unchecked() # Get the species and number of animals from the form species = form.cleaned_data["species"] sex = form.cleaned_data["sex"] num_animals = form.cleaned_data["num_animals"] date_of_birth = form.cleaned_data["date_of_birth"] for i in range(0, num_animals): Animal.objects.create(owner=request.user, name=f"{species} {i + 1}", adoption_notice=an_instance, species=species, sex=sex, date_of_birth=date_of_birth) """Log""" Log.objects.create(user=request.user, action="add_adoption_notice", text=f"{request.user} hat Vermittlung {an_instance.pk} hinzugefügt") """Spin up a task that adds the location and notifies search subscribers""" post_adoption_notice_save.delay(an_instance.id) """Subscriptions""" # Automatically subscribe user that created AN to AN Subscriptions.objects.create(owner=request.user, adoption_notice=an_instance) return redirect(reverse("adoption-notice-detail", args=[an_instance.pk])) else: print(form.errors) else: form = AdoptionNoticeFormAutoAnimal() return render(request, 'fellchensammlung/forms/form-add-adoption.html', {'form': form}) @login_required def adoption_notice_add_animal(request, adoption_notice_id): # Only users that are mods or owners of the adoption notice are allowed to add to it adoption_notice = AdoptionNotice.objects.get(pk=adoption_notice_id) fail_if_user_not_owner_or_trust_level(request.user, adoption_notice) if request.method == 'POST': form = AnimalForm(request.POST, request.FILES) if form.is_valid(): instance = form.save(commit=False) instance.adoption_notice_id = adoption_notice_id instance.owner = request.user instance.save() form.save_m2m() if "save-and-add-another-animal" in request.POST: form = AnimalForm() return render(request, 'fellchensammlung/forms/form-add-animal-to-adoption.html', {'form': form}) else: return redirect(reverse("adoption-notice-detail", args=[adoption_notice_id])) else: form = AnimalForm() return render(request, 'fellchensammlung/forms/form-add-animal-to-adoption.html', {'form': form}) @login_required def add_photo_to_animal(request, animal_id): animal = Animal.objects.get(id=animal_id) # Only users that are mods or owners of the animal are allowed to add to it fail_if_user_not_owner_or_trust_level(request.user, animal) if request.method == 'POST': form = ImageForm(request.POST, request.FILES) if form.is_valid(): instance = form.save(commit=False) instance.owner = request.user instance.save() animal.photos.add(instance) """Log""" Log.objects.create(user=request.user, action="add_photo_to_animal", text=f"{request.user} hat Foto {instance.pk} zum Tier {animal.pk} hinzugefügt") if "save-and-add-another" in request.POST: form = ImageForm(in_flow=True) return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) else: return redirect(reverse("adoption-notice-detail", args=[animal.adoption_notice.pk], )) else: return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) else: form = ImageForm(in_flow=True) return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) @login_required def add_photo_to_adoption_notice(request, adoption_notice_id): adoption_notice = AdoptionNotice.objects.get(id=adoption_notice_id) # Only users that are mods or owners of the adoption notice are allowed to add to it fail_if_user_not_owner_or_trust_level(request.user, adoption_notice) if request.method == 'POST': form = ImageForm(request.POST, request.FILES) if form.is_valid(): instance = form.save(commit=False) instance.owner = request.user instance.save() adoption_notice.photos.add(instance) """Log""" Log.objects.create(user=request.user, action="add_photo_to_animal", text=f"{request.user} hat Foto {instance.pk} zur Vermittlung {adoption_notice.pk} hinzugefügt") if "save-and-add-another" in request.POST: form = ImageForm(in_flow=True) return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) else: return redirect(reverse("adoption-notice-detail", args=[adoption_notice_id])) else: return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) else: form = ImageForm(in_flow=True) return render(request, 'fellchensammlung/forms/form-image.html', {'form': form}) @login_required def animal_edit(request, animal_id): """ View implements the following methods * Updating an Animal """ animal = Animal.objects.get(pk=animal_id) # Only users that are mods or owners of the animal are allowed to edit it fail_if_user_not_owner_or_trust_level(request.user, animal) if request.method == 'POST': form = AnimalForm(request.POST, instance=animal) if form.is_valid(): animal = form.save() """Log""" Log.objects.create(user=request.user, action="add_photo_to_animal", text=f"{request.user} hat Tier {animal.pk} zum Tier geändert") return redirect(reverse("adoption-notice-detail", args=[animal.adoption_notice.pk], )) else: form = AnimalForm(instance=animal) return render(request, 'fellchensammlung/forms/form-animal.html', context={"form": form, "animal": animal}) @login_required def animal_delete(request, animal_id): """ Shows a conformation page from which a user can delete an animal or go back to the adoption notice. """ animal = Animal.objects.get(pk=animal_id) # Only users that are mods or owners of the animal are allowed to edit it fail_if_user_not_owner_or_trust_level(request.user, animal) if request.method == 'POST': if "delete" in request.POST: # First delete related images, then animal images = animal.get_photos() for image in images: image.delete() animal.delete() """Log""" Log.objects.create(user=request.user, action="delete_animal", text=f"{request.user} hat Tier {animal.pk} gelöscht") return redirect(reverse("adoption-notice-detail", args=[animal.adoption_notice.pk], )) return render(request, 'fellchensammlung/forms/form-delete-animal.html', context={"animal": animal}) def about(request): context = i18n.get_texts_by_language(["about_us", "faq"]) return render( request, "fellchensammlung/about.html", context=context ) def render_text(request, text): context = {"text": text} return render( request, "fellchensammlung/one-text.html", context=context ) def imprint(request): text = i18n.get_text_by_language("imprint") return render_text(request, text) def privacy(request): text = i18n.get_text_by_language("privacy_statement") return render_text(request, text) def terms_of_service(request): text = i18n.get_text_by_language("terms_of_service") rules = Rule.objects.all() context = {"rules": rules, "text": text} return render( request, "fellchensammlung/terms-of-service.html", context=context ) def report_adoption(request, adoption_notice_id): """ Form to report adoption notices """ if request.method == 'POST': form = ReportAdoptionNoticeForm(request.POST) if form.is_valid(): report_instance = form.save(commit=False) report_instance.adoption_notice_id = adoption_notice_id report_instance.status = Report.WAITING report_instance.save() form.save_m2m() notify_mods_new_report(report_instance, NotificationTypeChoices.NEW_REPORT_AN) return redirect(reverse("report-detail-success", args=[report_instance.pk], )) else: form = ReportAdoptionNoticeForm() return render(request, 'fellchensammlung/forms/form-report.html', {'form': form}) def report_comment(request, comment_id): """ Form to report comments """ if request.method == 'POST': form = ReportCommentForm(request.POST) if form.is_valid(): report_instance = form.save(commit=False) report_instance.reported_comment_id = comment_id report_instance.status = Report.WAITING report_instance.save() form.save_m2m() notify_mods_new_report(report_instance, NotificationTypeChoices.NEW_REPORT_COMMENT) return redirect(reverse("report-detail-success", args=[report_instance.pk], )) else: form = ReportCommentForm() return render(request, 'fellchensammlung/forms/form-report.html', {'form': form}) def report_detail(request, report_id, form_complete=False): """ Detailed view of a report, including moderation actions """ # Prefetching reduces the number of queries to the database that are needed (see reported_content) report = Report.objects.select_related("reportadoptionnotice", "reportcomment").get(pk=report_id) moderation_actions = ModerationAction.objects.filter(report_id=report_id) is_mod_or_above = user_is_trust_level_or_above(request.user, TrustLevel.MODERATOR) context = {"report": report, "moderation_actions": moderation_actions, "form_complete": form_complete, "is_mod_or_above": is_mod_or_above} return render(request, 'fellchensammlung/details/detail-report.html', context) def report_detail_success(request, report_id): """ Calls the report detail view with form_complete set to true, so success message shows """ return report_detail(request, report_id, form_complete=True) def user_detail(request, user, token=None): context = {"user": user, "adoption_notices": AdoptionNotice.objects.filter(owner=user), "notifications": Notification.objects.filter(user_to_notify=user, read=False), "search_subscriptions": SearchSubscription.objects.filter(owner=user), } if token is not None: context["token"] = token return render(request, 'fellchensammlung/details/detail-user.html', context=context) @login_required def user_by_id(request, user_id): user = User.objects.get(id=user_id) # Only users that are mods or owners of the user are allowed to view fail_if_user_not_owner_or_trust_level(request.user, user) if user == request.user: return my_profile(request) else: return user_detail(request, user) def process_notification_actions(request, action): """ As multiple views allow to mark notifications as read, this function can be used to process these actions The function allows users to mark only their own notifications as read. """ if action == "notification_mark_read": notification_id = request.POST.get("notification_id") notification = Notification.objects.get(pk=notification_id) # Ensures a user can only mark their own notifications as read if not notification.user_to_notify == request.user: return render(request, "fellchensammlung/errors/403.html", status=403) notification.mark_read() elif action == "notification_mark_all_read": notifications = Notification.objects.filter(user=request.user, mark_read=False) for notification in notifications: notification.mark_read() return None @login_required() def my_profile(request): if request.method == 'POST': if "create_token" in request.POST: Token.objects.create(user=request.user) elif "delete_token" in request.POST: Token.objects.get(user=request.user).delete() elif "toggle_email_notifications" in request.POST: user = request.user user.email_notifications = not user.email_notifications user.save() action = request.POST.get("action") process_notification_actions(request, action) if action == "search_subscription_delete": search_subscription_id = request.POST.get("search_subscription_id") SearchSubscription.objects.get(pk=search_subscription_id).delete() logging.info(f"Deleted subscription {search_subscription_id}") try: token = Token.objects.get(user=request.user) except Token.DoesNotExist: token = None return user_detail(request, request.user, token) @login_required() def my_notifications(request): if request.method == 'POST': action = request.POST.get("action") process_notification_actions(request, action) context = {"notifications_unread": Notification.objects.filter(user_to_notify=request.user, read=False).order_by("-created_at"), "notifications_read_last": Notification.objects.filter(user_to_notify=request.user, read=True).order_by("-read_at") } return render(request, 'fellchensammlung/notifications.html', context=context) @user_passes_test(user_is_trust_level_or_above) def modqueue(request): open_reports = Report.objects.select_related("reportadoptionnotice", "reportcomment").filter(status=Report.WAITING) context = {"reports": open_reports} return render(request, 'fellchensammlung/modqueue.html', context=context) @login_required def updatequeue(request): if request.method == "POST": adoption_notice = AdoptionNotice.objects.get(id=request.POST.get("adoption_notice_id")) edit_permission = request.user == adoption_notice.owner or 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_inactive": adoption_notice.set_closed() if action == "checked_active": adoption_notice.set_active() if user_is_trust_level_or_above(request.user, TrustLevel.MODERATOR): last_checked_adoption_list = AdoptionNotice.objects.order_by("last_checked") else: last_checked_adoption_list = AdoptionNotice.objects.filter(owner=request.user).order_by("last_checked") adoption_notices_active = [adoption for adoption in last_checked_adoption_list if adoption.is_active] adoption_notices_disabled = [adoption for adoption in last_checked_adoption_list if adoption.is_disabled_unchecked] context = {"adoption_notices_disabled": adoption_notices_disabled, "adoption_notices_active": adoption_notices_active} return render(request, 'fellchensammlung/updatequeue.html', context=context) def map(request): context = {"show_ANs": True, "show_rescue_orgs": True} return render(request, 'fellchensammlung/map.html', context=context) def metrics(request): data = gather_metrics_data() return JsonResponse(data) @login_required def instance_health_check(request): """ Allows an administrator to check common problems of an instance """ if request.method == "POST": action = request.POST.get("action") if action == "clean_locations": clean_locations(quiet=False) elif action == "deactivate_unchecked_adoption_notices": deactivate_unchecked_adoption_notices() elif action == "deactivate_404": deactivate_404_adoption_notices() elif action == "send_test_email": target_email = request.POST.get("test_email_address") send_test_email(target_email) number_of_adoption_notices = AdoptionNotice.objects.all().count() none_geocoded_adoption_notices = AdoptionNotice.objects.filter(location__isnull=True) number_not_geocoded_adoption_notices = len(none_geocoded_adoption_notices) number_of_rescue_orgs = RescueOrganization.objects.all().count() none_geocoded_rescue_orgs = RescueOrganization.objects.filter(location__isnull=True) number_not_geocoded_rescue_orgs = len(none_geocoded_rescue_orgs) unchecked_ans = get_unchecked_adoption_notices() number_unchecked_ans = len(unchecked_ans) # CHECK FOR MISSING TEXTS languages = Language.objects.all() texts = Text.objects.all() text_codes = set([text.text_code for text in texts]) missing_texts = [] for language in languages: for text_code in text_codes: try: Text.objects.get(text_code=text_code, language=language) except Text.DoesNotExist: missing_texts.append((text_code, language)) # Timestamps timestamps = Timestamp.objects.all() context = { "number_of_adoption_notices": number_of_adoption_notices, "number_not_geocoded_adoption_notices": number_not_geocoded_adoption_notices, "none_geocoded_adoption_notices": none_geocoded_adoption_notices, "number_of_rescue_orgs": number_of_rescue_orgs, "number_not_geocoded_rescue_orgs": number_not_geocoded_rescue_orgs, "none_geocoded_rescue_orgs": none_geocoded_rescue_orgs, "missing_texts": missing_texts, "number_unchecked_ans": number_unchecked_ans, "unchecked_ans": unchecked_ans, "timestamps": timestamps } return render(request, 'fellchensammlung/instance-health-check.html', context=context) def external_site_warning(request, template_name='fellchensammlung/external-site-warning.html'): url = request.GET.get("url") context = {"url": url} language_code = translation.get_language() lang = Language.objects.get(languagecode=language_code) texts = Text.get_texts(["external_site_warning", "good_adoption_practices"], language=lang) context.update(texts) return render(request, template_name, context=context) def list_rescue_organizations(request, template='fellchensammlung/animal-shelters.html'): rescue_organizations = RescueOrganization.objects.all() paginator = Paginator(rescue_organizations, 10) page_number = request.GET.get("page") if page_number is None: page_number = 1 else: try: page_number = int(page_number) except ValueError: page_number = 1 rescue_organizations_to_list = paginator.get_page(page_number) context = {"rescue_organizations_to_list": rescue_organizations_to_list, "show_rescue_orgs": True, "elided_page_range": paginator.get_elided_page_range(page_number, on_each_side=2, on_ends=1)} return render(request, template, context=context) def detail_view_rescue_organization(request, rescue_organization_id, template='fellchensammlung/details/detail-rescue-organization.html'): org = RescueOrganization.objects.get(pk=rescue_organization_id) org_meta = org._meta return render(request, template, context={"org": org, "map_center": org.position, "zoom_level": 6, "map_pins": [org], "org_meta": org_meta}) def export_own_profile(request): user = request.user ANs = AdoptionNotice.objects.filter(owner=user) user_as_json = serialize('json', [user]) user_editable = json.loads(user_as_json) user_editable[0]["fields"]["password"] = "Password hash redacted for security reasons" user_as_json = json.dumps(user_editable) ANs_as_json = serialize('json', ANs) full_json = f"{user_as_json}, {ANs_as_json}" return HttpResponse(full_json, content_type="application/json") @login_required def rescue_organization_check(request, context=None): if context is None: context = {} 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() elif action == "exclude": rescue_org.set_exclusion_from_checks() elif action == "set_species_url": species_url_form = SpeciesURLForm(request.POST) if species_url_form.is_valid(): species_url_instance = species_url_form.save(commit=False) species_url_instance.rescue_organization_id = rescue_org.id species_url_instance.save() elif action == "update_internal_comment": comment_form = RescueOrgInternalComment(request.POST, instance=rescue_org) if comment_form.is_valid(): comment_form.save() rescue_orgs_to_check = RescueOrganization.objects.filter(exclude_from_check=False).order_by("last_checked")[:10] # Prepare a form for each organization comment_forms = { org.id: RescueOrgInternalComment(instance=org) for org in rescue_orgs_to_check } rescue_orgs_last_checked = RescueOrganization.objects.filter().order_by("-last_checked")[:10] timeframe = timezone.now().date() - timedelta(days=14) num_rescue_orgs_to_check = RescueOrganization.objects.filter(exclude_from_check=False).filter( last_checked__lt=timeframe).count() num_rescue_orgs_checked = RescueOrganization.objects.filter(exclude_from_check=False).filter( last_checked__gte=timeframe).count() try: percentage_checked = 100 * num_rescue_orgs_checked / (num_rescue_orgs_to_check + num_rescue_orgs_checked) except ZeroDivisionError: percentage_checked = 100 context["rescue_orgs_to_check"] = rescue_orgs_to_check context["rescue_orgs_last_checked"] = rescue_orgs_last_checked context["comment_forms"] = comment_forms context["num_rescue_orgs_to_check"] = num_rescue_orgs_to_check context["percentage_checked"] = percentage_checked context["num_rescue_orgs_checked"] = num_rescue_orgs_checked return render(request, 'fellchensammlung/rescue-organization-check.html', context=context) @login_required def rescue_organization_check_dq(request): """ Modified view to allow setting species specific urls DQ = data quality """ context = {"set_species_url_available": True, "set_internal_comment_available": True, "species_url_form": SpeciesURLForm, "internal_comment_form": RescueOrgInternalComment} return rescue_organization_check(request, context) @user_passes_test(user_is_trust_level_or_above) def moderation_tools_overview(request): return render(request, 'fellchensammlung/mod-tool-overview.html')