Compare commits

..

No commits in common. "caf98ba60b8a2b364405beeef15958695ae2351e" and "8de5f162eb46a7737a779abfad635acd8458d24a" have entirely different histories.

7 changed files with 33 additions and 83 deletions

View File

@ -20,35 +20,11 @@ http://notfellchen.org/
Via token Via token
--------- ---------
.. warning::
This is currently not supported.
All users are able to generate a token that allows them to use the API. This can be done in the user's profile. All users are able to generate a token that allows them to use the API. This can be done in the user's profile.
An application can then send this token in the request header for authorization. An application can then send this token in the request header for authorization.
.. code-block:: .. code-block::
$ curl -X GET http://notfellchen.org/api/adoption_notice -H 'Authorization: Token 49b39856955dc6e5cc04365498d4ad30ea3aed78' $ curl -X GET http://notfellchen.org/api/adoption_notice -H 'Authorization: Token 49b39856955dc6e5cc04365498d4ad30ea3aed78'
Endpoints
---------
Get Adoption Notices
++++++++++++++++++++
.. code-block::
curl --request GET \
--url http://localhost:8000/api/adoption_notice \
--header 'Authorization: {{token}}'
Create Adoption Notice
++++++++++++++++++++++
.. code-block::
curl --request POST \
--url http://localhost:8000/api/adoption_notice \
--header 'Authorization: {{token}}' \
--header 'content-type: multipart/form-data' \
--form name=TestAdoption1 \
--form searching_since=2024-11-19 \
--form 'description=Lorem ipsum **dolor sit** amet' \
--form further_information=https://notfellchen.org \
--form location_string=Berlin \
--form group_only=true

View File

@ -24,13 +24,6 @@ class AdoptionNoticeAdmin(admin.ModelAdmin):
inlines = [ inlines = [
StatusInline, StatusInline,
] ]
actions = ("activate",)
def activate(self, request, queryset):
for obj in queryset:
obj.set_active()
activate.short_description = _("Ausgewählte Vermittlungen aktivieren")
# Re-register UserAdmin # Re-register UserAdmin

View File

@ -7,4 +7,4 @@ from rest_framework import serializers
class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer): class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = AdoptionNotice model = AdoptionNotice
fields = ['created_at', 'last_checked', "searching_since", "name", "description", "further_information", "group_only"] fields = ['created_at', 'last_checked', "searching_since", "name", "description", "further_information", "group_only"]

View File

@ -1,12 +1,9 @@
from django.contrib.auth.models import User
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework import permissions from rest_framework import permissions
from rest_framework.decorators import api_view, permission_classes from ..models import AdoptionNotice
from rest_framework.permissions import IsAuthenticated
from django.db import transaction
from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel
from fellchensammlung.tasks import add_adoption_notice_location
from .serializers import AdoptionNoticeSerializer from .serializers import AdoptionNoticeSerializer
@ -21,35 +18,20 @@ class AdoptionNoticeApiView(APIView):
serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context=serializer_context) serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
@transaction.atomic
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
""" data = {
API view to add an adoption notice.b 'name': request.data.get('name'),
""" "searching_since": request.data.get('searching_since'),
serializer = AdoptionNoticeSerializer(data=request.data, context={'request': request}) "description": request.data.get('description'),
if not serializer.is_valid(): "organization": request.data.get('organization'),
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) "further_information": request.data.get('further_information'),
"location_string": request.data.get('location_string'),
"group_only": request.data.get('group_only'),
"owner": request.data.get('owner')
}
serializer = AdoptionNoticeSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
adoption_notice = serializer.save(owner=request.user) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# Add the location
add_adoption_notice_location.delay_on_commit(adoption_notice.pk)
# Only set active when user has trust level moderator or higher
if request.user.trust_level >= TrustLevel.MODERATOR:
adoption_notice.set_active()
else:
adoption_notice.set_unchecked()
# Log the action
Log.objects.create(
user=request.user,
action="add_adoption_notice",
text=f"{request.user} added adoption notice {adoption_notice.pk} via API",
)
# Return success response with new adoption notice details
return Response(
{"message": "Adoption notice created successfully!", "id": adoption_notice.pk},
status=status.HTTP_201_CREATED,
)

View File

