diora-web/projects/models.py
mrwnslz c8fd139b44
Some checks failed
Build and push Docker image / build (push) Successful in 1m11s
Test / test (push) Successful in 10m32s
Test / e2e (push) Failing after 1m31s
Projekte: Radar – wöchentlich neue Papers (PubMed, arXiv, bioRxiv, medRxiv), von Claude im Batch sortiert, Entscheidungen verschlüsselt, → Quellen; Preset Neuropolitik aus F:\neuropolitik\tools; sw v145
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:43:43 +02:00

81 lines
3.7 KiB
Python

"""Projekte (docs/projects.md): a project of a preset (Forschung first), its sources and notes.
End-to-end encrypted like the books: a project's name and settings, and every item's content
(a note's text, a source's citation data, a remark) are AES-GCM blobs the server can't read.
What it knows: that a project exists, its preset, and which book, episode or article an item
points to (kind + ref) — the ids, never their titles."""
from django.contrib.auth.models import User
from django.db import models
class Project(models.Model):
RESEARCH = 'research'
PRESETS = [(RESEARCH, 'Forschung')]
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='projects')
preset = models.CharField(max_length=20, choices=PRESETS, default=RESEARCH)
data_ct = models.TextField() # {name, description, …} encrypted
data_iv = models.CharField(max_length=32)
archived = models.BooleanField(default=False)
position = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['position', '-updated_at']
class ProjectItem(models.Model):
"""Something in a project. Sources point at what's elsewhere in diora (a book, an episode,
an article: ref is its id) or stand alone (a paper known by its citation data: no ref);
notes are text of their own (no ref)."""
BOOK = 'book'
EPISODE = 'episode'
ARTICLE = 'article'
PAPER = 'paper'
NOTE = 'note'
# The Radar's decisions ({paper id: read | later | drop}), one per project.
RADAR = 'radar'
KINDS = [(BOOK, 'Buch'), (EPISODE, 'Folge'), (ARTICLE, 'Artikel'), (PAPER, 'Paper'), (NOTE, 'Notiz'),
(RADAR, 'Radar')]
LINKED = (BOOK, EPISODE, ARTICLE)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='items')
kind = models.CharField(max_length=10, choices=KINDS)
ref = models.BigIntegerField(null=True, blank=True)
data_ct = models.TextField()
data_iv = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['created_at']
constraints = [models.UniqueConstraint(fields=['project', 'kind', 'ref'], name='project_item_once',
condition=models.Q(ref__isnull=False))]
indexes = [models.Index(fields=['kind', 'ref'])]
class Radar(models.Model):
"""A project's Radar (docs/projects.md): every week new papers from PubMed, arXiv, bioRxiv,
medRxiv (projects/papers.py), sorted and summed up by Claude in a batch (projects/radar.py).
Plain on the server, unlike the rest of a project: the server searches with the config and
the papers are public."""
project = models.OneToOneField(Project, on_delete=models.CASCADE, related_name='radar')
enabled = models.BooleanField(default=True)
config = models.JSONField(default=dict) # projects/radar_presets.py
seen = models.JSONField(default=list) # paper ids reported before
pending = models.JSONField(null=True, blank=True) # {batch_id, generated, days, records, errors, submitted}
last_run_at = models.DateTimeField(null=True, blank=True)
last_error = models.TextField(blank=True)
class RadarDigest(models.Model):
"""One week's digest: the bundle as digest_batch.py makes it (version 1)."""
radar = models.ForeignKey(Radar, on_delete=models.CASCADE, related_name='digests')
generated = models.DateField()
batch_id = models.CharField(max_length=100, blank=True)
bundle = models.JSONField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-generated', '-created_at']