@wrangled/cli 0.3.152 → 0.3.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ import { renderTable } from "./table.js";
2
+ const COLUMNS = ["ad_id", "ad_name", "meta", "first_touch", "first_delta", "last_touch", "last_delta"];
3
+ const count = (n) => (n === null ? "n/a" : String(n));
4
+ const signed = (n) => (n !== null && n > 0 ? `+${n}` : count(n));
5
+ function counted(report) {
6
+ if (report.event !== null)
7
+ return `Meta's ${report.event} events`;
8
+ if (report.result_types.length === 0)
9
+ return "Meta's results";
10
+ return `Meta's results (${report.result_types.join(", ")})`;
11
+ }
12
+ export function renderAdCalls(report) {
13
+ const header = `Calls per Facebook ad ${report.from} to ${report.to}: ${counted(report)} against booked calls credited to the first and last touch. Deltas are booked minus Meta.`;
14
+ if (report.ads.length === 0)
15
+ return `${header}\nNo ad has calls in either source.\n`;
16
+ const row = (c) => ({ meta: count(c.meta_calls), first_touch: count(c.first_touch), first_delta: signed(c.first_touch_delta), last_touch: count(c.last_touch), last_delta: signed(c.last_touch_delta) });
17
+ const table = renderTable(COLUMNS, [...report.ads.map((a) => ({ ad_id: a.ad_id, ad_name: a.ad_name, ...row(a) })), { ad_id: "total", ...row(report.totals) }]);
18
+ const unconnected = report.booked_calls_connected ? "" : "No booked-call source is connected, so booked calls and deltas are n/a.\n";
19
+ return `${header}\n${table}\n${unconnected}`;
20
+ }
package/dist/guide.js CHANGED
@@ -155,7 +155,8 @@ For "how is the funnel doing?", "which campaigns are working?" and "where does t
155
155
 
