A download that failed for days and then went through sat days down the library, under its request date, and read as missing. A job now records when it became done (`ready_at`), stamped where the expiry already is (`folders.stamp_done`, `finalize_if_running`) and on an insert that is already done; a check constraint holds every path to done to it. The library sorts and dates its "Added" by it; the queue keeps the request order. The migration dates a file from before by its "ready to watch" notice where the inbox still holds one, else by its request date, where it already sat.
45 lines
1.6 KiB
Python
45 lines
1.6 KiB
Python
"""A file's "Added" is when it became done
|
|
|
|
Revision ID: 0104_job_ready_at
|
|
Revises: 0103_files_sha256
|
|
Create Date: 2026-10-12
|
|
|
|
`download_jobs.ready_at` is when a job became done, and the Files library sorts and dates by it: a
|
|
download that failed for days and then went through used to sit days down the list, under its
|
|
request date. Every path to done stamps it, held by `ck_download_jobs_ready_at`.
|
|
|
|
A done job from before has no record of that moment, except where its "ready to watch" notice is
|
|
still in the owner's inbox (read notices are pruned past a cap): that notice was written the moment
|
|
it finished. The rest take the request date, which is where they already sat.
|
|
"""
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0104_job_ready_at"
|
|
down_revision: str | None = "0103_files_sha256"
|
|
branch_labels: str | None = None
|
|
depends_on: str | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("download_jobs", sa.Column("ready_at", sa.DateTime(timezone=True)))
|
|
op.execute(
|
|
"""
|
|
UPDATE download_jobs j SET ready_at = COALESCE(
|
|
(SELECT max(n.created_at) FROM notifications n
|
|
WHERE n.type = 'download_done' AND n.user_id = j.user_id
|
|
AND n.data->>'job_id' = j.id::text),
|
|
j.created_at)
|
|
WHERE j.status = 'done'
|
|
"""
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_download_jobs_ready_at", "download_jobs", "status <> 'done' OR ready_at IS NOT NULL"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_constraint("ck_download_jobs_ready_at", "download_jobs", type_="check")
|
|
op.drop_column("download_jobs", "ready_at")
|