import os
import tempfile
import zipfile
from functools import wraps
from flask import Flask, request, render_template_string, send_from_directory, redirect, url_for, session
from werkzeug.utils import secure_filename
from PyPDF2 import PdfReader
from authlib.integrations.flask_client import OAuth
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = tempfile.mkdtemp()
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB limit
app.secret_key = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
# Entra ID Configuration
TENANT_ID = os.getenv('TENANT_ID', 'your-tenant-id')
CLIENT_ID = os.getenv('CLIENT_ID', 'your-client-id')
CLIENT_SECRET = os.getenv('CLIENT_SECRET', 'your-client-secret')
# Admin Configuration
ADMIN_USERNAME = os.getenv('ADMIN_USERNAME', 'admin')
ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD', 'change-this-password')
oauth = OAuth(app)
microsoft = oauth.register(
name='microsoft',
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
server_metadata_url=f'https://login.microsoftonline.com/{TENANT_ID}/v2.0/.well-known/openid_configuration',
client_kwargs={'scope': 'openid profile email'}
)
ALLOWED_EXTENSIONS = {'pdf', 'zip'}
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user' not in session and 'admin' not in session:
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def extract_names_from_pdf(pdf_path):
try:
reader = PdfReader(pdf_path)
fields = reader.get_fields()
last_name = fields.get('Last Name', {}).get('/V', '')
first_name = fields.get('First Name', {}).get('/V', '')
if last_name and first_name:
return f"{last_name}_{first_name}.pdf"
else:
return None
except Exception as e:
return None
@app.route('/login')
def login():
return render_template_string('''
Login
PDF Renamer Login
''')
@app.route('/entra-login')
def entra_login():
redirect_uri = url_for('auth', _external=True)
return microsoft.authorize_redirect(redirect_uri)
@app.route('/admin-login', methods=['POST'])
def admin_login():
username = request.form.get('username')
password = request.form.get('password')
if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
session['admin'] = True
return redirect(url_for('upload_files'))
else:
return render_template_string('''
Login Failed
Login Failed
Invalid username or password.
Try Again
''')
@app.route('/auth')
def auth():
token = microsoft.authorize_access_token()
user = microsoft.parse_id_token(token)
session['user'] = user
return redirect(url_for('upload_files'))
@app.route('/logout')
@login_required
def logout():
session.pop('user', None)
session.pop('admin', None)
return redirect(url_for('login'))
@app.route('/', methods=['GET', 'POST'])
@login_required
def upload_files():
if request.method == 'POST':
files = request.files.getlist('files')
processed_files = []
temp_extract_dirs = []
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
if filename.lower().endswith('.zip'):
# Extract ZIP
extract_dir = os.path.join(app.config['UPLOAD_FOLDER'], 'extract_' + os.path.splitext(filename)[0])
os.makedirs(extract_dir, exist_ok=True)
temp_extract_dirs.append(extract_dir)
with zipfile.ZipFile(filepath, 'r') as zip_ref:
zip_ref.extractall(extract_dir)
# Process PDFs in extract_dir
for root, dirs, files_in_zip in os.walk(extract_dir):
for f in files_in_zip:
if f.lower().endswith('.pdf'):
pdf_path = os.path.join(root, f)
new_name = extract_names_from_pdf(pdf_path)
if new_name:
new_path = os.path.join(root, new_name)
counter = 1
while os.path.exists(new_path):
base, ext = os.path.splitext(new_name)
new_path = os.path.join(root, f"{base}_{counter}{ext}")
counter += 1
os.rename(pdf_path, new_path)
processed_files.append(('success', os.path.relpath(new_path, extract_dir), new_path))
else:
processed_files.append(('skipped', f, None))
else:
# Single PDF
new_name = extract_names_from_pdf(filepath)
if new_name:
new_filepath = os.path.join(app.config['UPLOAD_FOLDER'], new_name)
counter = 1
while os.path.exists(new_filepath):
base, ext = os.path.splitext(new_name)
new_filepath = os.path.join(app.config['UPLOAD_FOLDER'], f"{base}_{counter}{ext}")
counter += 1
os.rename(filepath, new_filepath)
processed_files.append(('success', os.path.basename(new_filepath), new_filepath))
else:
processed_files.append(('skipped', filename, None))
# Create ZIP of all successful renames
successful_files = [p for p in processed_files if p[0] == 'success']
if successful_files:
zip_output = os.path.join(app.config['UPLOAD_FOLDER'], 'renamed_files.zip')
with zipfile.ZipFile(zip_output, 'w') as zip_out:
for _, rel_name, full_path in successful_files:
zip_out.write(full_path, rel_name)
zip_name = 'renamed_files.zip'
else:
zip_name = None
skipped = [p[1] for p in processed_files if p[0] == 'skipped']
return render_template_string('''
Results
Logout
Processing Results
{% if zip_name %}
Download Renamed PDFs (ZIP)
{% endif %}
Skipped Files:
{% for file in skipped %}
- {{ file }}
{% endfor %}
Upload more files
''', zip_name=zip_name, skipped=skipped)
return render_template_string('''
PDF Renamer
Logout
Upload PDF Files or ZIP to Rename
''')
@app.route('/download/')
@login_required
def download_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True)
if __name__ == '__main__':
app.run(debug=True, port=3000)