Fix batch process
This commit is contained in:
parent
8cd93df1a9
commit
dc01d099e1
1 changed files with 214 additions and 0 deletions
214
backend/app.py
214
backend/app.py
|
|
@ -536,6 +536,220 @@ if batch_processing_available:
|
||||||
state['logs'] = state['logs'][-20:] # Last 20 logs only
|
state['logs'] = state['logs'][-20:] # Last 20 logs only
|
||||||
return jsonify(state)
|
return jsonify(state)
|
||||||
|
|
||||||
|
|
||||||
|
# Add these routes to your app.py file after the other batch processing endpoints
|
||||||
|
|
||||||
|
@app.route('/batch-report/<batch_id>')
|
||||||
|
def batch_report(batch_id):
|
||||||
|
"""Serve the batch processing report HTML"""
|
||||||
|
try:
|
||||||
|
processor = get_batch_processor(batch_id)
|
||||||
|
if not processor:
|
||||||
|
# Try to find existing report even if processor is gone
|
||||||
|
report_path = ensure_results_folder() / f"batch_{batch_id}" / "report.html"
|
||||||
|
if report_path.exists():
|
||||||
|
return send_file(report_path)
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'Batch report not found'}), 404
|
||||||
|
|
||||||
|
# Check if report exists
|
||||||
|
report_path = processor.results_dir / "report.html"
|
||||||
|
if not report_path.exists():
|
||||||
|
# Generate report if it doesn't exist
|
||||||
|
processor.generate_report()
|
||||||
|
|
||||||
|
return send_file(report_path)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error serving batch report: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/batch-report-image/<batch_id>/<image_name>')
|
||||||
|
def batch_report_image(batch_id, image_name):
|
||||||
|
"""Serve images from batch report directory"""
|
||||||
|
try:
|
||||||
|
image_path = ensure_results_folder() / f"batch_{batch_id}" / image_name
|
||||||
|
|
||||||
|
if not image_path.exists():
|
||||||
|
return jsonify({'error': 'Image not found'}), 404
|
||||||
|
|
||||||
|
return send_file(image_path)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error serving batch image: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/pause-batch/<batch_id>', methods=['POST'])
|
||||||
|
def pause_batch(batch_id):
|
||||||
|
"""Pause a batch processing job"""
|
||||||
|
try:
|
||||||
|
processor = get_batch_processor(batch_id)
|
||||||
|
if not processor:
|
||||||
|
return jsonify({'error': 'Batch not found'}), 404
|
||||||
|
|
||||||
|
processor.pause()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error pausing batch: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/resume-batch/<batch_id>', methods=['POST'])
|
||||||
|
def resume_batch(batch_id):
|
||||||
|
"""Resume a batch processing job"""
|
||||||
|
try:
|
||||||
|
processor = get_batch_processor(batch_id)
|
||||||
|
if not processor:
|
||||||
|
return jsonify({'error': 'Batch not found'}), 404
|
||||||
|
|
||||||
|
processor.resume()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error resuming batch: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/cancel-batch/<batch_id>', methods=['POST'])
|
||||||
|
def cancel_batch(batch_id):
|
||||||
|
"""Cancel a batch processing job"""
|
||||||
|
try:
|
||||||
|
processor = get_batch_processor(batch_id)
|
||||||
|
if not processor:
|
||||||
|
return jsonify({'error': 'Batch not found'}), 404
|
||||||
|
|
||||||
|
processor.cancel()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error cancelling batch: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/download-batch-results/<batch_id>')
|
||||||
|
def download_batch_results(batch_id):
|
||||||
|
"""Download batch results as ZIP"""
|
||||||
|
try:
|
||||||
|
processor = get_batch_processor(batch_id)
|
||||||
|
if not processor:
|
||||||
|
# Try to find existing results
|
||||||
|
batch_dir = ensure_results_folder() / f"batch_{batch_id}"
|
||||||
|
if not batch_dir.exists():
|
||||||
|
return jsonify({'error': 'Batch results not found'}), 404
|
||||||
|
|
||||||
|
# Create a temporary processor just for zipping
|
||||||
|
class TempProcessor:
|
||||||
|
def __init__(self, batch_id):
|
||||||
|
self.batch_id = batch_id
|
||||||
|
self.results_dir = batch_dir
|
||||||
|
|
||||||
|
def create_zip_archive(self):
|
||||||
|
import zipfile
|
||||||
|
zip_path = self.results_dir / f"batch_results_{self.batch_id}.zip"
|
||||||
|
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||||
|
for file_path in self.results_dir.rglob('*'):
|
||||||
|
if file_path.is_file() and file_path != zip_path:
|
||||||
|
arcname = file_path.relative_to(self.results_dir)
|
||||||
|
zipf.write(file_path, arcname)
|
||||||
|
return zip_path
|
||||||
|
|
||||||
|
processor = TempProcessor(batch_id)
|
||||||
|
|
||||||
|
# Create ZIP archive
|
||||||
|
zip_path = processor.create_zip_archive()
|
||||||
|
if not zip_path or not zip_path.exists():
|
||||||
|
return jsonify({'error': 'Failed to create ZIP archive'}), 500
|
||||||
|
|
||||||
|
return send_file(
|
||||||
|
zip_path,
|
||||||
|
as_attachment=True,
|
||||||
|
download_name=f"doctags_batch_{batch_id}.zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error downloading batch results: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/retry-page', methods=['POST'])
|
||||||
|
def retry_page():
|
||||||
|
"""Retry processing a failed page"""
|
||||||
|
try:
|
||||||
|
pdf_file = request.form.get('pdf_file')
|
||||||
|
page_num = int(request.form.get('page_num'))
|
||||||
|
adjust = request.form.get('adjust') == 'true'
|
||||||
|
|
||||||
|
if not pdf_file or not os.path.exists(pdf_file):
|
||||||
|
return jsonify({'success': False, 'error': 'Invalid PDF file'}), 400
|
||||||
|
|
||||||
|
# Run the three processing steps
|
||||||
|
results = {'success': True, 'errors': []}
|
||||||
|
|
||||||
|
# Step 1: Analyzer
|
||||||
|
command = (f"python backend/page_treatment/analyzer.py --image {pdf_file} "
|
||||||
|
f"--page {page_num} --start-page {page_num} --end-page {page_num}")
|
||||||
|
success, stdout, stderr = run_command_with_timeout(command, 60)
|
||||||
|
if not success:
|
||||||
|
results['errors'].append(f"Analyzer: {stderr}")
|
||||||
|
results['success'] = False
|
||||||
|
|
||||||
|
# Step 2: Visualizer (only if analyzer succeeded)
|
||||||
|
if results['success']:
|
||||||
|
command = (f"python backend/page_treatment/visualizer.py "
|
||||||
|
f"--doctags results/output.doctags.txt --pdf {pdf_file} --page {page_num}")
|
||||||
|
if adjust:
|
||||||
|
command += " --adjust"
|
||||||
|
success, stdout, stderr = run_command_with_timeout(command, 60)
|
||||||
|
if not success:
|
||||||
|
results['errors'].append(f"Visualizer: {stderr}")
|
||||||
|
results['success'] = False
|
||||||
|
|
||||||
|
# Step 3: Extractor (only if previous steps succeeded)
|
||||||
|
if results['success']:
|
||||||
|
command = (f"python backend/page_treatment/picture_extractor.py "
|
||||||
|
f"--doctags results/output.doctags.txt --pdf {pdf_file} --page {page_num}")
|
||||||
|
if adjust:
|
||||||
|
command += " --adjust"
|
||||||
|
success, stdout, stderr = run_command_with_timeout(command, 60)
|
||||||
|
if not success:
|
||||||
|
results['errors'].append(f"Extractor: {stderr}")
|
||||||
|
# Don't fail completely if just extractor fails
|
||||||
|
|
||||||
|
if results['success']:
|
||||||
|
return jsonify({'success': True})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': '; '.join(results['errors'])
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error retrying page: {e}")
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/open-results-folder', methods=['POST'])
|
||||||
|
def open_results_folder():
|
||||||
|
"""Open the results folder in the system file explorer"""
|
||||||
|
try:
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
results_dir = ensure_results_folder()
|
||||||
|
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
os.startfile(str(results_dir))
|
||||||
|
elif platform.system() == 'Darwin': # macOS
|
||||||
|
subprocess.Popen(['open', str(results_dir)])
|
||||||
|
else: # Linux and others
|
||||||
|
subprocess.Popen(['xdg-open', str(results_dir)])
|
||||||
|
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error opening results folder: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Could not open folder automatically. ' +
|
||||||
|
f'Please navigate to: {ensure_results_folder()}'
|
||||||
|
})
|
||||||
|
|
||||||
# API endpoints for file upload
|
# API endpoints for file upload
|
||||||
@app.route('/api/upload/doctags', methods=['POST'])
|
@app.route('/api/upload/doctags', methods=['POST'])
|
||||||
def api_upload_doctags():
|
def api_upload_doctags():
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue