Add flask pages CLI group for auditing and fixing page order

This commit is contained in:
jessikitty committed 2026-10-02 19:56:13 +10:00
1 parent 42956cff97
commit 95425986d8
1 file changed
+125
+125
View File
@@ -0,0 +1,125 @@
"""
CLI commands for checking and fixing image instruction page order.
Lives in its own module rather than in cli.py so the page-ordering work stays
together: the logic is in app/services/page_order.py and the commands that
drive it are here.
flask pages audit which sets have broken numbering
flask pages fix --dry-run what would be repaired
flask pages fix repair every set that needs it
flask pages fix --set 60123 repair one set
flask pages sort 60123 --by filename reorder one set
flask pages sort 60123 --by page_hint --reverse
Created by: Jess Rogerson (yelling commands at Claude.AI)
"""
import click
from flask.cli import AppGroup
pages_cli = AppGroup(
'pages', help='Check and fix the page order of image instructions.')
def _sets_with_images():
"""Every set that has at least one image page, in set-number order."""
from app import db
from app.models.set import Set
from app.models.instruction import Instruction
ids = [row[0] for row in db.session.query(Instruction.set_id)
.filter(Instruction.file_type == 'IMAGE').distinct()]
return Set.query.filter(Set.id.in_(ids)).order_by(Set.set_number).all()
@pages_cli.command('audit')
@click.option('--all/--problems', 'show_all', default=False,
help='List every set, or only the ones with a numbering problem '
'(default).')
def pages_audit(show_all):
"""Report duplicate, missing or unnumbered pages across the library."""
from app.services import page_order
problems = 0
for lego_set in _sets_with_images():
report = page_order.audit(lego_set.id)
if report['is_clean'] and not show_all:
continue
if not report['is_clean']:
problems += 1
state = 'ok' if report['is_clean'] else 'NEEDS FIXING'
colour = 'green' if report['is_clean'] else 'yellow'
click.secho(f"{lego_set.set_number:<12} {report['total']:>4} pages "
f"{state}", fg=colour)
if report['duplicates']:
click.echo(f" duplicates : {report['duplicates']}")
if report['gaps']:
click.echo(f" gaps : {report['gaps']}")
if report['unnumbered']:
click.echo(f" unnumbered : {report['unnumbered']}")
click.echo(f"\n{problems} set(s) need fixing.")
@pages_cli.command('fix')
@click.option('--set', 'set_number', default=None,
help='One set number. Omit to fix every set that needs it.')
@click.option('--dry-run', is_flag=True, help='Report without writing.')
def pages_fix(set_number, dry_run):
"""Renumber pages 1..n, closing gaps and clearing duplicates."""
from app.models.set import Set
from app.services import page_order
if set_number:
targets = Set.query.filter_by(set_number=set_number).all()
if not targets:
raise click.ClickException(f'No set numbered {set_number}')
else:
targets = _sets_with_images()
fixed = 0
for lego_set in targets:
report = page_order.audit(lego_set.id)
if report['is_clean']:
continue
fixed += 1
if dry_run:
click.echo(f"would fix {lego_set.set_number} "
f"({report['total']} pages)")
continue
page_order.renumber(lego_set.id)
click.secho(f"fixed {lego_set.set_number} ({report['total']} pages)",
fg='green')
click.echo(f"{fixed} set(s) {'would be ' if dry_run else ''}fixed.")
@pages_cli.command('sort')
@click.argument('set_number')
@click.option('--by', 'strategy', default='filename',
help='filename, page_hint, uploaded or size.')
@click.option('--reverse', is_flag=True, help='Sort the other way round.')
def pages_sort(set_number, strategy, reverse):
"""Reorder one set's image pages by filename, upload order or size."""
from app.models.set import Set
from app.services import page_order
lego_set = Set.query.filter_by(set_number=set_number).first()
if lego_set is None:
raise click.ClickException(f'No set numbered {set_number}')
try:
pages = page_order.auto_sort(lego_set.id, strategy, reverse=reverse)
except ValueError as e:
raise click.ClickException(str(e))
for page in pages:
click.echo(f" {page.page_number:>4} {page.file_name}")
click.secho(f'Sorted {len(pages)} pages.', fg='green')
def register_pages_cli(app):
"""Attach the `flask pages` group to `app`."""
app.cli.add_command(pages_cli)