docs(campaigns): document insights storage and analytics

Covers the three things most likely to be broken by a well-meant change:
insights are re-fetched and upserted rather than appended, breakdown rows
duplicate the spend they break down and must be filtered out of
aggregates, and both dashboard windows end yesterday because today is
partial.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
AFFAANh
2026-08-03 18:17:25 +05:30
co-authored by Claude Opus 5
parent e0cec074ff
commit 53e4a64b22
+40
View File
@@ -234,6 +234,46 @@ raising a budget on a campaign that is awaiting approval returns `409`,
because it would change the figure the approver is reviewing. Reject it back
to draft, edit, and resubmit.
### Analytics
Insights are stored in `maskanx_campaign_insights` and read from there, so
the dashboard stays fast and keeps working while Meta is rate-limiting. Only
`POST /campaigns/{id}/insights/refresh` goes out to Meta.
| Route | Returns |
| --- | --- |
| `GET /campaigns/analytics/dashboard` | Yesterday and last-7-days spend, leads and cost per lead, plus the most and least efficient campaign |
| `GET /campaigns/{id}/insights` | A daily series and the total for one campaign |
| `GET /campaigns/{id}/insights?breakdown=age` | The same metrics split by one breakdown |
| `POST /campaigns/{id}/insights/refresh` | Re-fetches from Meta now |
Three things about how this works are worth knowing before changing it:
**Insights are re-fetched, not appended.** Meta revises a day's figures for
about 28 days as conversions are attributed late, so each sync re-reads a
trailing window and upserts on
`(level, object_id, date, breakdown_key, breakdown_value)`. Appending would
double spend the first time a day was refreshed. Window:
`MASKANX_INSIGHTS_WINDOW_DAYS`, default 28 — shortening it freezes late
conversions out permanently.
**Breakdowns multiply rows, not columns.** The same spend appears in every
breakdown's rows, so they are tagged with a `breakdown_key` and every
aggregate filters to `breakdown_key = ''`. A query that summed across
breakdowns would count the same spend once per age bracket.
**Both dashboard windows end yesterday.** Today's figures are partial;
including them would make every "vs. last week" comparison read as a
collapse each morning.
Costs are always computed from summed totals, never averaged from per-day
costs — the average of daily costs per lead is not the cost per lead over the
period. Cost fields are `null`, not `0`, when there is nothing to divide by.
Sync interval: `MASKANX_INSIGHTS_SYNC_SECONDS`, default 3600. `0` disables
the loop; the dashboard then only updates when a campaign is refreshed by
hand.
### Live smoke test
The unit suite runs entirely against a fake transport: it proves the code