feat(marketing-analytics): add a customer acquisition cost column#73264
Draft
jabahamondes wants to merge 5 commits into
Draft
feat(marketing-analytics): add a customer acquisition cost column#73264jabahamondes wants to merge 5 commits into
jabahamondes wants to merge 5 commits into
Conversation
CAC per channel is the channel's spend over the customers produced by the conversion goals marked counts_as_customer. It mirrors ROAS: same guards (a customer goal must exist and spend must be available), inverse formula. Each customer goal contributes its conversion count as customers, which holds when the goal marks a once-per-person conversion. A repeatable event would overcount and needs a first-time-per-person scan we don't do here.
Contributor
🤖 CI reportℹ️ MCP UI apps size — 31 app(s), 16602.7 KB JSBuilt size of each MCP UI app (
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Top of the chain, on top of ROAS (#73052). This is now the branch that has everything: flags, write endpoints, MCP tools, currency conversion, ROAS, and CAC. Check out
javier/ma-cac-columnto try the full read-side end to end.Problem
The companion to ROAS. Once you can see revenue over spend, the next question is what a customer costs to acquire. Spend per channel is there, and now goals can be marked
counts_as_customer, so the ratio is computable.Changes
A
Cost per customercolumn: the channel's spend divided by the customers produced by the conversion goals markedcounts_as_customer.It's the mirror image of the ROAS column from #73052, and shares the same machinery (
_sum_conversion_values, theinclude_cost_perguard, the same "hidden until a goal is flagged" behavior). Two differences:Cost perprefix, so it falls throughto_marketing_analytics_datato the currency / higher-is-worse formatting the other cost-per columns already get. No new metadata.The assumption, stated plainly
Each customer goal contributes its conversion count as the customer count. That is correct when the goal marks a once-per-person conversion, a sign-up or a first purchase, where counting conversions and counting people are the same thing.
It is wrong if someone points a customer goal at a repeatable event, say a generic
paymentthat fires on every monthly renewal. Then a returning customer's second payment would be counted as a new customer, understating CAC. Getting that right needs each person's first-ever conversion, a historical first-time-per-user scan the marketing precompute can't do today (it only materializes the queried window). That's a real piece of work, and it's deliberately not here.So this ships the version that's correct for the natural case (a goal marking the moment someone becomes a customer) and is honest in code and comment about where it breaks. The column name says "customer", not "new customer", so it doesn't over-promise acquisition semantics it isn't enforcing.
How did you test this code?
3 new unit tests in
test_conversion_goals_aggregator.py, mirroring the ROAS ones, run by me (Claude):Full
test_conversion_goals_aggregator.py,test_marketing_analytics_table_query_runner.pyand_business.pypass (112), snapshots unchanged.ruffclean;mypyclean on the file (the one printed error is the pre-existing unrelatedpersonhog_clientone).No end-to-end ClickHouse test, same reasoning as ROAS: the construction is unit-covered and the propagation is the shared path the cost-per columns already exercise.
Automatic notifications
Docs update
Holding with the rest of the read-side until the dashboard redesign decides how these columns surface.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
I directed this, Claude wrote it. Invoked
/writing-tests.Worth recording the design turn, since it's the interesting part. My first read was that CAC needed a historical first-time-per-user scan and was therefore a large, precompute-touching project, so I nearly deferred it. Javier pushed back: a person can't sign up twice, so why the history? That's right, and it reframed the whole thing. The history only matters for repeatable events; for a once-per-person conversion, counting conversions already counts customers. So CAC collapsed to the same shape as ROAS. This PR takes that simpler, correct-for-the-common-case path and documents exactly where the harder version would be needed, rather than building the heavy scan speculatively.