Merge pull request 'feature/wc' (#51) from feature/wc into feature/icecheng/metrics
Reviewed-on: freeleaps/freeleaps-service-hub#51
This commit is contained in:
commit
806778a5b9
@ -10,9 +10,28 @@ COPY requirements.txt .
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy environment file
|
||||
COPY local.env .
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
ENV MONGODB_NAME = "freeleaps2"
|
||||
ENV MONGODB_URI = "mongodb://freeleaps2-mongodb:27017"
|
||||
|
||||
#app_settings
|
||||
ENV GITEA_TOKEN = ""
|
||||
ENV GITEA_URL = ""
|
||||
ENV GITEA_DEPOT_ORGANIZATION = ""
|
||||
ENV CODE_DEPOT_HTTP_PORT = ""
|
||||
ENV CODE_DEPOT_SSH_PORT = ""
|
||||
ENV CODE_DEPOT_DOMAIN_NAME = ""
|
||||
|
||||
#log_settings
|
||||
ENV LOG_BASE_PATH = "./logs"
|
||||
ENV BACKEND_LOG_FILE_NAME = "freeleaps-metrics"
|
||||
ENV APPLICATION_ACTIVITY_LOG = "freeleaps-metrics-activity"
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8009
|
||||
|
||||
|
||||
@ -1,35 +1,28 @@
|
||||
# 📊 Metrics Service
|
||||
|
||||
> A lightweight FastAPI microservice for user registration analytics and statistics
|
||||
> A lightweight FastAPI microservice for user registration analytics and metrics
|
||||
|
||||
[](https://python.org)
|
||||
[](https://fastapi.tiangolo.com)
|
||||
[](https://docker.com)
|
||||
|
||||
The Metrics service provides real-time APIs for querying user registration data from StarRocks database, offering flexible analytics and insights into user growth patterns.
|
||||
The Metrics service provides real-time APIs for querying user registration data (via StarRocks) and querying monitoring metrics (via Prometheus).
|
||||
|
||||
## ✨ Features
|
||||
|
||||
### 📊 User Registration Statistics APIs
|
||||
- **Date Range Query** - Query registration data for specific date ranges
|
||||
- **Recent N Days Query** - Get registration data for the last N days
|
||||
- **Start Date + Days Query** - Query N days starting from a specified date
|
||||
- **Statistics Summary** - Get comprehensive statistics and analytics
|
||||
- **POST Method Support** - JSON request body support for complex queries
|
||||
### 📊 Registration Analytics (StarRocks)
|
||||
- Date Range Query (start_date ~ end_date)
|
||||
- Typed responses with Pydantic models
|
||||
|
||||
### 🗄️ Database Integration
|
||||
- **StarRocks Database Connection**
|
||||
- Host: `freeleaps-starrocks-cluster-fe-service.freeleaps-data-platform.svc`
|
||||
- Port: `9030`
|
||||
- Database: `freeleaps`
|
||||
- Table: `dws_daily_registered_users`
|
||||
### 📈 Prometheus Metrics
|
||||
- Predefined PromQL metrics per product
|
||||
- Time-range queries and metric info discovery
|
||||
|
||||
### 🔧 Technical Features
|
||||
- **Data Models**: Pydantic validation for data integrity
|
||||
- **Connection Management**: Automatic database connection and disconnection
|
||||
- **Error Handling**: Comprehensive exception handling with user-friendly error messages
|
||||
- **Logging**: Structured logging using Loguru
|
||||
- **API Documentation**: Auto-generated Swagger/OpenAPI documentation
|
||||
- Data Models: Pydantic v2
|
||||
- Async HTTP and robust error handling
|
||||
- Structured logging
|
||||
- Auto-generated Swagger/OpenAPI docs
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
@ -37,31 +30,43 @@ The Metrics service provides real-time APIs for querying user registration data
|
||||
metrics/
|
||||
├── backend/ # Business logic layer
|
||||
│ ├── infra/ # Infrastructure components
|
||||
│ │ └── database_client.py
|
||||
│ │ └── external_service/
|
||||
│ │ ├── prometheus_client.py
|
||||
│ │ └── starrocks_client.py
|
||||
│ ├── models/ # Data models
|
||||
│ │ └── user_registration_models.py
|
||||
│ └── services/ # Business services
|
||||
│ ├── prometheus_metrics_service.py
|
||||
│ └── registration_analytics_service.py
|
||||
├── webapi/ # API layer
|
||||
│ ├── routes/ # API endpoints
|
||||
│ │ └── registration_metrics.py
|
||||
│ ├── config/ # Configuration
|
||||
│ │ └── app_settings.py
|
||||
│ ├── bootstrap/ # App initialization
|
||||
│ │ └── app_factory.py
|
||||
│ └── main.py # FastAPI app entry point
|
||||
├── common/ # Shared utilities
|
||||
├── requirements.txt # Dependencies
|
||||
├── Dockerfile # Container config
|
||||
├── local.env # Environment variables
|
||||
└── README.md # Documentation
|
||||
│ │ ├── metrics/ # Aggregated routers (prefix: /api/metrics)
|
||||
│ │ ├── prometheus_metrics/
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── available_metrics.py
|
||||
│ │ │ ├── metric_info.py
|
||||
│ │ │ └── metrics_query.py
|
||||
│ │ └── starrocks_metrics/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── available_metrics.py
|
||||
│ │ ├── metric_info.py
|
||||
│ │ └── metrics_query.py
|
||||
│ ├── bootstrap/
|
||||
│ │ └── application.py
|
||||
│ └── main.py
|
||||
├── common/
|
||||
├── requirements.txt
|
||||
├── Dockerfile
|
||||
├── local.env
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.12+ or Docker
|
||||
- Access to StarRocks database
|
||||
- Access to StarRocks database (for registration analytics)
|
||||
- Access to Prometheus (for monitoring metrics)
|
||||
|
||||
### 🐍 Python Setup
|
||||
|
||||
@ -79,8 +84,12 @@ python3 -m uvicorn webapi.main:app --host 0.0.0.0 --port 8009 --reload
|
||||
# 1. Build image
|
||||
docker build -t metrics:latest .
|
||||
|
||||
# 2. Run container
|
||||
# 2. Run container (env from baked local.env)
|
||||
docker run --rm -p 8009:8009 metrics:latest
|
||||
|
||||
# Alternatively: run with a custom env file
|
||||
# (this overrides envs copied into the image)
|
||||
docker run --rm -p 8009:8009 --env-file local.env metrics:latest
|
||||
```
|
||||
|
||||
### 📖 Access Documentation
|
||||
@ -88,69 +97,36 @@ Visit `http://localhost:8009/docs` for interactive API documentation.
|
||||
|
||||
## 📊 API Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/metrics/daily-registered-users` | GET/POST | Query registration data by date range |
|
||||
| `/api/metrics/recent-registered-users` | GET | Get recent N days data |
|
||||
| `/api/metrics/registered-users-by-days` | GET | Query N days from start date |
|
||||
| `/api/metrics/registration-summary` | GET | Get statistical summary |
|
||||
All endpoints are exposed under the API base prefix `/api/metrics`.
|
||||
|
||||
### Example Requests
|
||||
### StarRocks (Registration Analytics)
|
||||
- POST `/api/metrics/starrocks/dru_query` — Query daily registered users time series for a date range
|
||||
- GET `/api/metrics/starrocks/product/{product_id}/available-metrics` — List available StarRocks-backed metrics for the product
|
||||
- GET `/api/metrics/starrocks/product/{product_id}/metric/{metric_name}/info` — Get metric info for the product
|
||||
|
||||
Example request:
|
||||
```bash
|
||||
# Get last 7 days
|
||||
curl "http://localhost:8009/api/metrics/recent-registered-users?days=7"
|
||||
|
||||
# Get date range
|
||||
curl "http://localhost:8009/api/metrics/daily-registered-users?start_date=2024-09-10&end_date=2024-09-20"
|
||||
|
||||
# Get summary statistics
|
||||
curl "http://localhost:8009/api/metrics/registration-summary?start_date=2024-09-10&end_date=2024-09-20"
|
||||
curl -X POST "http://localhost:8009/api/metrics/starrocks/dru_query" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"product_id": "freeleaps",
|
||||
"start_date": "2024-09-10",
|
||||
"end_date": "2024-09-20"
|
||||
}'
|
||||
```
|
||||
|
||||
### Parameters
|
||||
- `start_date` / `end_date`: Date in `YYYY-MM-DD` format
|
||||
- `days`: Number of days (max: 365)
|
||||
- `product_id`: Product identifier (default: "freeleaps")
|
||||
|
||||
## 📈 Response Format
|
||||
|
||||
### Standard Response
|
||||
```json
|
||||
{
|
||||
"dates": ["2024-09-10", "2024-09-11", "2024-09-12"],
|
||||
"counts": [39, 38, 31],
|
||||
"total_registrations": 108,
|
||||
"query_period": "2024-09-10 to 2024-09-12"
|
||||
}
|
||||
```
|
||||
|
||||
### Summary Response
|
||||
```json
|
||||
{
|
||||
"total_registrations": 282,
|
||||
"average_daily": 25.64,
|
||||
"max_daily": 39,
|
||||
"min_daily": 8,
|
||||
"days_with_registrations": 10,
|
||||
"total_days": 11
|
||||
}
|
||||
```
|
||||
### Prometheus
|
||||
- POST `/api/metrics/prometheus/metrics_query` — Query metric time series by product/metric
|
||||
- GET `/api/metrics/prometheus/product/{product_id}/available-metrics` — List available metrics for product
|
||||
- GET `/api/metrics/prometheus/product/{product_id}/metric/{metric_name}/info` — Get metric info
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Quick Test
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:8009/
|
||||
|
||||
# Test recent registrations
|
||||
curl "http://localhost:8009/api/metrics/recent-registered-users?days=7"
|
||||
```
|
||||
|
||||
### Interactive Testing
|
||||
Visit `http://localhost:8009/docs` for the Swagger UI interface where you can test all endpoints directly.
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Environment Variables
|
||||
@ -170,9 +146,12 @@ STARROCKS_DATABASE=freeleaps
|
||||
LOG_BASE_PATH=./logs
|
||||
BACKEND_LOG_FILE_NAME=metrics
|
||||
APPLICATION_ACTIVITY_LOG=metrics-activity
|
||||
|
||||
# Prometheus
|
||||
PROMETHEUS_ENDPOINT=http://localhost:9090
|
||||
```
|
||||
|
||||
> 💡 **Tip**: Copy `local.env` to `.env` and modify as needed for your environment.
|
||||
> Tip: Copy `local.env` to `.env` and modify as needed for your environment.
|
||||
|
||||
### 🐳 Docker Deployment
|
||||
|
||||
@ -206,20 +185,16 @@ python -m uvicorn webapi.main:app --reload
|
||||
```
|
||||
|
||||
## 📝 API Documentation
|
||||
|
||||
- **Swagger UI**: `http://localhost:8009/docs`
|
||||
- **ReDoc**: `http://localhost:8009/redoc`
|
||||
- **OpenAPI JSON**: `http://localhost:8009/openapi.json`
|
||||
- Swagger UI: `http://localhost:8009/docs`
|
||||
- ReDoc: `http://localhost:8009/redoc`
|
||||
- OpenAPI JSON: `http://localhost:8009/openapi.json`
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
- Date format: `YYYY-MM-DD`
|
||||
- Max query range: 365 days
|
||||
- Date format: `YYYY-MM-DD` (single-digit month/day also accepted by API)
|
||||
- Default `product_id`: "freeleaps"
|
||||
- Requires StarRocks database access
|
||||
- Requires StarRocks database access and/or Prometheus endpoint
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Port in use | `docker stop $(docker ps -q --filter ancestor=metrics:latest)` |
|
||||
|
||||
@ -88,12 +88,3 @@ class StarRocksClient:
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Context manager exit"""
|
||||
self.disconnect()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from typing import List, Dict, Any
|
||||
from datetime import date, timedelta
|
||||
from loguru import logger
|
||||
from backend.infra.database_client import StarRocksClient
|
||||
from backend.infra.external_service.starrocks_client import StarRocksClient
|
||||
from backend.models.user_registration_models import UserRegistrationResponse, DailyRegisteredUsers
|
||||
|
||||
|
||||
@ -33,7 +33,6 @@ class RegistrationService:
|
||||
raw_data = self.starrocks_client.get_daily_registered_users(
|
||||
start_date, end_date, product_id
|
||||
)
|
||||
|
||||
# Convert to DailyRegisteredUsers objects
|
||||
daily_data = [
|
||||
DailyRegisteredUsers(
|
||||
@ -44,7 +43,6 @@ class RegistrationService:
|
||||
)
|
||||
for row in raw_data
|
||||
]
|
||||
|
||||
# Create date-to-count mapping
|
||||
data_dict = {str(item.date_id): item.registered_cnt for item in daily_data}
|
||||
|
||||
|
||||
@ -14,3 +14,4 @@ pytest==8.4.1
|
||||
pytest-asyncio==0.21.2
|
||||
pymysql==1.1.0
|
||||
sqlalchemy==2.0.23
|
||||
python-dotenv
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter
|
||||
from webapi.routes.metrics.registration_metrics import router as registration_router
|
||||
from webapi.routes.starrocks_metrics import api_router as starrocks_metrics_router
|
||||
from webapi.routes.prometheus_metrics import api_router as prometheus_metrics_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(registration_router, prefix="/metrics", tags=["registration-metrics"])
|
||||
router.include_router(starrocks_metrics_router, prefix="/metrics", tags=["starrocks-metrics"])
|
||||
router.include_router(prometheus_metrics_router, prefix="/metrics", tags=["prometheus-metrics"])
|
||||
|
||||
@ -1,229 +0,0 @@
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import Optional
|
||||
from loguru import logger
|
||||
from backend.services.registration_analytics_service import RegistrationService
|
||||
from backend.models.user_registration_models import UserRegistrationResponse, UserRegistrationQuery
|
||||
|
||||
router = APIRouter(tags=["registration"])
|
||||
|
||||
# Initialize service
|
||||
registration_service = RegistrationService()
|
||||
|
||||
|
||||
@router.get("/daily-registered-users", response_model=UserRegistrationResponse)
|
||||
async def get_daily_registered_users(
|
||||
start_date: date = Query(..., description="Start date in YYYY-MM-DD format"),
|
||||
end_date: date = Query(..., description="End date in YYYY-MM-DD format"),
|
||||
product_id: str = Query("freeleaps", description="Product identifier")
|
||||
):
|
||||
"""
|
||||
Get daily registered users count for a date range
|
||||
|
||||
Returns two lists:
|
||||
- dates: List of dates in YYYY-MM-DD format
|
||||
- counts: List of daily registration counts
|
||||
|
||||
Example:
|
||||
- GET /api/metrics/daily-registered-users?start_date=2024-01-01&end_date=2024-01-07
|
||||
"""
|
||||
try:
|
||||
# Validate date range
|
||||
if start_date > end_date:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Start date must be before or equal to end date"
|
||||
)
|
||||
|
||||
# Check date range is not too large (max 1 year)
|
||||
if (end_date - start_date).days > 365:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Date range cannot exceed 365 days"
|
||||
)
|
||||
|
||||
logger.info(f"Querying registration data from {start_date} to {end_date} for product {product_id}")
|
||||
|
||||
# Get data from service
|
||||
result = registration_service.get_daily_registered_users(
|
||||
start_date, end_date, product_id
|
||||
)
|
||||
|
||||
logger.info(f"Successfully retrieved data for {len(result.dates)} days")
|
||||
return result
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get daily registered users: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/registration-summary")
|
||||
async def get_registration_summary(
|
||||
start_date: date = Query(..., description="Start date in YYYY-MM-DD format"),
|
||||
end_date: date = Query(..., description="End date in YYYY-MM-DD format"),
|
||||
product_id: str = Query("freeleaps", description="Product identifier")
|
||||
):
|
||||
"""
|
||||
Get summary statistics for user registrations in a date range
|
||||
|
||||
Returns summary statistics including:
|
||||
- total_registrations: Total number of registrations
|
||||
- average_daily: Average daily registrations
|
||||
- max_daily: Maximum daily registrations
|
||||
- min_daily: Minimum daily registrations
|
||||
- days_with_registrations: Number of days with registrations
|
||||
- total_days: Total number of days in range
|
||||
"""
|
||||
try:
|
||||
# Validate date range
|
||||
if start_date > end_date:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Start date must be before or equal to end date"
|
||||
)
|
||||
|
||||
if (end_date - start_date).days > 365:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Date range cannot exceed 365 days"
|
||||
)
|
||||
|
||||
logger.info(f"Querying registration summary from {start_date} to {end_date} for product {product_id}")
|
||||
|
||||
# Get summary from service
|
||||
summary = registration_service.get_registration_summary(
|
||||
start_date, end_date, product_id
|
||||
)
|
||||
|
||||
return summary
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get registration summary: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/recent-registered-users", response_model=UserRegistrationResponse)
|
||||
async def get_recent_registered_users(
|
||||
days: int = Query(7, ge=1, le=365, description="Number of recent days to query"),
|
||||
product_id: str = Query("freeleaps", description="Product identifier")
|
||||
):
|
||||
"""
|
||||
Get daily registered users count for recent N days
|
||||
|
||||
Returns registration data for the last N days from today
|
||||
|
||||
Example:
|
||||
- GET /api/metrics/recent-registered-users?days=7
|
||||
- GET /api/metrics/recent-registered-users?days=30&product_id=freeleaps
|
||||
"""
|
||||
try:
|
||||
# Calculate date range
|
||||
end_date = date.today()
|
||||
start_date = end_date - timedelta(days=days-1)
|
||||
|
||||
logger.info(f"Querying recent {days} days registration data from {start_date} to {end_date} for product {product_id}")
|
||||
|
||||
# Get data from service
|
||||
result = registration_service.get_daily_registered_users(
|
||||
start_date, end_date, product_id
|
||||
)
|
||||
|
||||
logger.info(f"Successfully retrieved recent {days} days data, total registrations: {result.total_registrations}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get recent registered users: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/registered-users-by-days", response_model=UserRegistrationResponse)
|
||||
async def get_registered_users_by_days(
|
||||
start_date: date = Query(..., description="Start date in YYYY-MM-DD format"),
|
||||
days: int = Query(..., ge=1, le=365, description="Number of days from start date"),
|
||||
product_id: str = Query("freeleaps", description="Product identifier")
|
||||
):
|
||||
"""
|
||||
Get daily registered users count starting from a specific date for N days
|
||||
|
||||
Returns registration data for N days starting from the specified start date
|
||||
|
||||
Example:
|
||||
- GET /api/metrics/registered-users-by-days?start_date=2024-01-01&days=7
|
||||
- GET /api/metrics/registered-users-by-days?start_date=2024-09-01&days=30&product_id=freeleaps
|
||||
"""
|
||||
try:
|
||||
# Calculate end date
|
||||
end_date = start_date + timedelta(days=days-1)
|
||||
|
||||
logger.info(f"Querying registration data from {start_date} for {days} days (until {end_date}) for product {product_id}")
|
||||
|
||||
# Get data from service
|
||||
result = registration_service.get_daily_registered_users(
|
||||
start_date, end_date, product_id
|
||||
)
|
||||
|
||||
logger.info(f"Successfully retrieved {days} days data from {start_date}, total registrations: {result.total_registrations}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get registered users by days: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/daily-registered-users", response_model=UserRegistrationResponse)
|
||||
async def get_daily_registered_users_post(
|
||||
query: UserRegistrationQuery
|
||||
):
|
||||
"""
|
||||
Get daily registered users count for a date range (POST method)
|
||||
|
||||
Same as GET method but accepts parameters in request body
|
||||
"""
|
||||
try:
|
||||
# Validate date range
|
||||
if query.start_date > query.end_date:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Start date must be before or equal to end date"
|
||||
)
|
||||
|
||||
if (query.end_date - query.start_date).days > 365:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Date range cannot exceed 365 days"
|
||||
)
|
||||
|
||||
logger.info(f"Querying registration data from {query.start_date} to {query.end_date} for product {query.product_id}")
|
||||
|
||||
# Get data from service
|
||||
result = registration_service.get_daily_registered_users(
|
||||
query.start_date, query.end_date, query.product_id
|
||||
)
|
||||
|
||||
logger.info(f"Successfully retrieved data for {len(result.dates)} days")
|
||||
return result
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get daily registered users: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error: {str(e)}"
|
||||
)
|
||||
9
apps/metrics/webapi/routes/starrocks_metrics/__init__.py
Normal file
9
apps/metrics/webapi/routes/starrocks_metrics/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from fastapi import APIRouter
|
||||
from .metrics_query import router as metrics_query_router
|
||||
from .available_metrics import router as available_metrics_router
|
||||
from .metric_info import router as metric_info_router
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(available_metrics_router, tags=["starrocks-metrics"])
|
||||
api_router.include_router(metrics_query_router, tags=["starrocks-metrics"])
|
||||
api_router.include_router(metric_info_router, tags=["starrocks-metrics"])
|
||||
@ -0,0 +1,45 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from common.log.module_logger import ModuleLogger
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Initialize logger
|
||||
module_logger = ModuleLogger(__file__)
|
||||
|
||||
# Product -> supported StarRocks-backed metrics
|
||||
SUPPORTED_STARROCKS_METRICS_MAP = {
|
||||
"freeleaps": [
|
||||
"daily_registered_users",
|
||||
],
|
||||
"magicleaps": [
|
||||
"daily_registered_users",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@router.get("/starrocks/product/{product_id}/available-metrics")
|
||||
async def get_available_metrics(product_id: str):
|
||||
"""
|
||||
Get list of available StarRocks-backed metrics for a specific product.
|
||||
|
||||
Args:
|
||||
product_id: Product ID to get metrics for (required).
|
||||
|
||||
Returns a list of metric names available via StarRocks for the specified product.
|
||||
"""
|
||||
await module_logger.log_info(
|
||||
f"Getting StarRocks available metrics list for product_id: {product_id}"
|
||||
)
|
||||
|
||||
if product_id not in SUPPORTED_STARROCKS_METRICS_MAP:
|
||||
raise HTTPException(status_code=404, detail=f"Unknown product_id: {product_id}")
|
||||
|
||||
metrics = SUPPORTED_STARROCKS_METRICS_MAP[product_id]
|
||||
|
||||
return {
|
||||
"product_id": product_id,
|
||||
"available_metrics": metrics,
|
||||
"total_count": len(metrics),
|
||||
"description": f"List of StarRocks-backed metrics for product '{product_id}'",
|
||||
}
|
||||
53
apps/metrics/webapi/routes/starrocks_metrics/metric_info.py
Normal file
53
apps/metrics/webapi/routes/starrocks_metrics/metric_info.py
Normal file
@ -0,0 +1,53 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from common.log.module_logger import ModuleLogger
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Initialize logger
|
||||
module_logger = ModuleLogger(__file__)
|
||||
|
||||
# Product -> metric -> description
|
||||
STARROCKS_METRIC_DESCRIPTIONS = {
|
||||
"freeleaps": {
|
||||
"daily_registered_users": "Daily registered users count from StarRocks table dws_daily_registered_users",
|
||||
},
|
||||
"magicleaps": {
|
||||
"daily_registered_users": "Daily registered users count from StarRocks table dws_daily_registered_users",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@router.get("/starrocks/product/{product_id}/metric/{metric_name}/info")
|
||||
async def get_metric_info(
|
||||
product_id: str,
|
||||
metric_name: str
|
||||
):
|
||||
"""
|
||||
Get information about a specific StarRocks-backed metric.
|
||||
|
||||
Args:
|
||||
product_id: Product identifier for the product's data.
|
||||
metric_name: Name of the StarRocks-backed metric.
|
||||
"""
|
||||
await module_logger.log_info(
|
||||
f"Getting StarRocks metric info for metric '{metric_name}' from product '{product_id}'"
|
||||
)
|
||||
|
||||
if product_id not in STARROCKS_METRIC_DESCRIPTIONS:
|
||||
raise HTTPException(status_code=404, detail=f"Unknown product_id: {product_id}")
|
||||
|
||||
product_metrics = STARROCKS_METRIC_DESCRIPTIONS[product_id]
|
||||
if metric_name not in product_metrics:
|
||||
raise HTTPException(status_code=404, detail=f"Unknown metric '{metric_name}' for product '{product_id}'")
|
||||
|
||||
metric_info = {
|
||||
"product_id": product_id,
|
||||
"metric_name": metric_name,
|
||||
"description": product_metrics[metric_name],
|
||||
}
|
||||
|
||||
return {
|
||||
"metric_info": metric_info,
|
||||
"description": f"Information about StarRocks metric '{metric_name}' in product '{product_id}'",
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
from fastapi import APIRouter
|
||||
from typing import Optional, List, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import date
|
||||
|
||||
from common.log.module_logger import ModuleLogger
|
||||
from backend.services.registration_analytics_service import RegistrationService
|
||||
|
||||
|
||||
class RegistrationDataPoint(BaseModel):
|
||||
"""Single data point in registration time series."""
|
||||
date: str = Field(..., description="Date in YYYY-MM-DD format")
|
||||
value: int = Field(..., description="Number of registered users")
|
||||
product_id: str = Field(..., description="Product identifier")
|
||||
|
||||
|
||||
class RegistrationTimeSeriesResponse(BaseModel):
|
||||
"""Response model for registration time series data."""
|
||||
metric_name: str = Field(..., description="Name of the queried metric")
|
||||
data_points: List[RegistrationDataPoint] = Field(..., description="List of data points")
|
||||
total_points: int = Field(..., description="Total number of data points")
|
||||
time_range: Dict[str, str] = Field(..., description="Start and end date of the query")
|
||||
total_registrations: int = Field(..., description="Total number of registrations in the period")
|
||||
|
||||
|
||||
class RegistrationQueryRequest(BaseModel):
|
||||
"""Request model for registration query."""
|
||||
product_id: str = Field("freeleaps", description="Product ID to identify which product's data to query")
|
||||
start_date: str = Field(..., description="Start date in YYYY-MM-DD format")
|
||||
end_date: str = Field(..., description="End date in YYYY-MM-DD format")
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Initialize service and logger
|
||||
registration_service = RegistrationService()
|
||||
module_logger = ModuleLogger(__file__)
|
||||
|
||||
|
||||
@router.post("/starrocks/dru_query", response_model=RegistrationTimeSeriesResponse)
|
||||
async def metrics_query(
|
||||
request: RegistrationQueryRequest
|
||||
):
|
||||
"""
|
||||
Query registration time series data.
|
||||
|
||||
Returns XY curve data (time series) for user registrations within the given date range.
|
||||
"""
|
||||
await module_logger.log_info(
|
||||
f"Querying registration data for product '{request.product_id}' from {request.start_date} to {request.end_date}")
|
||||
|
||||
# Parse dates - handle both YYYY-M-D and YYYY-MM-DD formats
|
||||
def parse_date(date_str: str) -> date:
|
||||
try:
|
||||
return date.fromisoformat(date_str)
|
||||
except ValueError:
|
||||
# Try to parse YYYY-M-D format and convert to YYYY-MM-DD
|
||||
parts = date_str.split('-')
|
||||
if len(parts) == 3:
|
||||
year, month, day = parts
|
||||
return date(int(year), int(month), int(day))
|
||||
raise ValueError(f"Invalid date format: {date_str}")
|
||||
|
||||
start_date = parse_date(request.start_date)
|
||||
end_date = parse_date(request.end_date)
|
||||
|
||||
# Query the registration data
|
||||
result = registration_service.get_daily_registered_users(
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
product_id=request.product_id
|
||||
)
|
||||
|
||||
# Format response
|
||||
response = RegistrationTimeSeriesResponse(
|
||||
metric_name="daily_registered_users",
|
||||
data_points=[
|
||||
RegistrationDataPoint(
|
||||
date=date_str,
|
||||
value=count,
|
||||
product_id=request.product_id
|
||||
)
|
||||
for date_str, count in zip(result.dates, result.counts)
|
||||
],
|
||||
total_points=len(result.dates),
|
||||
time_range={
|
||||
"start": request.start_date,
|
||||
"end": request.end_date
|
||||
},
|
||||
total_registrations=result.total_registrations
|
||||
)
|
||||
|
||||
await module_logger.log_info(
|
||||
f"Successfully queried registration data with {len(result.dates)} data points, total registrations: {result.total_registrations}")
|
||||
return response
|
||||
Loading…
Reference in New Issue
Block a user