Compare commits

...

2 Commits

Author SHA1 Message Date
Pirmin Hinderling (fedora) 28aab3b16a Remove ambiguous evidence level field from claims and the AI assistant.
Drops Evidenzlevel from models, admin, public UI, and draft workflow while keeping Nachweise file uploads unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 22:11:47 +02:00
Pirmin Hinderling (fedora) 3a5849660a Document collectstatic requirements for hashed static files in production.
Clarifies that collectstatic must run with production env vars and --clear after static updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 21:54:52 +02:00
13 changed files with 40 additions and 79 deletions
+14 -1
View File
@@ -117,11 +117,24 @@ cd /pfad/zu/ArgumentAtlas
source .venv/bin/activate source .venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
set -a && source /etc/faktenkompass.env && set +a
python manage.py migrate python manage.py migrate
python manage.py collectstatic --noinput python manage.py collectstatic --noinput
python manage.py createsuperuser # falls noch nicht vorhanden python manage.py createsuperuser # falls noch nicht vorhanden
``` ```
**Wichtig:** `collectstatic` immer mit denselben Umgebungsvariablen ausführen wie der Daphne-Dienst (`DJANGO_DEBUG=False`). Sonst fehlen gehashte Dateinamen bzw. `staticfiles.json` und die Seite antwortet mit 500.
Nach jedem `git pull` mit geänderten Static-Dateien:
```bash
set -a && source /etc/faktenkompass.env && set +a
python manage.py collectstatic --clear --noinput
sudo systemctl restart faktenkompass
```
Erfolg prüfen: `staticfiles/staticfiles.json` existiert, CSS heißt z.B. `custom.<hash>.css`.
Stellen Sie sicher, dass der Prozessnutzer Schreibrechte für `media/` und die Datenbank hat. Stellen Sie sicher, dass der Prozessnutzer Schreibrechte für `media/` und die Datenbank hat.
### 3. Daphne manuell testen ### 3. Daphne manuell testen
@@ -257,7 +270,7 @@ ArgumentAtlas/
- **Startseite:** Kategorien als Karten mit Behauptungsanzahl - **Startseite:** Kategorien als Karten mit Behauptungsanzahl
- **Kategorieseite:** Behauptungen filtern und durchsuchen - **Kategorieseite:** Behauptungen filtern und durchsuchen
- **Behauptungsdetail:** Status, Kurzantwort, Nachweise (Bilder/PDFs), Evidenzlevel, Quellen, Gegenargumente, Tags - **Behauptungsdetail:** Status, Kurzantwort, Nachweise (Bilder/PDFs), Quellen, Gegenargumente, Tags
- **Globale Suche:** Behauptungen über alle Kategorien hinweg - **Globale Suche:** Behauptungen über alle Kategorien hinweg
- **Admin:** Alle Inhalte über Django Admin pflegen - **Admin:** Alle Inhalte über Django Admin pflegen
- **SEO:** Sprechende URLs (`/kategorie/klimawandel/`, `/behauptung/...`) - **SEO:** Sprechende URLs (`/kategorie/klimawandel/`, `/behauptung/...`)
+3 -3
View File
@@ -106,9 +106,9 @@ class CategoryAdmin(FaktenAdminMixin, admin.ModelAdmin):
class ClaimAdmin(FaktenAdminMixin, admin.ModelAdmin): class ClaimAdmin(FaktenAdminMixin, admin.ModelAdmin):
form = ClaimAdminForm form = ClaimAdminForm
change_form_template = 'admin/claims/claim/change_form.html' change_form_template = 'admin/claims/claim/change_form.html'
list_display = ('title', 'category', 'status', 'evidence_level', 'updated_at') list_display = ('title', 'category', 'status', 'updated_at')
list_display_links = ('title',) list_display_links = ('title',)
list_filter = ('category', 'status', 'evidence_level', 'tags') list_filter = ('category', 'status', 'tags')
search_fields = ('title', 'short_answer', 'detailed_explanation') search_fields = ('title', 'short_answer', 'detailed_explanation')
prepopulated_fields = {'slug': ('title',)} prepopulated_fields = {'slug': ('title',)}
filter_horizontal = ('tags',) filter_horizontal = ('tags',)
@@ -119,7 +119,7 @@ class ClaimAdmin(FaktenAdminMixin, admin.ModelAdmin):
readonly_fields = ('public_link', 'created_at', 'updated_at') readonly_fields = ('public_link', 'created_at', 'updated_at')
fieldsets = ( fieldsets = (
('Einordnung', { ('Einordnung', {
'fields': ('category', 'status', 'evidence_level', 'tags'), 'fields': ('category', 'status', 'tags'),
}), }),
('Behauptung', { ('Behauptung', {
'fields': ('title', 'slug', 'public_link'), 'fields': ('title', 'slug', 'public_link'),
-2
View File
@@ -73,7 +73,6 @@ class ReviewDraftForm(forms.ModelForm):
'claim_status', 'claim_status',
'short_answer', 'short_answer',
'detailed_explanation', 'detailed_explanation',
'evidence_level',
'new_category_name', 'new_category_name',
'new_category_description', 'new_category_description',
'new_category_icon', 'new_category_icon',
@@ -83,7 +82,6 @@ class ReviewDraftForm(forms.ModelForm):
'claim_status': forms.Select(attrs={'class': 'form-select'}), 'claim_status': forms.Select(attrs={'class': 'form-select'}),
'short_answer': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), 'short_answer': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'detailed_explanation': forms.Textarea(attrs={'class': 'form-control', 'rows': 6}), 'detailed_explanation': forms.Textarea(attrs={'class': 'form-control', 'rows': 6}),
'evidence_level': forms.Select(attrs={'class': 'form-select'}),
'new_category_name': forms.TextInput(attrs={'class': 'form-control'}), 'new_category_name': forms.TextInput(attrs={'class': 'form-control'}),
'new_category_description': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'new_category_description': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}),
'new_category_icon': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'bi-book'}), 'new_category_icon': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'bi-book'}),
@@ -87,7 +87,6 @@ class Command(BaseCommand):
'Sie bleibt im Zytoplasma und wird nicht in den Zellkern transportiert, wo die DNA liegt. ' 'Sie bleibt im Zytoplasma und wird nicht in den Zellkern transportiert, wo die DNA liegt. '
'Integration in das Erbgut wäre ohne spezielle Hilfsmechanismen, die Impfstoffe nicht enthalten, praktisch nicht möglich.' 'Integration in das Erbgut wäre ohne spezielle Hilfsmechanismen, die Impfstoffe nicht enthalten, praktisch nicht möglich.'
), ),
evidence_level=Claim.EvidenceLevel.SEHR_STARK,
) )
c1.tags.add(tags['impfstoff'], tags['gesundheit']) c1.tags.add(tags['impfstoff'], tags['gesundheit'])
Source.objects.create( Source.objects.create(
@@ -109,7 +108,6 @@ class Command(BaseCommand):
status=Claim.Status.FALSCH, status=Claim.Status.FALSCH,
short_answer='In den ersten Pandemiejahren war COVID-19 deutlich tödlicher als saisonale Influenza und führte weltweit zu Millionen überzähliger Todesfälle.', short_answer='In den ersten Pandemiejahren war COVID-19 deutlich tödlicher als saisonale Influenza und führte weltweit zu Millionen überzähliger Todesfälle.',
detailed_explanation='Vergleiche mit der Grippe vereinfachen oft: COVID-19 verursachte höhere Belastungen für Intensivstationen und wies in vulnerablen Gruppen eine höhere Letalität auf.', detailed_explanation='Vergleiche mit der Grippe vereinfachen oft: COVID-19 verursachte höhere Belastungen für Intensivstationen und wies in vulnerablen Gruppen eine höhere Letalität auf.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
c2.tags.add(tags['gesundheit']) c2.tags.add(tags['gesundheit'])
Source.objects.create( Source.objects.create(
@@ -126,7 +124,6 @@ class Command(BaseCommand):
status=Claim.Status.WISSENSCHAFTLICHER_KONSENS, status=Claim.Status.WISSENSCHAFTLICHER_KONSENS,
short_answer='Gut sitzende medizinische Masken und FFP2-Masken reduzieren nachweislich die Übertragung von Tröpfchen und Aerosolen, besonders in Innenräumen.', short_answer='Gut sitzende medizinische Masken und FFP2-Masken reduzieren nachweislich die Übertragung von Tröpfchen und Aerosolen, besonders in Innenräumen.',
detailed_explanation='Metaanalysen zeigen, dass Masken die Emission und Aufnahme infektiöser Partikel verringern. Der Schutz hängt von Maskentyp, Passform und Nutzung ab.', detailed_explanation='Metaanalysen zeigen, dass Masken die Emission und Aufnahme infektiöser Partikel verringern. Der Schutz hängt von Maskentyp, Passform und Nutzung ab.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
def _create_climate_claims(self, category, tags): def _create_climate_claims(self, category, tags):
@@ -136,7 +133,6 @@ class Command(BaseCommand):
status=Claim.Status.FALSCH, status=Claim.Status.FALSCH,
short_answer='Die aktuelle Erwärmung lässt sich nicht durch natürliche Zyklen allein erklären. Treibhausgasemissionen sind laut IPCC die dominierende Ursache.', short_answer='Die aktuelle Erwärmung lässt sich nicht durch natürliche Zyklen allein erklären. Treibhausgasemissionen sind laut IPCC die dominierende Ursache.',
detailed_explanation='Klimamodelle mit nur natürlichen Faktoren reproduzieren die gemessene Temperaturanomalie nicht. Mit anthropogenen Emissionen stimmen Beobachtungen und Modelle überein.', detailed_explanation='Klimamodelle mit nur natürlichen Faktoren reproduzieren die gemessene Temperaturanomalie nicht. Mit anthropogenen Emissionen stimmen Beobachtungen und Modelle überein.',
evidence_level=Claim.EvidenceLevel.SEHR_STARK,
) )
c1.tags.add(tags['klima'], tags['co2']) c1.tags.add(tags['klima'], tags['co2'])
Source.objects.create( Source.objects.create(
@@ -158,7 +154,6 @@ class Command(BaseCommand):
status=Claim.Status.FALSCH, status=Claim.Status.FALSCH,
short_answer='Über 97 % der klimabezogenen Fachpublikationen stützen den Konsens, dass der Mensch die Hauptursache der aktuellen Erwärmung ist.', short_answer='Über 97 % der klimabezogenen Fachpublikationen stützen den Konsens, dass der Mensch die Hauptursache der aktuellen Erwärmung ist.',
detailed_explanation='Große wissenschaftliche Gesellschaften weltweit befürworten diese Einschätzung.', detailed_explanation='Große wissenschaftliche Gesellschaften weltweit befürworten diese Einschätzung.',
evidence_level=Claim.EvidenceLevel.SEHR_STARK,
) )
Claim.objects.create( Claim.objects.create(
@@ -167,7 +162,6 @@ class Command(BaseCommand):
status=Claim.Status.TEILWEISE_RICHTIG, status=Claim.Status.TEILWEISE_RICHTIG,
short_answer='Viele Extremwetterarten zeigen einen Zusammenhang mit dem Klimawandel, aber nicht jedes Ereignis ist direkt darauf zurückzuführen.', short_answer='Viele Extremwetterarten zeigen einen Zusammenhang mit dem Klimawandel, aber nicht jedes Ereignis ist direkt darauf zurückzuführen.',
detailed_explanation='Hitzewellen und starke Niederschläge werden in vielen Regionen häufiger. Attributionsforschung berechnet Wahrscheinlichkeitsveränderungen für Einzelereignisse.', detailed_explanation='Hitzewellen und starke Niederschläge werden in vielen Regionen häufiger. Attributionsforschung berechnet Wahrscheinlichkeitsveränderungen für Einzelereignisse.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
def _create_energy_claims(self, category, tags): def _create_energy_claims(self, category, tags):
@@ -177,7 +171,6 @@ class Command(BaseCommand):
status=Claim.Status.TEILWEISE_RICHTIG, status=Claim.Status.TEILWEISE_RICHTIG,
short_answer='Langfristig ist ein sehr hoher Anteil erneuerbarer Energien möglich, erfordert aber Speicher, Netzausbau und Flexibilität.', short_answer='Langfristig ist ein sehr hoher Anteil erneuerbarer Energien möglich, erfordert aber Speicher, Netzausbau und Flexibilität.',
detailed_explanation='Studien zeigen Szenarien mit nahezu 100 % erneuerbarem Strom, erfordern aber Infrastruktur und Sektorkopplung.', detailed_explanation='Studien zeigen Szenarien mit nahezu 100 % erneuerbarem Strom, erfordern aber Infrastruktur und Sektorkopplung.',
evidence_level=Claim.EvidenceLevel.MITTEL,
) )
c1.tags.add(tags['erneuerbare']) c1.tags.add(tags['erneuerbare'])
CounterArgument.objects.create( CounterArgument.objects.create(
@@ -192,7 +185,6 @@ class Command(BaseCommand):
status=Claim.Status.IRREFUEHREND, status=Claim.Status.IRREFUEHREND,
short_answer='Atomkraft erzeugt wenig CO₂ im Betrieb, verursacht aber andere Risiken und hohe Folgekosten. Sie ist nicht die einzige klimafreundliche Option.', short_answer='Atomkraft erzeugt wenig CO₂ im Betrieb, verursacht aber andere Risiken und hohe Folgekosten. Sie ist nicht die einzige klimafreundliche Option.',
detailed_explanation='Lebenszyklusanalysen zeigen niedrige CO₂-Emissionen, aber Endlagerung und Unfallrisiken sind zentrale Gegenargumente.', detailed_explanation='Lebenszyklusanalysen zeigen niedrige CO₂-Emissionen, aber Endlagerung und Unfallrisiken sind zentrale Gegenargumente.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
Claim.objects.create( Claim.objects.create(
@@ -201,7 +193,6 @@ class Command(BaseCommand):
status=Claim.Status.IRREFUEHREND, status=Claim.Status.IRREFUEHREND,
short_answer='Strompreise werden von Brennstoffkosten, Netzentgelten und Abgaben beeinflusst. Erneuerbare senken Grenzkosten, verursachen aber Netzausbaukosten.', short_answer='Strompreise werden von Brennstoffkosten, Netzentgelten und Abgaben beeinflusst. Erneuerbare senken Grenzkosten, verursachen aber Netzausbaukosten.',
detailed_explanation='In Phasen hoher Gaspreise stiegen Börsenstrompreise stark. Langfristig sind Wind und Solar kostengünstige Neubau-Technologien.', detailed_explanation='In Phasen hoher Gaspreise stiegen Börsenstrompreise stark. Langfristig sind Wind und Solar kostengünstige Neubau-Technologien.',
evidence_level=Claim.EvidenceLevel.MITTEL,
) )
def _create_migration_claims(self, category, tags): def _create_migration_claims(self, category, tags):
@@ -211,7 +202,6 @@ class Command(BaseCommand):
status=Claim.Status.IRREFUEHREND, status=Claim.Status.IRREFUEHREND,
short_answer='Die Erwerbstätigenquote variiert stark nach Herkunft und Aufenthaltsdauer. Pauschale Aussagen sind nicht haltbar.', short_answer='Die Erwerbstätigenquote variiert stark nach Herkunft und Aufenthaltsdauer. Pauschale Aussagen sind nicht haltbar.',
detailed_explanation='Viele Zuwanderer arbeiten nach mehreren Jahren ähnlich häufig wie die Gesamtbevölkerung. Neuankömmlinge weisen anfangs häufiger Arbeitslosigkeit auf.', detailed_explanation='Viele Zuwanderer arbeiten nach mehreren Jahren ähnlich häufig wie die Gesamtbevölkerung. Neuankömmlinge weisen anfangs häufiger Arbeitslosigkeit auf.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
c1.tags.add(tags['migration']) c1.tags.add(tags['migration'])
Source.objects.create( Source.objects.create(
@@ -228,7 +218,6 @@ class Command(BaseCommand):
status=Claim.Status.TEILWEISE_RICHTIG, status=Claim.Status.TEILWEISE_RICHTIG,
short_answer='Deutschland nimmt absolut viele Zuwanderer auf, liegt pro Kopf aber nicht immer an der Spitze in Europa.', short_answer='Deutschland nimmt absolut viele Zuwanderer auf, liegt pro Kopf aber nicht immer an der Spitze in Europa.',
detailed_explanation='Eurostat-Daten zeigen Deutschland unter den Ländern mit höchsten absoluten Zahlen. Relativ zur Bevölkerung liegen andere Länder teils höher.', detailed_explanation='Eurostat-Daten zeigen Deutschland unter den Ländern mit höchsten absoluten Zahlen. Relativ zur Bevölkerung liegen andere Länder teils höher.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
def _create_economy_claims(self, category, tags): def _create_economy_claims(self, category, tags):
@@ -238,7 +227,6 @@ class Command(BaseCommand):
status=Claim.Status.IRREFUEHREND, status=Claim.Status.IRREFUEHREND,
short_answer='Lohnkosten können Preise mitverursachen, aber Energiepreise, Lieferketten und Geldpolitik spielen ebenfalls eine zentrale Rolle.', short_answer='Lohnkosten können Preise mitverursachen, aber Energiepreise, Lieferketten und Geldpolitik spielen ebenfalls eine zentrale Rolle.',
detailed_explanation='Die Inflation 2022/2023 wurde maßgeblich durch Energie- und Rohstoffpreisschocks getrieben.', detailed_explanation='Die Inflation 2022/2023 wurde maßgeblich durch Energie- und Rohstoffpreisschocks getrieben.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
Claim.objects.create( Claim.objects.create(
@@ -247,7 +235,6 @@ class Command(BaseCommand):
status=Claim.Status.FALSCH, status=Claim.Status.FALSCH,
short_answer='Deutschland ist ein führender Exporteur, lag in den letzten Jahren aber hinter China.', short_answer='Deutschland ist ein führender Exporteur, lag in den letzten Jahren aber hinter China.',
detailed_explanation='Nach WTO-Daten exportierte China mehr Waren als Deutschland. Deutschland bleibt stark im Maschinen- und Fahrzeugbau.', detailed_explanation='Nach WTO-Daten exportierte China mehr Waren als Deutschland. Deutschland bleibt stark im Maschinen- und Fahrzeugbau.',
evidence_level=Claim.EvidenceLevel.SEHR_STARK,
) )
def _create_politics_claims(self, category, tags): def _create_politics_claims(self, category, tags):
@@ -257,7 +244,6 @@ class Command(BaseCommand):
status=Claim.Status.TEILWEISE_RICHTIG, status=Claim.Status.TEILWEISE_RICHTIG,
short_answer='Bei Bundestagswahlen schwankt die Beteiligung; langfristig gibt es einen leichten Rückgang, aber keine einheitliche Abwärtsspirale.', short_answer='Bei Bundestagswahlen schwankt die Beteiligung; langfristig gibt es einen leichten Rückgang, aber keine einheitliche Abwärtsspirale.',
detailed_explanation='Historische Daten zeigen Schwankungen zwischen etwa 70 % und 90 % bei Bundestagswahlen.', detailed_explanation='Historische Daten zeigen Schwankungen zwischen etwa 70 % und 90 % bei Bundestagswahlen.',
evidence_level=Claim.EvidenceLevel.STARK,
) )
def _create_science_claims(self, category, tags): def _create_science_claims(self, category, tags):
@@ -267,7 +253,6 @@ class Command(BaseCommand):
status=Claim.Status.FALSCH, status=Claim.Status.FALSCH,
short_answer='Einzelstudien sind Ausgangspunkte, kein abschließender Beweis. Konsens entsteht durch Replikation und Synthese vieler Arbeiten.', short_answer='Einzelstudien sind Ausgangspunkte, kein abschließender Beweis. Konsens entsteht durch Replikation und Synthese vieler Arbeiten.',
detailed_explanation='Peer Review filtert Fehler, garantiert aber keine Wahrheit. Metaanalysen fassen viele Studien zusammen.', detailed_explanation='Peer Review filtert Fehler, garantiert aber keine Wahrheit. Metaanalysen fassen viele Studien zusammen.',
evidence_level=Claim.EvidenceLevel.SEHR_STARK,
) )
c1.tags.add(tags['forschung']) c1.tags.add(tags['forschung'])
CounterArgument.objects.create( CounterArgument.objects.create(
@@ -0,0 +1,21 @@
# Generated by Django 5.2.15 on 2026-07-07 20:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('claims', '0005_allow_svg_draft_upload'),
]
operations = [
migrations.RemoveField(
model_name='claim',
name='evidence_level',
),
migrations.RemoveField(
model_name='claimdraft',
name='evidence_level',
),
]
-27
View File
@@ -72,12 +72,6 @@ class Claim(models.Model):
WISSENSCHAFTLICHER_KONSENS = 'wissenschaftlicher_konsens', 'Wissenschaftlicher Konsens' WISSENSCHAFTLICHER_KONSENS = 'wissenschaftlicher_konsens', 'Wissenschaftlicher Konsens'
OFFENE_FRAGE = 'offene_frage', 'Offene Frage' OFFENE_FRAGE = 'offene_frage', 'Offene Frage'
class EvidenceLevel(models.TextChoices):
SEHR_STARK = 'sehr_stark', 'Sehr stark'
STARK = 'stark', 'Stark'
MITTEL = 'mittel', 'Mittel'
SCHWACH = 'schwach', 'Schwach'
category = models.ForeignKey( category = models.ForeignKey(
Category, Category,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -94,12 +88,6 @@ class Claim(models.Model):
) )
short_answer = models.TextField('Kurzantwort') short_answer = models.TextField('Kurzantwort')
detailed_explanation = models.TextField('Ausführliche Erklärung', blank=True) detailed_explanation = models.TextField('Ausführliche Erklärung', blank=True)
evidence_level = models.CharField(
'Evidenzlevel',
max_length=20,
choices=EvidenceLevel.choices,
default=EvidenceLevel.MITTEL,
)
tags = models.ManyToManyField(Tag, blank=True, verbose_name='Schlagwörter') tags = models.ManyToManyField(Tag, blank=True, verbose_name='Schlagwörter')
created_at = models.DateTimeField('Erstellt am', auto_now_add=True) created_at = models.DateTimeField('Erstellt am', auto_now_add=True)
updated_at = models.DateTimeField('Aktualisiert am', auto_now=True) updated_at = models.DateTimeField('Aktualisiert am', auto_now=True)
@@ -137,15 +125,6 @@ class Claim(models.Model):
} }
return mapping.get(self.status, 'secondary') return mapping.get(self.status, 'secondary')
def get_evidence_badge_class(self):
mapping = {
self.EvidenceLevel.SEHR_STARK: 'success',
self.EvidenceLevel.STARK: 'primary',
self.EvidenceLevel.MITTEL: 'info',
self.EvidenceLevel.SCHWACH: 'warning',
}
return mapping.get(self.evidence_level, 'secondary')
def evidence_upload_path(instance, filename): def evidence_upload_path(instance, filename):
ext = os.path.splitext(filename)[1].lower().lstrip('.') or 'bin' ext = os.path.splitext(filename)[1].lower().lstrip('.') or 'bin'
@@ -305,12 +284,6 @@ class ClaimDraft(models.Model):
) )
short_answer = models.TextField('Kurzantwort', blank=True) short_answer = models.TextField('Kurzantwort', blank=True)
detailed_explanation = models.TextField('Ausführliche Erklärung', blank=True) detailed_explanation = models.TextField('Ausführliche Erklärung', blank=True)
evidence_level = models.CharField(
'Evidenzlevel',
max_length=20,
choices=Claim.EvidenceLevel.choices,
blank=True,
)
tags = models.JSONField('Schlagwörter', default=list, blank=True) tags = models.JSONField('Schlagwörter', default=list, blank=True)
sources = models.JSONField('Quellen', default=list, blank=True) sources = models.JSONField('Quellen', default=list, blank=True)
counter_arguments = models.JSONField('Gegenargumente', default=list, blank=True) counter_arguments = models.JSONField('Gegenargumente', default=list, blank=True)
+1 -7
View File
@@ -8,7 +8,6 @@ from django.conf import settings
from claims.models import Category, Claim from claims.models import Category, Claim
VALID_CLAIM_STATUSES = {choice[0] for choice in Claim.Status.choices} VALID_CLAIM_STATUSES = {choice[0] for choice in Claim.Status.choices}
VALID_EVIDENCE_LEVELS = {choice[0] for choice in Claim.EvidenceLevel.choices}
SYSTEM_PROMPT = """Du bist ein sachlicher Fakten-Assistent für die Plattform „Faktenkompass“. SYSTEM_PROMPT = """Du bist ein sachlicher Fakten-Assistent für die Plattform „Faktenkompass“.
Deine Aufgabe: Aus Artikeln, Bildern und Nutzereingaben einen überprüfbaren Fakteneintrag erstellen. Deine Aufgabe: Aus Artikeln, Bildern und Nutzereingaben einen überprüfbaren Fakteneintrag erstellen.
@@ -36,8 +35,7 @@ Antworte ausschließlich als gültiges JSON mit dieser Struktur:
"title": "Die zu prüfende Behauptung", "title": "Die zu prüfende Behauptung",
"status": "falsch|irrefuehrend|teilweise_richtig|unbelegt|wissenschaftlicher_konsens|offene_frage", "status": "falsch|irrefuehrend|teilweise_richtig|unbelegt|wissenschaftlicher_konsens|offene_frage",
"short_answer": "Kurze Einordnung", "short_answer": "Kurze Einordnung",
"detailed_explanation": "Ausführlichere Erklärung", "detailed_explanation": "Ausführlichere Erklärung"
"evidence_level": "sehr_stark|stark|mittel|schwach"
}, },
"tags": ["Schlagwort1", "Schlagwort2"], "tags": ["Schlagwort1", "Schlagwort2"],
"sources": [ "sources": [
@@ -180,11 +178,8 @@ def _call_openai(messages, *, has_image=False):
def _validate_payload(payload): def _validate_payload(payload):
claim = payload.get('claim') or {} claim = payload.get('claim') or {}
status = claim.get('status', '') status = claim.get('status', '')
evidence = claim.get('evidence_level', '')
if status and status not in VALID_CLAIM_STATUSES: if status and status not in VALID_CLAIM_STATUSES:
raise AIAssistantError(f'Ungültiger Status von der KI: {status}') raise AIAssistantError(f'Ungültiger Status von der KI: {status}')
if evidence and evidence not in VALID_EVIDENCE_LEVELS:
raise AIAssistantError(f'Ungültiges Evidenzlevel von der KI: {evidence}')
return payload return payload
@@ -248,7 +243,6 @@ def apply_payload_to_draft(draft, payload):
draft.claim_status = claim.get('status', Claim.Status.UNBELEGT) draft.claim_status = claim.get('status', Claim.Status.UNBELEGT)
draft.short_answer = claim.get('short_answer', '') draft.short_answer = claim.get('short_answer', '')
draft.detailed_explanation = claim.get('detailed_explanation', '') draft.detailed_explanation = claim.get('detailed_explanation', '')
draft.evidence_level = claim.get('evidence_level', Claim.EvidenceLevel.MITTEL)
draft.tags = payload.get('tags') or [] draft.tags = payload.get('tags') or []
draft.sources = payload.get('sources') or [] draft.sources = payload.get('sources') or []
draft.counter_arguments = payload.get('counter_arguments') or [] draft.counter_arguments = payload.get('counter_arguments') or []
-1
View File
@@ -57,7 +57,6 @@ def publish_draft(draft):
status=draft.claim_status or Claim.Status.UNBELEGT, status=draft.claim_status or Claim.Status.UNBELEGT,
short_answer=draft.short_answer, short_answer=draft.short_answer,
detailed_explanation=draft.detailed_explanation, detailed_explanation=draft.detailed_explanation,
evidence_level=draft.evidence_level or Claim.EvidenceLevel.MITTEL,
) )
claim.tags.set(_get_or_create_tags(draft.tags)) claim.tags.set(_get_or_create_tags(draft.tags))
-11
View File
@@ -9,12 +9,6 @@ def status_label(value):
return dict(Claim.Status.choices).get(value, value) return dict(Claim.Status.choices).get(value, value)
@register.filter
def evidence_label(value):
from claims.models import Claim
return dict(Claim.EvidenceLevel.choices).get(value, value)
@register.filter @register.filter
def quoted(value): def quoted(value):
if not value: if not value:
@@ -25,8 +19,3 @@ def quoted(value):
@register.inclusion_tag('claims/partials/status_badge.html') @register.inclusion_tag('claims/partials/status_badge.html')
def status_badge(claim): def status_badge(claim):
return {'claim': claim} return {'claim': claim}
@register.inclusion_tag('claims/partials/evidence_badge.html')
def evidence_badge(claim):
return {'claim': claim}
+1 -2
View File
@@ -306,8 +306,7 @@ a:hover .claim-card {
gap: 0.35rem; gap: 0.35rem;
} }
.fk-status, .fk-status {
.fk-evidence {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.4rem; gap: 0.4rem;
-5
View File
@@ -81,11 +81,6 @@
{{ form.claim_status }} {{ form.claim_status }}
</div> </div>
<div class="field-box">
<label for="{{ form.evidence_level.id_for_label }}">{{ form.evidence_level.label }}</label>
{{ form.evidence_level }}
</div>
<div class="field-box"> <div class="field-box">
<label for="{{ form.short_answer.id_for_label }}">{{ form.short_answer.label }}</label> <label for="{{ form.short_answer.id_for_label }}">{{ form.short_answer.label }}</label>
{{ form.short_answer }} {{ form.short_answer }}
-1
View File
@@ -18,7 +18,6 @@
<div class="fk-meta-stack mb-3"> <div class="fk-meta-stack mb-3">
{% status_badge claim %} {% status_badge claim %}
{% evidence_badge claim %}
</div> </div>
<div class="fk-card fk-answer-card"> <div class="fk-card fk-answer-card">
@@ -1,4 +0,0 @@
<span class="fk-evidence">
<i class="bi bi-bar-chart" aria-hidden="true"></i>
Evidenz: {{ claim.get_evidence_level_display }}
</span>