diff --git a/pdf_renamer.py b/pdf_renamer.py index c54db58..b3be657 100644 --- a/pdf_renamer.py +++ b/pdf_renamer.py @@ -3,6 +3,7 @@ import tempfile import zipfile import threading import uuid +from datetime import datetime from functools import wraps from flask import Flask, request, render_template_string, redirect, url_for, session, send_from_directory, flash, get_flashed_messages, jsonify from werkzeug.utils import secure_filename @@ -666,7 +667,8 @@ def upload_files(): 'zip_path': None, 'zip_name': None, 'processed_files': [], - 'skipped': [] + 'skipped': [], + 'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } file_list = [] @@ -756,7 +758,17 @@ def jobs(): .flash.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .logout { position: absolute; top: 20px; right: 20px; background-color: #dc3545; color: white; text-decoration: none; padding: 10px 20px; border-radius: 5px; } .logout:hover { background-color: #c82333; } + .nav-links { margin-bottom: 20px; } + .nav-links a { color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #f8f9fa; border-radius: 5px; margin: 0 10px; } + .nav-links a:hover { background-color: #e9ecef; } .zip-content { background-color: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid #007bff; } + .zip-toggle { cursor: pointer; background-color: #e9ecef; padding: 8px; margin: 5px 0; border-radius: 5px; border: 1px solid #dee2e6; } + .zip-toggle:hover { background-color: #dee2e6; } + .zip-details { display: none; margin-top: 10px; } + .skipped-section { background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 5px; padding: 10px; margin: 10px 0; } + .skipped-toggle { cursor: pointer; background-color: #ffeaa7; padding: 8px; margin: 5px 0; border-radius: 5px; border: 1px solid #ffc107; font-weight: bold; color: #856404; } + .skipped-toggle:hover { background-color: #ffc107; } + .skipped-details { display: none; margin-top: 10px; } ul { list-style-type: none; padding: 0; } li { background-color: #ffffff; margin: 2px 0; padding: 5px; border-radius: 3px; } @@ -765,6 +777,10 @@ def jobs(): Logout Logo

Processing Jobs

+ {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} @@ -784,12 +800,16 @@ def jobs():

ZIP Contents:

{% for zip_name, pdf_files in job.zip_contents.items() %}
- {{ zip_name }}: - +
+ {{ zip_name }} ({{ pdf_files|length }} files) ▶ +
+
+ +
{% endfor %} {% endif %} @@ -806,12 +826,18 @@ def jobs():

{% endif %} {% if job.skipped %} -

Skipped Files:

- +
+
+ ⚠️ Skipped Files ({{ job.skipped|length }}) ▶ +
+
+ +
+
{% endif %} {% endif %} @@ -853,10 +879,14 @@ def jobs():

ZIP Contents:

${Object.entries(job.zip_contents).map(([zipName, pdfFiles]) => `
- ${zipName}: - +
+ ${zipName} (${pdfFiles.length} files) ▶ +
+
+ +
`).join('')} ` : ''} @@ -873,12 +903,19 @@ def jobs():

`} ${job.skipped ? ` -

Skipped Files:

- +
+
+ ⚠️ Skipped Files (${job.skipped.length}) ▶ +
+
+ +
+
` : ''} ` : ''} + ` : ''} `; jobsContainer.insertBefore(jobDiv, jobsContainer.querySelector('script') || jobsContainer.lastElementChild); }); @@ -893,6 +930,21 @@ def jobs(): // Update jobs every 2 seconds setInterval(updateJobs, 2000); + + // Collapsible section functions + function toggleZipDetails(element) { + const details = element.nextElementSibling; + const isVisible = details.style.display === 'block'; + details.style.display = isVisible ? 'none' : 'block'; + element.innerHTML = element.innerHTML.replace(isVisible ? ' ▼' : ' ▶', isVisible ? ' ▶' : ' ▼'); + } + + function toggleSkippedDetails(element) { + const details = element.nextElementSibling; + const isVisible = details.style.display === 'block'; + details.style.display = isVisible ? 'none' : 'block'; + element.innerHTML = element.innerHTML.replace(isVisible ? ' ▼' : ' ▶', isVisible ? ' ▶' : ' ▼'); + } @@ -935,5 +987,138 @@ def delete_job(job_id): flash('Job not found.', 'error') return redirect('/jobs') +@app.route('/completed-jobs') +@login_required +def completed_jobs(): + # Filter only completed jobs + completed_jobs = {job_id: job for job_id, job in job_queue.items() if job.get('status') == 'completed'} + return render_template_string(''' + + + + Completed Jobs Archive + + + + Logout + Logo +

Completed Jobs Archive

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + {% for job_id, job in jobs.items() %} +
+

Job {{ job_id[:8] }} - {{ job.created_at if job.created_at else 'Unknown Date' }}

+
+ Completed + {{ job.processed_files|selectattr('0', 'equalto', 'success')|list|length }} files processed + {% if job.skipped %} + {{ job.skipped|length }} files skipped + {% endif %} +
+ + {% if job.zip_contents %} +

Original ZIP Contents:

+ {% for zip_name, pdf_files in job.zip_contents.items() %} +
+
+ {{ zip_name }} ({{ pdf_files|length }} files) ▶ +
+
+
    + {% for pdf_file in pdf_files %} +
  • {{ pdf_file }}
  • + {% endfor %} +
+
+
+ {% endfor %} + {% endif %} + + {% if job.zip_name %} +

Download: 📁 Download Organized ZIP ({{ job.zip_name }})

+ {% endif %} + + {% if job.skipped %} +
+
+ ⚠️ Skipped Files ({{ job.skipped|length }}) ▶ +
+
+
    + {% for skip_info in job.skipped %} +
  • {{ skip_info[0] }}: {{ skip_info[1] }}
  • + {% endfor %} +
+
+
+ {% endif %} + +
+ +
+
+ {% endfor %} + {% if not jobs %} +

No completed jobs yet. Upload some files to get started.

+ {% endif %} + + + + ''', jobs=completed_jobs) + if __name__ == '__main__': app.run(debug=True, port=3000) \ No newline at end of file