┌─────────────────────────────────────────────────────────┐
│ FastAPI Application │
│ (app.py) │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Endpoints │ │ Middleware │ │ Handlers │ │
│ │ /api/* │ │ CORS, etc │ │ Async │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ▲ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Scraper │ │ Database │ │ Collector │ │
│ │(requests)│ │ (SQLite) │ │(APScheduler) │
│ └──────────┘ └──────────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
Hell Divers 2 API helldivers2.db (File)
- Purpose: FastAPI application factory and route handlers
- Key Components:
lifespan(): Startup/shutdown lifecycle management- Route handlers for all endpoints
- CORS middleware configuration
- Error handling
- Purpose: HTTP client for Hell Divers 2 API
- Key Methods:
get_war_status(): Fetch war informationget_planets(): Get all planetsget_statistics(): Get global statsget_campaigns(): Get campaign infoget_factions(): Get faction infoget_biomes(): Get biome info
- Purpose: SQLite database operations
- Tables:
war_status: War informationstatistics: Global game statsplanet_status: Individual planet datacampaigns: Campaign informationassignments: Major ordersdispatches: News and announcementsplanet_events: Special planet eventssystem_status: Internal metadata (upstream API status)
- Key Methods:
save_*(): Insert dataget_*(): Retrieve data*_history(): Get historical dataget_latest_*_snapshot(): Get cached data for fallbackset_upstream_status(): Track upstream API availabilityget_upstream_status(): Check last known upstream status
- Purpose: Schedule and manage data collection
- Key Components:
- APScheduler background scheduler
- Periodic data collection (default: 5 minutes)
- Event-driven collection
- Purpose: Environment-based configuration
- Classes:
Config: Base configurationDevelopmentConfig: Dev settingsProductionConfig: Prod settingsTestingConfig: Test settings
All responses follow a consistent JSON format:
Success Response:
{
"field": "value",
"nested": {
"data": "here"
}
}Error Response:
{
"detail": "Error message describing what went wrong"
}Certain endpoints implement cache fallback for resilience:
- Endpoints with cache fallback:
/api/campaigns,/api/planets,/api/planets/{planet_index},/api/factions,/api/biomes - Fallback logic:
- Try to fetch from live upstream API
- If upstream fails, retrieve from database cache
- If cache exists, return 200 with cached data
- If no cache exists, return 503 Service Unavailable
Status Codes:
200 OK: Success (may be live or cached data)404 Not Found: Resource doesn't exist or no data collected503 Service Unavailable: Upstream API down AND no cached data available500 Internal Server Error: Unexpected server error
GET /api/resource - List/Get all
GET /api/resource/{id} - Get single
POST /api/resource/action - Custom action
In scraper.py:
def get_new_data(self) -> Optional[Dict]:
"""Fetch new data from Hell Divers 2 API"""
try:
response = self.session.get(
f"{self.BASE_URL}/new-endpoint",
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
logger.error(f"Failed to fetch new data: {e}")
return NoneIn database.py:
def save_new_data(self, data: Dict) -> bool:
"""Save new data to database"""
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute(
'INSERT INTO new_data_table (data) VALUES (?)',
(json.dumps(data),)
)
conn.commit()
return True
except Exception as e:
logger.error(f"Failed to save new data: {e}")
return FalseIn app.py:
@app.get("/api/new-data", tags=["New Data"])
async def get_new_data():
"""Get new data"""
data = db.get_latest_new_data()
if data:
return data
raise HTTPException(status_code=404, detail="No data available")For critical endpoints that should continue serving during upstream outages:
@app.get("/api/new-data", tags=["New Data"])
async def get_new_data():
"""Get new data (with cache fallback)"""
# Try live API first
data = scraper.get_new_data()
# Fallback to cache if live API fails
if data is None:
data = db.get_latest_new_data_snapshot()
if data is not None:
return data
raise HTTPException(status_code=503, detail="No new data available (live fetch failed and no cached data)")The system_status table tracks internal metadata:
- Purpose: Monitor upstream API health and other system metrics
- Usage: Collector updates upstream status after each collection cycle
- Schema:
CREATE TABLE system_status ( id INTEGER PRIMARY KEY AUTOINCREMENT, status_key TEXT UNIQUE, status_value BOOLEAN, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )
- Current Keys:
upstream_api_available: Boolean indicating if Hell Divers 2 API is reachable
- Start the API:
python app.py- Run demo tests:
python demo.py- Manual curl tests:
# Get war status
curl http://localhost:5000/api/war/status
# Refresh statistics
curl -X POST http://localhost:5000/api/statistics/refresh
# Get planets with limit
curl "http://localhost:5000/api/planets/1/history?limit=20"- Swagger UI: http://localhost:5000/docs
- ReDoc: http://localhost:5000/redoc
- Python client:
import requests
API = "http://localhost:5000/api"
response = requests.get(f"{API}/planets")
print(response.json())Update app.py:
logging.basicConfig(
level=logging.DEBUG, # Changed from INFO
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)# Connect to database
sqlite3 helldivers2.db
# View tables
.tables
# View schema
.schema war_status
# Query data
SELECT * FROM war_status LIMIT 1;
# Count rows
SELECT COUNT(*) FROM war_status;The scheduler logs all job execution. Check logs for:
Added job "DataCollector.collect_all_data"
Scheduler started
Job executed successfully
Already implemented for:
war_status.timestampstatistics.timestampplanet_status.planet_index
Add new indexes:
cursor.execute('CREATE INDEX idx_column ON table(column)')Use LIMIT for large result sets:
SELECT * FROM planet_status LIMIT 100;Implement response caching:
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_computation():
...Solution: Check if the API is running and listening on port 5000
lsof -i :5000Solution: Close all database connections and restart the app
Solution: Check logs and verify network connectivity
Solution: Reinstall requirements
pip install -r requirements.txt --force-reinstallSolution:
- Check if upstream API is accessible:
curl https://api.helldivers2.dev/api/v1/war/info - Verify database has cached data:
sqlite3 helldivers2.db "SELECT COUNT(*) FROM planet_status" - Wait for collector to run and populate cache (runs every 5 minutes)
If upgrading from a version before the cache-fallback feature, the system_status table will be automatically created on next startup. No manual migration is required.
What changed:
- New table:
system_statuswith columnsid,status_key,status_value,timestamp - New index:
idx_system_status_keyonstatus_keycolumn - New methods:
set_upstream_status(),get_upstream_status()indatabase.py - New snapshot methods:
get_latest_planets_snapshot(),get_latest_campaigns_snapshot(), etc.
To verify migration:
sqlite3 helldivers2.db "SELECT name FROM sqlite_master WHERE type='table' AND name='system_status'"Expected output: system_status
- Use type hints for all functions
- Document functions with docstrings
- Follow PEP 8 style guide
- Use meaningful variable names
- Handle errors gracefully
- Create a feature branch
- Make changes following the patterns in existing code
- Test thoroughly with
demo.py - Update documentation
- Submit a pull request