Compare commits
3 Commits
712c3d32f3
...
ci-test-co
Author | SHA1 | Date | |
---|---|---|---|
c9f46d7547 | |||
9f23f5768c | |||
19210f90cd |
@@ -6,6 +6,9 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- cd docs && make html
|
- cd docs && make html
|
||||||
|
|
||||||
|
when:
|
||||||
|
event: [ tag, push ]
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
image: appleboy/drone-scp
|
image: appleboy/drone-scp
|
||||||
settings:
|
settings:
|
||||||
@@ -19,6 +22,8 @@ steps:
|
|||||||
source: docs/_build/html/
|
source: docs/_build/html/
|
||||||
key:
|
key:
|
||||||
from_secret: ssh_key
|
from_secret: ssh_key
|
||||||
|
when:
|
||||||
|
event: [ tag, push ]
|
||||||
|
|
||||||
|
|
||||||
|
|
14
.woodpecker/test.yml
Normal file
14
.woodpecker/test.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
steps:
|
||||||
|
test:
|
||||||
|
image: python
|
||||||
|
commands:
|
||||||
|
- python -m pip install '.[develop]'
|
||||||
|
- coverage run --source='.' src/manage.py test src && coverage html
|
||||||
|
- coverage html
|
||||||
|
- cat htmlcov/index.html
|
||||||
|
when:
|
||||||
|
event: [tag, push]
|
||||||
|
|
||||||
|
|
@@ -1,97 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
DEFAULT_OSM_DATA_FILE = "osm_data.geojson"
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
"""Parse command-line arguments."""
|
|
||||||
parser = argparse.ArgumentParser(description="Upload animal shelter data to the Notfellchen API.")
|
|
||||||
parser.add_argument("--api-token", type=str, help="API token for authentication.")
|
|
||||||
parser.add_argument("--instance", type=str, help="API instance URL.")
|
|
||||||
parser.add_argument("--data-file", type=str, help="Path to the GeoJSON file containing (only) animal shelters.")
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
|
||||||
"""Get configuration from environment variables or command-line arguments."""
|
|
||||||
args = parse_args()
|
|
||||||
|
|
||||||
api_token = args.api_token or os.getenv("NOTFELLCHEN_API_TOKEN")
|
|
||||||
instance = args.instance or os.getenv("NOTFELLCHEN_INSTANCE")
|
|
||||||
data_file = args.data_file or os.getenv("NOTFELLCHEN_DATA_FILE", DEFAULT_OSM_DATA_FILE)
|
|
||||||
|
|
||||||
if not api_token or not instance:
|
|
||||||
raise ValueError("API token and instance URL must be provided via environment variables or CLI arguments.")
|
|
||||||
|
|
||||||
return api_token, instance, data_file
|
|
||||||
|
|
||||||
|
|
||||||
def load_osm_data(file_path):
|
|
||||||
"""Load OSM data from a GeoJSON file."""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
data = json.load(file)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def load_osm_data(file_path):
|
|
||||||
#Load OSM data from a GeoJSON file.
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
data = json.load(file)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def transform_osm_data(feature):
|
|
||||||
#Transform a single OSM feature into the API payload format
|
|
||||||
prop = feature.get("properties", {})
|
|
||||||
geometry = feature.get("geometry", {})
|
|
||||||
|
|
||||||
return {
|
|
||||||
"name": prop.get("name", "Unnamed Shelter"),
|
|
||||||
"phone": prop.get("phone"),
|
|
||||||
"website": prop.get("website"),
|
|
||||||
"opening_hours": prop.get("opening_hours"),
|
|
||||||
"email": prop.get("email"),
|
|
||||||
"location_string": f'{prop.get("addr:street", "")} {prop.get("addr:housenumber", "")} {prop.get("addr:postcode", "")} {prop.get("addr:city", "")}',
|
|
||||||
"external_object_id": prop.get("@id"),
|
|
||||||
"external_source_id": "OSM"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def send_to_api(data, endpoint, headers):
|
|
||||||
# Send transformed data to the Notfellchen API.
|
|
||||||
response = requests.post(endpoint, headers=headers, json=data)
|
|
||||||
if response.status_code == 201:
|
|
||||||
print(f"Success: Shelter '{data['name']}' uploaded.")
|
|
||||||
elif response.status_code == 400:
|
|
||||||
print(f"Error: Shelter '{data['name']}' already exists or invalid data. {response.text}")
|
|
||||||
else:
|
|
||||||
print(f"Unexpected Error: {response.status_code} - {response.text}")
|
|
||||||
raise ConnectionError
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Get configuration
|
|
||||||
api_token, instance, data_file = get_config()
|
|
||||||
|
|
||||||
# Set headers and endpoint
|
|
||||||
endpoint = f"{instance}/api/organizations/"
|
|
||||||
headers = {
|
|
||||||
"Authorization": f"Token {api_token}",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Step 1: Load OSM data
|
|
||||||
osm_data = load_osm_data(data_file)
|
|
||||||
|
|
||||||
# Step 2: Process each shelter and send it to the API
|
|
||||||
for feature in osm_data.get("features", []):
|
|
||||||
shelter_data = transform_osm_data(feature)
|
|
||||||
send_to_api(shelter_data, endpoint, headers)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@@ -16,7 +16,6 @@ from .serializers import (
|
|||||||
from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image
|
from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image
|
||||||
from drf_spectacular.utils import extend_schema
|
from drf_spectacular.utils import extend_schema
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeApiView(APIView):
|
class AdoptionNoticeApiView(APIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
@@ -85,6 +84,7 @@ class AdoptionNoticeApiView(APIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AnimalApiView(APIView):
|
class AnimalApiView(APIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
@@ -118,7 +118,6 @@ class AnimalApiView(APIView):
|
|||||||
)
|
)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class RescueOrganizationApiView(APIView):
|
class RescueOrganizationApiView(APIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
@@ -160,14 +159,13 @@ class RescueOrganizationApiView(APIView):
|
|||||||
"""
|
"""
|
||||||
serializer = RescueOrgSerializer(data=request.data, context={"request": request})
|
serializer = RescueOrgSerializer(data=request.data, context={"request": request})
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
rescue_org = serializer.save()
|
rescue_org = serializer.save(owner=request.user)
|
||||||
return Response(
|
return Response(
|
||||||
{"message": "Rescue organization created/updated successfully!", "id": rescue_org.id},
|
{"message": "Rescue organization created/updated successfully!", "id": rescue_org.id},
|
||||||
status=status.HTTP_201_CREATED,
|
status=status.HTTP_201_CREATED,
|
||||||
)
|
)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class AddImageApiView(APIView):
|
class AddImageApiView(APIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
from django.contrib.sitemaps import Sitemap
|
|
||||||
from django.urls import reverse
|
|
||||||
from .models import AdoptionNotice, RescueOrganization
|
|
||||||
|
|
||||||
|
|
||||||
class StaticViewSitemap(Sitemap):
|
|
||||||
priority = 0.8
|
|
||||||
changefreq = "weekly"
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
return ["index", "search", "map", "about", "rescue-organizations"]
|
|
||||||
|
|
||||||
def location(self, item):
|
|
||||||
return reverse(item)
|
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeSitemap(Sitemap):
|
|
||||||
priority = 0.5
|
|
||||||
changefreq = "daily"
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
return AdoptionNotice.get_active_ANs()
|
|
||||||
|
|
||||||
def lastmod(self, obj):
|
|
||||||
return obj.updated_at
|
|
||||||
|
|
||||||
|
|
||||||
class AnimalSitemap(Sitemap):
|
|
||||||
priority = 0.2
|
|
||||||
changefreq = "daily"
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
return AdoptionNotice.objects.all()
|
|
||||||
|
|
||||||
def lastmod(self, obj):
|
|
||||||
return obj.updated_at
|
|
||||||
|
|
||||||
|
|
||||||
class RescueOrganizationSitemap(Sitemap):
|
|
||||||
priority = 0.3
|
|
||||||
changefreq = "weekly"
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
return RescueOrganization.objects.all()
|
|
||||||
|
|
||||||
def lastmod(self, obj):
|
|
||||||
return obj.updated_at
|
|
@@ -1,7 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Disallow: /admin/
|
|
||||||
|
|
||||||
User-agent: OpenAI
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
Sitemap: https://notfellchen.org/sitemap.xml
|
|
@@ -1,102 +0,0 @@
|
|||||||
{% extends "fellchensammlung/base_generic.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
{% block title %}<title>{% translate "Styleguide" %}</title>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>This is a heading</h1>
|
|
||||||
<p>And this is a short paragraph below</p>
|
|
||||||
<div class="container-cards">
|
|
||||||
<h2>Card Containers</h2>
|
|
||||||
<div class="card">
|
|
||||||
<h3>I am a card</h3>
|
|
||||||
<p>Cards are responsive. Use them to display multiple items of the same category</p>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<h3>Photos</h3>
|
|
||||||
<p>Cards are responsive. Use them to display multiple items of the same category</p>
|
|
||||||
<img src="{% static 'fellchensammlung/img/example_rat_single.png' %}" alt="A rat sitting on a wooden house">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container-cards">
|
|
||||||
<form class="form-search card half" method="post">
|
|
||||||
<label for="inputA">Input Alpha</label>
|
|
||||||
<input name="inputA" maxlength="200" id="inputA">
|
|
||||||
<label for="inputB">Beta</label>
|
|
||||||
<input name="inputB" maxlength="200" id="inputB">
|
|
||||||
<label for="id_location_string">Ort</label>
|
|
||||||
<input name="location_string" id="id_location_string">
|
|
||||||
<ul id="results"></ul>
|
|
||||||
<div class="container-edit-buttons">
|
|
||||||
<button class="btn" type="submit" value="search" name="search">
|
|
||||||
<i class="fas fa-search"></i> {% trans 'Suchen' %}
|
|
||||||
</button>
|
|
||||||
{% if searched %}
|
|
||||||
{% if subscribed_search %}
|
|
||||||
<button class="btn" type="submit" value="{{ subscribed_search.pk }}"
|
|
||||||
name="unsubscribe_to_search">
|
|
||||||
<i class="fas fa-bell-slash"></i> {% trans 'Suche nicht mehr abonnieren' %}
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn" type="submit" name="subscribe_to_search">
|
|
||||||
<i class="fas fa-bell"></i> {% trans 'Suche abonnieren' %}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if place_not_found %}
|
|
||||||
<p class="error">
|
|
||||||
{% trans 'Ort nicht gefunden' %}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
|
||||||
<div class="card half">
|
|
||||||
{% include "fellchensammlung/partials/partial-map.html" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const locationInput = document.getElementById('id_location_string');
|
|
||||||
const resultsList = document.getElementById('results');
|
|
||||||
const placeIdInput = document.getElementById('place_id');
|
|
||||||
|
|
||||||
locationInput.addEventListener('input', async function () {
|
|
||||||
const query = locationInput.value.trim();
|
|
||||||
|
|
||||||
if (query.length < 3) {
|
|
||||||
resultsList.innerHTML = ''; // Don't search for or show results if input is less than 3 characters
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`{{ geocoding_api_url }}/?q=${encodeURIComponent(query)}&limit=5&lang=de`);
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data && data.features) {
|
|
||||||
resultsList.innerHTML = ''; // Clear previous results
|
|
||||||
|
|
||||||
const locations = data.features.slice(0, 5); // Show only the first 5 results
|
|
||||||
|
|
||||||
locations.forEach(location => {
|
|
||||||
const listItem = document.createElement('li');
|
|
||||||
listItem.classList.add('result-item');
|
|
||||||
listItem.textContent = geojson_to_summary(location);
|
|
||||||
|
|
||||||
// Add event when user clicks on a result location
|
|
||||||
listItem.addEventListener('click', () => {
|
|
||||||
|
|
||||||
locationInput.value = geojson_to_searchable_string(location); // Set input field to selected location
|
|
||||||
resultsList.innerHTML = ''; // Clear the results after selecting a location
|
|
||||||
});
|
|
||||||
|
|
||||||
resultsList.appendChild(listItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching location data:', error);
|
|
||||||
resultsList.innerHTML = '<li class="result-item">Error fetching data. Please try again.</li>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
@@ -7,15 +7,6 @@ from .feeds import LatestAdoptionNoticesFeed
|
|||||||
from . import views
|
from . import views
|
||||||
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
||||||
|
|
||||||
from django.contrib.sitemaps.views import sitemap
|
|
||||||
from .sitemap import StaticViewSitemap, AdoptionNoticeSitemap, AnimalSitemap
|
|
||||||
|
|
||||||
sitemaps = {
|
|
||||||
"static": StaticViewSitemap,
|
|
||||||
"vermittlungen": AdoptionNoticeSitemap,
|
|
||||||
"tiere": AnimalSitemap,
|
|
||||||
}
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("rss/", LatestAdoptionNoticesFeed(), name="rss"),
|
path("rss/", LatestAdoptionNoticesFeed(), name="rss"),
|
||||||
@@ -104,10 +95,4 @@ urlpatterns = [
|
|||||||
###################
|
###################
|
||||||
path('external-site/', views.external_site_warning, name="external-site"),
|
path('external-site/', views.external_site_warning, name="external-site"),
|
||||||
|
|
||||||
###############
|
|
||||||
## TECHNICAL ##
|
|
||||||
###############
|
|
||||||
path("sitemap.xml", sitemap, {"sitemaps": sitemaps}, name="django.contrib.sitemaps.views.sitemap"),
|
|
||||||
path("styleguide", views.styleguide, name="styleguide"),
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@@ -2,7 +2,6 @@ import logging
|
|||||||
|
|
||||||
from django.contrib.auth.views import redirect_to_login
|
from django.contrib.auth.views import redirect_to_login
|
||||||
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse
|
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse
|
||||||
from django.http.response import HttpResponseForbidden
|
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
@@ -134,7 +133,7 @@ def adoption_notice_detail(request, adoption_notice_id):
|
|||||||
elif action == "subscribe":
|
elif action == "subscribe":
|
||||||
return redirect_to_login(next=request.path)
|
return redirect_to_login(next=request.path)
|
||||||
else:
|
else:
|
||||||
return HttpResponseForbidden()
|
raise PermissionDenied
|
||||||
else:
|
else:
|
||||||
comment_form = CommentForm(instance=adoption_notice)
|
comment_form = CommentForm(instance=adoption_notice)
|
||||||
context = {"adoption_notice": adoption_notice, "comment_form": comment_form, "user": request.user,
|
context = {"adoption_notice": adoption_notice, "comment_form": comment_form, "user": request.user,
|
||||||
@@ -633,9 +632,3 @@ def export_own_profile(request):
|
|||||||
ANs_as_json = serialize('json', ANs)
|
ANs_as_json = serialize('json', ANs)
|
||||||
full_json = f"{user_as_json}, {ANs_as_json}"
|
full_json = f"{user_as_json}, {ANs_as_json}"
|
||||||
return HttpResponse(full_json, content_type="application/json")
|
return HttpResponse(full_json, content_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
def styleguide(request):
|
|
||||||
|
|
||||||
context = {"geocoding_api_url": settings.GEOCODING_API_URL, }
|
|
||||||
return render(request, 'fellchensammlung/styleguide.html', context=context)
|
|
||||||
|
@@ -168,7 +168,6 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
"django.contrib.sitemaps",
|
|
||||||
'fontawesomefree',
|
'fontawesomefree',
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
"crispy_bootstrap4",
|
"crispy_bootstrap4",
|
||||||
|
@@ -5,7 +5,7 @@ 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
|
||||||
from fellchensammlung.views import add_adoption_notice
|
from fellchensammlung.views import add_adoption_notice
|
||||||
|
|
||||||
|
|
||||||
@@ -233,10 +233,10 @@ class AdoptionDetailTest(TestCase):
|
|||||||
password='12345')
|
password='12345')
|
||||||
test_user0.save()
|
test_user0.save()
|
||||||
|
|
||||||
cls.test_user1 = User.objects.create_user(username='testuser1',
|
test_user1 = User.objects.create_user(username='testuser1',
|
||||||
first_name="Max",
|
first_name="Max",
|
||||||
last_name="Müller",
|
last_name="Müller",
|
||||||
password='12345')
|
password='12345')
|
||||||
test_user0.trust_level = TrustLevel.ADMIN
|
test_user0.trust_level = TrustLevel.ADMIN
|
||||||
test_user0.save()
|
test_user0.save()
|
||||||
|
|
||||||
@@ -256,16 +256,6 @@ class AdoptionDetailTest(TestCase):
|
|||||||
adoption3.set_active()
|
adoption3.set_active()
|
||||||
adoption2.set_unchecked()
|
adoption2.set_unchecked()
|
||||||
|
|
||||||
def test_basic_view(self):
|
|
||||||
response = self.client.get(
|
|
||||||
reverse('adoption-notice-detail', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)), )
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_basic_view_logged_in(self):
|
|
||||||
self.client.login(username='testuser0', password='12345')
|
|
||||||
response = self.client.get(
|
|
||||||
reverse('adoption-notice-detail', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)), )
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_subscribe(self):
|
def test_subscribe(self):
|
||||||
self.client.login(username='testuser0', password='12345')
|
self.client.login(username='testuser0', password='12345')
|
||||||
@@ -274,6 +264,7 @@ class AdoptionDetailTest(TestCase):
|
|||||||
data={"action": "subscribe"})
|
data={"action": "subscribe"})
|
||||||
self.assertTrue(Subscriptions.objects.filter(owner__username="testuser0").exists())
|
self.assertTrue(Subscriptions.objects.filter(owner__username="testuser0").exists())
|
||||||
|
|
||||||
|
|
||||||
def test_unsubscribe(self):
|
def test_unsubscribe(self):
|
||||||
# Make sure subscription exists
|
# Make sure subscription exists
|
||||||
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
||||||
@@ -293,72 +284,3 @@ class AdoptionDetailTest(TestCase):
|
|||||||
data={"action": "subscribe"})
|
data={"action": "subscribe"})
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, "/accounts/login/?next=/vermittlung/1/")
|
self.assertEqual(response.url, "/accounts/login/?next=/vermittlung/1/")
|
||||||
|
|
||||||
def test_unauthenticated_comment(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse('adoption-notice-detail', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)),
|
|
||||||
data={"action": "comment"})
|
|
||||||
self.assertEqual(response.status_code, 403)
|
|
||||||
|
|
||||||
def test_comment(self):
|
|
||||||
an1 = AdoptionNotice.objects.get(name="TestAdoption1")
|
|
||||||
# Set up subscription
|
|
||||||
Subscriptions.objects.create(owner=self.test_user1, adoption_notice=an1)
|
|
||||||
self.client.login(username='testuser0', password='12345')
|
|
||||||
response = self.client.post(
|
|
||||||
reverse('adoption-notice-detail', args=str(an1.pk)),
|
|
||||||
data={"action": "comment", "text": "Test"})
|
|
||||||
self.assertTrue(Comment.objects.filter(user__username="testuser0").exists())
|
|
||||||
self.assertFalse(CommentNotification.objects.filter(user__username="testuser0").exists())
|
|
||||||
self.assertTrue(CommentNotification.objects.filter(user__username="testuser1").exists())
|
|
||||||
|
|
||||||
|
|
||||||
class AdoptionEditTest(TestCase):
|
|
||||||
@classmethod
|
|
||||||
def setUpTestData(cls):
|
|
||||||
test_user0 = User.objects.create_user(username='testuser0',
|
|
||||||
first_name="Admin",
|
|
||||||
last_name="BOFH",
|
|
||||||
password='12345')
|
|
||||||
test_user0.save()
|
|
||||||
|
|
||||||
cls.test_user1 = User.objects.create_user(username='testuser1',
|
|
||||||
first_name="Max",
|
|
||||||
last_name="Müller",
|
|
||||||
password='12345')
|
|
||||||
|
|
||||||
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1", description="Test1", owner=test_user0)
|
|
||||||
adoption2 = baker.make(AdoptionNotice, name="TestAdoption2", description="Test2")
|
|
||||||
|
|
||||||
def test_basic_view(self):
|
|
||||||
response = self.client.get(
|
|
||||||
reverse('adoption-notice-edit', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)), )
|
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
|
|
||||||
def test_basic_view_logged_in_unauthorized(self):
|
|
||||||
self.client.login(username='testuser1', password='12345')
|
|
||||||
response = self.client.get(
|
|
||||||
reverse('adoption-notice-edit', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)), )
|
|
||||||
self.assertEqual(response.status_code, 403)
|
|
||||||
|
|
||||||
def test_basic_view_logged_in(self):
|
|
||||||
self.client.login(username='testuser0', password='12345')
|
|
||||||
response = self.client.get(
|
|
||||||
reverse('adoption-notice-edit', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)), )
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_edit(self):
|
|
||||||
data = {"name": "Mia",
|
|
||||||
"searching_since": "01.01.2025",
|
|
||||||
"location_string": "Paderborn",
|
|
||||||
"organization": "",
|
|
||||||
"description": "Test3",
|
|
||||||
"further_information": ""}
|
|
||||||
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
|
||||||
assert self.client.login(username='testuser0', password='12345')
|
|
||||||
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.status_code, 200) # Redirects to AN page
|
|
||||||
self.assertContains(response, "Test3")
|
|
||||||
self.assertContains(response, "Mia")
|
|
||||||
self.assertNotContains(response, "Test1")
|
|
||||||
|
Reference in New Issue
Block a user