GA4/GTM Cleanup Workbook and Container Data Extraction
After a GA4/GTM audit, the client needed non-technical stakeholders to make structural cleanup decisions. I parsed the GTM container (GTM-XXXXXXX) for the Conversion Linker's 56 linkerDomains (only client.org and client.org confirmed owned) and its GA4 event tags, and parsed a GA4 Mar-vs-Feb source/medium export to isolate 41 new/malformed combos out of 235 distinct ones. I merged the container events with the audit's inventory into a 36-row plain-language list flagging duplicates, zombie key events, a paused tag and a malformed URL-named event. Everything was assembled with openpyxl into a four-tab branded workbook with data-validation dropdowns, colour-coded input cells, example rows and a 'Start here' guide, then validated with LibreOffice recalc (zero errors). Two supporting CSVs accompanied it.
Raw container and report exports in; a dropdown-driven, non-technical decision workbook out, with zero invented values.
Role: Sole implementer (data extraction + workbook engineering)
The Problem
Following a completed GA4/GTM audit, the client agreed to make structural fixes but the decisions (which UTMs to fix, which cross-domain domains to keep, which events to keep/remove/merge) required input from non-technical marketing stakeholders. Web-based conversion numbers had been unusable for optimization for the whole window.
The actionable inputs were buried in a GTM container export (GTM-XXXXXXX) and GA4 report exports: a 56-domain Conversion Linker list, hundreds of malformed source/medium combinations, and a sprawl of duplicated/zombie GA4 and GTM events. These needed to be extracted accurately and presented so non-technical users could make decisions without touching the live setup.
The data had to be extracted verbatim from real exports (no invented values), duplicates and 'zombie' key events identified, and the whole thing packaged with guardrails (dropdowns, colour-coded input cells, example rows) for users unaccustomed to analytics tooling. It also had to be branded and safe to share.
Approach & Architecture
Programmatically extracted the real Conversion Linker domains and new source/medium combinations from the GTM container and GA4 exports, and the GA4 event tags from the container. Combined those with the audit's event inventory into a plain-language events list. Built a four-tab branded xlsx (Google Sheets-ready) with data-validation dropdowns, colour-coded input cells, example rows, frozen headers and a 'Start here' instructions tab. Validated with LibreOffice recalc and a visual render.
Three Python extraction passes feed three review tabs: (1) GA4 source/medium comparison CSV parsed in Feb-vs-March triplets to isolate new/malformed combos; (2) GTM container JSON parsed for the gclidw (Conversion Linker) tag's linkerDomains and for gaawe (GA4 event) tags; (3) audit event inventory merged with container events into a plain-English list. openpyxl builds the branded workbook with DataValidation dropdowns; recalc.py verifies integrity.
Key Decisions & Trade-offs
Hardest Part
Parsing the GA4 source/medium comparison CSV, whose rows come in per-combo triplets (a % change label row followed by a March row and a February row with blank source/medium), then correctly attributing March vs February session counts to each combo to isolate the new/malformed ones.
Technical Detail
Four-tab review workbook: instructions + three decision tabs (UTMs, cross-domain, events). Events tab classifies each event by where it is defined (GA4 custom / GTM tag / enhanced measurement), plain-English meaning, current conversion status, data status, and Summit Chase note, with decision, conversion and destination dropdowns.
openpyxl workbook build; LibreOffice (soffice) recalc.py validation and PDF render for QA.
recalc.py reported status success, total_errors 0; data-validation counts verified per tab (3 dropdowns on UTMs, 1 on cross-domain, 3 on events); visual render to PDF to confirm branding and layout.
Code
for t in cont.get('tag',[]): if t.get('type')=='gclidw': for p in t.get('parameter',[]): if p.get('key')=='linkerDomains': linker=[x.strip() for x in p['value'].split(',') if x.strip()]
owned={'client.org','client.org'}
other=[x for x in linker if x not in owned] # 54 to confirm/removeif dc=='% change': cur=sm
elif dc.startswith('Mar') and cur is not None: combos.setdefault(cur,{})['mar']=int(r[3].replace(',',''))
elif dc.startswith('Feb') and cur is not None: combos.setdefault(cur,{})['feb']=int(r[3].replace(',',''))
new=[(k,v.get('feb',0),v.get('mar',0)) for k,v in combos.items() if v.get('feb',0)<=5 and v.get('mar',0)>=20]dv_krm=DataValidation(type="list", formula1='"Keep,Remove,Merge into another,Not sure"', allow_blank=True) s3.add_data_validation(dv_krm) dv_krm.add(s3.cell(row=rr,column=8))
Used an AI coding environment to parse the real GTM container JSON and GA4 report exports, derive the decision lists, and generate a branded, dropdown-driven xlsx with openpyxl, then validate it with LibreOffice recalc and a visual render.
Repeatable extraction-to-review-workbook flow: GTM/GA4 exports in, colour-coded dropdown-driven client decision spreadsheet out.
Measured Results
Turned raw GTM/GA4 exports into a branded, four-tab, dropdown-driven decision workbook that let non-technical stakeholders confirm which UTMs, cross-domain domains and events to keep, remove or merge without touching the live setup. Also produced two supporting CSVs (56 linker domains; 41 new source/medium combos). Outcome was an enablement/decision artifact; no performance metric was measured in-chat.
| Metric | Value | Before | Source |
|---|---|---|---|
| Conversion Linker domains extracted | 56 (2 client-owned, 54 to confirm/remove) | n/a | Parse of GTM-XXXXXXX linkerDomains |
| New/malformed source/medium combos isolated | 41 | n/a | Parse of GA4 Mar-vs-Feb source/medium export (Feb<=5, Mar>=20) |
| Distinct source/medium combos in export The audit's headline 355 was a broader count; not reproduced from this export | 235 | n/a | GA4 export parse |
| GTM GA4 event tags in container | 8 | n/a | GTM-XXXXXXX parse (gaawe tags) |
| Total tags in container | 93 | n/a | GTM-XXXXXXX parse |
| Events compiled into review tab | 36 | n/a | Merge of container events + audit inventory + enhanced measurement |
| Singapore direct/none sessions Bot-signature figure from the prior audit, referenced in this chat | 3,161 of 3,262 | n/a | GA4 (from audit, cited in meeting prep) |
| Missing gclids reported by client Client-stated, not independently measured | 50% | n/a | Client email (a stakeholder) |
| Workbook formula errors | 0 | n/a | recalc.py (LibreOffice) |
Every figure above was recorded during the work itself. Where no number was measured, none is claimed.