Faktenkompass: UI-Rebrand, Admin-Assistent und Produktions-Setup.

Modernisiert die öffentliche Oberfläche mit Inter, Dark Mode und Live-Suche, ergänzt KI-gestützte Entwürfe im Admin mit Nachweis-Uploads und dokumentiert Daphne/Nginx für den Serverbetrieb.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Pirmin Hinderling (fedora)
2026-07-07 15:33:58 +02:00
parent 2c7fb7d030
commit db3558872e
43 changed files with 3014 additions and 208 deletions
+135
View File
@@ -0,0 +1,135 @@
from django.contrib import admin, messages
from django.contrib.admin.views.decorators import staff_member_required
from django.core.exceptions import SuspiciousFileOperation
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import path, reverse
from django.utils.decorators import method_decorator
from django.views import View
from claims.forms import AssistantInputForm, RefineDraftForm, ReviewDraftForm
from claims.models import ClaimDraft
from claims.services.ai_assistant import AIAssistantError
from claims.services.article_fetcher import ArticleFetchError
from claims.services.draft_processor import process_claim_draft
from claims.services.draft_publisher import DraftPublishError, publish_draft
def _get_user_draft(request, draft_id):
if request.user.is_superuser:
return get_object_or_404(ClaimDraft, pk=draft_id)
return get_object_or_404(ClaimDraft, pk=draft_id, created_by=request.user)
@method_decorator(staff_member_required, name='dispatch')
class AssistantView(View):
template_name = 'admin/claims/assistant.html'
def get(self, request):
form = AssistantInputForm()
drafts = ClaimDraft.objects.filter(created_by=request.user).order_by('-created_at')[:10]
return render(request, self.template_name, {
**admin.site.each_context(request),
'form': form,
'recent_drafts': drafts,
'title': 'Fakten-Assistent',
'opts': ClaimDraft._meta,
})
def post(self, request):
form = AssistantInputForm(request.POST, request.FILES)
if not form.is_valid():
return render(request, self.template_name, {
**admin.site.each_context(request),
'form': form,
'recent_drafts': ClaimDraft.objects.filter(created_by=request.user)[:10],
'title': 'Fakten-Assistent',
'opts': ClaimDraft._meta,
})
draft = ClaimDraft.objects.create(
created_by=request.user,
article_url=form.cleaned_data.get('article_url') or '',
user_notes=form.cleaned_data.get('user_notes') or '',
uploaded_file=form.cleaned_data.get('uploaded_file'),
)
try:
process_claim_draft(draft)
except (ArticleFetchError, AIAssistantError):
return redirect('admin:claims_claimdraft_review', draft.pk)
messages.success(request, 'Entwurf wurde erstellt. Bitte vor der Veröffentlichung prüfen.')
return redirect('admin:claims_claimdraft_review', draft.pk)
@method_decorator(staff_member_required, name='dispatch')
class ReviewDraftView(View):
template_name = 'admin/claims/review_draft.html'
def get(self, request, draft_id):
draft = _get_user_draft(request, draft_id)
return render(request, self.template_name, {
**admin.site.each_context(request),
'draft': draft,
'form': ReviewDraftForm(instance=draft),
'refine_form': RefineDraftForm(),
'title': f'Entwurf prüfen: {draft.title or draft.pk}',
'opts': ClaimDraft._meta,
})
def post(self, request, draft_id):
draft = _get_user_draft(request, draft_id)
action = request.POST.get('action')
if action == 'refine':
refine_form = RefineDraftForm(request.POST)
if refine_form.is_valid():
try:
process_claim_draft(draft, refinement=refine_form.cleaned_data['refinement'])
messages.success(request, 'Entwurf wurde überarbeitet.')
except (ArticleFetchError, AIAssistantError) as exc:
messages.error(request, str(exc))
return redirect('admin:claims_claimdraft_review', draft.pk)
if action == 'reject':
draft.status = ClaimDraft.DraftStatus.REJECTED
draft.save(update_fields=['status', 'updated_at'])
messages.info(request, 'Entwurf wurde verworfen.')
return redirect('admin:claims_claimdraft_assistant')
form = ReviewDraftForm(request.POST, instance=draft)
if not form.is_valid():
return render(request, self.template_name, {
**admin.site.each_context(request),
'draft': draft,
'form': form,
'refine_form': RefineDraftForm(),
'title': f'Entwurf prüfen: {draft.title or draft.pk}',
'opts': ClaimDraft._meta,
})
draft = form.save()
if action == 'save':
draft.status = ClaimDraft.DraftStatus.REVIEW
draft.save(update_fields=['status', 'updated_at'])
messages.success(request, 'Änderungen gespeichert.')
return redirect('admin:claims_claimdraft_review', draft.pk)
if action == 'publish':
try:
claim = publish_draft(draft)
except DraftPublishError as exc:
messages.error(request, str(exc))
return redirect('admin:claims_claimdraft_review', draft.pk)
except SuspiciousFileOperation as exc:
messages.error(request, f'Datei konnte nicht gespeichert werden: {exc}')
return redirect('admin:claims_claimdraft_review', draft.pk)
messages.success(
request,
f'Behauptung „{claim.title}" wurde veröffentlicht.',
)
return redirect(reverse('admin:claims_claim_change', args=[claim.pk]))
return redirect('admin:claims_claimdraft_review', draft.pk)