pdf-quiz-generator/backend/app/tasks/__init__.py
Daniel 831cb01650 feat: an article follows a topic, rather than copying it once
"Questions filed there later are not added" was the honest description of what
the previous commit built, and it was the wrong thing to build. "The Cardiology
article covers the Cardiology questions" is a standing statement about the
material, not a snapshot of who happened to be filed where on the afternoon
somebody pressed a button — and a copy stops being true the first time a
question is added, silently, with nothing on any screen to say so.

So the claim is now stored, and it is what writes the links:

* `question_article_links` is still the **only** table anything reads. No count,
  no QBank button, no mirror panel on a question, no AI Mode boost learns a
  second question to ask.
* `article_topic_claims` records *why* some of those rows exist, and is the one
  place that makes them — when the claim is staked, when a question is filed
  into the category (single, bulk, or on create), and on a half-hourly sweep
  that catches whatever bypassed both.

A link made this way is an ordinary row and can still be deleted by hand; a
sweep puts it back, which is the honest consequence of a standing claim.
Dropping the claim is how you stop it, and the panel now lists what an article
follows with two ways out — stop following and keep the links, or stop and
remove them.

Migration k1b2c3d4e5f6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 19:57:06 +02:00

36 lines
1.2 KiB
Python

from celery import Celery
from app.config import settings
from app.logging_config import setup_logging
# Configure structured JSON logging for Celery workers
setup_logging(settings.LOG_LEVEL)
celery_app = Celery(
"quiz_tasks",
broker=settings.REDIS_URL,
backend=settings.REDIS_URL,
include=["app.tasks.pdf_tasks", "app.tasks.quiz_tasks"],
)
celery_app.conf.task_serializer = "json"
celery_app.conf.result_serializer = "json"
celery_app.conf.accept_content = ["json"]
celery_app.conf.worker_hijack_root_logger = False # Don't override our JSON logging
celery_app.conf.broker_connection_retry_on_startup = True
# Questions whose embedding failed at creation would otherwise never be
# searchable semantically; this sweeps them up. It normally finds nothing.
celery_app.conf.beat_schedule = {
"retry-missing-embeddings": {
"task": "retry_missing_embeddings",
"schedule": 900.0, # every 15 minutes
},
# An article's claim over a topic is applied when it is staked and when a
# question is filed; this catches whatever bypassed both. Also normally
# finds nothing.
"apply-topic-claims": {
"task": "apply_topic_claims",
"schedule": 1800.0, # every 30 minutes
},
}
celery_app.conf.timezone = "UTC"