rozsireni o keywords a uprava swaggeru

This commit is contained in:
JiriUhlir
2026-07-20 09:02:42 +02:00
parent 0ed3e11a4d
commit 71eeb51847
7 changed files with 608 additions and 62 deletions
+45 -19
View File
@@ -45,6 +45,7 @@ _MONEY_FIELDS: dict[str, dict[str, tuple[int, str]]] = {
"cpt": (config.SKLIK_MAX_CPC_HALERS, "max CPT"),
},
"ads": {},
"keywords": {"cpc": (config.SKLIK_MAX_CPC_HALERS, "max CPC")},
}
#: Fields Sklik requires on create, checked here so the caller gets a clear
@@ -53,8 +54,19 @@ _REQUIRED_FIELDS: dict[str, tuple[str, ...]] = {
"campaigns": ("name", "dayBudget", "type"),
"groups": ("campaignId", "name", "cpc"),
"ads": ("groupId",),
"keywords": ("name", "groupId"),
}
#: Entities whose status is forced to "suspend" on create.
#:
#: Keywords are deliberately NOT here. Spending is gated by the campaign, group
#: and ad above them, all of which this proxy creates paused - a keyword cannot
#: spend anything on its own. Forcing keywords paused as well would only create
#: a trap: you activate the campaign in the Sklik UI, nothing happens, and the
#: reason is a fourth paused level nobody expected. Callers may still send
#: status explicitly.
_FORCE_PAUSED_ON_CREATE = ("campaigns", "groups", "ads")
def _halers_to_czk(value: int) -> str:
return f"{value / 100:.2f} Kc"
@@ -117,31 +129,44 @@ def prepare_for_create(entity: str, items: list[Any]) -> list[dict]:
clean = dict(item)
# Rule 1: always paused, never negotiable.
requested = clean.get("status")
if requested is not None and requested != PAUSED_STATUS:
# Not an error - we simply override it - but it must be visible.
logger.warning(
"Item %d requested status=%r for %s; forcing %r. This proxy "
"cannot create active entities.",
index,
requested,
entity,
PAUSED_STATUS,
)
clean["status"] = PAUSED_STATUS
# Rule 1: campaigns/groups/ads are always created paused.
if entity in _FORCE_PAUSED_ON_CREATE:
requested = clean.get("status")
if requested is not None and requested != PAUSED_STATUS:
# Not an error - we simply override it - but it must be visible.
logger.warning(
"Item %d requested status=%r for %s; forcing %r. This proxy "
"cannot create active entities.",
index,
requested,
entity,
PAUSED_STATUS,
)
clean["status"] = PAUSED_STATUS
else:
status = clean.get("status")
if status is not None and status not in _VALID_STATUSES:
raise UpstreamError(
f"Item {index}: status must be one of "
+ ", ".join(_VALID_STATUSES)
+ f" (got {status!r}).",
status=400,
)
# Rule 2: money ceilings - only where one is configured (0 = disabled).
_check_money(entity, index, clean)
prepared.append(clean)
logger.info(
"Prepared %d %s for creation (all forced to status=%s).",
len(prepared),
entity,
PAUSED_STATUS,
)
if entity in _FORCE_PAUSED_ON_CREATE:
logger.info(
"Prepared %d %s for creation (all forced to status=%s).",
len(prepared),
entity,
PAUSED_STATUS,
)
else:
logger.info("Prepared %d %s for creation.", len(prepared), entity)
return prepared
@@ -248,6 +273,7 @@ def budget_limits() -> dict[str, Any]:
)
),
"createdStatus": PAUSED_STATUS,
"createdPausedEntities": list(_FORCE_PAUSED_ON_CREATE),
"activationBlocked": config.SKLIK_BLOCK_ACTIVATION,
"note": (
"Everything created through this proxy is paused. null ceilings "