stupa-pdf-api/backend/src/startup.py

261 lines
7.9 KiB
Python

#!/usr/bin/env python3
"""
Application Startup Script
This script initializes the database and creates default data for the dynamic application system.
"""
import sys
import os
import logging
# Add src directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config.database import init_database, get_engine
from sqlalchemy.orm import Session
from models.application_type import (
ApplicationType, ApplicationField, ApplicationTypeStatus,
StatusTransition, FieldType, TransitionTriggerType
)
from models.user import User, Role
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def create_default_application_types():
"""Create default QSM and VSM application types if they don't exist"""
engine = get_engine()
with Session(engine) as session:
# Check if types already exist
existing_qsm = session.query(ApplicationType).filter_by(type_id="qsm").first()
existing_vsm = session.query(ApplicationType).filter_by(type_id="vsm").first()
if existing_qsm and existing_vsm:
logger.info("Default application types already exist")
return
# Create QSM type if not exists
if not existing_qsm:
logger.info("Creating QSM application type...")
qsm_type = ApplicationType(
type_id="qsm",
name="QSM - Qualitätssicherungsmittel",
description="Antrag für Qualitätssicherungsmittel zur Verbesserung der Lehre",
is_active=True,
is_public=True,
max_cost_positions=100,
max_comparison_offers=100
)
session.add(qsm_type)
session.flush()
# Create default statuses for QSM
create_default_statuses(session, qsm_type)
logger.info("QSM application type created successfully")
# Create VSM type if not exists
if not existing_vsm:
logger.info("Creating VSM application type...")
vsm_type = ApplicationType(
type_id="vsm",
name="VSM - Verfasste Studierendenschaft",
description="Antrag für Mittel der Verfassten Studierendenschaft",
is_active=True,
is_public=True,
max_cost_positions=100,
max_comparison_offers=100
)
session.add(vsm_type)
session.flush()
# Create default statuses for VSM
create_default_statuses(session, vsm_type)
logger.info("VSM application type created successfully")
session.commit()
logger.info("Default application types initialization complete")
def create_default_statuses(session, app_type: ApplicationType):
"""Create default statuses and transitions for an application type"""
# Define default statuses
statuses = [
{
"status_id": "draft",
"name": "Entwurf",
"is_editable": True,
"color": "#6B7280",
"is_initial": True,
"display_order": 0
},
{
"status_id": "submitted",
"name": "Eingereicht",
"is_editable": False,
"color": "#3B82F6",
"send_notification": True,
"display_order": 10
},
{
"status_id": "under_review",
"name": "In Prüfung",
"is_editable": False,
"color": "#8B5CF6",
"display_order": 20
},
{
"status_id": "approved",
"name": "Genehmigt",
"is_editable": False,
"color": "#10B981",
"is_final": True,
"send_notification": True,
"display_order": 30
},
{
"status_id": "rejected",
"name": "Abgelehnt",
"is_editable": False,
"color": "#EF4444",
"is_final": True,
"send_notification": True,
"display_order": 40
}
]
status_objects = {}
for status_data in statuses:
status = ApplicationTypeStatus(
application_type_id=app_type.id,
**status_data
)
session.add(status)
session.flush()
status_objects[status_data["status_id"]] = status
# Create transitions
transitions = [
{
"from": "draft",
"to": "submitted",
"name": "Einreichen",
"trigger_type": TransitionTriggerType.APPLICANT_ACTION
},
{
"from": "submitted",
"to": "under_review",
"name": "Prüfung starten",
"trigger_type": TransitionTriggerType.USER_APPROVAL,
"config": {"role": "admin"}
},
{
"from": "under_review",
"to": "approved",
"name": "Genehmigen",
"trigger_type": TransitionTriggerType.USER_APPROVAL,
"config": {"role": "admin"}
},
{
"from": "under_review",
"to": "rejected",
"name": "Ablehnen",
"trigger_type": TransitionTriggerType.USER_APPROVAL,
"config": {"role": "admin"}
}
]
for trans in transitions:
transition = StatusTransition(
from_status_id=status_objects[trans["from"]].id,
to_status_id=status_objects[trans["to"]].id,
name=trans["name"],
trigger_type=trans["trigger_type"],
trigger_config=trans.get("config", {}),
is_active=True
)
session.add(transition)
def create_admin_user():
"""Create a default admin user if none exists"""
engine = get_engine()
with Session(engine) as session:
# Check if any admin user exists
admin_role = session.query(Role).filter_by(name="admin").first()
if not admin_role:
logger.warning("Admin role not found, skipping admin user creation")
return
admin_users = session.query(User).join(User.roles).filter(Role.name == "admin").all()
if admin_users:
logger.info("Admin user(s) already exist")
return
logger.info("Creating default admin user...")
# Create admin user
from services.auth_service import get_password_hash
admin = User(
email="admin@example.com",
given_name="System",
family_name="Administrator",
display_name="System Admin",
auth_provider="local",
verification_status="fully_verified",
email_verified=True
)
# Set password (you should change this!)
# For production, this should be set via environment variable
default_password = os.getenv("ADMIN_PASSWORD", "changeme123")
# Note: You'll need to implement password storage separately
# This is just a placeholder
session.add(admin)
admin.roles.append(admin_role)
session.commit()
logger.info(f"Default admin user created: admin@example.com")
logger.warning(f"IMPORTANT: Change the default admin password immediately!")
def main():
"""Main startup function"""
logger.info("Starting application initialization...")
try:
# Initialize database schema
logger.info("Initializing database schema...")
init_database()
logger.info("Database schema initialized")
# Create default application types
create_default_application_types()
# Create admin user
create_admin_user()
logger.info("Application initialization complete!")
logger.info("You can now start the application server.")
except Exception as e:
logger.error(f"Initialization failed: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
main()