| |
| """Beta Demo Clear Script β DocDoe AI. |
| |
| Removes ONLY the data created by seed_beta_demo.py: |
| - Video render jobs owned by demo@docdoe.in |
| - Document chunks owned by demo@docdoe.in |
| - Documents owned by demo@docdoe.in (+ stub files on disk) |
| - UserPlan owned by demo@docdoe.in |
| - StudyProfile owned by demo@docdoe.in |
| - All normalized learning-state rows owned by demo@docdoe.in |
| - The demo user account itself |
| |
| Safe to run when demo data is absent (no-ops on missing rows). |
| Does NOT touch any other user data. |
| |
| Usage: |
| cd backend |
| python scripts/clear_beta_demo.py |
| python scripts/clear_beta_demo.py --dry-run |
| """ |
| from __future__ import annotations |
|
|
| import argparse |
| import sys |
| from pathlib import Path |
|
|
| |
| BACKEND_DIR = Path(__file__).resolve().parents[1] |
| if str(BACKEND_DIR) not in sys.path: |
| sys.path.insert(0, str(BACKEND_DIR)) |
|
|
| DEMO_EMAIL = "demo@docdoe.in" |
|
|
|
|
| def clear(dry_run: bool = False) -> None: |
| from app.core.database import SessionLocal, init_db |
| from app.models.document import Document |
| from app.models.document_chunk import DocumentChunk |
| from app.models.learning_state import ( |
| Chapter, |
| DailyTask, |
| GeneratedResource, |
| LessonProgress, |
| QuizAttempt, |
| StudentProfileState, |
| StudyPlan, |
| StudySession, |
| Subject, |
| Subscription, |
| TopicMastery, |
| UsageEvent, |
| ) |
| from app.models.study_profile import StudyProfile |
| from app.models.user import User |
| from app.models.user_plan import UserPlan |
| from app.models.video_render_job import VideoRenderJob |
|
|
| init_db() |
|
|
| prefix = "[DRY RUN] " if dry_run else "" |
|
|
| with SessionLocal() as db: |
| user = db.query(User).filter(User.email == DEMO_EMAIL).first() |
| if user is None: |
| print(f"Demo user '{DEMO_EMAIL}' not found β nothing to clear.") |
| return |
|
|
| uid = user.id |
| print(f"{prefix}Clearing demo data for user {uid} ({DEMO_EMAIL})") |
|
|
| |
| jobs = db.query(VideoRenderJob).filter(VideoRenderJob.user_id == uid).all() |
| print(f"{prefix} Video jobs : {len(jobs)}") |
| if not dry_run: |
| for job in jobs: |
| db.delete(job) |
| db.flush() |
|
|
| |
| docs = db.query(Document).filter(Document.user_id == uid).all() |
| chunk_total = 0 |
| file_total = 0 |
| for doc in docs: |
| chunks = ( |
| db.query(DocumentChunk) |
| .filter(DocumentChunk.document_id == doc.id) |
| .all() |
| ) |
| chunk_total += len(chunks) |
| if not dry_run: |
| for chunk in chunks: |
| db.delete(chunk) |
|
|
| if not dry_run: |
| db.flush() |
|
|
| for doc in docs: |
| |
| if doc.file_path: |
| stub = Path(doc.file_path) |
| if stub.exists() and stub.name.startswith("demo_"): |
| file_total += 1 |
| if not dry_run: |
| stub.unlink(missing_ok=True) |
| if not dry_run: |
| db.delete(doc) |
|
|
| if not dry_run: |
| db.flush() |
|
|
| print(f"{prefix} Documents : {len(docs)} (chunks: {chunk_total}, stub files: {file_total})") |
|
|
| |
| plans = db.query(UserPlan).filter(UserPlan.user_id == uid).all() |
| print(f"{prefix} User plans : {len(plans)}") |
| if not dry_run: |
| for p in plans: |
| db.delete(p) |
| db.flush() |
|
|
| |
| profiles = db.query(StudyProfile).filter(StudyProfile.user_id == uid).all() |
| print(f"{prefix} Study profiles : {len(profiles)}") |
| if not dry_run: |
| for sp in profiles: |
| db.delete(sp) |
| db.flush() |
|
|
| |
| learning_models = [ |
| (UsageEvent, "Usage events"), |
| (GeneratedResource, "Generated resources"), |
| (TopicMastery, "Topic mastery"), |
| (QuizAttempt, "Quiz attempts"), |
| (LessonProgress, "Lesson progress"), |
| (StudySession, "Study sessions"), |
| (DailyTask, "Daily tasks"), |
| (StudyPlan, "Study plans"), |
| (Chapter, "Chapters"), |
| (Subject, "Subjects"), |
| (Subscription, "Subscriptions"), |
| (StudentProfileState, "Student profile state"), |
| ] |
| for model, label in learning_models: |
| rows = db.query(model).filter(model.user_id == uid).all() |
| print(f"{prefix} {label:<22}: {len(rows)}") |
| if not dry_run: |
| for row in rows: |
| db.delete(row) |
| db.flush() |
|
|
| |
| print(f"{prefix} User account : 1 ({DEMO_EMAIL})") |
| if not dry_run: |
| db.delete(user) |
|
|
| if not dry_run: |
| db.commit() |
| print() |
| print("[OK] Demo data cleared.") |
| else: |
| print() |
| print("Dry run complete β no changes made.") |
| print("Run without --dry-run to delete.") |
|
|
|
|
| |
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description="Remove beta demo seed data from DocDoe AI.") |
| parser.add_argument( |
| "--dry-run", |
| action="store_true", |
| help="Show what would be deleted without making changes.", |
| ) |
| args = parser.parse_args() |
| clear(dry_run=args.dry_run) |
|
|