2025-12-27 22:34:21 +01:00
|
|
|
"""
|
|
|
|
|
Route handlers for the Flask job application system.
|
|
|
|
|
"""
|
|
|
|
|
import os
|
|
|
|
|
import uuid
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from flask import render_template, request, redirect, url_for, flash, session, send_from_directory, abort
|
|
|
|
|
from werkzeug.utils import secure_filename
|
|
|
|
|
|
|
|
|
|
from app.models import (
|
|
|
|
|
load_application_data,
|
|
|
|
|
save_application_data,
|
|
|
|
|
get_attachments_path
|
|
|
|
|
)
|
|
|
|
|
from app.validators import (
|
|
|
|
|
validate_email,
|
|
|
|
|
validate_phone,
|
|
|
|
|
validate_year,
|
|
|
|
|
validate_zip,
|
|
|
|
|
allowed_file
|
|
|
|
|
)
|
|
|
|
|
from app.email_service import send_resume_email, send_hr_notification
|
|
|
|
|
from app.utils import check_rate_limit
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_routes(app, mail):
|
|
|
|
|
"""Register all application routes"""
|
|
|
|
|
|
2025-12-27 23:12:21 +01:00
|
|
|
def can_access_page(session_id, requested_page):
|
|
|
|
|
"""
|
|
|
|
|
Determine if user can access the requested page.
|
|
|
|
|
|
|
|
|
|
Rules:
|
|
|
|
|
- Can always access pages <= current_page (backward navigation)
|
|
|
|
|
- Cannot access pages > current_page (must progress sequentially)
|
|
|
|
|
- Page 1 is always accessible
|
|
|
|
|
"""
|
|
|
|
|
if requested_page == 1:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
current_page = app_data.get('current_page', 1)
|
|
|
|
|
return requested_page <= current_page
|
|
|
|
|
|
2025-12-27 22:34:21 +01:00
|
|
|
@app.route('/')
|
|
|
|
|
def index():
|
|
|
|
|
"""Redirect to apply page"""
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply')
|
|
|
|
|
def page1_email():
|
|
|
|
|
"""Page 1: Email capture and session initialization"""
|
|
|
|
|
job_name = request.args.get('job', 'Offene Position')
|
|
|
|
|
return render_template('page1_email.html', job_name=job_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/submit-email', methods=['POST'])
|
|
|
|
|
def submit_email():
|
|
|
|
|
"""Process email submission and create session"""
|
|
|
|
|
# Check rate limit
|
|
|
|
|
allowed, wait_seconds = check_rate_limit()
|
|
|
|
|
if not allowed:
|
|
|
|
|
flash(f'Bitte warten Sie noch {wait_seconds} Sekunden vor der nächsten Eingabe.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email')), 429
|
|
|
|
|
|
|
|
|
|
email = request.form.get('email', '').strip()
|
|
|
|
|
job_name = request.form.get('job_name', 'Offene Position')
|
|
|
|
|
|
|
|
|
|
# Validate email
|
|
|
|
|
if not validate_email(email):
|
|
|
|
|
flash('Bitte geben Sie eine gültige E-Mail-Adresse ein.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email', job=job_name))
|
|
|
|
|
|
|
|
|
|
# Generate session ID
|
|
|
|
|
session_id = str(uuid.uuid4())
|
|
|
|
|
|
|
|
|
|
# Create application data
|
|
|
|
|
app_data = {
|
|
|
|
|
'session_id': session_id,
|
|
|
|
|
'email': email,
|
|
|
|
|
'job_name': job_name,
|
|
|
|
|
'current_page': 2,
|
|
|
|
|
'created_at': datetime.now().isoformat(),
|
|
|
|
|
'updated_at': datetime.now().isoformat(),
|
|
|
|
|
'personal_info': {},
|
|
|
|
|
'motivation_answers': {},
|
|
|
|
|
'uploaded_files': []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Save application data
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
# Send resume email
|
|
|
|
|
if send_resume_email(email, session_id, job_name):
|
|
|
|
|
flash('Eine E-Mail mit einem Link zum Fortsetzen wurde an Ihre Adresse gesendet.', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash('Warnung: Die E-Mail konnte nicht gesendet werden. Bitte notieren Sie sich diese URL.', 'warning')
|
|
|
|
|
|
|
|
|
|
# Store session ID in Flask session
|
|
|
|
|
session['application_id'] = session_id
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/personal')
|
|
|
|
|
def page2_personal(session_id):
|
|
|
|
|
"""Page 2: Basic personal information"""
|
2025-12-27 23:12:21 +01:00
|
|
|
# Validate page access
|
|
|
|
|
if not can_access_page(session_id, 2):
|
|
|
|
|
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
2025-12-27 22:34:21 +01:00
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
return render_template('page2_personal.html',
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
job_name=app_data['job_name'],
|
2025-12-27 23:12:21 +01:00
|
|
|
current_page=app_data.get('current_page', 2),
|
2025-12-27 22:34:21 +01:00
|
|
|
data=app_data.get('personal_info', {}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/submit-personal', methods=['POST'])
|
|
|
|
|
def submit_personal(session_id):
|
|
|
|
|
"""Process personal information submission"""
|
|
|
|
|
# Check rate limit
|
|
|
|
|
allowed, wait_seconds = check_rate_limit()
|
|
|
|
|
if not allowed:
|
|
|
|
|
flash(f'Bitte warten Sie noch {wait_seconds} Sekunden vor der nächsten Eingabe.', 'error')
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id)), 429
|
|
|
|
|
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
# Get form data
|
|
|
|
|
name = request.form.get('name', '').strip()
|
|
|
|
|
firstname = request.form.get('firstname', '').strip()
|
|
|
|
|
address = request.form.get('address', '').strip()
|
|
|
|
|
zip_code = request.form.get('zip_code', '').strip()
|
|
|
|
|
city = request.form.get('city', '').strip()
|
|
|
|
|
phone = request.form.get('phone', '').strip()
|
|
|
|
|
birth_year = request.form.get('birth_year', '').strip()
|
|
|
|
|
civil_status = request.form.get('civil_status', '').strip()
|
|
|
|
|
|
|
|
|
|
# Validate required fields
|
|
|
|
|
errors = []
|
|
|
|
|
|
|
|
|
|
if not name or len(name) > app.config['MAX_STRING_LENGTH']:
|
|
|
|
|
errors.append('Name ist erforderlich und darf maximal 255 Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if not firstname or len(firstname) > app.config['MAX_STRING_LENGTH']:
|
|
|
|
|
errors.append('Vorname ist erforderlich und darf maximal 255 Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if not address or len(address) > app.config['MAX_STRING_LENGTH']:
|
|
|
|
|
errors.append('Adresse ist erforderlich und darf maximal 255 Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if not zip_code or not validate_zip(zip_code):
|
|
|
|
|
errors.append('PLZ ist erforderlich und darf maximal 10 Ziffern lang sein.')
|
|
|
|
|
|
|
|
|
|
if not city or len(city) > app.config['MAX_STRING_LENGTH']:
|
|
|
|
|
errors.append('Stadt ist erforderlich und darf maximal 255 Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if not phone or not validate_phone(phone):
|
|
|
|
|
errors.append('Telefonnummer ist erforderlich und muss im internationalen Format sein (z.B. +41 79 123 45 67).')
|
|
|
|
|
|
|
|
|
|
if not birth_year or not validate_year(birth_year):
|
|
|
|
|
errors.append(f'Geburtsjahr ist erforderlich und muss zwischen {app.config["MIN_BIRTH_YEAR"]} und {app.config["MAX_BIRTH_YEAR"]} liegen.')
|
|
|
|
|
|
|
|
|
|
if civil_status and len(civil_status) > app.config['MAX_STRING_LENGTH']:
|
|
|
|
|
errors.append('Zivilstand darf maximal 255 Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if errors:
|
|
|
|
|
for error in errors:
|
|
|
|
|
flash(error, 'error')
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Save personal information
|
|
|
|
|
app_data['personal_info'] = {
|
|
|
|
|
'name': name,
|
|
|
|
|
'firstname': firstname,
|
|
|
|
|
'address': address,
|
|
|
|
|
'zip_code': zip_code,
|
|
|
|
|
'city': city,
|
|
|
|
|
'phone': phone,
|
|
|
|
|
'birth_year': birth_year,
|
|
|
|
|
'civil_status': civil_status
|
|
|
|
|
}
|
|
|
|
|
app_data['current_page'] = 3
|
|
|
|
|
app_data['updated_at'] = datetime.now().isoformat()
|
|
|
|
|
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/motivation')
|
|
|
|
|
def page3_motivation(session_id):
|
|
|
|
|
"""Page 3: Motivation and qualification questions"""
|
2025-12-27 23:12:21 +01:00
|
|
|
# Validate page access
|
|
|
|
|
if not can_access_page(session_id, 3):
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if app_data:
|
|
|
|
|
current_page = app_data.get('current_page', 2)
|
|
|
|
|
if current_page == 2:
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
2025-12-27 22:34:21 +01:00
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
return render_template('page3_motivation.html',
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
job_name=app_data['job_name'],
|
2025-12-27 23:12:21 +01:00
|
|
|
current_page=app_data.get('current_page', 3),
|
2025-12-27 22:34:21 +01:00
|
|
|
data=app_data.get('motivation_answers', {}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/submit-motivation', methods=['POST'])
|
|
|
|
|
def submit_motivation(session_id):
|
|
|
|
|
"""Process motivation questions submission"""
|
|
|
|
|
# Check rate limit
|
|
|
|
|
allowed, wait_seconds = check_rate_limit()
|
|
|
|
|
if not allowed:
|
|
|
|
|
flash(f'Bitte warten Sie noch {wait_seconds} Sekunden vor der nächsten Eingabe.', 'error')
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id)), 429
|
|
|
|
|
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
# Get form data
|
|
|
|
|
current_job = request.form.get('current_job', '').strip()
|
|
|
|
|
motivation = request.form.get('motivation', '').strip()
|
|
|
|
|
qualifications = request.form.get('qualifications', '').strip()
|
|
|
|
|
salary = request.form.get('salary', '').strip()
|
|
|
|
|
|
|
|
|
|
# Validate length
|
|
|
|
|
errors = []
|
|
|
|
|
|
|
|
|
|
if current_job and len(current_job) > app.config['MAX_TEXT_AREA_LENGTH']:
|
|
|
|
|
errors.append(f'Die Beschreibung Ihrer beruflichen Situation darf maximal {app.config["MAX_TEXT_AREA_LENGTH"]} Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if motivation and len(motivation) > app.config['MAX_TEXT_AREA_LENGTH']:
|
|
|
|
|
errors.append(f'Die Motivationsbeschreibung darf maximal {app.config["MAX_TEXT_AREA_LENGTH"]} Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if qualifications and len(qualifications) > app.config['MAX_TEXT_AREA_LENGTH']:
|
|
|
|
|
errors.append(f'Die Qualifikationsbeschreibung darf maximal {app.config["MAX_TEXT_AREA_LENGTH"]} Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if salary and len(salary) > app.config['MAX_TEXT_AREA_LENGTH']:
|
|
|
|
|
errors.append(f'Die Gehaltsvorstellungen dürfen maximal {app.config["MAX_TEXT_AREA_LENGTH"]} Zeichen lang sein.')
|
|
|
|
|
|
|
|
|
|
if errors:
|
|
|
|
|
for error in errors:
|
|
|
|
|
flash(error, 'error')
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Save motivation answers
|
|
|
|
|
app_data['motivation_answers'] = {
|
|
|
|
|
'current_job': current_job,
|
|
|
|
|
'motivation': motivation,
|
|
|
|
|
'qualifications': qualifications,
|
|
|
|
|
'salary': salary
|
|
|
|
|
}
|
|
|
|
|
app_data['current_page'] = 4
|
|
|
|
|
app_data['updated_at'] = datetime.now().isoformat()
|
|
|
|
|
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/upload')
|
|
|
|
|
def page4_upload(session_id):
|
|
|
|
|
"""Page 4: Document upload"""
|
2025-12-27 23:12:21 +01:00
|
|
|
# Validate page access
|
|
|
|
|
if not can_access_page(session_id, 4):
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if app_data:
|
|
|
|
|
current_page = app_data.get('current_page', 2)
|
|
|
|
|
if current_page == 2:
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
elif current_page == 3:
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id))
|
|
|
|
|
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
2025-12-27 22:34:21 +01:00
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
return render_template('page4_upload.html',
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
job_name=app_data['job_name'],
|
2025-12-27 23:12:21 +01:00
|
|
|
current_page=app_data.get('current_page', 4),
|
2025-12-27 22:34:21 +01:00
|
|
|
uploaded_files=app_data.get('uploaded_files', []),
|
|
|
|
|
max_files=app.config['MAX_FILES'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/upload-file', methods=['POST'])
|
|
|
|
|
def upload_file(session_id):
|
|
|
|
|
"""Handle file upload"""
|
|
|
|
|
# Check rate limit
|
|
|
|
|
allowed, wait_seconds = check_rate_limit()
|
|
|
|
|
if not allowed:
|
|
|
|
|
flash(f'Bitte warten Sie noch {wait_seconds} Sekunden vor der nächsten Eingabe.', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id)), 429
|
|
|
|
|
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
# Check file count
|
|
|
|
|
if len(app_data.get('uploaded_files', [])) >= app.config['MAX_FILES']:
|
|
|
|
|
flash(f'Sie können maximal {app.config["MAX_FILES"]} Dokumente hochladen.', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Check if file was uploaded
|
|
|
|
|
if 'file' not in request.files:
|
|
|
|
|
flash('Keine Datei ausgewählt.', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
file = request.files['file']
|
|
|
|
|
|
|
|
|
|
if file.filename == '':
|
|
|
|
|
flash('Keine Datei ausgewählt.', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Validate file
|
|
|
|
|
if not allowed_file(file.filename):
|
|
|
|
|
flash('Ungültiger Dateityp. Erlaubte Formate: PDF, DOC, DOCX, TXT, JPG, JPEG, PNG', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Check file size
|
|
|
|
|
file.seek(0, os.SEEK_END)
|
|
|
|
|
file_size = file.tell()
|
|
|
|
|
file.seek(0)
|
|
|
|
|
|
|
|
|
|
if file_size > app.config['MAX_FILE_SIZE']:
|
|
|
|
|
flash(f'Die Datei ist zu groß. Maximale Größe: 4 MB', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
# Save file
|
|
|
|
|
filename = secure_filename(file.filename)
|
|
|
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
|
|
|
unique_filename = f"{timestamp}_{filename}"
|
|
|
|
|
|
|
|
|
|
attachments_path = get_attachments_path(session_id)
|
|
|
|
|
Path(attachments_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
file_path = os.path.join(attachments_path, unique_filename)
|
|
|
|
|
file.save(file_path)
|
|
|
|
|
|
|
|
|
|
# Update application data
|
|
|
|
|
if 'uploaded_files' not in app_data:
|
|
|
|
|
app_data['uploaded_files'] = []
|
|
|
|
|
|
|
|
|
|
app_data['uploaded_files'].append({
|
|
|
|
|
'original_name': filename,
|
|
|
|
|
'stored_name': unique_filename,
|
|
|
|
|
'uploaded_at': datetime.now().isoformat(),
|
|
|
|
|
'size': file_size
|
|
|
|
|
})
|
|
|
|
|
app_data['updated_at'] = datetime.now().isoformat()
|
|
|
|
|
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
flash('Datei erfolgreich hochgeladen.', 'success')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/remove-file/<int:file_index>', methods=['POST'])
|
|
|
|
|
def remove_file(session_id, file_index):
|
|
|
|
|
"""Remove an uploaded file"""
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
uploaded_files = app_data.get('uploaded_files', [])
|
|
|
|
|
|
|
|
|
|
if 0 <= file_index < len(uploaded_files):
|
|
|
|
|
file_info = uploaded_files[file_index]
|
|
|
|
|
|
|
|
|
|
# Delete physical file
|
|
|
|
|
file_path = os.path.join(get_attachments_path(session_id), file_info['stored_name'])
|
|
|
|
|
if os.path.exists(file_path):
|
|
|
|
|
os.remove(file_path)
|
|
|
|
|
|
|
|
|
|
# Remove from data
|
|
|
|
|
uploaded_files.pop(file_index)
|
|
|
|
|
app_data['updated_at'] = datetime.now().isoformat()
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
flash('Datei erfolgreich entfernt.', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash('Datei nicht gefunden.', 'error')
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/submit-application', methods=['POST'])
|
|
|
|
|
def submit_application(session_id):
|
|
|
|
|
"""Submit final application"""
|
|
|
|
|
# Check rate limit
|
|
|
|
|
allowed, wait_seconds = check_rate_limit()
|
|
|
|
|
if not allowed:
|
|
|
|
|
flash(f'Bitte warten Sie noch {wait_seconds} Sekunden vor der nächsten Eingabe.', 'error')
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id)), 429
|
|
|
|
|
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
# Mark as submitted
|
|
|
|
|
app_data['current_page'] = 5
|
|
|
|
|
app_data['submitted_at'] = datetime.now().isoformat()
|
|
|
|
|
app_data['updated_at'] = datetime.now().isoformat()
|
|
|
|
|
app_data['status'] = 'submitted'
|
|
|
|
|
|
|
|
|
|
save_application_data(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
# Send HR notification
|
|
|
|
|
send_hr_notification(session_id, app_data)
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('page5_confirmation', session_id=session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/apply/<session_id>/confirmation')
|
|
|
|
|
def page5_confirmation(session_id):
|
|
|
|
|
"""Page 5: Confirmation page"""
|
2025-12-27 23:12:21 +01:00
|
|
|
# Validate page access
|
|
|
|
|
if not can_access_page(session_id, 5):
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if app_data:
|
|
|
|
|
current_page = app_data.get('current_page', 2)
|
|
|
|
|
if current_page == 2:
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
elif current_page == 3:
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id))
|
|
|
|
|
elif current_page == 4:
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
2025-12-27 22:34:21 +01:00
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
return render_template('page5_confirmation.html',
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
job_name=app_data['job_name'],
|
2025-12-27 23:12:21 +01:00
|
|
|
current_page=app_data.get('current_page', 5),
|
2025-12-27 22:34:21 +01:00
|
|
|
email=app_data['email'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/application/<session_id>/')
|
|
|
|
|
def view_application(session_id):
|
|
|
|
|
"""View complete application details (for HR)"""
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
return render_template('application_view.html',
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
app_data=app_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/application/<session_id>/download/<filename>')
|
|
|
|
|
def download_file(session_id, filename):
|
|
|
|
|
"""Download an uploaded file"""
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
# Verify the file is in the uploaded files list
|
|
|
|
|
uploaded_files = app_data.get('uploaded_files', [])
|
|
|
|
|
file_found = False
|
|
|
|
|
|
|
|
|
|
for file_info in uploaded_files:
|
|
|
|
|
if file_info.get('stored_name') == filename:
|
|
|
|
|
file_found = True
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not file_found:
|
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
# Serve the file
|
|
|
|
|
attachments_path = get_attachments_path(session_id)
|
|
|
|
|
file_path = os.path.join(attachments_path, filename)
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(file_path):
|
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
return send_from_directory(attachments_path, filename, as_attachment=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/resume/<session_id>')
|
|
|
|
|
def resume_application(session_id):
|
|
|
|
|
"""Resume application from email link"""
|
|
|
|
|
app_data = load_application_data(session_id)
|
|
|
|
|
if not app_data:
|
|
|
|
|
flash('Bewerbung nicht gefunden.', 'error')
|
|
|
|
|
return redirect(url_for('page1_email'))
|
|
|
|
|
|
|
|
|
|
# Store session ID in Flask session
|
|
|
|
|
session['application_id'] = session_id
|
|
|
|
|
|
|
|
|
|
# Redirect to current page
|
|
|
|
|
current_page = app_data.get('current_page', 2)
|
|
|
|
|
|
|
|
|
|
if current_page == 2:
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|
|
|
|
|
elif current_page == 3:
|
|
|
|
|
return redirect(url_for('page3_motivation', session_id=session_id))
|
|
|
|
|
elif current_page == 4:
|
|
|
|
|
return redirect(url_for('page4_upload', session_id=session_id))
|
|
|
|
|
elif current_page == 5:
|
|
|
|
|
return redirect(url_for('page5_confirmation', session_id=session_id))
|
|
|
|
|
else:
|
|
|
|
|
return redirect(url_for('page2_personal', session_id=session_id))
|