diff --git a/backend/scripts/seed_sample_content.py b/backend/scripts/seed_sample_content.py new file mode 100644 index 0000000..dfd0db1 --- /dev/null +++ b/backend/scripts/seed_sample_content.py @@ -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 months–5 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 3–5 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 months–5 years without CNS infection or metabolic cause. **Sample content** — verify clinically."), + ("Sample: jaundice timeline", + "Physiologic jaundice appears after 24 h and peaks at 3–5 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())