Add page organiser routes; fix bulk upload tuple unpack and harden reorder
This commit is contained in:
1 parent
2e2f2d66d6
commit
48c82ab763
1 file changed
+134
-30
+134
-30
@@ -5,6 +5,7 @@ from app import db
|
||||
from app.models.set import Set
|
||||
from app.models.instruction import Instruction
|
||||
from app.services.file_handler import FileHandler
|
||||
from app.services import page_order
|
||||
import os
|
||||
|
||||
instructions_bp = Blueprint('instructions', __name__, url_prefix='/instructions')
|
||||
@@ -41,14 +42,7 @@ def upload(set_id):
|
||||
# Determine page number for images
|
||||
page_number = 1
|
||||
if file_type == 'IMAGE':
|
||||
# Get the highest page number for this set
|
||||
max_page = db.session.query(
|
||||
db.func.max(Instruction.page_number)
|
||||
).filter_by(
|
||||
set_id=set_id,
|
||||
file_type='IMAGE'
|
||||
).scalar()
|
||||
page_number = (max_page or 0) + 1
|
||||
page_number = page_order.next_page_number(set_id)
|
||||
|
||||
# Create instruction record
|
||||
instruction = Instruction(
|
||||
@@ -156,25 +150,25 @@ def view(instruction_id):
|
||||
@instructions_bp.route('/<int:set_id>/reorder', methods=['POST'])
|
||||
@login_required
|
||||
def reorder(set_id):
|
||||
"""Reorder image instructions for a set."""
|
||||
lego_set = Set.query.get_or_404(set_id)
|
||||
"""Save a new page order for a set's image instructions."""
|
||||
Set.query.get_or_404(set_id)
|
||||
|
||||
# Get new order from request
|
||||
new_order = request.json.get('order', [])
|
||||
payload = request.get_json(silent=True) or {}
|
||||
new_order = payload.get('order', [])
|
||||
|
||||
if not new_order:
|
||||
return jsonify({'error': 'No order provided'}), 400
|
||||
|
||||
try:
|
||||
# Update page numbers
|
||||
for index, instruction_id in enumerate(new_order, start=1):
|
||||
instruction = Instruction.query.get(instruction_id)
|
||||
if instruction and instruction.set_id == set_id:
|
||||
instruction.page_number = index
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({'success': True})
|
||||
|
||||
pages = page_order.apply_order(set_id, new_order)
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'pages': [p.to_dict() for p in pages],
|
||||
'audit': page_order.audit(set_id),
|
||||
})
|
||||
except ValueError as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 400
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
@@ -201,8 +195,10 @@ def bulk_upload(set_id):
|
||||
# Determine file type
|
||||
file_type = FileHandler.get_file_type(file.filename)
|
||||
|
||||
# Save file
|
||||
file_path, file_size = FileHandler.save_file(
|
||||
# Save file (save_file returns three values; the thumbnail was being
|
||||
# dropped here, which blew up every bulk upload with a tuple-unpack
|
||||
# error before it ever reached the database)
|
||||
file_path, file_size, thumbnail_path = FileHandler.save_file(
|
||||
file,
|
||||
lego_set.set_number,
|
||||
file_type
|
||||
@@ -211,13 +207,7 @@ def bulk_upload(set_id):
|
||||
# Determine page number for images
|
||||
page_number = 1
|
||||
if file_type == 'IMAGE':
|
||||
max_page = db.session.query(
|
||||
db.func.max(Instruction.page_number)
|
||||
).filter_by(
|
||||
set_id=set_id,
|
||||
file_type='IMAGE'
|
||||
).scalar()
|
||||
page_number = (max_page or 0) + 1
|
||||
page_number = page_order.next_page_number(set_id)
|
||||
|
||||
# Create instruction record
|
||||
instruction = Instruction(
|
||||
@@ -227,6 +217,7 @@ def bulk_upload(set_id):
|
||||
file_name=secure_filename(file.filename),
|
||||
file_size=file_size,
|
||||
page_number=page_number,
|
||||
thumbnail_path=thumbnail_path,
|
||||
user_id=current_user.id
|
||||
)
|
||||
|
||||
@@ -303,3 +294,116 @@ def debug_paths(set_id):
|
||||
debug_info['instructions'].append(info)
|
||||
|
||||
return jsonify(debug_info)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Page organiser: fixing the order of image instructions after upload.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@instructions_bp.route('/<int:set_id>/organise')
|
||||
@login_required
|
||||
def organise(set_id):
|
||||
"""Drag-and-drop page organiser for a set's image instructions."""
|
||||
lego_set = Set.query.get_or_404(set_id)
|
||||
pages = page_order.image_pages(set_id)
|
||||
|
||||
if not pages:
|
||||
flash('There are no image pages to organise for this set.', 'info')
|
||||
return redirect(url_for('sets.view_set', set_id=set_id))
|
||||
|
||||
return render_template(
|
||||
'instructions/organise.html',
|
||||
set=lego_set,
|
||||
pages=pages,
|
||||
page_hints={p.id: page_order.page_hint(p.file_name or '') for p in pages},
|
||||
strategies=page_order.strategy_choices(),
|
||||
audit=page_order.audit(set_id),
|
||||
)
|
||||
|
||||
|
||||
@instructions_bp.route('/<int:set_id>/auto-sort', methods=['POST'])
|
||||
@login_required
|
||||
def auto_sort(set_id):
|
||||
"""Sort a set's pages by filename, detected page number, or upload order."""
|
||||
Set.query.get_or_404(set_id)
|
||||
|
||||
payload = request.get_json(silent=True) or request.form
|
||||
strategy = (payload.get('strategy') or 'filename').strip()
|
||||
reverse = str(payload.get('reverse', '')).lower() in ('1', 'true', 'yes', 'on')
|
||||
|
||||
try:
|
||||
if strategy == page_order.REVERSIBLE:
|
||||
pages = page_order.reverse_order(set_id)
|
||||
message = 'Page order reversed.'
|
||||
else:
|
||||
pages = page_order.auto_sort(set_id, strategy, reverse=reverse)
|
||||
label = dict(page_order.strategy_choices()).get(strategy, strategy)
|
||||
message = f'Sorted {len(pages)} pages by {label.lower()}.'
|
||||
except ValueError as e:
|
||||
if request.is_json:
|
||||
return jsonify({'error': str(e)}), 400
|
||||
flash(str(e), 'danger')
|
||||
return redirect(url_for('instructions.organise', set_id=set_id))
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
if request.is_json:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
flash(f'Could not sort pages: {e}', 'danger')
|
||||
return redirect(url_for('instructions.organise', set_id=set_id))
|
||||
|
||||
if request.is_json:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': message,
|
||||
'pages': [p.to_dict() for p in pages],
|
||||
})
|
||||
|
||||
flash(message, 'success')
|
||||
return redirect(url_for('instructions.organise', set_id=set_id))
|
||||
|
||||
|
||||
@instructions_bp.route('/<int:set_id>/renumber', methods=['POST'])
|
||||
@login_required
|
||||
def renumber(set_id):
|
||||
"""Close gaps and clear duplicate page numbers without moving anything."""
|
||||
Set.query.get_or_404(set_id)
|
||||
|
||||
try:
|
||||
pages = page_order.renumber(set_id)
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
if request.is_json:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
flash(f'Could not renumber pages: {e}', 'danger')
|
||||
return redirect(url_for('instructions.organise', set_id=set_id))
|
||||
|
||||
message = f'Renumbered {len(pages)} pages 1 to {len(pages)}.'
|
||||
|
||||
if request.is_json:
|
||||
return jsonify({'success': True, 'message': message,
|
||||
'pages': [p.to_dict() for p in pages]})
|
||||
|
||||
flash(message, 'success')
|
||||
return redirect(url_for('instructions.organise', set_id=set_id))
|
||||
|
||||
|
||||
@instructions_bp.route('/<int:instruction_id>/move', methods=['POST'])
|
||||
@login_required
|
||||
def move(instruction_id):
|
||||
"""Move one page to a given position, shuffling the rest around it."""
|
||||
payload = request.get_json(silent=True) or request.form
|
||||
position = payload.get('position')
|
||||
|
||||
try:
|
||||
pages = page_order.move_page(instruction_id, int(position))
|
||||
except (TypeError, ValueError) as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 400
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'pages': [p.to_dict() for p in pages],
|
||||
})
|
||||
Reference in new issue
Block a user