Aug 7 - Update: UI change - MT16
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""phase52 — per-user web portal design preference
|
||||
|
||||
Adds to `users`:
|
||||
|
||||
ui_theme VARCHAR(16) NOT NULL DEFAULT 'classic'
|
||||
|
||||
'classic' renders the original top-navbar shell (layouts/classic.html),
|
||||
'modern' renders the sidebar shell (layouts/modern.html). Every existing
|
||||
account starts on 'classic', so the portal looks and behaves exactly as before
|
||||
until a user opts in from the account menu.
|
||||
|
||||
MULTI-TENANT NOTE — why this differs from ST
|
||||
--------------------------------------------
|
||||
ST shipped this as phase48 (default 'classic') and then phase50, which flipped
|
||||
the column default to 'modern' AND ran
|
||||
|
||||
UPDATE users SET ui_theme = 'modern' WHERE ui_theme = 'classic'
|
||||
|
||||
overwriting every saved preference. That was a defensible call for a
|
||||
single-tenant deployment deciding for its own staff after its own A/B test.
|
||||
|
||||
It is NOT portable to MT. Here the same statement would run against EVERY
|
||||
tenant database, flipping the entire UI for tenants who never saw the test and
|
||||
never asked. So MT ships the phase48 semantics only: the column defaults to
|
||||
'classic' and there is no backfill of any kind.
|
||||
|
||||
The default for accounts that have never chosen is config DEFAULT_UI_THEME
|
||||
(app/__init__.py::resolve_ui_theme), which reads the environment. A stored
|
||||
users.ui_theme always wins over it. To put a tenant on the modern design by
|
||||
default, set DEFAULT_UI_THEME=modern for that tenant's process — no migration,
|
||||
no overwritten preferences, and individual users can still switch either way.
|
||||
|
||||
Uses an INFORMATION_SCHEMA existence check — safe to re-run.
|
||||
"""
|
||||
|
||||
revision = 'phase52_user_ui_theme'
|
||||
down_revision = 'phase51_external_inspector'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def _column_exists(conn, table, column):
|
||||
return conn.execute(sa.text(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() "
|
||||
"AND TABLE_NAME = :t AND COLUMN_NAME = :c"
|
||||
), {"t": table, "c": column}).scalar() > 0
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
if not _column_exists(bind, 'users', 'ui_theme'):
|
||||
op.execute(sa.text(
|
||||
"ALTER TABLE users "
|
||||
"ADD COLUMN ui_theme VARCHAR(16) NOT NULL DEFAULT 'classic'"
|
||||
))
|
||||
# Idempotent repair for any row holding a value outside the known set (for
|
||||
# example a partially-applied earlier run). This only ever touches NULL or
|
||||
# invalid values — it does NOT reset a user's deliberate 'modern' choice.
|
||||
op.execute(sa.text(
|
||||
"UPDATE users SET ui_theme = 'classic' "
|
||||
"WHERE ui_theme IS NULL OR ui_theme NOT IN ('classic', 'modern')"
|
||||
))
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
if _column_exists(bind, 'users', 'ui_theme'):
|
||||
op.execute(sa.text("ALTER TABLE users DROP COLUMN ui_theme"))
|
||||
Reference in New Issue
Block a user