Aug 4 - Update code to follow up - MT11
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""phase46 — inspection schedule day-of-week / day-of-month recurrence
|
||||
|
||||
Ports single-tenant phase43 onto MT's `inspection_schedules` table (MT's name
|
||||
for the same thing — no rename, rule 7). Adds the recurrence-detail columns so a
|
||||
weekly schedule can name the weekdays it runs on (Mon/Wed/Fri) and a monthly (or
|
||||
quarterly / bi-annual / annual) schedule can name either a day of the month
|
||||
("the 15th") or an nth weekday ("the 2nd Tuesday"):
|
||||
|
||||
weekdays VARCHAR(20) -- CSV of Python weekday ints, Mon=0, e.g. '0,2,4'
|
||||
month_mode VARCHAR(20) -- 'day_of_month' | 'nth_weekday'
|
||||
day_of_month SMALLINT -- 1–31, clamped to the month's last day
|
||||
nth_week SMALLINT -- 1–5, or -1 for "last"
|
||||
nth_weekday SMALLINT -- 0–6, Mon=0
|
||||
|
||||
All nullable with no backfill: existing phase34/phase43 rows keep NULLs and fall
|
||||
back to the plain-interval behaviour in InspectionSchedule._add_interval(), so
|
||||
no schedule changes cadence on deploy.
|
||||
|
||||
`next_run_at` is NOT touched — it keeps its name, its DATETIME type and its
|
||||
index. It remains the due datetime (ST's `next_due_date` by another name); the
|
||||
recurrence maths runs on its DATE part and preserves its TIME part.
|
||||
|
||||
`month_mode` is VARCHAR rather than ENUM so adding a recurrence style later
|
||||
needs no 3-step MySQL ENUM migration.
|
||||
|
||||
Uses INFORMATION_SCHEMA column-existence checks — safe to re-run on every
|
||||
tenant DB. Additive only: nothing is renamed, retyped or dropped.
|
||||
"""
|
||||
|
||||
revision = 'phase46_schedule_recurrence'
|
||||
down_revision = 'phase45_schedule_frequency_enum'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
_TABLE = 'inspection_schedules'
|
||||
|
||||
_COLUMNS = (
|
||||
('weekdays', 'VARCHAR(20) NULL'),
|
||||
('month_mode', 'VARCHAR(20) NULL'),
|
||||
('day_of_month', 'SMALLINT NULL'),
|
||||
('nth_week', 'SMALLINT NULL'),
|
||||
('nth_weekday', 'SMALLINT NULL'),
|
||||
)
|
||||
|
||||
|
||||
def _table_exists(conn, table):
|
||||
return conn.execute(sa.text(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t"
|
||||
), {"t": table}).scalar() > 0
|
||||
|
||||
|
||||
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 _table_exists(bind, _TABLE):
|
||||
return
|
||||
for name, ddl in _COLUMNS:
|
||||
if not _column_exists(bind, _TABLE, name):
|
||||
op.execute(sa.text(
|
||||
f"ALTER TABLE {_TABLE} ADD COLUMN {name} {ddl}"
|
||||
))
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
if not _table_exists(bind, _TABLE):
|
||||
return
|
||||
for name, _ddl in reversed(_COLUMNS):
|
||||
if _column_exists(bind, _TABLE, name):
|
||||
op.execute(sa.text(
|
||||
f"ALTER TABLE {_TABLE} DROP COLUMN {name}"
|
||||
))
|
||||
Reference in New Issue
Block a user