feat: sample content seeder for demonstrations

Clearly-marked sample articles, cards, question links and lab deep links; idempotent. Seeded in production so every linking feature is visible end to end.
This commit is contained in:
Daniel 2026-09-08 18:29:34 +02:00
parent f5a084e49a
commit 0c03a6f286

View file

@ -0,0 +1,163 @@
"""Seed clearly-marked sample articles, cards and lab deep links for demonstration.
Every created row is labeled as sample content and links existing bank questions,
cards and cited lab references so the linking features are visible end to end.
Idempotent: existing sample rows are left untouched.
"""
import sys
from app.database import SessionLocal
from app.models.article import Article, QuestionArticleLink
from app.models.flashcard import Flashcard, FlashcardDeck, FlashcardArticleLink, FlashcardQuestionLink
from app.models.lab_reference import LabReference, LabReferenceCardLink
from app.models.question import Question
from app.models.user import User
SAMPLE_NOTE = "Sample content for demonstration — replace with educator-reviewed material."
SECTION_JAUNDICE = "000000000000000000000000000000a1"
SECTION_JAUNDICE_BILI = "000000000000000000000000000000a2"
SECTION_SEIZURE = "000000000000000000000000000000b1"
SECTION_SEIZURE_LABS = "000000000000000000000000000000b2"
def main():
db = SessionLocal()
try:
moderator = db.query(User.id).filter(User.role.in_(["admin", "moderator"])).order_by(User.id).first()
moderator = moderator[0] if moderator else None
# ── Sample articles ────────────────────────────────────────────
articles = []
for slug, title, summary, content, sections, category_hint in [
("sample-febrile-seizures", "Febrile seizures — sample", SAMPLE_NOTE,
"This **sample article** demonstrates article sections, question links and card links.",
[
{"id": SECTION_SEIZURE, "slug": "definition", "title": "Definition and evaluation",
"content": "A febrile seizure is a seizure associated with fever in a child 6 months5 years without CNS infection or metabolic cause.\n\n**Sample text** — verify against your local guidance."},
{"id": SECTION_SEIZURE_LABS, "slug": "reference-values", "title": "Reference values",
"content": "Linked laboratory reference values appear in the quiz lab panel and deep-link back to this section.\n\n**Sample text** — verify against your local guidance."},
], None),
("sample-neonatal-jaundice", "Neonatal jaundice — sample", SAMPLE_NOTE,
"This **sample article** demonstrates deep links from lab references into a specific section.",
[
{"id": SECTION_JAUNDICE, "slug": "overview", "title": "Overview",
"content": "Physiologic jaundice appears after 24 hours of life and peaks at 35 days in term infants.\n\n**Sample text** — verify against your local guidance."},
{"id": SECTION_JAUNDICE_BILI, "slug": "bilirubin-reference-values", "title": "Bilirubin reference values",
"content": "Total bilirubin reference values used in the lab panel link to this section.\n\n**Sample text** — verify against your local guidance."},
], None),
]:
if not db.query(Article.id).filter(Article.slug == slug).first():
article = Article(slug=slug, title=title, summary=summary, content=content,
sections=sections, user_id=moderator, status="published")
db.add(article)
db.flush()
articles.append(article)
print(f"Created sample article: {title}")
db.commit()
# ── Link bank questions ─────────────────────────────────────────
from app.services.quiz_builder import shareable_question_predicate
bank = db.query(Question.id).filter(
shareable_question_predicate(),
Question.source_quiz_id.is_(None),
).order_by(Question.id).limit(6).all()
bank_ids = [row[0] for row in bank]
by_slug = {a.slug: a for a in db.query(Article).filter(Article.slug.in_(
["sample-febrile-seizures", "sample-neonatal-jaundice"])).all()}
pairs = [
("sample-febrile-seizures", None, 0), ("sample-febrile-seizures", SECTION_SEIZURE, 1),
("sample-febrile-seizures", SECTION_SEIZURE_LABS, 2),
("sample-neonatal-jaundice", None, 3), ("sample-neonatal-jaundice", SECTION_JAUNDICE, 4),
("sample-neonatal-jaundice", SECTION_JAUNDICE_BILI, 5),
]
for slug, section_id, bank_index in pairs:
article = by_slug.get(slug)
if not article or not bank_ids:
continue
question_id = bank_ids[bank_index % len(bank_ids)]
if not db.query(QuestionArticleLink.id).filter_by(
question_id=question_id, article_id=article.id, section_id=section_id).first():
db.add(QuestionArticleLink(question_id=question_id, article_id=article.id,
section_id=section_id, user_id=moderator))
print(f"Linked question {question_id} -> {slug} {section_id or 'whole article'}")
db.commit()
# ── Sample deck and cards ───────────────────────────────────────
deck = db.query(FlashcardDeck).filter_by(title="Sample cards", user_id=moderator).first()
if not deck:
deck = FlashcardDeck(title="Sample cards", user_id=moderator, card_count=0, is_shared=1)
db.add(deck)
db.flush()
sample_cards = [
("Sample: febrile seizure definition",
"Seizure with fever in a child 6 months5 years without CNS infection or metabolic cause. **Sample content** — verify clinically."),
("Sample: jaundice timeline",
"Physiologic jaundice appears after 24 h and peaks at 35 days in term infants. **Sample content** — verify clinically."),
]
created_cards = []
for front, back in sample_cards:
card = db.query(Flashcard).filter_by(deck_id=deck.id, front=front).first()
if not card:
card = Flashcard(deck_id=deck.id, front=front, back=back)
db.add(card)
db.flush()
created_cards.append(card)
if created_cards:
deck.card_count = db.query(Flashcard).filter(Flashcard.deck_id == deck.id).count()
db.commit()
print(f"Sample deck cards: {len(created_cards)}")
cards = db.query(Flashcard).filter_by(deck_id=deck.id).order_by(Flashcard.id).all()
seizure_article = by_slug.get("sample-febrile-seizures")
jaundice_article = by_slug.get("sample-neonatal-jaundice")
if cards and seizure_article and bank_ids:
card = cards[0]
if not db.query(FlashcardArticleLink.id).filter_by(
flashcard_id=card.id, article_id=seizure_article.id, article_section_id=SECTION_SEIZURE).first():
db.add(FlashcardArticleLink(flashcard_id=card.id, article_id=seizure_article.id,
article_section_id=SECTION_SEIZURE))
if not db.query(FlashcardQuestionLink.id).filter_by(flashcard_id=card.id, question_id=bank_ids[0]).first():
db.add(FlashcardQuestionLink(flashcard_id=card.id, question_id=bank_ids[0]))
print(f"Card {card.id} linked to question {bank_ids[0]}")
if len(cards) > 1 and jaundice_article:
card = cards[1]
if not db.query(FlashcardArticleLink.id).filter_by(
flashcard_id=card.id, article_id=jaundice_article.id, article_section_id=SECTION_JAUNDICE_BILI).first():
db.add(FlashcardArticleLink(flashcard_id=card.id, article_id=jaundice_article.id,
article_section_id=SECTION_JAUNDICE_BILI))
if bank_ids and not db.query(FlashcardQuestionLink.id).filter_by(
flashcard_id=card.id, question_id=bank_ids[1 % len(bank_ids)]).first():
db.add(FlashcardQuestionLink(flashcard_id=card.id, question_id=bank_ids[1 % len(bank_ids)]))
db.commit()
# ── Deep-link lab references to the sample sections ─────────────
lab_targets = [
("Bilirubin (total)", SECTION_JAUNDICE_BILI, "sample-neonatal-jaundice"),
("CSF white cell count", SECTION_SEIZURE_LABS, "sample-febrile-seizures"),
("CSF protein", SECTION_SEIZURE_LABS, "sample-febrile-seizures"),
]
for name, section_id, slug in lab_targets:
article = by_slug.get(slug)
if not article:
continue
for entry in db.query(LabReference).filter(LabReference.name == name).all():
if entry.article_id is None:
entry.article_id = article.id
entry.article_section_id = section_id
print(f"Lab '{name}' deep-links to {slug}#{section_id[:8]}")
# Link a sample card to one lab reference.
lab = db.query(LabReference).filter(LabReference.name == "CSF white cell count").first()
if lab and cards:
if not db.query(LabReferenceCardLink.id).filter_by(
lab_reference_id=lab.id, flashcard_id=cards[0].id).first():
db.add(LabReferenceCardLink(lab_reference_id=lab.id, flashcard_id=cards[0].id))
print(f"Lab '{lab.name}' linked to sample card")
db.commit()
print("Sample content ready.")
finally:
db.close()
if __name__ == "__main__":
sys.exit(main())