Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -158,6 +158,36 @@ app.title = "Intelligent PDF Splitter"
|
|
158 |
def get_split_results_placeholder():
|
159 |
return html.Div("", id="split-results-inner")
|
160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
app.layout = dbc.Container(
|
162 |
[
|
163 |
dcc.Store(id='session-id-store', storage_type='session'),
|
@@ -253,11 +283,12 @@ def ensure_session_id(session_id):
|
|
253 |
Input('clear-session', 'n_clicks'),
|
254 |
Input({'type': 'delete-upload-btn', 'index': ALL}, 'n_clicks'),
|
255 |
Input('split-btn', 'n_clicks'),
|
|
|
256 |
State('session-store', 'data'),
|
257 |
State('session-id-store', 'data'),
|
258 |
prevent_initial_call=True
|
259 |
)
|
260 |
-
def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, session_data, session_id):
|
261 |
trigger = ctx.triggered_id
|
262 |
logging.info(f"handle_upload: Triggered by {trigger}, session_id={session_id}")
|
263 |
|
@@ -300,6 +331,63 @@ def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, se
|
|
300 |
logging.info(f"Session files deleted for {session_id}")
|
301 |
return "", True, get_split_results_placeholder(), {}
|
302 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
# Handle Upload
|
304 |
if trigger == 'upload-pdf':
|
305 |
logging.info(f"handle_upload: Upload triggered for filename={filename}, session_id={session_id}")
|
@@ -363,10 +451,11 @@ def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, se
|
|
363 |
try:
|
364 |
logging.info(f"Splitting PDF for session {session_id}. File: {pdf_path}")
|
365 |
with lock:
|
366 |
-
logging.info(f"handle_upload: Acquired lock for session {session_id}, starting split.")
|
367 |
split_files = intelligent_pdf_split(pdf_path, session_dir)
|
|
|
|
|
368 |
zip_path = make_zip_of_splits(split_files, session_dir)
|
369 |
-
logging.info(f"
|
370 |
session_data['split_files'] = split_files
|
371 |
session_data['zip_ready'] = True
|
372 |
file_info = dbc.Row([
|
@@ -376,11 +465,7 @@ def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, se
|
|
376 |
width=3, style={'display': 'flex', 'justifyContent': 'end'}
|
377 |
)
|
378 |
], className='mb-3', align='center', style={'marginTop': "15px", 'marginBottom': '25px'})
|
379 |
-
split_files_list =
|
380 |
-
html.Li([
|
381 |
-
f"{fi['filename']} ({fi['size']:.2f} MB)"
|
382 |
-
]) for fi in split_files
|
383 |
-
])
|
384 |
download_zip_btn = dbc.Button(
|
385 |
"Download All (ZIP)", color="primary", size="lg", className='mb-3 mt-4',
|
386 |
href=f"/download_zip/{session_id}/split_files.zip"
|
@@ -407,11 +492,7 @@ def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, se
|
|
407 |
width=3, style={'display': 'flex', 'justifyContent': 'end'}
|
408 |
)
|
409 |
], className='mb-3', align='center', style={'marginTop': "15px", 'marginBottom': '25px'})
|
410 |
-
split_files_list =
|
411 |
-
html.Li([
|
412 |
-
f"{fi['filename']} ({fi['size']:.2f} MB)"
|
413 |
-
]) for fi in split_files
|
414 |
-
])
|
415 |
download_zip_btn = dbc.Button(
|
416 |
"Download All (ZIP)", color="primary", size="lg", className='mb-3 mt-4',
|
417 |
href=f"/download_zip/{session_id}/split_files.zip"
|
@@ -438,6 +519,17 @@ def download_zip_file(session_id, filename):
|
|
438 |
logging.error(f"ZIP file not found for download: {file_path}")
|
439 |
return "File not found", 404
|
440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
441 |
if __name__ == '__main__':
|
442 |
print("Starting the Dash application...")
|
443 |
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|
|
|
158 |
def get_split_results_placeholder():
|
159 |
return html.Div("", id="split-results-inner")
|
160 |
|
161 |
+
def get_split_files_ui(split_files, session_id):
|
162 |
+
items = []
|
163 |
+
for i, fi in enumerate(split_files):
|
164 |
+
fname = fi['filename']
|
165 |
+
size = fi['size']
|
166 |
+
download_link = html.A(
|
167 |
+
f"Download {fname} ({size:.2f} MB)",
|
168 |
+
href=f"/download_split/{session_id}/{fname}",
|
169 |
+
target="_blank",
|
170 |
+
style={'marginRight': '16px'}
|
171 |
+
)
|
172 |
+
delete_btn = dbc.Button(
|
173 |
+
"Delete",
|
174 |
+
id={'type': 'delete-split-btn', 'index': i},
|
175 |
+
color='danger',
|
176 |
+
size='sm',
|
177 |
+
className='ms-3',
|
178 |
+
n_clicks=0
|
179 |
+
)
|
180 |
+
items.append(
|
181 |
+
html.Li(
|
182 |
+
dbc.Row([
|
183 |
+
dbc.Col(download_link, width=9, style={'display': 'flex', 'alignItems': 'center'}),
|
184 |
+
dbc.Col(delete_btn, width=3, style={'display': 'flex', 'justifyContent': 'end'})
|
185 |
+
], align='center'),
|
186 |
+
style={'marginBottom': '10px'}
|
187 |
+
)
|
188 |
+
)
|
189 |
+
return html.Ul(items)
|
190 |
+
|
191 |
app.layout = dbc.Container(
|
192 |
[
|
193 |
dcc.Store(id='session-id-store', storage_type='session'),
|
|
|
283 |
Input('clear-session', 'n_clicks'),
|
284 |
Input({'type': 'delete-upload-btn', 'index': ALL}, 'n_clicks'),
|
285 |
Input('split-btn', 'n_clicks'),
|
286 |
+
Input({'type': 'delete-split-btn', 'index': ALL}, 'n_clicks'),
|
287 |
State('session-store', 'data'),
|
288 |
State('session-id-store', 'data'),
|
289 |
prevent_initial_call=True
|
290 |
)
|
291 |
+
def handle_upload(contents, filename, clear_n, delete_upload_n_list, split_n, delete_split_n_list, session_data, session_id):
|
292 |
trigger = ctx.triggered_id
|
293 |
logging.info(f"handle_upload: Triggered by {trigger}, session_id={session_id}")
|
294 |
|
|
|
331 |
logging.info(f"Session files deleted for {session_id}")
|
332 |
return "", True, get_split_results_placeholder(), {}
|
333 |
|
334 |
+
# Handle Delete Split File
|
335 |
+
delete_split_pressed = False
|
336 |
+
delete_split_idx = None
|
337 |
+
if isinstance(trigger, dict) and trigger.get('type') == 'delete-split-btn':
|
338 |
+
delete_split_pressed = True
|
339 |
+
delete_split_idx = trigger.get('index')
|
340 |
+
if not delete_split_pressed and delete_split_n_list is not None and len(delete_split_n_list) > 0:
|
341 |
+
for i, n in enumerate(delete_split_n_list):
|
342 |
+
if n is not None and n > 0:
|
343 |
+
delete_split_pressed = True
|
344 |
+
delete_split_idx = i
|
345 |
+
break
|
346 |
+
if delete_split_pressed and session_data.get('split_files'):
|
347 |
+
split_files = session_data['split_files']
|
348 |
+
if 0 <= delete_split_idx < len(split_files):
|
349 |
+
del_file = split_files[delete_split_idx]
|
350 |
+
file_path = del_file['path']
|
351 |
+
if os.path.exists(file_path):
|
352 |
+
os.remove(file_path)
|
353 |
+
logging.info(f"Deleted split file: {file_path} for session {session_id}")
|
354 |
+
split_files = [f for i, f in enumerate(split_files) if i != delete_split_idx]
|
355 |
+
session_data['split_files'] = split_files
|
356 |
+
# Recreate ZIP if any splits remain, else remove ZIP
|
357 |
+
zip_path = os.path.join(session_dir, "split_files.zip")
|
358 |
+
if split_files:
|
359 |
+
make_zip_of_splits(split_files, session_dir)
|
360 |
+
session_data['zip_ready'] = True
|
361 |
+
logging.info(f"Regenerated ZIP file after split delete for session {session_id}")
|
362 |
+
else:
|
363 |
+
if os.path.exists(zip_path):
|
364 |
+
os.remove(zip_path)
|
365 |
+
logging.info(f"Deleted ZIP file as no splits remain for session {session_id}")
|
366 |
+
session_data['zip_ready'] = False
|
367 |
+
# Build UI
|
368 |
+
orig_filename = session_data.get('orig_filename', '')
|
369 |
+
file_info = dbc.Row([
|
370 |
+
dbc.Col(html.Div(f"Uploaded: {orig_filename}"), width=9, style={'display': 'flex', 'alignItems': 'center'}),
|
371 |
+
dbc.Col(
|
372 |
+
dbc.Button("Delete", id={'type': 'delete-upload-btn', 'index': 0}, color='danger', n_clicks=0, className='ms-5'),
|
373 |
+
width=3, style={'display': 'flex', 'justifyContent': 'end'}
|
374 |
+
)
|
375 |
+
], className='mb-3', align='center', style={'marginTop': "15px", 'marginBottom': '25px'})
|
376 |
+
results = html.Div([
|
377 |
+
html.H5("Split Files:"),
|
378 |
+
get_split_files_ui(split_files, session_id) if split_files else html.Div("No split files remain."),
|
379 |
+
html.Div(
|
380 |
+
dbc.Button(
|
381 |
+
"Download All (ZIP)", color="primary", size="lg", className='mb-3 mt-4',
|
382 |
+
href=f"/download_zip/{session_id}/split_files.zip"
|
383 |
+
),
|
384 |
+
style={'marginTop': '30px', 'display': 'block' if split_files else 'none'}
|
385 |
+
),
|
386 |
+
], id="split-results-inner")
|
387 |
+
return file_info, False, results, session_data
|
388 |
+
else:
|
389 |
+
logging.warning(f"Split file delete index {delete_split_idx} invalid for session {session_id}")
|
390 |
+
|
391 |
# Handle Upload
|
392 |
if trigger == 'upload-pdf':
|
393 |
logging.info(f"handle_upload: Upload triggered for filename={filename}, session_id={session_id}")
|
|
|
451 |
try:
|
452 |
logging.info(f"Splitting PDF for session {session_id}. File: {pdf_path}")
|
453 |
with lock:
|
|
|
454 |
split_files = intelligent_pdf_split(pdf_path, session_dir)
|
455 |
+
for fi in split_files:
|
456 |
+
logging.info(f"Split file saved: {fi['path']} ({fi['size']:.2f} MB)")
|
457 |
zip_path = make_zip_of_splits(split_files, session_dir)
|
458 |
+
logging.info(f"Split/ZIP finished for {session_id}, zip_path={zip_path}")
|
459 |
session_data['split_files'] = split_files
|
460 |
session_data['zip_ready'] = True
|
461 |
file_info = dbc.Row([
|
|
|
465 |
width=3, style={'display': 'flex', 'justifyContent': 'end'}
|
466 |
)
|
467 |
], className='mb-3', align='center', style={'marginTop': "15px", 'marginBottom': '25px'})
|
468 |
+
split_files_list = get_split_files_ui(split_files, session_id)
|
|
|
|
|
|
|
|
|
469 |
download_zip_btn = dbc.Button(
|
470 |
"Download All (ZIP)", color="primary", size="lg", className='mb-3 mt-4',
|
471 |
href=f"/download_zip/{session_id}/split_files.zip"
|
|
|
492 |
width=3, style={'display': 'flex', 'justifyContent': 'end'}
|
493 |
)
|
494 |
], className='mb-3', align='center', style={'marginTop': "15px", 'marginBottom': '25px'})
|
495 |
+
split_files_list = get_split_files_ui(split_files, session_id)
|
|
|
|
|
|
|
|
|
496 |
download_zip_btn = dbc.Button(
|
497 |
"Download All (ZIP)", color="primary", size="lg", className='mb-3 mt-4',
|
498 |
href=f"/download_zip/{session_id}/split_files.zip"
|
|
|
519 |
logging.error(f"ZIP file not found for download: {file_path}")
|
520 |
return "File not found", 404
|
521 |
|
522 |
+
@app.server.route('/download_split/<session_id>/<filename>')
|
523 |
+
def download_split_file(session_id, filename):
|
524 |
+
session_dir = get_session_dir(session_id)
|
525 |
+
file_path = os.path.join(session_dir, filename)
|
526 |
+
if os.path.exists(file_path):
|
527 |
+
logging.info(f"Serving split file {file_path} for session {session_id}")
|
528 |
+
return send_file(file_path, mimetype='application/pdf', as_attachment=True, download_name=filename)
|
529 |
+
else:
|
530 |
+
logging.error(f"Split file not found for download: {file_path}")
|
531 |
+
return "File not found", 404
|
532 |
+
|
533 |
if __name__ == '__main__':
|
534 |
print("Starting the Dash application...")
|
535 |
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|