Compare commits
2 Commits
cfd064aad5
...
3fa980583d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fa980583d | ||
|
|
01825190a3 |
@ -56,9 +56,9 @@ _None. Plugin uses only the public API._ ✓
|
|||||||
| `static/bte.js` | 360 | `/api/query/assetDtos` |
|
| `static/bte.js` | 360 | `/api/query/assetDtos` |
|
||||||
| `static/bte.js` | 372 | `/api/assets/` |
|
| `static/bte.js` | 372 | `/api/assets/` |
|
||||||
| `static/bte.js` | 481 | `/api/command/rateAsset` |
|
| `static/bte.js` | 481 | `/api/command/rateAsset` |
|
||||||
| `static/adwright.js` | 175 | `/api/profile/switch` |
|
| `static/adwright.js` | 176 | `/api/profile/switch` |
|
||||||
| `static/adwright.js` | 196 | `/api/profile/active` |
|
| `static/adwright.js` | 197 | `/api/profile/active` |
|
||||||
| `static/adwright.js` | 583 | `/api/adwright/provision-creds` |
|
| `static/adwright.js` | 606 | `/api/adwright/provision-creds` |
|
||||||
| `static/umbrella.js` | 41 | `/api/umbrella` |
|
| `static/umbrella.js` | 41 | `/api/umbrella` |
|
||||||
| `static/app.js` | 165 | `/api/vault/status` |
|
| `static/app.js` | 165 | `/api/vault/status` |
|
||||||
|
|
||||||
|
|||||||
@ -162,6 +162,9 @@ def _real_payload_for(tool: str, qs: dict) -> dict | None:
|
|||||||
raw = core.list_recipes_tool(limit=limit)
|
raw = core.list_recipes_tool(limit=limit)
|
||||||
elif tool == "adwright_get_connections_status":
|
elif tool == "adwright_get_connections_status":
|
||||||
raw = core.get_connections_status_tool()
|
raw = core.get_connections_status_tool()
|
||||||
|
elif tool == "adwright_get_cycle_metrics":
|
||||||
|
cycle_id = int((qs.get("cycle_id", ["0"])[0] or "0"))
|
||||||
|
raw = core.get_cycle_metrics_tool(cycle_id=cycle_id)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
return json.loads(raw) if isinstance(raw, str) else raw
|
return json.loads(raw) if isinstance(raw, str) else raw
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
data: {
|
data: {
|
||||||
cycles: null,
|
cycles: null,
|
||||||
cycleDetail: {},
|
cycleDetail: {},
|
||||||
|
metricsByCycle: {}, // {<cycle_id>: [Metric, ...]} — populated via fan-out
|
||||||
segments: null,
|
segments: null,
|
||||||
recipes: null,
|
recipes: null,
|
||||||
connections: null,
|
connections: null,
|
||||||
@ -349,22 +350,44 @@
|
|||||||
function _deriveKpis(cycles, recipes) {
|
function _deriveKpis(cycles, recipes) {
|
||||||
const c = cycles || [];
|
const c = cycles || [];
|
||||||
const r = recipes || [];
|
const r = recipes || [];
|
||||||
const spend = c.reduce((s, x) => s + _cycleSpend(x), 0);
|
// Metrics live in cycle_metric (GetCycleMetrics), fanned out per cycle
|
||||||
|
// into state.data.metricsByCycle. Flatten across all loaded cycles for
|
||||||
|
// the Overview totals. Stays 0 until ads dispatch + Meta polling fills.
|
||||||
|
const mby = NS.state.data.metricsByCycle || {};
|
||||||
|
const allMetrics = c.flatMap((x) => mby[x.id] || []);
|
||||||
|
const impressions = allMetrics.reduce((s, m) => s + (m.impressions || 0), 0);
|
||||||
|
const clicks = allMetrics.reduce((s, m) => s + (m.clicks || 0), 0);
|
||||||
|
const spend = allMetrics.reduce((s, m) => s + (parseFloat(m.spend) || 0), 0);
|
||||||
const budget = c.reduce((s, x) => s + _cycleBudget(x), 0) || 6000;
|
const budget = c.reduce((s, x) => s + _cycleBudget(x), 0) || 6000;
|
||||||
const impressions = c.reduce((s, x) => s + (x.impressions || 0), 0);
|
const ctr = impressions ? (clicks / impressions * 100) : 0;
|
||||||
const ctrs = c.map((x) => x.ctr || 0).filter((v) => v > 0);
|
|
||||||
const ctr = ctrs.length ? (ctrs.reduce((s, x) => s + x, 0) / ctrs.length) : 0;
|
|
||||||
return {
|
return {
|
||||||
impressions: impressions ? _abbrev(impressions) : "0",
|
impressions: impressions ? _abbrev(impressions) : "0",
|
||||||
imprDelta: impressions ? "+12%" : "",
|
imprDelta: "", // real delta needs a previous-window snapshot
|
||||||
ctr: ctr ? (ctr.toFixed(2) + "%") : "0%",
|
ctr: ctr ? (ctr.toFixed(2) + "%") : "0%",
|
||||||
ctrDelta: ctr ? "-0.3%" : "",
|
ctrDelta: "", // same — drop fake delta until backend supplies
|
||||||
cycleCount: String(c.length),
|
cycleCount: String(c.length),
|
||||||
recipeCount: String(r.length),
|
recipeCount: String(r.length),
|
||||||
spend: spend,
|
spend: spend,
|
||||||
budget: budget,
|
budget: budget,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// Fan-out: fetch metrics for one cycle straight from the panel-update
|
||||||
|
// endpoint (no CMO chat needed — route calls gRPC live per request).
|
||||||
|
function _fetchCycleMetrics(cycleId) {
|
||||||
|
if (!cycleId && cycleId !== 0) return;
|
||||||
|
const url = "/api/adwright/last-panel-update?session_id=" +
|
||||||
|
encodeURIComponent(_activeSessionId()) +
|
||||||
|
"&since=0&tool=adwright_get_cycle_metrics&cycle_id=" +
|
||||||
|
encodeURIComponent(cycleId);
|
||||||
|
fetch(url)
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((r) => {
|
||||||
|
if (!r || !r.update || !r.update.payload) return;
|
||||||
|
NS.state.data.metricsByCycle[cycleId] = r.update.payload.metrics || [];
|
||||||
|
if (NS.state.activeTab === "overview") _renderTab("overview");
|
||||||
|
})
|
||||||
|
.catch(() => { /* best-effort */ });
|
||||||
|
}
|
||||||
|
|
||||||
// Cycles — list with click-to-expand ───────────────────────────────────
|
// Cycles — list with click-to-expand ───────────────────────────────────
|
||||||
function _renderCycles() {
|
function _renderCycles() {
|
||||||
@ -765,6 +788,17 @@
|
|||||||
const payload = u.payload || {};
|
const payload = u.payload || {};
|
||||||
if (tool === "adwright_refresh_cycles" || tool === "adwright_list_cycles") {
|
if (tool === "adwright_refresh_cycles" || tool === "adwright_list_cycles") {
|
||||||
NS.state.data.cycles = payload.cycles || [];
|
NS.state.data.cycles = payload.cycles || [];
|
||||||
|
// Fan out metrics fetch for each cycle so Overview KPIs (impressions /
|
||||||
|
// CTR / spend) come from cycle_metric, not from cycle headers (which
|
||||||
|
// don't carry them).
|
||||||
|
(payload.cycles || []).forEach((c) => {
|
||||||
|
if (c && (c.id != null)) _fetchCycleMetrics(c.id);
|
||||||
|
});
|
||||||
|
} else if (tool === "adwright_get_cycle_metrics") {
|
||||||
|
// Direct fetch (no chat) — payload has {metrics:[...]} but no cycle_id;
|
||||||
|
// the fetch URL carried cycle_id, so this branch is mostly for the rare
|
||||||
|
// case the polling channel sees a metrics update first. Skip — handled
|
||||||
|
// by _fetchCycleMetrics promise resolution.
|
||||||
} else if (tool === "adwright_get_cycle") {
|
} else if (tool === "adwright_get_cycle") {
|
||||||
if (payload.id != null) NS.state.data.cycleDetail[payload.id] = payload;
|
if (payload.id != null) NS.state.data.cycleDetail[payload.id] = payload;
|
||||||
} else if (tool === "adwright_list_segments") {
|
} else if (tool === "adwright_list_segments") {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user