Files
shopdb-flask/plugins/printers/migrations/versions/0005_observedqueues.py

91 lines
3.9 KiB
Python

"""printers: printerobservedqueues table (what a bay reported it HAS).
ShopDB already knows what a bay SHOULD have (usesprinter/defaultprinter rows on
the machine). This table holds the other half: the queues a PC reported through
POST /api/collector/printers. Observed and assigned stay in separate tables on
purpose, so observed drift can never be mistaken for desired state.
One row per observed queue; the latest report for a host replaces all of that
host's rows. The unique index on (hostname, queuename) is the guard that turns a
half-finished replace into an IntegrityError instead of duplicate queues.
Explicit ops rather than create_plugin_tables: the helper builds a per-plugin
MetaData filtered to the plugin's own tables, so the foreign key to the core
assets table cannot resolve at CreateTable-compile time. Same reason the backups
baseline spells its ops out.
Guarded both ways, so a re-run (or a database where db.create_all already built
the table) is a no-op.
Revision ID: printers0005observedqueues
Revises: printers0004drivervendor
"""
from alembic import op
from shopdb.plugins.alembic_template import (
create_index_if_missing,
create_table_if_missing,
)
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'printers0005observedqueues'
down_revision = 'printers0004drivervendor'
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
inspector = sa.inspect(bind)
if 'printerobservedqueues' in inspector.get_table_names():
return
create_table_if_missing(
'printerobservedqueues',
sa.Column('printerobservedqueueid', sa.Integer(), nullable=False),
# Nullable and no index of its own beyond the explicit one below: an
# unenrolled bay still gets to report, and hostname is what identifies
# the report.
sa.Column('assetid', sa.Integer(), nullable=True),
sa.Column('hostname', sa.String(length=255), nullable=False),
sa.Column('queuename', sa.String(length=255), nullable=False),
sa.Column('drivername', sa.String(length=255), nullable=True),
sa.Column('portname', sa.String(length=255), nullable=True),
sa.Column('portaddress', sa.String(length=255), nullable=True),
sa.Column('isdefault', sa.Boolean(), nullable=False,
server_default=sa.false()),
sa.Column('isshared', sa.Boolean(), nullable=False,
server_default=sa.false()),
sa.Column('observedat', sa.DateTime(), nullable=False),
sa.Column('createddate', sa.DateTime(), nullable=False),
sa.Column('modifieddate', sa.DateTime(), nullable=False),
sa.Column('isactive', sa.Boolean(), nullable=False,
server_default=sa.true()),
sa.ForeignKeyConstraint(['assetid'], ['assets.assetid'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('printerobservedqueueid'),
# Doubles as the read index for the hostname filter (leftmost column),
# so no separate hostname index is created.
sa.UniqueConstraint('hostname', 'queuename',
name='uq_printerobservedqueue_host_queue'),
)
# Port address is the primary match key from an observed queue back to a
# printer asset, so every comparison read hits it.
create_index_if_missing('idx_printerobservedqueues_portaddress',
'printerobservedqueues', ['portaddress'])
create_index_if_missing('idx_printerobservedqueues_assetid',
'printerobservedqueues', ['assetid'])
def downgrade():
bind = op.get_bind()
inspector = sa.inspect(bind)
if 'printerobservedqueues' not in inspector.get_table_names():
return
op.drop_index('idx_printerobservedqueues_assetid',
table_name='printerobservedqueues')
op.drop_index('idx_printerobservedqueues_portaddress',
table_name='printerobservedqueues')
op.drop_table('printerobservedqueues')