From b0933e17300e0f8f0b2d6f14bede850bccae0c77 Mon Sep 17 00:00:00 2001 From: Uku Taht Date: Thu, 14 Nov 2024 20:22:14 +0200 Subject: [PATCH] Import acquisiton channel from GA4 (#4814) * Import acquisiton channel from GA4 * Remove unnecessary step * Fix spelling * Remove migration * Show empty channel in imported data as (not set) * Revert "Remove migration" This reverts commit da0b9403e49b6dc233e1c637421601fd98663af2. * Fix channel suggestions with imported data * Merge group field entries * Add note about channel mappings * Revert "Revert "Remove migration"" This reverts commit 7958a46c5c96339f4d83f8274a2e80033317c087. --- fixture/ga4_report_imported_sources.json | 635 +++++++++++++++++- lib/plausible/google/ga4/report_request.ex | 1 + lib/plausible/imported/google_analytics4.ex | 2 + lib/plausible/imported/source.ex | 1 + lib/plausible/stats/imported/base.ex | 1 + lib/plausible/stats/imported/imported.ex | 1 + .../stats/imported/sql/expression.ex | 2 +- .../api/stats_controller/imported_test.exs | 185 ++++- .../api/stats_controller/suggestions_test.exs | 25 + .../controllers/stats_controller_test.exs | 3 +- test/support/factory.ex | 16 +- 11 files changed, 848 insertions(+), 24 deletions(-) diff --git a/fixture/ga4_report_imported_sources.json b/fixture/ga4_report_imported_sources.json index 9c91df7e43..52fed7d13a 100644 --- a/fixture/ga4_report_imported_sources.json +++ b/fixture/ga4_report_imported_sources.json @@ -9,6 +9,9 @@ { "name": "sessionSource" }, + { + "name": "sessionDefaultChannelGroup" + }, { "name": "sessionMedium" }, @@ -59,6 +62,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -98,6 +104,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -137,6 +146,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -176,6 +188,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -215,6 +230,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -254,6 +272,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -293,6 +314,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -332,6 +356,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -371,6 +398,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -410,6 +440,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -449,6 +482,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -488,6 +524,9 @@ { "value": "kuhinjica-mignone.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -527,6 +566,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -566,6 +608,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -605,6 +650,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -644,6 +692,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -683,6 +734,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -722,6 +776,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -761,6 +818,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -800,6 +860,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -839,6 +902,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -878,6 +944,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -917,6 +986,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -956,6 +1028,9 @@ { "value": "pinterest.com.au" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -995,6 +1070,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1034,6 +1112,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -1073,6 +1154,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1112,6 +1196,9 @@ { "value": "vikendkuvarica.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1151,6 +1238,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1190,6 +1280,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1229,6 +1322,9 @@ { "value": "yahoo" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1268,6 +1364,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1307,6 +1406,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -1346,6 +1448,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1385,6 +1490,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1424,6 +1532,9 @@ { "value": "(not set)" }, + { + "value": "Unassigned" + }, { "value": "(not set)" }, @@ -1463,6 +1574,9 @@ { "value": "abaiak.com" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -1502,6 +1616,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1541,6 +1658,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1580,6 +1700,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -1619,6 +1742,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1658,6 +1784,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1697,6 +1826,9 @@ { "value": "pinterest.it" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1736,6 +1868,9 @@ { "value": "parfe-dunja.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1775,6 +1910,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1814,6 +1952,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -1853,6 +1994,9 @@ { "value": "vikendkuvarica.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1892,6 +2036,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -1931,6 +2078,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -1970,6 +2120,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2009,6 +2162,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2048,6 +2204,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -2087,6 +2246,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2126,6 +2288,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2165,6 +2330,9 @@ { "value": "pinterest.se" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2204,6 +2372,9 @@ { "value": "pinterest.com.au" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2243,6 +2414,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2282,6 +2456,9 @@ { "value": "links.google.wdsystem.cn" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -2321,6 +2498,9 @@ { "value": "nely-bluehortensia.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2360,6 +2540,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2399,6 +2582,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -2438,6 +2624,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2477,6 +2666,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2516,6 +2708,9 @@ { "value": "links.google.wdsystem.cn" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -2555,6 +2750,9 @@ { "value": "pinterest.ca" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2594,6 +2792,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2633,6 +2834,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -2672,6 +2876,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2711,6 +2918,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2750,6 +2960,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2789,6 +3002,9 @@ { "value": "nl.pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2828,6 +3044,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -2867,6 +3086,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2906,6 +3128,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -2945,6 +3170,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -2984,6 +3212,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3023,6 +3254,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3062,6 +3296,9 @@ { "value": "pinterest.ca" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3101,6 +3338,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3140,6 +3380,9 @@ { "value": "(not set)" }, + { + "value": "Unassigned" + }, { "value": "(not set)" }, @@ -3179,6 +3422,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3218,6 +3464,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -3257,6 +3506,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3296,6 +3548,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3335,6 +3590,9 @@ { "value": "duckduckgo" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3374,6 +3632,9 @@ { "value": "hitree.shop" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -3413,6 +3674,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3452,6 +3716,9 @@ { "value": "pinterest.se" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3491,6 +3758,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3530,6 +3800,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -3569,6 +3842,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3608,6 +3884,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3647,6 +3926,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3686,6 +3968,9 @@ { "value": "(not set)" }, + { + "value": "Unassigned" + }, { "value": "(not set)" }, @@ -3725,6 +4010,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3764,6 +4052,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -3803,6 +4094,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3842,6 +4136,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3881,6 +4178,9 @@ { "value": "l.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -3920,6 +4220,9 @@ { "value": "thursdaycooking.com.hr" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -3959,6 +4262,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -3998,6 +4304,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -4037,6 +4346,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4076,6 +4388,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4115,6 +4430,9 @@ { "value": "pinterest.at" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4154,6 +4472,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4193,6 +4514,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4232,6 +4556,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -4271,6 +4598,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4310,6 +4640,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4349,6 +4682,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4388,6 +4724,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -4427,6 +4766,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4466,6 +4808,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4505,6 +4850,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4544,6 +4892,9 @@ { "value": "duckduckgo" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4583,6 +4934,9 @@ { "value": "pinterest.it" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4622,6 +4976,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4661,6 +5018,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -4700,6 +5060,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4739,6 +5102,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4778,6 +5144,9 @@ { "value": "alohafind.com" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -4817,6 +5186,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4856,6 +5228,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -4895,6 +5270,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -4934,6 +5312,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -4973,6 +5354,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5012,6 +5396,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5051,6 +5438,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5090,6 +5480,9 @@ { "value": "duckduckgo" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5129,6 +5522,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5168,6 +5564,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -5207,6 +5606,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5246,6 +5648,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5285,6 +5690,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5324,6 +5732,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5363,6 +5774,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5402,6 +5816,9 @@ { "value": "thursdaycooking.com.hr" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -5441,6 +5858,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5480,6 +5900,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -5519,6 +5942,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5558,6 +5984,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5597,6 +6026,9 @@ { "value": "de.search.yahoo.com" }, + { + "value": "Organic Search" + }, { "value": "referral" }, @@ -5636,6 +6068,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5675,6 +6110,9 @@ { "value": "gagasrce.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5714,6 +6152,9 @@ { "value": "petalsearch.com" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -5753,6 +6194,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5792,6 +6236,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -5831,6 +6278,9 @@ { "value": "kadjakuvam.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5870,6 +6320,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5909,6 +6362,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -5948,6 +6404,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -5987,6 +6446,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6026,6 +6488,9 @@ { "value": "pinterest.ch" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6065,6 +6530,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6104,6 +6572,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -6143,6 +6614,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6182,6 +6656,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6221,6 +6698,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6260,6 +6740,9 @@ { "value": "ecosia.org" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6299,6 +6782,9 @@ { "value": "kuhinjica-mignone.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6338,6 +6824,9 @@ { "value": "pinterest.ch" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6377,6 +6866,9 @@ { "value": "tr.pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6416,6 +6908,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6455,6 +6950,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -6494,6 +6992,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6533,6 +7034,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6572,6 +7076,9 @@ { "value": "lm.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6611,6 +7118,9 @@ { "value": "pinterest.com.au" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6650,6 +7160,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6689,6 +7202,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -6728,6 +7244,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6767,6 +7286,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6806,6 +7328,9 @@ { "value": "pinterest.es" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6845,6 +7370,9 @@ { "value": "l.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -6884,6 +7412,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -6923,6 +7454,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -6962,6 +7496,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7001,6 +7538,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7040,6 +7580,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7079,6 +7622,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7118,6 +7664,9 @@ { "value": "nely-bluehortensia.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7157,6 +7706,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7196,6 +7748,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -7235,6 +7790,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7274,6 +7832,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7313,6 +7874,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7352,6 +7916,9 @@ { "value": "m.facebook.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7391,6 +7958,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7430,6 +8000,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -7469,6 +8042,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7508,6 +8084,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7547,6 +8126,9 @@ { "value": "kadjakuvam.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7586,6 +8168,9 @@ { "value": "kuhinjica-mignone.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7625,6 +8210,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7664,6 +8252,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -7703,6 +8294,9 @@ { "value": "kuhinjica-mignone.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7742,6 +8336,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7781,6 +8378,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7820,6 +8420,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -7859,6 +8462,9 @@ { "value": "emea.search.yahoo.com" }, + { + "value": "Organic Search" + }, { "value": "referral" }, @@ -7898,6 +8504,9 @@ { "value": "pinterest.de" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -7937,6 +8546,9 @@ { "value": "startraffic.online" }, + { + "value": "Referral" + }, { "value": "referral" }, @@ -7976,6 +8588,9 @@ { "value": "google" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -8015,6 +8630,9 @@ { "value": "(direct)" }, + { + "value": "Direct" + }, { "value": "(none)" }, @@ -8054,6 +8672,9 @@ { "value": "pinterest.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -8093,6 +8714,9 @@ { "value": "baidu" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -8132,6 +8756,9 @@ { "value": "bing" }, + { + "value": "Organic Search" + }, { "value": "organic" }, @@ -8171,6 +8798,9 @@ { "value": "mojaslatkakuhinja.wordpress.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -8210,6 +8840,9 @@ { "value": "vrtaljica.blogspot.com" }, + { + "value": "Organic Social" + }, { "value": "referral" }, @@ -8244,4 +8877,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/lib/plausible/google/ga4/report_request.ex b/lib/plausible/google/ga4/report_request.ex index 7a94d8b0f3..2019320f4c 100644 --- a/lib/plausible/google/ga4/report_request.ex +++ b/lib/plausible/google/ga4/report_request.ex @@ -51,6 +51,7 @@ defmodule Plausible.Google.GA4.ReportRequest do dimensions: [ "date", "sessionSource", + "sessionDefaultChannelGroup", "sessionMedium", "sessionCampaignName", "sessionManualAdContent", diff --git a/lib/plausible/imported/google_analytics4.ex b/lib/plausible/imported/google_analytics4.ex index 4cc4de2187..90709f17d2 100644 --- a/lib/plausible/imported/google_analytics4.ex +++ b/lib/plausible/imported/google_analytics4.ex @@ -175,6 +175,8 @@ defmodule Plausible.Imported.GoogleAnalytics4 do import_id: import_id, date: get_date(row), source: row.dimensions |> Map.fetch!("sessionSource") |> parse_source(), + # GA4 channels map 1-1 to Plausible channels + channel: row.dimensions |> Map.fetch!("sessionDefaultChannelGroup"), referrer: nil, # Only `source` exists in GA4 API utm_source: nil, diff --git a/lib/plausible/imported/source.ex b/lib/plausible/imported/source.ex index cc703f9a45..03f61dacbb 100644 --- a/lib/plausible/imported/source.ex +++ b/lib/plausible/imported/source.ex @@ -8,6 +8,7 @@ defmodule Plausible.Imported.Source do field :import_id, Ch, type: "UInt64" field :date, :date field :source, :string + field :channel, Ch, type: "LowCardinality(String)" field :referrer, :string field :utm_source, :string field :utm_medium, :string diff --git a/lib/plausible/stats/imported/base.ex b/lib/plausible/stats/imported/base.ex index 49fea32c9f..831314e6dc 100644 --- a/lib/plausible/stats/imported/base.ex +++ b/lib/plausible/stats/imported/base.ex @@ -12,6 +12,7 @@ defmodule Plausible.Stats.Imported.Base do @property_to_table_mappings %{ "visit:source" => "imported_sources", + "visit:channel" => "imported_sources", "visit:referrer" => "imported_sources", "visit:utm_source" => "imported_sources", "visit:utm_medium" => "imported_sources", diff --git a/lib/plausible/stats/imported/imported.ex b/lib/plausible/stats/imported/imported.ex index e2330f08bd..3e9e6d9fc5 100644 --- a/lib/plausible/stats/imported/imported.ex +++ b/lib/plausible/stats/imported/imported.ex @@ -197,6 +197,7 @@ defmodule Plausible.Stats.Imported do @filter_suggestions_mapping %{ referrer_source: :source, + acquisition_channel: :channel, screen_size: :device, pathname: :page } diff --git a/lib/plausible/stats/imported/sql/expression.ex b/lib/plausible/stats/imported/sql/expression.ex index 4ef755f3da..a22549b694 100644 --- a/lib/plausible/stats/imported/sql/expression.ex +++ b/lib/plausible/stats/imported/sql/expression.ex @@ -236,7 +236,7 @@ defmodule Plausible.Stats.Imported.SQL.Expression do end defp select_group_fields(q, dimension, key, _query) - when dimension in ["visit:device", "visit:browser"] do + when dimension in ["visit:device", "visit:browser", "visit:channel"] do select_merge_as(q, [i], %{ key => fragment( diff --git a/test/plausible_web/controllers/api/stats_controller/imported_test.exs b/test/plausible_web/controllers/api/stats_controller/imported_test.exs index ef5666d2f7..7224fbd364 100644 --- a/test/plausible_web/controllers/api/stats_controller/imported_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/imported_test.exs @@ -164,7 +164,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "organic", - "sessionSource" => "duckduckgo.com" + "sessionSource" => "duckduckgo.com", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -181,7 +182,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210131", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "organic", - "sessionSource" => "google.com" + "sessionSource" => "google.com", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -198,7 +200,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "paid", - "sessionSource" => "google.com" + "sessionSource" => "google.com", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -215,7 +218,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "social", - "sessionSource" => "Twitter" + "sessionSource" => "Twitter", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -232,7 +236,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210131", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "email", - "sessionSource" => "A Nice Newsletter" + "sessionSource" => "A Nice Newsletter", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -249,7 +254,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "(none)", - "sessionSource" => "(direct)" + "sessionSource" => "(direct)", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -283,6 +289,140 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do ] end + test "Channels are imported", %{conn: conn, site: site, import_id: import_id} do + populate_stats(site, [ + # Organic Search + build(:pageview, + referrer_source: "Bing", + timestamp: ~N[2021-01-01 00:00:00] + ), + # Paid Search + build(:pageview, + referrer_source: "Google", + utm_medium: "paid", + timestamp: ~N[2021-01-01 00:00:00] + ), + # Direct + build(:pageview, + timestamp: ~N[2021-01-01 00:00:00] + ) + ]) + + import_data( + [ + %{ + dimensions: %{ + "sessionManualAdContent" => "", + "sessionCampaignName" => "", + "date" => "20210101", + "sessionGoogleAdsKeyword" => "", + "sessionMedium" => "organic", + "sessionSource" => "duckduckgo.com", + "sessionDefaultChannelGroup" => "Organic Search" + }, + metrics: %{ + "bounces" => "0", + "userEngagementDuration" => "60", + "sessions" => "1", + "totalUsers" => "1", + "screenPageViews" => "1" + } + }, + %{ + dimensions: %{ + "sessionManualAdContent" => "", + "sessionCampaignName" => "", + "date" => "20210131", + "sessionGoogleAdsKeyword" => "", + "sessionMedium" => "organic", + "sessionSource" => "google.com", + "sessionDefaultChannelGroup" => "Organic Search" + }, + metrics: %{ + "bounces" => "1", + "userEngagementDuration" => "60", + "sessions" => "1", + "totalUsers" => "1", + "screenPageViews" => "1" + } + }, + %{ + dimensions: %{ + "sessionManualAdContent" => "", + "sessionCampaignName" => "", + "date" => "20210101", + "sessionGoogleAdsKeyword" => "", + "sessionMedium" => "paid", + "sessionSource" => "google.com", + "sessionDefaultChannelGroup" => "Paid Search" + }, + metrics: %{ + "bounces" => "1", + "userEngagementDuration" => "60", + "sessions" => "1", + "totalUsers" => "1", + "screenPageViews" => "1" + } + }, + %{ + dimensions: %{ + "sessionManualAdContent" => "", + "sessionCampaignName" => "", + "date" => "20210101", + "sessionGoogleAdsKeyword" => "", + "sessionMedium" => "(none)", + "sessionSource" => "(direct)", + "sessionDefaultChannelGroup" => "Direct" + }, + metrics: %{ + "bounces" => "1", + "userEngagementDuration" => "60", + "sessions" => "1", + "totalUsers" => "1", + "screenPageViews" => "1" + } + }, + %{ + dimensions: %{ + "sessionManualAdContent" => "", + "sessionCampaignName" => "", + "date" => "20210101", + "sessionGoogleAdsKeyword" => "", + "sessionMedium" => "(none)", + "sessionSource" => "(direct)", + "sessionDefaultChannelGroup" => "" + }, + metrics: %{ + "bounces" => "1", + "userEngagementDuration" => "60", + "sessions" => "1", + "totalUsers" => "1", + "screenPageViews" => "1" + } + } + ], + site.id, + import_id, + "imported_sources" + ) + + results = + conn + |> get( + "/api/stats/#{site.domain}/channels?period=month&date=2021-01-01&with_imported=true" + ) + |> json_response(200) + |> Map.get("results") + |> Enum.sort() + + assert results == [ + %{"name" => "(not set)", "visitors" => 1}, + %{"name" => "Direct", "visitors" => 2}, + %{"name" => "Organic Search", "visitors" => 3}, + %{"name" => "Paid Search", "visitors" => 2} + ] + end + test "UTM mediums data imported from Google Analytics", %{ conn: conn, site: site, @@ -308,7 +448,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "social", - "sessionSource" => "Twitter" + "sessionSource" => "Twitter", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -325,7 +466,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "(none)", - "sessionSource" => "(direct)" + "sessionSource" => "(direct)", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -376,7 +518,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "social", - "sessionSource" => "Twitter" + "sessionSource" => "Twitter", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -393,7 +536,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "email", - "sessionSource" => "Gmail" + "sessionSource" => "Gmail", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -410,7 +554,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "email", - "sessionSource" => "Gmail" + "sessionSource" => "Gmail", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -468,7 +613,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "oat milk", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -485,7 +631,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "Sweden", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -502,7 +649,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "(not set)", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -559,7 +707,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "1", @@ -576,7 +725,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", @@ -593,7 +743,8 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do "date" => "20210101", "sessionGoogleAdsKeyword" => "", "sessionMedium" => "paid", - "sessionSource" => "Google" + "sessionSource" => "Google", + "sessionDefaultChannelGroup" => "" }, metrics: %{ "bounces" => "0", diff --git a/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs b/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs index 53c6500de2..3aed02080a 100644 --- a/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/suggestions_test.exs @@ -1142,6 +1142,31 @@ defmodule PlausibleWeb.Api.StatsController.SuggestionsTest do %{"value" => "Bing", "label" => "Bing"} ] end + + test "merges channel suggestions from native and imported data #{label}", %{ + conn: conn, + site: site, + site_import: site_import + } do + populate_stats(site, site_import.id, [ + build(:pageview, timestamp: ~N[2019-01-01 23:00:01], referrer_source: "Bing"), + build(:pageview, timestamp: ~N[2019-01-01 23:30:01], referrer_source: "Bing"), + build(:pageview, timestamp: ~N[2019-01-01 23:40:01], referrer_source: "Bing"), + build(:pageview, timestamp: ~N[2019-01-01 23:00:01], referrer_source: "Google"), + build(:imported_sources, date: ~D[2019-01-01], channel: "Organic Social", pageviews: 3) + ]) + + conn = + get( + conn, + "/api/stats/#{site.domain}/suggestions/channel?period=month&date=2019-01-01&q=#{unquote(q)}&with_imported=true" + ) + + assert json_response(conn, 200) == [ + %{"value" => "Organic Search", "label" => "Organic Search"}, + %{"value" => "Organic Social", "label" => "Organic Social"} + ] + end end for {q, label} <- [{"", "without filter"}, {"o", "with filter"}] do diff --git a/test/plausible_web/controllers/stats_controller_test.exs b/test/plausible_web/controllers/stats_controller_test.exs index 1089c658c5..92fe8eb878 100644 --- a/test/plausible_web/controllers/stats_controller_test.exs +++ b/test/plausible_web/controllers/stats_controller_test.exs @@ -398,6 +398,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:imported_pages, page: "/test", pageviews: 1), build(:imported_sources, source: "Google", + channel: "Paid Search", utm_medium: "search", utm_campaign: "ads", utm_source: "google", @@ -473,10 +474,10 @@ defmodule PlausibleWeb.StatsControllerTest do [""] ] - # Dummy - imported data is not actually included in exported CSVs yet {~c"channels.csv", data} -> assert parse_csv(data) == [ ["name", "visitors", "bounce_rate", "visit_duration"], + ["Paid Search", "1", "0.0", "10.0"], [""] ] diff --git a/test/support/factory.ex b/test/support/factory.ex index 3344046d8f..2e21584123 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -136,14 +136,18 @@ defmodule Plausible.Factory do } end - def pageview_factory do - Map.put(event_factory(), :name, "pageview") + def pageview_factory(attrs) do + Map.put(event_factory(attrs), :name, "pageview") end - def event_factory do + def event_factory(attrs) do + if Map.get(attrs, :acquisition_channel) do + raise "Acquisition channel cannot be written directly since it's a materialized column." + end + hostname = sequence(:domain, &"example-#{&1}.com") - %Plausible.ClickhouseEventV2{ + event = %Plausible.ClickhouseEventV2{ hostname: hostname, site_id: Enum.random(1000..10_000), pathname: "/", @@ -151,6 +155,10 @@ defmodule Plausible.Factory do user_id: SipHash.hash!(hash_key(), Ecto.UUID.generate()), session_id: SipHash.hash!(hash_key(), Ecto.UUID.generate()) } + + event + |> merge_attributes(attrs) + |> evaluate_lazy_attributes() end def goal_factory(attrs) do