Display role vocabulary, migration commit fix, stale enforcement reports

This commit is contained in:
2026-08-13 09:30:40 -04:00
parent d327ea1a5c
commit 5dcc7e30af
16 changed files with 490 additions and 46 deletions

View File

@@ -147,6 +147,21 @@ def run_migrations_online():
with context.begin_transaction():
context.run_migrations()
# COMMIT THE RUN. SQLAlchemy 2.0 connections do not autocommit, and on
# MySQL alembic reports "non-transactional DDL" so begin_transaction()
# above is a no-op - nothing here commits on its own.
#
# It looked like it worked because MySQL implicitly commits on DDL: each
# ALTER/CREATE flushed everything queued before it, including the
# PREVIOUS migration's version stamp. The LAST migration of every run
# has no DDL after it, so its stamp was rolled back at close. The column
# changes survived (already committed by their own DDL) while
# alembic_version stayed one revision behind, so `flask db upgrade`
# exited 0 having silently re-run the final migration, and re-ran it
# again on the next deploy. A migration that is not idempotent would
# apply twice.
connection.commit()
if context.is_offline_mode():
run_migrations_offline()

View File

@@ -0,0 +1,67 @@
"""Move dashboarddefaults.displayrole onto the kiosk's own role vocabulary
Core named the roles dashboard / lobby / partskiosk. The kiosks name them
Dashboard / Lobby / 3DPrintRoom - the literal values a person types into
C:\\Enrollment\\display-type.txt, which the GE-Enforce dispatcher reads to pick
a target. Two vocabularies meant a display could report a role core could not
store, and core could store 'partskiosk', a value no kiosk would ever match.
The machine's own file wins, so the stored values move to it. 'partskiosk'
becomes '3DPrintRoom'; the other two are a case change only. Anything else is
left alone - an unrecognised value is somebody's data, not ours to guess at.
Idempotent and reversible: matching is case-insensitive, so a re-run is a no-op
rather than a second rewrite, and downgrade puts the old spellings back.
Revision ID: 7d32_displayrole_kiosk_vocabulary
Revises: 7d31_dashboarddefault_fqdn
Create Date: 2026-08-13
"""
from alembic import op
import sqlalchemy as sa
revision = '7d32_displayrole_kiosk_vocabulary'
down_revision = '7d31_dashboarddefault_fqdn'
branch_labels = None
depends_on = None
# old spelling -> new spelling
FORWARD = {
'dashboard': 'Dashboard',
'lobby': 'Lobby',
'partskiosk': '3DPrintRoom',
}
BACKWARD = {new: old for old, new in FORWARD.items()}
def _rewrite(mapping, newdefault):
connection = op.get_bind()
inspector = sa.inspect(connection)
if 'dashboarddefaults' not in inspector.get_table_names():
return
columns = {col['name'] for col in inspector.get_columns('dashboarddefaults')}
if 'displayrole' not in columns:
return
for source, target in mapping.items():
connection.execute(
sa.text('UPDATE dashboarddefaults SET displayrole = :target'
' WHERE LOWER(displayrole) = :source'),
{'target': target, 'source': source.lower()})
# 7d28 created the column with server_default='dashboard'; keep the default
# spelled the same way as the values.
op.alter_column('dashboarddefaults', 'displayrole',
existing_type=sa.String(length=20),
existing_nullable=False,
server_default=newdefault)
def upgrade():
_rewrite(FORWARD, 'Dashboard')
def downgrade():
_rewrite(BACKWARD, 'dashboard')