Initial public commit

Daybook: a self-hosted, multi-user Django + DRF app where agents, scripts,
or people publish artifacts and each logged-in user browses their own
time-ordered, label-filterable feed.

This squashes prior local development history (kept privately on a separate
branch) into a single clean starting point for the public repository.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
peter 2026-09-01 21:55:02 +01:00
commit 940a703cd7
62 changed files with 5755 additions and 0 deletions

View file

@ -0,0 +1,27 @@
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Artifact",
fields=[
("id", models.CharField(max_length=8, primary_key=True, serialize=False)),
("persona", models.CharField(db_index=True, max_length=80)),
("title", models.CharField(max_length=200)),
("kind", models.CharField(choices=[("html_fragment", "HTML fragment"), ("html_full", "Full HTML"), ("markdown", "Markdown"), ("deck", "Deck"), ("image", "Image"), ("pdf", "PDF"), ("binary", "Binary")], max_length=20)),
("theme", models.CharField(blank=True, max_length=40)),
("day", models.DateField(db_index=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
("path", models.CharField(max_length=500)),
("original_kind", models.CharField(max_length=20)),
("tags", models.JSONField(blank=True, default=list)),
("source_prompt", models.TextField(blank=True)),
],
options={"ordering": ["-created_at"]},
),
]

View file

@ -0,0 +1,41 @@
# Generated by Django 5.2.15 on 2026-06-10 15:14
from django.db import migrations, models
def fill_existing_source_names(apps, schema_editor):
Artifact = apps.get_model('artifacts', 'Artifact')
for artifact in Artifact.objects.filter(source_name=''):
artifact.source_name = artifact.persona
artifact.save(update_fields=['source_name'])
class Migration(migrations.Migration):
dependencies = [
('artifacts', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='artifact',
name='metadata',
field=models.JSONField(blank=True, default=dict),
),
migrations.AddField(
model_name='artifact',
name='source_id',
field=models.CharField(blank=True, max_length=120),
),
migrations.AddField(
model_name='artifact',
name='source_name',
field=models.CharField(blank=True, max_length=120),
),
migrations.AddField(
model_name='artifact',
name='source_type',
field=models.CharField(choices=[('agent', 'Agent'), ('person', 'Person'), ('system', 'System')], default='agent', max_length=20),
),
migrations.RunPython(fill_existing_source_names, migrations.RunPython.noop),
]

View file

@ -0,0 +1,20 @@
# Generated by Django 5.2.15 on 2026-07-23 06:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('artifacts', '0002_artifact_metadata_artifact_source_id_and_more'),
]
operations = [
migrations.CreateModel(
name='TagColor',
fields=[
('name', models.CharField(max_length=40, primary_key=True, serialize=False)),
('color', models.CharField(max_length=20)),
],
),
]

View file

@ -0,0 +1,16 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("artifacts", "0003_tagcolor"),
]
operations = [
migrations.RenameField(
model_name="artifact",
old_name="persona",
new_name="source",
),
]

View file

@ -0,0 +1,24 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("artifacts", "0004_rename_persona_to_source"),
]
operations = [
migrations.AddField(
model_name="artifact",
name="owner",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="artifacts",
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -0,0 +1,27 @@
from django.db import migrations
def backfill_owner(apps, schema_editor):
User = apps.get_model("auth", "User")
Artifact = apps.get_model("artifacts", "Artifact")
first_superuser = User.objects.filter(is_superuser=True).order_by("date_joined").first()
if first_superuser is None:
return
Artifact.objects.filter(owner__isnull=True).update(owner=first_superuser)
def noop_reverse(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
("artifacts", "0005_add_owner_nullable"),
]
operations = [
migrations.RunPython(backfill_owner, noop_reverse),
]

View file

@ -0,0 +1,23 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("artifacts", "0006_backfill_owner"),
]
operations = [
migrations.AlterField(
model_name="artifact",
name="owner",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="artifacts",
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 5.2.15 on 2026-07-23 21:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('artifacts', '0007_owner_required'),
]
operations = [
migrations.AddField(
model_name='artifact',
name='read_at',
field=models.DateTimeField(blank=True, null=True),
),
]

View file

@ -0,0 +1,34 @@
# Generated by Django 5.2.15 on 2026-07-24 07:15
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('artifacts', '0008_artifact_read_at'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='artifact',
name='is_kept',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='artifact',
name='size_bytes',
field=models.BigIntegerField(default=0),
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quota_bytes', models.BigIntegerField(default=1073741824)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -0,0 +1,43 @@
from pathlib import Path
from django.conf import settings
from django.db import migrations
def backfill(apps, schema_editor):
Artifact = apps.get_model("artifacts", "Artifact")
User = apps.get_model("auth", "User")
UserProfile = apps.get_model("artifacts", "UserProfile")
root = Path(settings.ARTIFACTS_ROOT)
for artifact in Artifact.objects.all():
total = 0
primary = root / artifact.path
if primary.exists():
total += primary.stat().st_size
if artifact.kind == "markdown":
sidecar = primary.with_suffix(".md")
if sidecar.exists():
total += sidecar.stat().st_size
if artifact.size_bytes != total:
artifact.size_bytes = total
artifact.save(update_fields=["size_bytes"])
default_quota = 1024 * 1024 * 1024 # 1 GB
for user in User.objects.all():
UserProfile.objects.get_or_create(user=user, defaults={"quota_bytes": default_quota})
def noop_reverse(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
("artifacts", "0009_quota_and_keep"),
]
operations = [
migrations.RunPython(backfill, noop_reverse),
]

View file

@ -0,0 +1,31 @@
# Generated by Django 5.2.15 on 2026-07-24 10:13
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('artifacts', '0010_backfill_sizes_and_profiles'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Asset',
fields=[
('id', models.CharField(max_length=12, primary_key=True, serialize=False)),
('path', models.CharField(max_length=500)),
('content_type', models.CharField(max_length=100)),
('size_bytes', models.BigIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('artifact', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='artifacts.artifact')),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assets', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
},
),
]

View file