A production-ready recommendation system microservice built with Python, SQLite, and Flask. Extends the Day 29 engine components into a full-stack application with a REST API, database layer, caching, and evaluation pipeline.
Score: 196 / 200 — Distinction (98%)
128 tests passing · avg latency 12ms · 483 req/s throughput · feedback loop verified
Rated content_id 1 ("Python for Beginners") with 5 stars → system adapted in real time:
| Change | Before | After |
|---|---|---|
| Python for Beginners | #1 (score 0.6374) | Removed (now rated) |
| SQL Masterclass | #4 (score 0.5149) | #2 (score 0.5379 — rescored) |
| React.js Complete Guide | Not in list | #5 (new entry) |
| Latency | 9.65ms | 18.69ms (cache cleared, fresh compute) |
User Request
│
▼
┌─────────────┐
│ Flask API │ ← auth, logging, request tracing
│ (api/app) │
└──────┬──────┘
│
▼
┌─────────────────────────┐
│ RecommendationOrch- │ ← in-memory cache (5 min TTL)
│ estrator (engine/) │
└───┬──────┬──────┬───────┘
│ │ │
▼ ▼ ▼
Cand. Scorer Evaluator
Gen.
│
▼
┌──────────────┐
│ SQLite DB │ ← 6 tables, repository pattern
│ (data/) │
└──────────────┘
day30_capstone/
├── data/
│ ├── database.py # SQLite connection + schema (6 tables)
│ ├── models.py # Table definitions reference
│ └── repositories.py # Data access layer (repository pattern)
├── engine/
│ ├── orchestrator.py # Main engine — ties all components together
│ ├── similarity.py # Cosine, Jaccard, Pearson similarity
│ ├── candidate_gen.py # Collaborative, content, popularity, hybrid
│ ├── scorer.py # Weighted multi-factor scoring + explanations
│ └── evaluator.py # Precision@K, Recall@K, NDCG@K, F1@K
├── api/
│ └── app.py # Flask REST API (4 endpoints)
├── tests/
│ ├── test_data.py # 28 data layer tests
│ ├── test_engine.py # 62 engine tests
│ └── test_api.py # 38 API endpoint tests
├── scripts/
│ ├── seed_data.py # Populate DB (12 users, 22 content, 64 interactions)
│ └── evaluate.py # Run evaluation + load test → evaluation_report.md
├── screenshots/ # Demo screenshots
├── evaluation_report.md # Auto-generated performance report
├── postman_collection.json
├── requirements.txt
└── README.md
pip install -r requirements.txtpython scripts/seed_data.pyCreates recommendation.db with 12 users, 22 content items, 12 skills, and 64 realistic interactions.
python api/app.pyAPI runs at http://localhost:5000
curl.exe -H "X-API-Key: dev-secret-key-123" http://localhost:5000/recommend/1python -c "import urllib.request, json; req = urllib.request.Request('http://localhost:5000/feedback', data=json.dumps({'user_id':1,'content_id':9,'type':'rate','rating':5.0}).encode(), headers={'X-API-Key':'dev-secret-key-123','Content-Type':'application/json'}, method='POST'); print(urllib.request.urlopen(req).read().decode())"All endpoints except /health require:
X-API-Key: dev-secret-key-123
Override via API_KEY environment variable.
Every response includes X-Request-ID and X-Response-Time-ms headers for tracing.
Health check. No auth required.
{ "status": "healthy", "request_id": "a1b2c3d4", "timestamp": 1712134800.123 }| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
int | 10 |
Items to return (1–50) |
strategy |
string | hybrid |
collaborative | content | popular | hybrid |
refresh |
bool | false |
Bypass cache |
Response:
{
"user_id": 1,
"strategy": "hybrid",
"cached": false,
"latency_ms": 12.4,
"recommendations": [
{
"content_id": 9,
"title": "Building REST APIs with Flask",
"category": "Web Dev",
"difficulty": "intermediate",
"score": 0.6821,
"explanation": ["Users with similar taste rated this highly"],
"breakdown": { "collaborative": 0.72, "content": 0.68, "popularity": 0.50, "skill_match": 0.80 }
}
]
}Errors: 401 no key · 400 bad params · 404 user not found
Cold start: Users with fewer than 3 interactions get popular items filtered by their declared interests. Response has "strategy": "cold_start".
| Field | Type | Required | Description |
|---|---|---|---|
user_id |
int | Yes | User ID |
content_id |
int | Yes | Content ID |
type |
string | Yes | view | like | complete | skip | rate |
rating |
float | If rate |
1.0 – 5.0 |
Response (201):
{ "interaction_id": 87, "status": "recorded", "request_id": "b2c3d4e5" }Clears the user's recommendation cache and refreshes engine data in real time.
{ "total_users": 12, "total_content": 22, "total_skills": 12, "cached_users": 3, "cache_size": 8 }python tests/test_data.py # 28 tests — database & repositories
python tests/test_engine.py # 62 tests — similarity, candidates, scorer, evaluator, orchestrator
python tests/test_api.py # 38 tests — all 4 API endpointsAll 128 tests use isolated temp databases — never touch recommendation.db.
python scripts/evaluate.pyOutputs evaluation_report.md with all metrics + load test.
| Strategy | P@5 | R@5 | NDCG@5 | HR@5 | Avg ms |
|---|---|---|---|---|---|
| collaborative | 0.15 | 0.19 | 0.135 | 0.58 | 5.7ms |
| content | 0.15 | 0.19 | 0.139 | 0.42 | 1.9ms |
| popular | 0.15 | 0.19 | 0.135 | 0.58 | 2.2ms |
| hybrid | 0.15 | 0.19 | 0.135 | 0.58 | 5.7ms |
Load test: 10 concurrent users · avg 11ms · 483 req/s · 0 failures
users (id, name, interests, created_at)
content (id, title, category, difficulty, popularity, created_at)
skills (id, name)
user_skills (user_id, skill_id, proficiency)
content_skills (content_id, skill_id)
interactions (id, user_id, content_id, type, rating, created_at)Foreign keys enforced. Types constrained to: view, like, complete, skip, rate.
| Signal | Weight | Description |
|---|---|---|
| Collaborative | 45% | Predicted rating from similar users |
| Content | 30% | Category + difficulty match with liked items |
| Popularity | 15% | Fraction of users who interacted with item |
| Skill match | 10% | Overlap between user skills and content skill tags |
Weights are configurable when instantiating RecommendationOrchestrator.
- In-memory cache, 5-minute TTL (configurable)
- Key =
(user_id, limit, strategy)— different params cached separately - Invalidated automatically on feedback submission
- Cold-start results cached independently
- Open Postman → Import → select
postman_collection.json - Set
base_url=http://localhost:5000 - Set
api_key=dev-secret-key-123 - Run — all 12 requests include automated assertions
- New scoring signal: Add method to
RecommendationScorer, add tocalculate_score()with a weight - New strategy: Add method to
CandidateGenerator, register instrategy_mapinorchestrator.py - Redis cache: Replace
_cachedict inorchestrator.pywith a Redis client - Docker:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN python scripts/seed_data.py
EXPOSE 5000
CMD ["python", "api/app.py"]- Python 3.10+
- Flask 3.0+
- No external ML libraries — pure Python standard library for all algorithms



