Merge pull request '1d and 1m' (#68) from feature/wc into dev

Reviewed-on: freeleaps/freeleaps-service-hub#68
This commit is contained in:
icecheng 2025-09-23 13:55:45 +00:00
commit bae09037a3
2 changed files with 100 additions and 43 deletions

View File

@ -92,6 +92,7 @@ class StarRocksMetricsService:
self, self,
product_id: str, product_id: str,
metric_name: str, metric_name: str,
step: str,
start_date: Union[str, date], start_date: Union[str, date],
end_date: Union[str, date] end_date: Union[str, date]
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
@ -162,6 +163,14 @@ class StarRocksMetricsService:
else: else:
end_dt = end_date end_dt = end_date
# Normalize and validate step (default '1d')
step = step or '1d'
if step not in {"1d", "1m"}:
raise HTTPException(
status_code=400,
detail="Invalid step. Supported values are '1d' and '1m'"
)
# Validate date range # Validate date range
if start_dt >= end_dt: if start_dt >= end_dt:
raise HTTPException( raise HTTPException(
@ -191,7 +200,14 @@ class StarRocksMetricsService:
) )
# Parse the result and format it # Parse the result and format it
formatted_data = self._format_query_result(result, metric_name, product_id, start_dt, end_dt) formatted_data = self._format_query_result(
starrocks_result=result,
metric_name=metric_name,
product_id=product_id,
step=step,
start_date=start_dt,
end_date=end_dt
)
await self.module_logger.log_info( await self.module_logger.log_info(
f"Successfully queried metric '{metric_name}' with {len(formatted_data)} data points") f"Successfully queried metric '{metric_name}' with {len(formatted_data)} data points")
@ -201,7 +217,7 @@ class StarRocksMetricsService:
await self.module_logger.log_error(f"Failed to query metric '{metric_name}': {e}") await self.module_logger.log_error(f"Failed to query metric '{metric_name}': {e}")
raise raise
def _format_query_result(self, starrocks_result: List[Dict[str, Any]], metric_name: str, product_id: str, start_date: datetime, end_date: datetime) -> List[Dict[str, Any]]: def _format_query_result(self, starrocks_result: List[Dict[str, Any]], metric_name: str, product_id: str, step: str, start_date: datetime, end_date: datetime) -> List[Dict[str, Any]]:
""" """
Format StarRocks query result into the required format and fill missing dates with 0 values. Format StarRocks query result into the required format and fill missing dates with 0 values.
@ -219,25 +235,42 @@ class StarRocksMetricsService:
result_dict = {} result_dict = {}
for row in starrocks_result: for row in starrocks_result:
# Format the date # Normalize the date according to step granularity
date_value = row.get("date") date_value = row.get("date")
if date_value: if not date_value:
if isinstance(date_value, str):
date_str = date_value
else:
# If it's a datetime object, format it as a string
if hasattr(date_value, 'strftime'):
# Convert to date first, then format consistently
if hasattr(date_value, 'date'):
date_obj = date_value.date() if hasattr(date_value, 'date') else date_value
else:
date_obj = date_value
date_str = date_obj.strftime('%Y-%m-%d') + ' 00:00:00'
else:
date_str = str(date_value)
else:
continue continue
def month_start(d: datetime) -> datetime:
return datetime(d.year, d.month, 1)
# Parse and normalize
if isinstance(date_value, str):
parsed_dt = None
for fmt in ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d'):
try:
parsed_dt = datetime.strptime(date_value, fmt)
break
except ValueError:
continue
if parsed_dt is None:
date_str = str(date_value)
else:
if step == '1m':
date_str = month_start(parsed_dt).strftime('%Y-%m-01 00:00:00')
else:
date_str = parsed_dt.strftime('%Y-%m-%d') + ' 00:00:00'
else:
if hasattr(date_value, 'strftime'):
dt_obj = date_value
if step == '1m':
date_str = month_start(dt_obj).strftime('%Y-%m-01 00:00:00')
else:
if hasattr(dt_obj, 'date'):
dt_obj = dt_obj.date()
date_str = dt_obj.strftime('%Y-%m-%d') + ' 00:00:00'
else:
date_str = str(date_value)
# Get the value # Get the value
value = row.get("value", 0) value = row.get("value", 0)
if value is None: if value is None:
@ -256,32 +289,54 @@ class StarRocksMetricsService:
"labels": labels "labels": labels
} }
# Generate complete date range and fill missing dates with 0 # Generate complete range and fill missing points with 0
formatted_data = [] formatted_data = []
current_date = start_date.date() if step == '1d':
end_date_only = end_date.date() current_dt = datetime(start_date.year, start_date.month, start_date.day)
end_dt_exclusive = datetime(end_date.year, end_date.month, end_date.day)
while current_date < end_date_only: while current_dt < end_dt_exclusive:
date_str = current_date.strftime('%Y-%m-%d') + ' 00:00:00' date_str = current_dt.strftime('%Y-%m-%d') + ' 00:00:00'
if date_str in result_dict:
if date_str in result_dict: formatted_data.append(result_dict[date_str])
# Use existing data else:
formatted_data.append(result_dict[date_str]) labels = {
else: "product_id": product_id,
# Fill missing date with 0 value "metric_type": metric_name
labels = { }
"product_id": product_id, formatted_data.append({
"metric_type": metric_name "date": date_str,
} "value": 0,
"metric": metric_name,
formatted_data.append({ "labels": labels
"date": date_str, })
"value": 0, current_dt += timedelta(days=1)
"metric": metric_name, elif step == '1m':
"labels": labels def month_start(d: datetime) -> datetime:
}) return datetime(d.year, d.month, 1)
current_date += timedelta(days=1) def add_one_month(d: datetime) -> datetime:
year = d.year + (1 if d.month == 12 else 0)
month = 1 if d.month == 12 else d.month + 1
return datetime(year, month, 1)
current_dt = month_start(start_date)
end_month_exclusive = month_start(end_date)
while current_dt < end_month_exclusive:
date_str = current_dt.strftime('%Y-%m-01 00:00:00')
if date_str in result_dict:
formatted_data.append(result_dict[date_str])
else:
labels = {
"product_id": product_id,
"metric_type": metric_name
}
formatted_data.append({
"date": date_str,
"value": 0,
"metric": metric_name,
"labels": labels
})
current_dt = add_one_month(current_dt)
return formatted_data return formatted_data

View File

@ -26,6 +26,7 @@ class MetricQueryRequest(BaseModel):
"""Request model for metric query.""" """Request model for metric query."""
product_id: str = Field(..., description="Product ID to identify which product's data to query") product_id: str = Field(..., description="Product ID to identify which product's data to query")
metric_name: str = Field(..., description="Name of the metric to query") metric_name: str = Field(..., description="Name of the metric to query")
step: str = Field(..., description="Aggregation step, e.g., 1d or 1m")
start_date: str = Field(..., description="Start date in YYYY-MM-DD HH:MM:SS format") start_date: str = Field(..., description="Start date in YYYY-MM-DD HH:MM:SS format")
end_date: str = Field(..., description="End date in YYYY-MM-DD HH:MM:SS format") end_date: str = Field(..., description="End date in YYYY-MM-DD HH:MM:SS format")
@ -53,6 +54,7 @@ async def metrics_query(
data_points = await starrocks_service.query_metric_by_time_range( data_points = await starrocks_service.query_metric_by_time_range(
product_id=request.product_id, product_id=request.product_id,
metric_name=request.metric_name, metric_name=request.metric_name,
step=request.step,
start_date=request.start_date, start_date=request.start_date,
end_date=request.end_date end_date=request.end_date
) )