feat: Memory-Konzept für Agents implementieren (STI-21)

- REST API: 9 Read-Only-Endpunkte unter /api/v1/ für alle Kernmodelle
  (Destinatäre, Ländereien, Pächter, Förderungen, Konten,
  Verpachtungen, Verwaltungskosten, Kalender, Transaktionen)
- Token-Authentifizierung via DRF TokenAuthentication
- Management-Command `create_agent_token` für Agent-Tokens
- Wissensbasis: knowledge/ mit Satzung, Richtlinien, Verfahren,
  Kontakte, Historie
- Agent-Instructions: Datenzugriff-Sektion in AGENTS.md dokumentiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-09 22:45:11 +00:00
parent 4b21f553c3
commit f8f9dc3319
17 changed files with 1224 additions and 1 deletions

View File

@@ -34,6 +34,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles",
"django.contrib.humanize",
"rest_framework",
"rest_framework.authtoken",
"django_otp",
"django_otp.plugins.otp_totp",
"django_otp.plugins.otp_static",
@@ -99,6 +100,15 @@ STATICFILES_DIRS = [
]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

View File

@@ -7,6 +7,7 @@ from django.urls import include, path
from stiftung.views import home
urlpatterns = [
path("api/v1/", include("stiftung.api_urls")),
path("", include("stiftung.urls")),
path("admin/", admin.site.urls),
# Authentication URLs

View File

@@ -0,0 +1,88 @@
from rest_framework import serializers
from .models import (
BankTransaction,
Destinataer,
DestinataerUnterstuetzung,
Foerderung,
Land,
LandVerpachtung,
Paechter,
StiftungsKalenderEintrag,
StiftungsKonto,
Verwaltungskosten,
)
class DestinataerUnterstuetzungSerializer(serializers.ModelSerializer):
class Meta:
model = DestinataerUnterstuetzung
fields = "__all__"
class DestinataerSerializer(serializers.ModelSerializer):
unterstuetzungen = DestinataerUnterstuetzungSerializer(many=True, read_only=True)
class Meta:
model = Destinataer
fields = "__all__"
class LandVerpachtungSerializer(serializers.ModelSerializer):
class Meta:
model = LandVerpachtung
fields = "__all__"
class LandSerializer(serializers.ModelSerializer):
aktive_verpachtungen = serializers.SerializerMethodField()
class Meta:
model = Land
fields = "__all__"
def get_aktive_verpachtungen(self, obj):
qs = obj.neue_verpachtungen.filter(status="aktiv")
return LandVerpachtungSerializer(qs, many=True).data
class PaechterSerializer(serializers.ModelSerializer):
aktive_verpachtungen = serializers.SerializerMethodField()
class Meta:
model = Paechter
fields = "__all__"
def get_aktive_verpachtungen(self, obj):
qs = obj.neue_verpachtungen.filter(status="aktiv")
return LandVerpachtungSerializer(qs, many=True).data
class FoerderungSerializer(serializers.ModelSerializer):
class Meta:
model = Foerderung
fields = "__all__"
class StiftungsKontoSerializer(serializers.ModelSerializer):
class Meta:
model = StiftungsKonto
fields = "__all__"
class VerwaltungskostenSerializer(serializers.ModelSerializer):
class Meta:
model = Verwaltungskosten
fields = "__all__"
class StiftungsKalenderEintragSerializer(serializers.ModelSerializer):
class Meta:
model = StiftungsKalenderEintrag
fields = "__all__"
class BankTransactionSerializer(serializers.ModelSerializer):
class Meta:
model = BankTransaction
fields = "__all__"

26
app/stiftung/api_urls.py Normal file
View File

@@ -0,0 +1,26 @@
from rest_framework.routers import DefaultRouter
from .api_views import (
BankTransactionViewSet,
DestinataerViewSet,
FoerderungViewSet,
LandVerpachtungViewSet,
LandViewSet,
PaechterViewSet,
StiftungsKalenderEintragViewSet,
StiftungsKontoViewSet,
VerwaltungskostenViewSet,
)
router = DefaultRouter()
router.register(r"destinataere", DestinataerViewSet)
router.register(r"laendereien", LandViewSet)
router.register(r"paechter", PaechterViewSet)
router.register(r"foerderungen", FoerderungViewSet)
router.register(r"konten", StiftungsKontoViewSet)
router.register(r"verpachtungen", LandVerpachtungViewSet)
router.register(r"verwaltungskosten", VerwaltungskostenViewSet)
router.register(r"kalender", StiftungsKalenderEintragViewSet)
router.register(r"transaktionen", BankTransactionViewSet)
urlpatterns = router.urls

69
app/stiftung/api_views.py Normal file
View File

@@ -0,0 +1,69 @@
from rest_framework.viewsets import ReadOnlyModelViewSet
from .api_serializers import (
BankTransactionSerializer,
DestinataerSerializer,
FoerderungSerializer,
LandSerializer,
LandVerpachtungSerializer,
PaechterSerializer,
StiftungsKalenderEintragSerializer,
StiftungsKontoSerializer,
VerwaltungskostenSerializer,
)
from .models import (
BankTransaction,
Destinataer,
Foerderung,
Land,
LandVerpachtung,
Paechter,
StiftungsKalenderEintrag,
StiftungsKonto,
Verwaltungskosten,
)
class DestinataerViewSet(ReadOnlyModelViewSet):
queryset = Destinataer.objects.all()
serializer_class = DestinataerSerializer
class LandViewSet(ReadOnlyModelViewSet):
queryset = Land.objects.all()
serializer_class = LandSerializer
class PaechterViewSet(ReadOnlyModelViewSet):
queryset = Paechter.objects.all()
serializer_class = PaechterSerializer
class FoerderungViewSet(ReadOnlyModelViewSet):
queryset = Foerderung.objects.all()
serializer_class = FoerderungSerializer
class StiftungsKontoViewSet(ReadOnlyModelViewSet):
queryset = StiftungsKonto.objects.all()
serializer_class = StiftungsKontoSerializer
class LandVerpachtungViewSet(ReadOnlyModelViewSet):
queryset = LandVerpachtung.objects.all()
serializer_class = LandVerpachtungSerializer
class VerwaltungskostenViewSet(ReadOnlyModelViewSet):
queryset = Verwaltungskosten.objects.all()
serializer_class = VerwaltungskostenSerializer
class StiftungsKalenderEintragViewSet(ReadOnlyModelViewSet):
queryset = StiftungsKalenderEintrag.objects.all()
serializer_class = StiftungsKalenderEintragSerializer
class BankTransactionViewSet(ReadOnlyModelViewSet):
queryset = BankTransaction.objects.all()
serializer_class = BankTransactionSerializer

View File

@@ -0,0 +1,28 @@
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
from rest_framework.authtoken.models import Token
class Command(BaseCommand):
help = "Erstellt oder gibt ein API-Token für einen Django-User aus (für Agent-Zugriff)"
def add_arguments(self, parser):
parser.add_argument("username", type=str, help="Django-Username")
def handle(self, *args, **options):
User = get_user_model()
username = options["username"]
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise CommandError(f'User "{username}" nicht gefunden.')
token, created = Token.objects.get_or_create(user=user)
if created:
self.stdout.write(self.style.SUCCESS(f"Neues Token erstellt für {username}:"))
else:
self.stdout.write(self.style.WARNING(f"Bestehendes Token für {username}:"))
self.stdout.write(token.key)