refactor: split monolithic app.py into modular Flask application with factory pattern

- Created app/ package with 6 modules:
  - __init__.py: Application factory (create_app function)
  - routes.py: All route handlers (473 lines)
  - models.py: Data persistence layer (41 lines)
  - validators.py: Input validation functions (48 lines)
  - email_service.py: Email sending functions (85 lines)
  - utils.py: Utility functions (30 lines)

- Implemented Flask application factory pattern
- Created run.py as minimal entry point
- Updated all test imports to use new module structure
- Fixed template/static folder paths for package structure

- All 102 tests passing
- Improved maintainability with clear separation of concerns
- Follows Flask best practices for scalable applications
This commit is contained in:
2025-12-27 22:36:05 +01:00
parent 733ee756c7
commit f1d8bae6a1
18 changed files with 1254 additions and 229 deletions
+48
View File
@@ -0,0 +1,48 @@
"""
Validation functions for the Flask job application system.
"""
import re
from flask import current_app
def validate_email(email):
"""Validate email format"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def validate_phone(phone):
"""Validate international phone format"""
# Accepts formats like: +41 79 123 45 67, +41791234567, etc.
# Remove spaces and check if it matches the pattern
pattern = r'^\+\d{1,3}[\s\d]{1,20}$'
if not re.match(pattern, phone):
return False
# Ensure there are at least some digits after the country code
digits_only = re.sub(r'\s', '', phone)
return len(digits_only) >= 5 # At least +XX XXX format
def validate_year(year):
"""Validate birth year"""
try:
year_int = int(year)
return (current_app.config['MIN_BIRTH_YEAR'] <= year_int <= current_app.config['MAX_BIRTH_YEAR']
and len(str(year)) == 4)
except ValueError:
return False
def validate_zip(zip_code):
"""Validate ZIP code"""
try:
zip_int = int(zip_code)
return len(str(zip_code)) <= current_app.config['MAX_ZIP_DIGITS']
except ValueError:
return False
def allowed_file(filename):
"""Check if file extension is allowed"""
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']