One 'display' image resolves what it shows from its own IP, like the existing
visitor-location BU mapping. Extend DashboardDefault with displayrole
(dashboard|lobby|partskiosk; migration 7d28, businessunitid now nullable since
only the dashboard role needs one) + a role->path map. New unauthenticated
GET /api/dashboarddefaults/display-role returns {role, path, businessunitid}
for the caller IP. Settings UI gains a Display selector, showing the business
unit only for the dashboard role.
64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
"""Add dashboarddefaults.displayrole; make businessunitid nullable
|
|
|
|
One "display" PC image resolves its role (dashboard / lobby / partskiosk) from
|
|
its IP. Existing rows are dashboard mappings, so displayrole defaults to
|
|
'dashboard'. Lobby / kiosk roles need no business unit, so businessunitid
|
|
becomes nullable.
|
|
|
|
Idempotent guards on column presence / nullability.
|
|
|
|
Revision ID: 7d28_dashboarddefault_displayrole
|
|
Revises: 7d27_roles_color
|
|
Create Date: 2026-07-21
|
|
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision = '7d28_dashboarddefault_displayrole'
|
|
down_revision = '7d27_roles_color'
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def _cols(insp):
|
|
return {c['name']: c for c in insp.get_columns('dashboarddefaults')}
|
|
|
|
|
|
def upgrade():
|
|
bind = op.get_bind()
|
|
insp = sa.inspect(bind)
|
|
if 'dashboarddefaults' not in insp.get_table_names():
|
|
return
|
|
cols = _cols(insp)
|
|
|
|
if 'displayrole' not in cols:
|
|
op.add_column('dashboarddefaults', sa.Column(
|
|
'displayrole', sa.String(length=20),
|
|
nullable=False, server_default='dashboard'))
|
|
|
|
if 'businessunitid' in cols and not cols['businessunitid']['nullable']:
|
|
op.alter_column('dashboarddefaults', 'businessunitid',
|
|
existing_type=sa.Integer(), nullable=True)
|
|
|
|
|
|
def downgrade():
|
|
bind = op.get_bind()
|
|
insp = sa.inspect(bind)
|
|
if 'dashboarddefaults' not in insp.get_table_names():
|
|
return
|
|
cols = _cols(insp)
|
|
|
|
# Only restore NOT NULL if no rows would violate it.
|
|
if 'businessunitid' in cols and cols['businessunitid']['nullable']:
|
|
nulls = bind.execute(sa.text(
|
|
'SELECT COUNT(*) FROM dashboarddefaults '
|
|
'WHERE businessunitid IS NULL')).scalar()
|
|
if not nulls:
|
|
op.alter_column('dashboarddefaults', 'businessunitid',
|
|
existing_type=sa.Integer(), nullable=False)
|
|
|
|
if 'displayrole' in cols:
|
|
op.drop_column('dashboarddefaults', 'displayrole')
|