This is the refactored version of the CV Generator with clean architecture, Pydantic models, and proper service layer separation. It is a FastAPI application ready for deployment.
cv-generator/
βββ app/
β βββ models/
β β βββ cv_data.py # Pydantic models for data validation
β βββ services/
β β βββ cv_service.py # Business logic layer
β βββ core/
β β βββ exceptions.py # Custom exceptions
β β βββ logging.py # Logging configuration
β βββ utils/
βββ templates/ # Jinja2 templates
βββ static/ # Static assets
βββ generated/ # Generated CV files
βββ main.py # FastAPI application
βββ requirements.txt
- Before: Manual field validation with 50+ individual parameters
- After: Structured data models with automatic validation
class CVData(BaseModel):
personal_info: PersonalInfo
education: List[EducationEntry]
internships: List[InternshipEntry]
# ... etc
- Before: Business logic mixed with API endpoints
- After: Clean separation with dedicated service classes
class CVService:
def generate_cv(self, cv_data: CVData) -> str:
# Clean business logic
def get_cv_data(self, cv_id: str) -> CVDocument:
# Data retrieval logic
- Before: Generic HTTP exceptions
- After: Custom exceptions with proper logging
@app.exception_handler(CVNotFoundError)
async def cv_not_found_handler(request: Request, exc: CVNotFoundError):
logger.warning(f"CV not found: {str(exc)}")
return HTTPException(status_code=404, detail=str(exc))
- Before: Print statements
- After: Proper structured logging with levels
logger.info(f"CV generated successfully: {cv_id}")
logger.error(f"Error generating PDF: {str(e)}")
- Before: No API structure
- After: Versioned API endpoints
@app.get("/api/v1/cvs")
@app.get("/api/v1/cv/{cv_id}")
@app.delete("/api/v1/cv/{cv_id}")
# Install dependencies
pip install -r requirements.txt
# Run the application
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
GET /
- CV formGET /test
- Pre-filled test formPOST /generate
- Generate CV from form dataGET /cv/{cv_id}
- View CV with download buttonGET /cv/{cv_id}/pdf
- Download PDF
GET /api/v1/cvs
- List all CVsGET /api/v1/cv/{cv_id}
- Get CV dataDELETE /api/v1/cv/{cv_id}
- Delete CVGET /health
- Health check
# Test the models and services
python -c "
from app.models.cv_data import CVData
from app.services.cv_service import CVService
import json
# Load test data
with open('test_data_structured.json', 'r') as f:
data = json.load(f)
# Validate data
cv_data = CVData(**data)
print(f'β Validation passed: {cv_data.personal_info.full_name}')
# Test service
service = CVService()
cv_id = service.generate_cv(cv_data)
print(f'β CV generated: {cv_id}')
"
The new version uses proper data structures instead of flat field names:
{
"personal_info": {
"full_name": "Jane Smith",
"email": "[email protected]"
},
"education": [
{
"qualification": "M.Tech",
"institute": "Stanford University",
"year": "2023"
}
],
"internships": [
{
"company": "Google",
"role": "Software Engineer",
"points": ["Achievement 1", "Achievement 2"]
}
]
}
The system still supports a legacy flat format for backward compatibility during form submission.
- Email validation with regex patterns
- Required field validation with meaningful error messages
- Data type validation (strings, lists, etc.)
- Length constraints (min/max characters)
- Array size limits (max entries per section)
- Custom exceptions for different error types
- Structured logging with different levels
- Graceful error recovery with fallback options
- Detailed error messages for debugging
- Efficient data processing with Pydantic models
- Proper file handling with context managers
- Memory-efficient PDF generation
- Caching-ready template rendering
- Input validation prevents injection attacks
- Proper error messages don't expose internals
- File path validation prevents directory traversal
- Content-type validation for uploads
-
Update the models in
app/models/cv_data.py
:class PersonalInfo(BaseModel): full_name: str new_field: str = Field(..., min_length=1)
-
Update the service in
app/services/cv_service.py
:def convert_legacy_data(self, legacy_data: dict) -> CVData: # Add conversion logic for new field
-
Update templates to display the new field
-
Add to main.py:
@app.get("/api/v1/new-endpoint") async def new_endpoint(): return {"message": "New endpoint"}
-
Add proper error handling:
try: result = service.new_operation() return result except CustomException as e: logger.error(f"Error in new endpoint: {str(e)}") raise HTTPException(status_code=500, detail=str(e))
# Test models
python -m pytest tests/test_models.py
# Test services
python -m pytest tests/test_services.py
# Test API endpoints
python -m pytest tests/test_api.py
# Test full workflow
python -m pytest tests/test_integration.py
For detailed deployment instructions, please see DEPLOYMENT_GUIDE.md
.
curl http://localhost:8000/health
# View logs
tail -f app.log
# Filter by level
grep "ERROR" app.log
- Follow the new architecture patterns
- Add proper validation for new fields
- Include error handling for new endpoints
- Add logging for debugging
- Update tests for new functionality
- Update documentation for API changes
- β Pydantic models for data validation
- β Service layer architecture
- β Proper error handling and logging
- β API versioning
- β Structured test data
- β Health check endpoint
- β Backward compatibility with legacy format
- β Basic CV generation
- β HTML and PDF output
- β Form-based interface
- β Template rendering