- 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
9.9 KiB
This refactoring will make the codebase easier to navigate, enable better unit testing of individual components, and follow Flask best practices for scalable applications.
Current state: - Single app.py file contains routes, validation logic, email functions, storage helpers, and Flask app initialization - 102 passing tests that import directly from app module - Flask application with CSRF protection, rate limiting, file uploads, and email notifications - Tech stack: Flask 3.0.0, Flask-Mail, Flask-WTF, PyYAMLTarget state:
- Organized app/ package structure with separated concerns
- Flask application factory pattern (create_app function)
- Maintained backward compatibility for all existing functionality
- All 102 tests updated and passing
Review the current implementation: @app.py
1. **Create app/ package structure**: - `app/__init__.py` - Application factory (create_app function), Flask app initialization - `app/routes.py` - All route handlers (@app.route decorators) - `app/models.py` - Data models and storage functions (save/load application data) - `app/validators.py` - All validation functions (email, phone, year, zip, file extensions) - `app/email_service.py` - Email-related functions (send_resume_email, send_hr_notification) - `app/utils.py` - Helper functions (path helpers, rate limiting, file handling)-
Implement Flask application factory pattern:
- Create
create_app(config_class=Config)function inapp/__init__.py - Initialize extensions (mail, csrf) within factory
- Register blueprints or routes within factory
- Update main entry point to use factory
- Create
-
Maintain all existing functionality:
- All routes must work identically
- CSRF protection, rate limiting, file uploads, email notifications unchanged
- Session handling, flash messages, redirects work as before
- Error handlers preserved
-
Update test suite:
- Modify test imports to use new module structure
- Update fixtures to work with application factory
- Ensure all 102 tests pass after refactoring
- No changes to test logic, only imports and app initialization
-
Update main entry point:
- Create
run.pyor update existing entry point to usecreate_app() - Ensure development server still runs with
python run.pyorflask run
- Create
-
Preserve imports and dependencies:
- Keep all existing imports (Flask, yaml, werkzeug, etc.)
- Maintain proper circular import prevention
- Use relative imports within app/ package
-
app/__init__.py should contain:
- Import statements for Flask, extensions
- Extension instances (mail, csrf) initialized but not bound
create_app(config_class=Config)function that:- Creates Flask app instance
- Loads configuration
- Initializes extensions (mail.init_app, csrf.init_app)
- Registers routes (import and register blueprint or routes)
- Registers error handlers
- Returns app instance
-
app/routes.py should contain:
- All @app.route decorated functions
- Import necessary validators, models, email_service, utils
- Either use Blueprint or accept app parameter for route registration
- Preserve all route logic exactly as is
-
app/models.py should contain:
- Path helper functions (get_application_path, get_data_file_path, get_attachments_path)
- Data persistence functions (load_application_data, save_application_data)
- Any data structure or schema-related code
-
app/validators.py should contain:
- validate_email, validate_phone, validate_year, validate_zip
- allowed_file function
- Each function should be independent and easily testable
-
app/email_service.py should contain:
- send_resume_email function
- send_hr_notification function
- Requires access to mail instance and app.config
- Use current_app for accessing config when needed
-
app/utils.py should contain:
- check_rate_limit function
- Any other utility functions not fitting elsewhere
Important considerations:
-
Circular imports: To avoid circular imports between routes and models:
- Routes import from models, validators, email_service, utils
- Models should NOT import from routes
- Use
from flask import current_appto access app instance in helper functions
-
Extension access: For mail, csrf in separate modules:
- Initialize extensions in
app/__init__.py - Import and use them in routes/email_service:
from app import mail, csrf - OR pass as parameters where needed
- Initialize extensions in
-
Configuration access: Use
current_app.configin utility functions instead ofapp.config -
Route registration: Two approaches (choose one):
- Blueprint approach: Create Blueprint in routes.py, register in factory
- Direct approach: Import route functions and call them with app instance
-
Error handlers: Register in
create_app()factory function
WHY this structure:
- Separation of concerns makes each module independently testable
- Application factory enables multiple app instances for testing
- Smaller files are easier to navigate and maintain
- Follows Flask community best practices for production applications
- Enables future scaling (add more routes, validators, services without bloating single file)
<migration_steps> Execute refactoring in this order to minimize breaking changes:
-
Create app/ package:
- Create
app/directory - Create
app/__init__.pywith imports and extension initialization
- Create
-
Extract validators (least dependencies):
- Move validation functions to
app/validators.py - Update imports in app.py
- Run tests to verify
- Move validation functions to
-
Extract models (storage layer):
- Move path helpers and data functions to
app/models.py - Update imports in app.py
- Run tests to verify
- Move path helpers and data functions to
-
Extract email service:
- Move email functions to
app/email_service.py - Ensure mail instance is accessible
- Update imports in app.py
- Run tests to verify
- Move email functions to
-
Extract utilities:
- Move rate limiting and other utils to
app/utils.py - Update imports in app.py
- Run tests to verify
- Move rate limiting and other utils to
-
Extract routes:
- Move all route handlers to
app/routes.py - Keep route registration in app.py temporarily
- Run tests to verify
- Move all route handlers to
-
Implement application factory:
- Create
create_app()inapp/__init__.py - Move app initialization into factory
- Update
app.pyor createrun.pyto use factory - Run tests to verify
- Create
-
Update all tests:
- Update imports in all test files
- Update app fixture in conftest.py to use factory
- Run full test suite
- Fix any remaining import issues
-
Final cleanup:
- Remove old app.py or convert to minimal run.py
- Verify all functionality works
- Run full test suite one final time </migration_steps>
<test_updates> Update test files to work with new structure:
tests/conftest.py changes:
from app import create_app
from app import mail
@pytest.fixture
def app():
"""Create and configure a test Flask application instance."""
test_app = create_app()
# ... rest of configuration
yield test_app
All test files should update imports:
- Change
from app import validate_email→from app.validators import validate_email - Change
from app import send_resume_email→from app.email_service import send_resume_email - Change
from app import load_application_data→from app.models import load_application_data - And so on for all imports
Run tests after each major change to catch import errors early. </test_updates>
Create the following new files:./app/__init__.py- Application factory and extension initialization./app/routes.py- All route handlers./app/models.py- Data persistence layer./app/validators.py- Validation functions./app/email_service.py- Email sending functions./app/utils.py- Utility functions (rate limiting, etc.)
Modify:
./app.py→ Convert to./run.py(or keep as minimal entry point using factory)./tests/conftest.py- Update app fixture to use create_app()./tests/*.py- Update all imports to new module structure
Delete after successful migration:
- Original
./app.py(only if converted to run.py)
- All tests pass: Run
pytest tests/ -vand confirm all 102 tests pass - Application runs: Start server with
python run.pyorflask runand verify pages load - No circular imports: Python can import app package without errors
- All routes work: Test critical user flow:
- Submit email on page 1
- Fill personal info on page 2
- Fill motivation on page 3
- Upload file and submit on page 4
- View confirmation on page 5
- Access /application/<session_id>/ view
- Download uploaded file
- Email functionality: HR notification sent on submission (check logs)
- No broken imports: All test files import successfully
If any tests fail, carefully review:
- Import statements in test files
- Application context in helper functions (use current_app)
- Extension initialization in factory
- Route registration in factory
<success_criteria>
- app/ package created with 6 modules (init, routes, models, validators, email_service, utils)
- Application factory pattern implemented (create_app function)
- Original app.py converted to minimal run.py entry point
- All 102 tests passing with updated imports
- Application runs and all features work identically
- Code is more maintainable with clear separation of concerns
- No circular import errors
- Flask development server starts successfully </success_criteria> Completed: Sa 27 Dez 2025 22:29:29 CET