364 lines
8.4 KiB
Markdown
364 lines
8.4 KiB
Markdown
# Hyperlocal School Calendar - Deployment Guide
|
|
|
|
## 🎯 Overview
|
|
|
|
This guide provides step-by-step instructions to deploy the hyperlocal school calendar feature for Prophet forecasting enhancement.
|
|
|
|
---
|
|
|
|
## ✅ Prerequisites
|
|
|
|
- External service database access
|
|
- Redis instance running
|
|
- Access to deploy to external, training, and forecasting services
|
|
|
|
---
|
|
|
|
## 📦 Deployment Steps
|
|
|
|
### Step 1: Run Database Migration
|
|
|
|
```bash
|
|
cd services/external
|
|
python -m alembic upgrade head
|
|
```
|
|
|
|
**Expected Output:**
|
|
```
|
|
INFO [alembic.runtime.migration] Running upgrade b97bab14ac47 -> 693e0d98eaf9, add_school_calendars_and_location_context
|
|
```
|
|
|
|
**Verify Tables Created:**
|
|
```sql
|
|
-- Connect to external service database
|
|
SELECT table_name FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name IN ('school_calendars', 'tenant_location_contexts');
|
|
```
|
|
|
|
### Step 2: Seed Calendar Data
|
|
|
|
```bash
|
|
cd services/external
|
|
python scripts/seed_school_calendars.py
|
|
```
|
|
|
|
**Expected Output:**
|
|
```
|
|
INFO Starting school calendar seeding...
|
|
INFO Found 2 calendars in registry
|
|
INFO Processing calendar calendar_id=madrid_primary_2024_2025 city=madrid type=primary
|
|
INFO Calendar seeded successfully calendar_id=<uuid> city=madrid type=primary
|
|
INFO Processing calendar calendar_id=madrid_secondary_2024_2025 city=madrid type=secondary
|
|
INFO Calendar seeded successfully calendar_id=<uuid> city=madrid type=secondary
|
|
INFO Calendar seeding completed seeded=2 skipped=0 total=2
|
|
```
|
|
|
|
**Verify Calendars Loaded:**
|
|
```sql
|
|
SELECT calendar_name, city_id, school_type, academic_year
|
|
FROM school_calendars;
|
|
```
|
|
|
|
Expected: 2 rows (Madrid Primary and Secondary 2024-2025)
|
|
|
|
### Step 3: Restart External Service
|
|
|
|
```bash
|
|
# Via Tilt or kubectl
|
|
kubectl rollout restart deployment external-service -n bakery-ia
|
|
kubectl wait --for=condition=ready pod -l app=external-service -n bakery-ia --timeout=60s
|
|
```
|
|
|
|
**Verify Service Health:**
|
|
```bash
|
|
curl -k https://localhost/api/v1/external/health
|
|
```
|
|
|
|
### Step 4: Test Calendar API
|
|
|
|
**List Calendars for Madrid:**
|
|
```bash
|
|
curl -k -H "X-Tenant-ID: <tenant-id>" \
|
|
https://localhost/api/v1/external/operations/cities/madrid/school-calendars
|
|
```
|
|
|
|
**Expected Response:**
|
|
```json
|
|
{
|
|
"city_id": "madrid",
|
|
"calendars": [
|
|
{
|
|
"calendar_id": "<uuid>",
|
|
"calendar_name": "Madrid Primary School Calendar 2024-2025",
|
|
"city_id": "madrid",
|
|
"school_type": "primary",
|
|
"academic_year": "2024-2025",
|
|
"holiday_periods": [...],
|
|
"school_hours": {...},
|
|
"enabled": true
|
|
},
|
|
...
|
|
],
|
|
"total": 2
|
|
}
|
|
```
|
|
|
|
### Step 5: Assign Calendar to Test Tenant
|
|
|
|
```bash
|
|
# Get a calendar ID from previous step
|
|
CALENDAR_ID="<uuid-from-previous-step>"
|
|
TENANT_ID="<your-test-tenant-id>"
|
|
|
|
curl -k -X POST \
|
|
-H "X-Tenant-ID: $TENANT_ID" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"city_id": "madrid",
|
|
"school_calendar_id": "'$CALENDAR_ID'",
|
|
"neighborhood": "Chamberí",
|
|
"notes": "Test bakery near primary school"
|
|
}' \
|
|
https://localhost/api/v1/external/tenants/$TENANT_ID/location-context
|
|
```
|
|
|
|
**Verify Assignment:**
|
|
```bash
|
|
curl -k -H "X-Tenant-ID: $TENANT_ID" \
|
|
https://localhost/api/v1/external/tenants/$TENANT_ID/location-context
|
|
```
|
|
|
|
### Step 6: Test Holiday Check
|
|
|
|
```bash
|
|
# Check if Christmas is a holiday
|
|
curl -k -H "X-Tenant-ID: $TENANT_ID" \
|
|
"https://localhost/api/v1/external/operations/school-calendars/$CALENDAR_ID/is-holiday?check_date=2024-12-25"
|
|
```
|
|
|
|
**Expected Response:**
|
|
```json
|
|
{
|
|
"date": "2024-12-25",
|
|
"is_holiday": true,
|
|
"holiday_name": "Christmas Holiday",
|
|
"calendar_id": "<uuid>",
|
|
"calendar_name": "Madrid Primary School Calendar 2024-2025"
|
|
}
|
|
```
|
|
|
|
### Step 7: Verify Redis Caching
|
|
|
|
**First Request (Cache Miss):**
|
|
```bash
|
|
time curl -k -H "X-Tenant-ID: $TENANT_ID" \
|
|
https://localhost/api/v1/external/tenants/$TENANT_ID/location-context
|
|
```
|
|
Expected: ~50-100ms
|
|
|
|
**Second Request (Cache Hit):**
|
|
```bash
|
|
time curl -k -H "X-Tenant-ID: $TENANT_ID" \
|
|
https://localhost/api/v1/external/tenants/$TENANT_ID/location-context
|
|
```
|
|
Expected: ~5-10ms (much faster!)
|
|
|
|
**Check Redis:**
|
|
```bash
|
|
redis-cli
|
|
> KEYS tenant_context:*
|
|
> GET tenant_context:<tenant-id>
|
|
> TTL tenant_context:<tenant-id> # Should show ~86400 seconds (24 hours)
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Optional: Integrate with Training/Forecasting Services
|
|
|
|
### Option A: Manual Integration (Recommended First)
|
|
|
|
The helper classes are ready to use:
|
|
|
|
**In Training Service:**
|
|
```python
|
|
# services/training/app/ml/data_processor.py
|
|
from app.ml.calendar_features import CalendarFeatureEngine
|
|
from shared.clients.external_client import ExternalServiceClient
|
|
|
|
# In __init__:
|
|
self.external_client = ExternalServiceClient(config=settings, calling_service_name="training")
|
|
self.calendar_engine = CalendarFeatureEngine(self.external_client)
|
|
|
|
# In _engineer_features():
|
|
if tenant_id:
|
|
df = await self.calendar_engine.add_calendar_features(df, tenant_id)
|
|
```
|
|
|
|
**In Forecasting Service:**
|
|
```python
|
|
# services/forecasting/app/services/forecasting_service.py or prediction_service.py
|
|
from app.ml.calendar_features import forecast_calendar_features
|
|
|
|
# When preparing future features:
|
|
future_df = await forecast_calendar_features.add_calendar_features(
|
|
future_df,
|
|
tenant_id=tenant_id,
|
|
date_column="ds"
|
|
)
|
|
```
|
|
|
|
### Option B: Gradual Rollout
|
|
|
|
1. **Phase 1:** Deploy infrastructure (Steps 1-6 above) ✅
|
|
2. **Phase 2:** Test with 1-2 bakeries near schools
|
|
3. **Phase 3:** Integrate into training service
|
|
4. **Phase 4:** Retrain models for test bakeries
|
|
5. **Phase 5:** Integrate into forecasting service
|
|
6. **Phase 6:** Compare forecast accuracy
|
|
7. **Phase 7:** Full rollout to all tenants
|
|
|
|
---
|
|
|
|
## 📊 Monitoring & Validation
|
|
|
|
### Database Metrics
|
|
|
|
```sql
|
|
-- Check calendar usage
|
|
SELECT COUNT(*) FROM tenant_location_contexts
|
|
WHERE school_calendar_id IS NOT NULL;
|
|
|
|
-- Check which calendars are most used
|
|
SELECT c.calendar_name, COUNT(t.tenant_id) as tenant_count
|
|
FROM school_calendars c
|
|
LEFT JOIN tenant_location_contexts t ON c.id = t.school_calendar_id
|
|
GROUP BY c.calendar_name;
|
|
```
|
|
|
|
### Redis Cache Metrics
|
|
|
|
```bash
|
|
redis-cli
|
|
> INFO stats # Check hit/miss rates
|
|
> KEYS calendar:* # List cached calendars
|
|
> KEYS tenant_context:* # List cached tenant contexts
|
|
```
|
|
|
|
### API Performance
|
|
|
|
Check external service logs for:
|
|
- Calendar API response times
|
|
- Cache hit rates
|
|
- Any errors
|
|
|
|
```bash
|
|
kubectl logs -n bakery-ia -l app=external-service --tail=100 | grep calendar
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Troubleshooting
|
|
|
|
### Problem: Migration Fails
|
|
|
|
**Error:** `alembic.util.exc.CommandError: Can't locate revision...`
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Check current migration version
|
|
cd services/external
|
|
python -m alembic current
|
|
|
|
# Force to specific version if needed
|
|
python -m alembic stamp head
|
|
```
|
|
|
|
### Problem: Seed Script Fails
|
|
|
|
**Error:** `No module named 'app'`
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Ensure you're in the right directory
|
|
cd services/external
|
|
# Set PYTHONPATH
|
|
export PYTHONPATH=$(pwd):$PYTHONPATH
|
|
python scripts/seed_school_calendars.py
|
|
```
|
|
|
|
### Problem: Calendar API Returns 404
|
|
|
|
**Check:**
|
|
1. External service deployed with new router?
|
|
```bash
|
|
kubectl logs -n bakery-ia -l app=external-service | grep "calendar_operations"
|
|
```
|
|
2. Migration completed?
|
|
```sql
|
|
SELECT * FROM alembic_version;
|
|
```
|
|
3. Calendars seeded?
|
|
```sql
|
|
SELECT COUNT(*) FROM school_calendars;
|
|
```
|
|
|
|
### Problem: Cache Not Working
|
|
|
|
**Check Redis Connection:**
|
|
```bash
|
|
# From external service pod
|
|
kubectl exec -it <external-pod> -n bakery-ia -- redis-cli -h <redis-host> PING
|
|
```
|
|
|
|
**Check Logs:**
|
|
```bash
|
|
kubectl logs -n bakery-ia -l app=external-service | grep "cache"
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 Rollback Procedure
|
|
|
|
If you need to rollback:
|
|
|
|
```bash
|
|
# 1. Rollback migration
|
|
cd services/external
|
|
python -m alembic downgrade -1
|
|
|
|
# 2. Restart external service
|
|
kubectl rollout restart deployment external-service -n bakery-ia
|
|
|
|
# 3. Clear Redis cache
|
|
redis-cli
|
|
> FLUSHDB
|
|
```
|
|
|
|
---
|
|
|
|
## 🎉 Success Criteria
|
|
|
|
- ✅ Migration completed successfully
|
|
- ✅ 2 calendars seeded (Madrid Primary & Secondary)
|
|
- ✅ Calendar API returns valid responses
|
|
- ✅ Tenant can be assigned to calendar
|
|
- ✅ Holiday check works correctly
|
|
- ✅ Redis cache reduces response time by >80%
|
|
- ✅ No errors in external service logs
|
|
|
|
---
|
|
|
|
## 📞 Support
|
|
|
|
For issues or questions:
|
|
- Check [HYPERLOCAL_CALENDAR_IMPLEMENTATION.md](HYPERLOCAL_CALENDAR_IMPLEMENTATION.md) for full technical details
|
|
- Review API endpoint documentation in calendar_operations.py
|
|
- Check logs for specific error messages
|
|
|
|
---
|
|
|
|
**Deployment Completed:** [Date]
|
|
**Deployed By:** [Name]
|
|
**Version:** 1.0.0
|