156
156
  - \`${cli} funnel [--grain day|week|month]\` prints ad spend, leads, booked calls, calls held, sales and net revenue per day, week (starting Monday) or month, with cost per lead, per booked call and per sale and ROAS, then totals for the whole range.
157
157
  - \`${cli} campaigns [--channel <channel>] [--limit <n>]\` prints each campaign's spend, leads, booked calls, sales and revenue, best first (most first-touch revenue, then most spend), then totals over every matching campaign. Each lead and sale is credited to the person's first touch and last touch side by side. \`--limit\` is 1 to 200 (default 50); \`--channel\` is meta, google, affiliate, a utm_source, unknown or recorded_in_crm.
158
- - \`${cli} calls unattributed [--attribution first_touch|last_touch] [--limit <n>]\` answers "why do these booked calls have no ad?": it counts the booked calls in the range (by day booked), how many were credited to a Facebook ad and how many were not, as \`ad_results_daily\` credits them (to the first touch by default), splits the calls with no ad by why (\`no_person\`: the booking's contact has no email or phone to match a person; \`meta_without_ad\`: a Facebook visit whose link named no single ad; \`not_meta\`: another channel; \`visit_after_call\`: the visits are dated after the booking; \`last_visit_only\`: only a latest visit, no first; \`no_visit\`: no tagged visit at all), then lists those calls newest first with the booking's contact id, the person id and the touch's channel, campaign, ad and landing page. With and without add up to every booked call. \`--limit\` is 1 to 1000 (default 200); the counts always cover every call. The rows are in the \`booked_call_credits\` view; join \`touches\` on \`person_id\` to see each visit's tags.
158
+ - \`${cli} calls unattributed [--attribution first_touch|last_touch] [--limit <n>]\` answers "why do these booked calls have no ad?": it counts the booked calls in the range (by day booked), how many were credited to a Facebook ad and how many were not, as \`ad_results_daily\` credits them (to the first touch by default), splits the calls with no ad by why (\`no_person\`: the booking's contact has no email or phone to match a person; \`meta_without_ad\`: a Facebook visit whose link named no single ad; \`not_meta\`: another channel; \`visit_after_call\`: the visits are dated after the booking; \`other_touch_only\`: only a visit of the other kind, such as a latest visit and no first one when crediting the first touch; \`no_visit\`: no tagged visit at all), then lists those calls newest first with the booking's contact id, the person id and the touch's channel, campaign, ad and landing page. With and without add up to every booked call. \`--limit\` is 1 to 1000 (default 200); the counts always cover every call. The rows are in the \`booked_call_credits\` view; join \`touches\` on \`person_id\` to see each visit's tags.
159
+ - \`${cli} calls compare [--event <event>]\` answers "do Meta's call numbers match the booked calls?": per Facebook ad over the range, the calls Meta reports (its results, the workspace's chosen result type as Ads Manager shows it, or with \`--event\` that one Meta action type, such as schedule_website), the booked calls \`ad_results_daily\` credits to the ad on the first touch and on the last touch, and each delta, booked minus Meta (negative when Meta reports more). An ad missing from one source counts 0 there. Most Meta calls first, then a total row; \`--format json\` gives the same numbers.
159
160
  - \`${cli} money [--grain day|week|month]\` prints money in (gross, refunds, net, orders), ad spend, affiliate payouts, what is left after ad costs and ROAS per period, then totals for the whole range, money in per payment source (stripe, ghl) and the biggest refund days. Refunds come off on the day they were made, not the day of the sale. \`left_after_ad_costs\` is net less ad spend and affiliate payouts; it is not profit. Its \`roas\` is net over ad spend alone, so it differs from \`${cli} funnel\`'s, which divides by all spend including affiliate payouts.
160
161
  - When no payment tool, store or CRM sales are connected but Triple Whale is, \`${cli} money\` takes money in from Triple Whale's store totals and \`${cli} campaigns\` adds the orders, revenue and ROAS Triple Whale credits to each Facebook campaign (\`orders_triple_whale\`, \`revenue_triple_whale\`, \`roas_triple_whale\`), counted as \`${cli} winners\` counts them. Each says so under its table. Triple Whale can credit one order to several ads, so campaign orders can add up to more than the store's. With neither, each report says what is missing and leaves out the columns that would be blank.
161
162
  - Money is never converted or added across currencies: there is one row per currency, and one total per currency. In \`${cli} funnel\`, leads and calls are counted for the whole period and repeat on every currency row, so never add them across its rows. In \`${cli} campaigns\`, each campaign's leads and calls sit on the row of the currency it spends in (or a blank-currency row when it has no spend), so they do add up across rows and totals. Ratios come from the summed numbers, not averages. An empty cell means the source is not connected, or a ratio would divide by 0.
package/dist/main.js CHANGED
@@ -9,6 +9,7 @@ import { api, ApiError, apiDownload, apiText, apiUpload, CliError, exitCodes, ge
9
9
  import { brand, envPrefix } from "./brand.js";
10
10
  import { campaignLayout, money, moneyLayout } from "./report-columns.js";
11
11
  import { renderDataMap, renderQuestions } from "./map.js";
12
+ import { renderAdCalls } from "./ad-calls.js";
12
13
  import { renderUnattributedCalls } from "./unattributed-calls.js";
13
14
  import { apiSetupPath, parseAuthFlag, parseQueryFlags, setupText, submittedText, testText, tryText, waitingText, } from "./api-setup.js";
14
15
  import { answerRow, batches, ENRICH_EMAIL_USAGE, parsePeople, parseSteps } from "./enrich.js";
@@ -593,6 +594,18 @@ const commands = {
593
594
  process.stdout.write(renderUnattributedCalls(unattributedCalls));
594
595
  },
595
596
  },
597
+ "calls compare": {
598
+ usage: "calls compare [--from <YYYY-MM-DD>] [--to <YYYY-MM-DD>] [--event <event>] (calls per Facebook ad: what Meta reports next to the booked calls credited on the first and last touch)",
599
+ async run(args, options) {
600
+ if (args.length > 0)
601
+ throw new CliError("Usage: calls compare [--from <date>] [--to <date>] [--event <event>]", exitCodes.usage);
602
+ const query = reportQuery(options, { event: options.event });
603
+ const { adCalls } = await api(await resolveConnection(options), `/api/v1/reports/ad-calls${query}`);
604
+ if (options.format === "json")
605
+ return printJson({ adCalls });
606
+ process.stdout.write(renderAdCalls(adCalls));
607
+ },
608
+ },
596
609
  "tools list": {
597
610
  usage: "tools list",
598
611
  async run(_args, options) {
@@ -5059,8 +5072,8 @@ Options:
5059
5072
  --where <json | @file.json> tables delete-rows: {column: value} pairs a row must all match to be deleted
5060
5073
  --mode append|replace tables import: add the file's rows (default) or replace every row the table has
5061
5074
  --yes confirm intake delete, import remove, tables drop, content collections remove or remove-item, which delete saved data for good
5062
- --from <YYYY-MM-DD> first day funnel, campaigns, money, calls unattributed, metrics query, brief, pages results and messages results cover (default: 29 days before --to)
5063
- --to <YYYY-MM-DD> last day funnel, campaigns, money, calls unattributed, metrics query, brief, pages results and messages results cover (default: today in the workspace reporting time zone);
5075
+ --from <YYYY-MM-DD> first day funnel, campaigns, money, calls unattributed, calls compare, metrics query, brief, pages results and messages results cover (default: 29 days before --to)
5076
+ --to <YYYY-MM-DD> last day funnel, campaigns, money, calls unattributed, calls compare, metrics query, brief, pages results and messages results cover (default: today in the workspace reporting time zone);
5064
5077
  for outbound push, where the leads go: instantly:<campaign-id>
5065
5078
  --grain <grain> how funnel and money group days: day (default), week (starting Monday) or month
5066
5079
  --month <YYYY-MM> the month tools usage covers, in the workspace reporting time zone (default: this month)
@@ -5135,6 +5148,7 @@ Options:
5135
5148
  --pixel <id> meta audience create --kind website: the pixel that saw the visits
5136
5149
  --url-contains <text> meta audience create --kind website: only visits to pages whose address contains this
5137
5150
  --event <event> meta audience create --kind website: only visits that fired this pixel event, such as Purchase or Lead
5151
+ calls compare: count the Meta action type <event>, such as schedule_website, as Meta's calls instead of its results
5138
5152
  --video <id> meta audience create --kind video: a video watched; repeat for several
5139
5153
  --watched <how much> meta audience create --kind video: 3s, 10s, thruplay, 25%, 50%, 75% or 95%
5140
5154
  --page <id> meta audience create --kind page: the Facebook Page
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrangled/cli",
3
- "version": "0.3.152",
3
+ "version": "0.3.154",
4
4
  "description": "Command-line access to your marketing data warehouse, for people and coding agents",
5
5
  "type": "module",
6
6
  "bin": {