Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28aab3b16a | |||
| 3a5849660a |
@@ -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
@@ -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'),
|
||||||
|
|||||||
@@ -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',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 []
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 }}
|
||||||
|
|||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user