261 lines
7.9 KiB
Python
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()
|