diff --git a/README.md b/README.md index f361c99..0c11757 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ ArgumentAtlas/ - **Startseite:** Kategorien als Karten mit Behauptungsanzahl - **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 - **Admin:** Alle Inhalte über Django Admin pflegen - **SEO:** Sprechende URLs (`/kategorie/klimawandel/`, `/behauptung/...`) diff --git a/claims/admin.py b/claims/admin.py index 1c5f1e1..a331ae5 100644 --- a/claims/admin.py +++ b/claims/admin.py @@ -106,9 +106,9 @@ class CategoryAdmin(FaktenAdminMixin, admin.ModelAdmin): class ClaimAdmin(FaktenAdminMixin, admin.ModelAdmin): form = ClaimAdminForm 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_filter = ('category', 'status', 'evidence_level', 'tags') + list_filter = ('category', 'status', 'tags') search_fields = ('title', 'short_answer', 'detailed_explanation') prepopulated_fields = {'slug': ('title',)} filter_horizontal = ('tags',) @@ -119,7 +119,7 @@ class ClaimAdmin(FaktenAdminMixin, admin.ModelAdmin): readonly_fields = ('public_link', 'created_at', 'updated_at') fieldsets = ( ('Einordnung', { - 'fields': ('category', 'status', 'evidence_level', 'tags'), + 'fields': ('category', 'status', 'tags'), }), ('Behauptung', { 'fields': ('title', 'slug', 'public_link'), diff --git a/claims/forms.py b/claims/forms.py index adfa09a..de34f4f 100644 --- a/claims/forms.py +++ b/claims/forms.py @@ -73,7 +73,6 @@ class ReviewDraftForm(forms.ModelForm): 'claim_status', 'short_answer', 'detailed_explanation', - 'evidence_level', 'new_category_name', 'new_category_description', 'new_category_icon', @@ -83,7 +82,6 @@ class ReviewDraftForm(forms.ModelForm): 'claim_status': forms.Select(attrs={'class': 'form-select'}), 'short_answer': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), '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_description': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'new_category_icon': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'bi-book'}), diff --git a/claims/management/commands/load_sample_data.py b/claims/management/commands/load_sample_data.py index 2cb068d..8be8029 100644 --- a/claims/management/commands/load_sample_data.py +++ b/claims/management/commands/load_sample_data.py @@ -87,7 +87,6 @@ class Command(BaseCommand): '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.' ), - evidence_level=Claim.EvidenceLevel.SEHR_STARK, ) c1.tags.add(tags['impfstoff'], tags['gesundheit']) Source.objects.create( @@ -109,7 +108,6 @@ class Command(BaseCommand): 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.', 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']) Source.objects.create( @@ -126,7 +124,6 @@ class Command(BaseCommand): 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.', 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): @@ -136,7 +133,6 @@ class Command(BaseCommand): 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.', 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']) Source.objects.create( @@ -158,7 +154,6 @@ class Command(BaseCommand): 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.', detailed_explanation='Große wissenschaftliche Gesellschaften weltweit befürworten diese Einschätzung.', - evidence_level=Claim.EvidenceLevel.SEHR_STARK, ) Claim.objects.create( @@ -167,7 +162,6 @@ class Command(BaseCommand): 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.', 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): @@ -177,7 +171,6 @@ class Command(BaseCommand): status=Claim.Status.TEILWEISE_RICHTIG, 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.', - evidence_level=Claim.EvidenceLevel.MITTEL, ) c1.tags.add(tags['erneuerbare']) CounterArgument.objects.create( @@ -192,7 +185,6 @@ class Command(BaseCommand): 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.', detailed_explanation='Lebenszyklusanalysen zeigen niedrige CO₂-Emissionen, aber Endlagerung und Unfallrisiken sind zentrale Gegenargumente.', - evidence_level=Claim.EvidenceLevel.STARK, ) Claim.objects.create( @@ -201,7 +193,6 @@ class Command(BaseCommand): status=Claim.Status.IRREFUEHREND, 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.', - evidence_level=Claim.EvidenceLevel.MITTEL, ) def _create_migration_claims(self, category, tags): @@ -211,7 +202,6 @@ class Command(BaseCommand): status=Claim.Status.IRREFUEHREND, 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.', - evidence_level=Claim.EvidenceLevel.STARK, ) c1.tags.add(tags['migration']) Source.objects.create( @@ -228,7 +218,6 @@ class Command(BaseCommand): status=Claim.Status.TEILWEISE_RICHTIG, 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.', - evidence_level=Claim.EvidenceLevel.STARK, ) def _create_economy_claims(self, category, tags): @@ -238,7 +227,6 @@ class Command(BaseCommand): status=Claim.Status.IRREFUEHREND, 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.', - evidence_level=Claim.EvidenceLevel.STARK, ) Claim.objects.create( @@ -247,7 +235,6 @@ class Command(BaseCommand): status=Claim.Status.FALSCH, 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.', - evidence_level=Claim.EvidenceLevel.SEHR_STARK, ) def _create_politics_claims(self, category, tags): @@ -257,7 +244,6 @@ class Command(BaseCommand): status=Claim.Status.TEILWEISE_RICHTIG, 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.', - evidence_level=Claim.EvidenceLevel.STARK, ) def _create_science_claims(self, category, tags): @@ -267,7 +253,6 @@ class Command(BaseCommand): status=Claim.Status.FALSCH, 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.', - evidence_level=Claim.EvidenceLevel.SEHR_STARK, ) c1.tags.add(tags['forschung']) CounterArgument.objects.create( diff --git a/claims/migrations/0006_remove_claim_evidence_level_and_more.py b/claims/migrations/0006_remove_claim_evidence_level_and_more.py new file mode 100644 index 0000000..2f90768 --- /dev/null +++ b/claims/migrations/0006_remove_claim_evidence_level_and_more.py @@ -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', + ), + ] diff --git a/claims/models.py b/claims/models.py index de14bb3..926a92f 100644 --- a/claims/models.py +++ b/claims/models.py @@ -72,12 +72,6 @@ class Claim(models.Model): WISSENSCHAFTLICHER_KONSENS = 'wissenschaftlicher_konsens', 'Wissenschaftlicher Konsens' 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, on_delete=models.CASCADE, @@ -94,12 +88,6 @@ class Claim(models.Model): ) short_answer = models.TextField('Kurzantwort') 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') created_at = models.DateTimeField('Erstellt am', auto_now_add=True) updated_at = models.DateTimeField('Aktualisiert am', auto_now=True) @@ -137,15 +125,6 @@ class Claim(models.Model): } 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): 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) 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) sources = models.JSONField('Quellen', default=list, blank=True) counter_arguments = models.JSONField('Gegenargumente', default=list, blank=True) diff --git a/claims/services/ai_assistant.py b/claims/services/ai_assistant.py index 5fefc63..6017ed4 100644 --- a/claims/services/ai_assistant.py +++ b/claims/services/ai_assistant.py @@ -8,7 +8,6 @@ from django.conf import settings from claims.models import Category, Claim 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“. 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", "status": "falsch|irrefuehrend|teilweise_richtig|unbelegt|wissenschaftlicher_konsens|offene_frage", "short_answer": "Kurze Einordnung", - "detailed_explanation": "Ausführlichere Erklärung", - "evidence_level": "sehr_stark|stark|mittel|schwach" + "detailed_explanation": "Ausführlichere Erklärung" }, "tags": ["Schlagwort1", "Schlagwort2"], "sources": [ @@ -180,11 +178,8 @@ def _call_openai(messages, *, has_image=False): def _validate_payload(payload): claim = payload.get('claim') or {} status = claim.get('status', '') - evidence = claim.get('evidence_level', '') if status and status not in VALID_CLAIM_STATUSES: 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 @@ -248,7 +243,6 @@ def apply_payload_to_draft(draft, payload): draft.claim_status = claim.get('status', Claim.Status.UNBELEGT) draft.short_answer = claim.get('short_answer', '') draft.detailed_explanation = claim.get('detailed_explanation', '') - draft.evidence_level = claim.get('evidence_level', Claim.EvidenceLevel.MITTEL) draft.tags = payload.get('tags') or [] draft.sources = payload.get('sources') or [] draft.counter_arguments = payload.get('counter_arguments') or [] diff --git a/claims/services/draft_publisher.py b/claims/services/draft_publisher.py index 7a52ca5..a402b74 100644 --- a/claims/services/draft_publisher.py +++ b/claims/services/draft_publisher.py @@ -57,7 +57,6 @@ def publish_draft(draft): status=draft.claim_status or Claim.Status.UNBELEGT, short_answer=draft.short_answer, detailed_explanation=draft.detailed_explanation, - evidence_level=draft.evidence_level or Claim.EvidenceLevel.MITTEL, ) claim.tags.set(_get_or_create_tags(draft.tags)) diff --git a/claims/templatetags/claims_extras.py b/claims/templatetags/claims_extras.py index 3f41fd4..d46ead2 100644 --- a/claims/templatetags/claims_extras.py +++ b/claims/templatetags/claims_extras.py @@ -9,12 +9,6 @@ def status_label(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 def quoted(value): if not value: @@ -25,8 +19,3 @@ def quoted(value): @register.inclusion_tag('claims/partials/status_badge.html') def status_badge(claim): return {'claim': claim} - - -@register.inclusion_tag('claims/partials/evidence_badge.html') -def evidence_badge(claim): - return {'claim': claim} diff --git a/static/css/custom.css b/static/css/custom.css index ea147ef..dbe73e0 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -306,8 +306,7 @@ a:hover .claim-card { gap: 0.35rem; } -.fk-status, -.fk-evidence { +.fk-status { display: inline-flex; align-items: center; gap: 0.4rem; diff --git a/templates/admin/claims/review_draft.html b/templates/admin/claims/review_draft.html index eb940da..6d9ebb8 100644 --- a/templates/admin/claims/review_draft.html +++ b/templates/admin/claims/review_draft.html @@ -81,11 +81,6 @@ {{ form.claim_status }} -