New alert system and panel de control page
This commit is contained in:
@@ -16,6 +16,12 @@ from app.models.orchestration_run import OrchestrationRun
|
||||
import uuid
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add shared utilities to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
||||
from shared.utils.demo_dates import adjust_date_for_demo, BASE_REFERENCE_DATE
|
||||
|
||||
router = APIRouter()
|
||||
logger = structlog.get_logger()
|
||||
@@ -24,6 +30,27 @@ logger = structlog.get_logger()
|
||||
INTERNAL_API_KEY = os.getenv("INTERNAL_API_KEY", "dev-internal-key-change-in-production")
|
||||
|
||||
|
||||
async def ensure_unique_run_number(db: AsyncSession, base_run_number: str) -> str:
|
||||
"""Ensure the run number is unique by appending a suffix if needed"""
|
||||
proposed_run_number = base_run_number
|
||||
|
||||
# Check if the proposed run number already exists in the database
|
||||
while True:
|
||||
result = await db.execute(
|
||||
select(OrchestrationRun)
|
||||
.where(OrchestrationRun.run_number == proposed_run_number)
|
||||
)
|
||||
existing_run = result.scalar_one_or_none()
|
||||
|
||||
if not existing_run:
|
||||
# Run number is unique, return it
|
||||
return proposed_run_number
|
||||
|
||||
# Generate a new run number with an additional random suffix
|
||||
random_suffix = str(uuid.uuid4())[:4].upper()
|
||||
proposed_run_number = f"{base_run_number[:50-len(random_suffix)-1]}-{random_suffix}"
|
||||
|
||||
|
||||
def verify_internal_api_key(x_internal_api_key: str = Header(...)):
|
||||
"""Verify internal API key for service-to-service communication"""
|
||||
if x_internal_api_key != INTERNAL_API_KEY:
|
||||
@@ -86,38 +113,60 @@ async def clone_demo_data(
|
||||
|
||||
# Clone each orchestration run with date adjustment
|
||||
for base_run in base_runs:
|
||||
# Calculate time offset: how old was this run relative to when it was created
|
||||
# We'll adjust all timestamps to be relative to the session creation time
|
||||
# Use the shared date adjustment utility to ensure dates are always in the past
|
||||
# This calculates the offset from BASE_REFERENCE_DATE and applies it to session creation time
|
||||
if base_run.started_at:
|
||||
# Calculate how many days ago this run was from a reference point
|
||||
# Use a fixed reference date for consistency
|
||||
reference_date = datetime(2025, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
|
||||
time_offset = base_run.started_at - reference_date
|
||||
|
||||
# Apply this offset to the current reference time
|
||||
new_started_at = reference_time + time_offset
|
||||
new_started_at = adjust_date_for_demo(
|
||||
base_run.started_at, reference_time, BASE_REFERENCE_DATE
|
||||
)
|
||||
else:
|
||||
new_started_at = reference_time - timedelta(hours=2)
|
||||
|
||||
# Adjust completed_at if it exists
|
||||
if base_run.completed_at and base_run.started_at:
|
||||
duration = base_run.completed_at - base_run.started_at
|
||||
new_completed_at = new_started_at + duration
|
||||
# Adjust completed_at using the same utility
|
||||
if base_run.completed_at:
|
||||
new_completed_at = adjust_date_for_demo(
|
||||
base_run.completed_at, reference_time, BASE_REFERENCE_DATE
|
||||
)
|
||||
# Ensure completion is after start (in case of edge cases)
|
||||
if new_completed_at and new_started_at and new_completed_at < new_started_at:
|
||||
# Preserve original duration
|
||||
duration = base_run.completed_at - base_run.started_at
|
||||
new_completed_at = new_started_at + duration
|
||||
else:
|
||||
new_completed_at = None
|
||||
|
||||
# Adjust all step timestamps proportionally
|
||||
# Adjust all step timestamps using the shared utility
|
||||
def adjust_timestamp(original_timestamp):
|
||||
if not original_timestamp or not base_run.started_at:
|
||||
if not original_timestamp:
|
||||
return None
|
||||
step_offset = original_timestamp - base_run.started_at
|
||||
return new_started_at + step_offset
|
||||
return adjust_date_for_demo(original_timestamp, reference_time, BASE_REFERENCE_DATE)
|
||||
|
||||
# Create new orchestration run for virtual tenant
|
||||
# Update run_number to have current year instead of original year, and make it unique
|
||||
current_year = reference_time.year
|
||||
# Extract type from original run number and create new format
|
||||
parts = base_run.run_number.split('-')
|
||||
if len(parts) >= 4:
|
||||
tenant_prefix = parts[1] if len(parts) > 1 else "DEMO"
|
||||
type_code = parts[2] if len(parts) > 2 else "TST"
|
||||
original_index = parts[3] if len(parts) > 3 else "001"
|
||||
|
||||
# Generate a more robust unique suffix to avoid collisions
|
||||
# Use UUID instead of just session_id substring to ensure uniqueness
|
||||
unique_suffix = str(uuid.uuid4())[:8].upper()
|
||||
proposed_run_number = f"ORCH-{tenant_prefix}-{type_code}-{current_year}-{original_index}-{unique_suffix}"
|
||||
else:
|
||||
unique_suffix = str(uuid.uuid4())[:12].upper()
|
||||
proposed_run_number = f"{base_run.run_number}-{unique_suffix}"
|
||||
|
||||
# Ensure the run number is truly unique by checking against existing entries
|
||||
# This prevents collisions especially in high-concurrency scenarios
|
||||
run_number = await ensure_unique_run_number(db, proposed_run_number)
|
||||
|
||||
new_run = OrchestrationRun(
|
||||
id=uuid.uuid4(),
|
||||
tenant_id=virtual_uuid,
|
||||
run_number=f"{base_run.run_number}-DEMO",
|
||||
run_number=run_number,
|
||||
status=base_run.status,
|
||||
run_type=base_run.run_type,
|
||||
priority=base_run.priority,
|
||||
|
||||
Reference in New Issue
Block a user