#!/usr/bin/env python3 """ Seed the database with initial testing data. """ import random import uuid from datetime import datetime, timedelta from sqlalchemy import inspect from sqlalchemy.orm import Session from passlib.context import CryptContext import asyncio from .models import User, Team, TeamMembership, QRCode, QRSet, TeamAchievement, Event, SystemSettings from .db import SessionLocal, engine from .db_migrations import run_migrations pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def get_password_hash(password: str) -> str: return pwd_context.hash(password) def table_has_column(engine, table_name, column_name): """Check if a table has a specific column.""" inspector = inspect(engine) if table_name not in inspector.get_table_names(): return False columns = [col['name'] for col in inspector.get_columns(table_name)] return column_name in columns async def init_db(): """Initialize the database, applying migrations and seeding data.""" # First, create all tables if they don't exist from .models import Base Base.metadata.create_all(bind=engine) # Then, run any needed migrations await asyncio.to_thread(run_migrations, engine) # Then proceed with seeding if needed await asyncio.to_thread(seed_db) # Initialize system settings if needed await asyncio.to_thread(init_system_settings) def init_system_settings(): """Initialize the system settings table if it doesn't exist.""" db = SessionLocal() try: # Check if there's already a system settings record settings = db.query(SystemSettings).first() if not settings: # Create initial system settings with setup_completed = False settings = SystemSettings(setup_completed=False) db.add(settings) db.commit() print("System settings initialized.") except Exception as e: print(f"Error initializing system settings: {e}") finally: db.close() def seed_db(): """Seed the database with test data.""" db = SessionLocal() try: # Only seed if tables are empty if db.query(User).count() > 0: print("Database already has data. Skipping seeding.") return # Create users with properly hashed passwords users = [ User( username="admin", email="admin@example.com", hashed_password=get_password_hash("password"), is_admin=True, # Set admin privileges privacy_settings={ "email": "private", "full_name": "friends", "teams": "public", "points": "public", "achievements": "public", "events": "friends" } ), User( username="john_quizmaster", email="john@example.com", hashed_password=get_password_hash("password123"), privacy_settings={ "email": "friends", "full_name": "public", "teams": "public", "points": "public", "achievements": "public", "events": "public" } ), User( username="sarah_johnson", email="sarah@example.com", hashed_password=get_password_hash("password123"), privacy_settings={ "email": "private", "full_name": "public", "teams": "public", "points": "friends", "achievements": "public", "events": "friends" } ), User( username="mike_peters", email="mike@example.com", hashed_password=get_password_hash("password123"), privacy_settings={ "email": "private", "full_name": "friends", "teams": "public", "points": "public", "achievements": "public", "events": "public" } ), User( username="emma_wilson", email="emma@example.com", hashed_password=get_password_hash("password123"), privacy_settings={ "email": "private", "full_name": "private", "teams": "friends", "points": "private", "achievements": "friends", "events": "private" } ), User( username="robert_brown", email="robert@example.com", hashed_password=get_password_hash("password123"), privacy_settings={ "email": "private", "full_name": "friends", "teams": "public", "points": "public", "achievements": "public", "events": "friends" } ), ] db.add_all(users) db.commit() # Create teams has_is_public = table_has_column(db.bind, 'teams', 'is_public') has_created_at = table_has_column(db.bind, 'teams', 'created_at') has_description = table_has_column(db.bind, 'teams', 'description') teams = [] for i, name in enumerate(["Quiz Wizards", "Trivia Titans", "Beer Brainiacs", "Knowledge Knights"]): team_attrs = {"name": name} if has_is_public: team_attrs["is_public"] = i % 2 == 1 # Alternate public/private if has_description: team_attrs["description"] = f"A team of quiz enthusiasts called {name}" teams.append(Team(**team_attrs)) db.add_all(teams) db.commit() # Create team memberships has_joined_at = table_has_column(db.bind, 'team_membership', 'joined_at') memberships = [] membership_data = [ # Quiz Wizards (1, 1, True, True, 160), # Admin user is team admin AND captain of Quiz Wizards (2, 1, True, True, 155), # John is also admin AND captain (3, 1, False, False, 130), (4, 1, False, False, 90), (5, 1, False, False, 45), # Trivia Titans (2, 2, True, True, 150), # John is admin AND captain of Trivia Titans (1, 2, False, False, 145), # Beer Brainiacs (3, 3, True, True, 120), # Sarah is admin AND captain of Beer Brainiacs ] for user_id, team_id, is_admin, is_captain, days_ago in membership_data: membership_attrs = { "user_id": user_id, "team_id": team_id, "is_admin": is_admin, "is_captain": is_captain # Added is_captain field } if has_joined_at: membership_attrs["joined_at"] = datetime.now() - timedelta(days=days_ago) memberships.append(TeamMembership(**membership_attrs)) db.add_all(memberships) db.commit() # Create events for QR code linking events = [ Event(name="Music Trivia Night", description="A night of musical quizzes", event_date=datetime.now() - timedelta(days=60), location="Irish Rover Pub"), Event(name="History Night", description="Test your history knowledge", event_date=datetime.now() - timedelta(days=45), location="Irish Rover Pub"), Event(name="Movie Trivia Night", description="All about cinema", event_date=datetime.now() - timedelta(days=30), location="Irish Rover Pub"), Event(name="Sports Quiz", description="For sports enthusiasts", event_date=datetime.now() - timedelta(days=15), location="Irish Rover Pub"), Event(name="General Knowledge", description="A bit of everything", event_date=datetime.now() - timedelta(days=7), location="Irish Rover Pub"), Event(name="Irish Rover Pub Quiz April 2025", description="Monthly pub quiz", event_date=datetime.now(), location="Irish Rover Pub") ] db.add_all(events) db.commit() # Create QR sets qr_sets = [ QRSet( name="Standard Pub Quiz", description="Contains QR codes for 1st place (25 points), 2nd place (15 points), 3rd place (10 points), and 4th place (5 points)", created_by=1 # Admin user ), QRSet( name="Trivia Night with Achievements", description="Special trivia night with QR codes for winners and achievement codes for trivia categories", created_by=2 # John the quizmaster ) ] db.add_all(qr_sets) db.commit() # Create QR codes (both used and unused) qr_codes = [] # First, create some QR codes in sets (unused) # Standard Pub Quiz set qr_set_1 = qr_sets[0] qr_codes.extend([ QRCode( code=str(uuid.uuid4()), points=25, title="1st Place", description="First place award (25 points)", achievement_name="First Place Winner", qr_set_id=qr_set_1.id, used=False ), QRCode( code=str(uuid.uuid4()), points=15, title="2nd Place", description="Second place award (15 points)", achievement_name="Second Place Winner", qr_set_id=qr_set_1.id, used=False ), QRCode( code=str(uuid.uuid4()), points=10, title="3rd Place", description="Third place award (10 points)", achievement_name="Third Place Winner", qr_set_id=qr_set_1.id, used=False ), QRCode( code=str(uuid.uuid4()), points=5, title="4th Place", description="Fourth place award (5 points)", achievement_name=None, qr_set_id=qr_set_1.id, used=False ) ]) # Trivia Night set with special achievements qr_set_2 = qr_sets[1] qr_codes.extend([ QRCode( code=str(uuid.uuid4()), points=20, title="Trivia Champion", description="Overall winner of trivia night", achievement_name="Trivia Champion", qr_set_id=qr_set_2.id, used=False ), QRCode( code=str(uuid.uuid4()), points=0, title="Estimate Winner", description="Closest guess to the correct answer", achievement_name="Closest Guess Award", is_achievement_only=True, qr_set_id=qr_set_2.id, used=False ), QRCode( code=str(uuid.uuid4()), points=0, title="Film Buff", description="Most movie questions correct", achievement_name="Film Buff", is_achievement_only=True, qr_set_id=qr_set_2.id, used=False ) ]) # Now create some already used/redeemed QR codes for i in range(15): points = random.choice([5, 10, 15, 20, 25]) team_id = random.randint(1, len(teams)) user_id = random.randint(1, len(users)) event_id = random.randint(1, len(events) - 1) # Exclude the latest event achievement = None if points >= 15: # Only high points get achievements achievement = random.choice(["Winner", "Top Scorer", "Quiz Master", None]) redeemed_at = datetime.now() - timedelta(days=random.randint(7, 90)) qr_codes.append( QRCode( code=f"TICKET{i:03d}", points=points, title=f"{points} Points Ticket", achievement_name=achievement, redeemed_by=user_id, redeemed_at_team=team_id, redeemed_at=redeemed_at, event_id=event_id, used=True ) ) # Create achievement record if applicable if achievement: team_achievement = TeamAchievement( team_id=team_id, name=achievement, event_id=event_id, achieved_at=redeemed_at, qr_code_id=i + 1 # This will be assigned after the QR codes are committed ) db.add(team_achievement) db.add_all(qr_codes) db.commit() print("Database seeded successfully!") except Exception as e: print(f"Error seeding database: {e}") finally: db.close() if __name__ == "__main__": asyncio.run(init_db())