81 lines
3.7 KiB
Python
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']
|