@ -350,22 +350,22 @@ class AdoptionNotice(models.Model):
def set_closed(self): def set_closed(self):
self.last_checked = timezone.now() self.last_checked = timezone.now()
self.save()
self.adoptionnoticestatus.set_closed() self.adoptionnoticestatus.set_closed()
self.save()
def set_active(self): def set_active(self):
self.last_checked = timezone.now() self.last_checked = timezone.now()
self.save()
if not hasattr(self, 'adoptionnoticestatus'): if not hasattr(self, 'adoptionnoticestatus'):
AdoptionNoticeStatus.create_other(self) AdoptionNoticeStatus.create_other(self)
self.adoptionnoticestatus.set_active() self.adoptionnoticestatus.set_active()
self.save()
def set_unchecked(self): def set_unchecked(self):
self.last_checked = timezone.now() self.last_checked = timezone.now()
self.save()
if not hasattr(self, 'adoptionnoticestatus'): if not hasattr(self, 'adoptionnoticestatus'):
AdoptionNoticeStatus.create_other(self) AdoptionNoticeStatus.create_other(self)
self.adoptionnoticestatus.set_unchecked() self.adoptionnoticestatus.set_unchecked()
self.save()
for subscription in self.get_subscriptions(): for subscription in self.get_subscriptions():
notification_title = _("Vermittlung deaktiviert:") + f" {self}" notification_title = _("Vermittlung deaktiviert:") + f" {self}"

View File

@ -53,11 +53,11 @@ def clean_locations(quiet=True):
def get_unchecked_adoption_notices(weeks=3): def get_unchecked_adoption_notices(weeks=3):
now = timezone.now() now = timezone.now()
n_weeks_ago = now - timedelta(weeks=weeks) three_weeks_ago = now - timedelta(weeks=weeks)
# Query for active adoption notices that were not checked in the last n weeks # Query for active adoption notices that were checked in the last three weeks
unchecked_adoptions = AdoptionNotice.objects.filter( unchecked_adoptions = AdoptionNotice.objects.filter(
last_checked__lte=n_weeks_ago last_checked__lte=three_weeks_ago
) )
active_unchecked_adoptions = [adoption for adoption in unchecked_adoptions if adoption.is_active] active_unchecked_adoptions = [adoption for adoption in unchecked_adoptions if adoption.is_active]
return active_unchecked_adoptions return active_unchecked_adoptions
@ -71,7 +71,7 @@ def get_active_adoption_notices():
def deactivate_unchecked_adoption_notices(): def deactivate_unchecked_adoption_notices():
for adoption_notice in get_unchecked_adoption_notices(weeks=3): for adoption_notice in get_unchecked_adoption_notices(weeks=3):
adoption_notice.set_unchecked() AdoptionNoticeStatus.objects.get(adoption_notice=adoption_notice).set_unchecked()
def deactivate_404_adoption_notices(): def deactivate_404_adoption_notices():

View File

@ -10,7 +10,7 @@ from model_bakery import baker
from fellchensammlung.models import AdoptionNotice from fellchensammlung.models import AdoptionNotice
class DeactivationTest(TestCase): class DeactiviationTest(TestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
now = timezone.now() now = timezone.now()
@ -19,18 +19,16 @@ class DeactivationTest(TestCase):
cls.adoption1 = baker.make(AdoptionNotice, cls.adoption1 = baker.make(AdoptionNotice,
name="TestAdoption1", name="TestAdoption1",
created_at=more_than_three_weeks_ago) created_at=more_than_three_weeks_ago,
last_checked=more_than_three_weeks_ago)
cls.adoption2 = baker.make(AdoptionNotice, name="TestAdoption2") cls.adoption2 = baker.make(AdoptionNotice, name="TestAdoption2")
cls.adoption3 = baker.make(AdoptionNotice, cls.adoption3 = baker.make(AdoptionNotice,
name="TestAdoption3", name="TestAdoption3",
created_at=less_than_three_weeks_ago) created_at=less_than_three_weeks_ago,
last_checked=less_than_three_weeks_ago)
cls.adoption1.set_active() cls.adoption1.set_active()
cls.adoption1.last_checked = more_than_three_weeks_ago # Reset updated_at to simulate test conditions
cls.adoption1.save()
cls.adoption3.set_active() cls.adoption3.set_active()
cls.adoption3.last_checked = less_than_three_weeks_ago # Reset updated_at to simulate test conditions
cls.adoption3.save()
def test_get_unchecked_adoption_notices(self): def test_get_unchecked_adoption_notices(self):
result = get_unchecked_adoption_notices() result = get_unchecked_adoption_notices()
@ -96,3 +94,4 @@ class PingTest(TestCase):
self.adoption2.refresh_from_db() self.adoption2.refresh_from_db()
self.assertTrue(self.adoption1.is_active) self.assertTrue(self.adoption1.is_active)
self.assertFalse(self.adoption2.is_active) self.assertFalse(self.adoption2.is_active)