174 lines
7.5 KiB
Python
174 lines
7.5 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
from typing import Annotated, Dict, List
|
|
import httpx
|
|
from loguru import logger
|
|
|
|
from app.core.config import settings
|
|
from app.api.v1.schemas.counter import CounterListResponse, Counter, CounterCreateRequest, CounterCreateResponse
|
|
from app.api.v1.schemas.goal import GoalCreateRequest, GoalCreateResponse, Goal
|
|
|
|
router = APIRouter()
|
|
bearer_scheme = HTTPBearer()
|
|
|
|
# ФИНАЛЬНАЯ ВЕРСИЯ: Ключи используют правильное написание "Marquiz" (в нижнем регистре для сравнения).
|
|
PREDEFINED_GOALS: Dict[str, str] = {
|
|
"marquiz-start": "Посетитель открыл квиз",
|
|
"marquiz-startquiz": "Посетитель нажал на кнопку стартовой страницы",
|
|
"marquiz-form": "Посетитель дошёл до формы контактов",
|
|
"marquiz-result": "Посетитель увидел результат",
|
|
"marquiz-contacts1": "Посетитель заполнил и отправил форму контактов",
|
|
}
|
|
|
|
# --- КОД ЭНДПОИНТОВ get_counters и create_counter ОСТАЕТСЯ БЕЗ ИЗМЕНЕНИЙ ---
|
|
@router.get("/", response_model=CounterListResponse, summary="Получение списка счетчиков")
|
|
async def get_counters(credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]):
|
|
token = credentials.credentials
|
|
logger.info("Fetching list of counters from Yandex.Metrika API.")
|
|
|
|
url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counters"
|
|
headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'}
|
|
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(url, headers=headers)
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
counters_list = [
|
|
Counter(id=c['id'], name=c['name'], site=c['site'])
|
|
for c in data.get('counters', [])
|
|
]
|
|
|
|
logger.success(f"Successfully fetched {len(counters_list)} counters.")
|
|
return CounterListResponse(counters=counters_list)
|
|
|
|
except httpx.HTTPStatusError as e:
|
|
error_details = e.response.json()
|
|
logger.error(f"Yandex Metrika API error: {e.response.status_code} - {error_details}")
|
|
raise HTTPException(
|
|
status_code=e.response.status_code,
|
|
detail=f"Yandex API Error: {error_details.get('message', 'Unknown error')}"
|
|
)
|
|
except Exception as e:
|
|
logger.opt(exception=True).error("An unexpected error occurred while fetching counters.")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="An internal server error occurred."
|
|
)
|
|
|
|
@router.post(
|
|
"/",
|
|
response_model=CounterCreateResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
summary="Создание нового счетчика"
|
|
)
|
|
async def create_counter(
|
|
request_body: CounterCreateRequest,
|
|
credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]
|
|
):
|
|
token = credentials.credentials
|
|
site_url = str(request_body.site_url).strip('/')
|
|
site_name = site_url.replace("https://", "").replace("http://", "")
|
|
|
|
logger.info(f"Attempting to create a new counter for site: {site_name}")
|
|
|
|
url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counters"
|
|
headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'}
|
|
payload = {
|
|
"counter": {
|
|
"name": site_name,
|
|
"site": site_name
|
|
}
|
|
}
|
|
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.post(url, headers=headers, json=payload)
|
|
response.raise_for_status()
|
|
|
|
created_counter_data = response.json().get("counter", {})
|
|
logger.success(f"Successfully created counter ID: {created_counter_data.get('id')}")
|
|
|
|
return CounterCreateResponse(
|
|
id=created_counter_data.get('id'),
|
|
name=created_counter_data.get('name'),
|
|
code_status=created_counter_data.get('code_status')
|
|
)
|
|
|
|
except httpx.HTTPStatusError as e:
|
|
error_details = e.response.json()
|
|
logger.error(f"Yandex Metrika API error during counter creation: {e.response.status_code} - {error_details}")
|
|
raise HTTPException(
|
|
status_code=e.response.status_code,
|
|
detail=f"Yandex API Error: {error_details.get('message', 'Unknown error')}"
|
|
)
|
|
except Exception as e:
|
|
logger.opt(exception=True).error("An unexpected error occurred during counter creation.")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="An internal server error occurred."
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/{counter_id}/goals",
|
|
response_model=GoalCreateResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
summary="Создание стандартного набора целей в счетчике"
|
|
)
|
|
async def create_goals_for_counter(
|
|
counter_id: int,
|
|
request_body: GoalCreateRequest,
|
|
credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]
|
|
):
|
|
token = credentials.credentials
|
|
logger.info(f"Attempting to create goals for counter ID: {counter_id}")
|
|
|
|
url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counter/{counter_id}/goals"
|
|
headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'}
|
|
|
|
created_goals_list: List[Goal] = []
|
|
|
|
async with httpx.AsyncClient() as client:
|
|
for identifier in request_body.goal_identifiers:
|
|
# Приводим входящий идентификатор к нижнему регистру для надежного поиска
|
|
goal_name = PREDEFINED_GOALS.get(identifier.lower())
|
|
|
|
if not goal_name:
|
|
logger.warning(f"Unknown goal identifier '{identifier}' skipped.")
|
|
continue
|
|
|
|
goal_payload = {
|
|
"name": goal_name,
|
|
"type": "action",
|
|
"conditions": [{"type": "exact", "url": identifier}]
|
|
}
|
|
payload = {"goal": goal_payload}
|
|
|
|
logger.debug(f"Sending payload for goal '{goal_name}': {payload}")
|
|
|
|
try:
|
|
response = await client.post(url, headers=headers, json=payload)
|
|
response.raise_for_status()
|
|
|
|
created_goal_data = response.json().get("goal", {})
|
|
if created_goal_data:
|
|
logger.success(f"Successfully created goal: {created_goal_data.get('name')} (ID: {created_goal_data.get('id')})")
|
|
created_goals_list.append(
|
|
Goal(id=created_goal_data['id'], name=created_goal_data['name'])
|
|
)
|
|
except httpx.HTTPStatusError as e:
|
|
error_details = e.response.json()
|
|
logger.error(f"Failed to create goal '{goal_name}'. Reason: {error_details.get('message', 'Unknown error')}")
|
|
except Exception:
|
|
logger.opt(exception=True).error(f"An unexpected error occurred while creating goal '{goal_name}'.")
|
|
|
|
if not created_goals_list:
|
|
logger.warning("No goals were created.")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Could not create any of the requested goals. Check logs for details."
|
|
)
|
|
|
|
return GoalCreateResponse(created_goals=created_goals_list) |