(short: bool)
| 56 | |
| 57 | |
| 58 | async def _install_async(short: bool) -> None: |
| 59 | learnhouse_config = get_learnhouse_config() |
| 60 | sql_url = learnhouse_config.database_config.sql_connection_string # type: ignore |
| 61 | |
| 62 | # Schema DDL runs on a sync engine (SQLModel.metadata.create_all is sync). |
| 63 | sync_engine = create_engine(_to_sync_url(sql_url), echo=False, pool_pre_ping=True) |
| 64 | SQLModel.metadata.create_all(sync_engine) |
| 65 | sync_engine.dispose() |
| 66 | |
| 67 | # The install_* coroutines use sqlmodel.ext.asyncio.session.AsyncSession. |
| 68 | # expire_on_commit=False keeps already-loaded attributes accessible after |
| 69 | # each commit — without it, `UserRead.model_validate(user)` inside |
| 70 | # `install_create_organization_user` triggers async refresh outside the |
| 71 | # session's greenlet context and raises MissingGreenlet. |
| 72 | async_engine = create_async_engine( |
| 73 | _to_async_url(sql_url), echo=False, pool_pre_ping=True |
| 74 | ) |
| 75 | |
| 76 | try: |
| 77 | async with AsyncSession( |
| 78 | async_engine, expire_on_commit=False |
| 79 | ) as db_session: |
| 80 | if short: |
| 81 | # Install the default elements |
| 82 | print("Installing default elements...") |
| 83 | await install_default_elements(db_session) |
| 84 | print("Default elements installed ✅") |
| 85 | |
| 86 | # Honor LEARNHOUSE_INITIAL_ORG_NAME / LEARNHOUSE_INITIAL_ORG_SLUG when |
| 87 | # the CLI passes them — falls back to "Default Organization" / "default" |
| 88 | # so existing standalone deployments still work unchanged. |
| 89 | org_name = os.environ.get("LEARNHOUSE_INITIAL_ORG_NAME", "Default Organization") |
| 90 | org_slug = os.environ.get("LEARNHOUSE_INITIAL_ORG_SLUG", "default").lower() |
| 91 | |
| 92 | # Create the Organization |
| 93 | print(f"Creating organization '{org_name}' (slug: {org_slug})...") |
| 94 | org = OrganizationCreate( |
| 95 | name=org_name, |
| 96 | description=org_name, |
| 97 | slug=org_slug, |
| 98 | email="", |
| 99 | logo_image="", |
| 100 | thumbnail_image="", |
| 101 | about="", |
| 102 | label="", |
| 103 | ) |
| 104 | await install_create_organization(org, db_session) |
| 105 | print(f"Organization '{org_name}' created ✅") |
| 106 | |
| 107 | # Create Organization User |
| 108 | print("Creating default organization user...") |
| 109 | # Use email from environment variable if provided, otherwise default to "admin@school.dev" |
| 110 | email = os.environ.get("LEARNHOUSE_INITIAL_ADMIN_EMAIL", "admin@school.dev") |
| 111 | # Require password from environment variable |
| 112 | password = os.environ.get("LEARNHOUSE_INITIAL_ADMIN_PASSWORD") |
| 113 | if not password: |
| 114 | print("❌ Error: LEARNHOUSE_INITIAL_ADMIN_PASSWORD environment variable is required") |
| 115 | print("Please set LEARNHOUSE_INITIAL_ADMIN_PASSWORD environment variable before running installation.") |
no test coverage detected