Add flask pages CLI group for auditing and fixing page order
This commit is contained in:
1 parent
42956cff97
commit
95425986d8
1 file changed
+125
@@ -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)
|
||||
Reference in new issue
Block a user