[object Object]

← back to Ga Allsites

GA4: delete 5 redundant duplicate properties (soft-trash, recoverable ~7d) — reconcile after detector fix

293388c33a20753103f3b1cd3ab62c9a21d5fb7b · 2026-08-18 10:32:29 -0700 · Steve Abrams

Files touched

Diff

commit 293388c33a20753103f3b1cd3ab62c9a21d5fb7b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 18 10:32:29 2026 -0700

    GA4: delete 5 redundant duplicate properties (soft-trash, recoverable ~7d) — reconcile after detector fix
---
 scripts/delete_dup_properties.py | 79 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/scripts/delete_dup_properties.py b/scripts/delete_dup_properties.py
new file mode 100644
index 0000000..82779ae
--- /dev/null
+++ b/scripts/delete_dup_properties.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+"""
+Delete the 5 REDUNDANT duplicate GA4 properties created 2026-08-18.
+
+Each of these 5 domains already had a live GA4 property (kept); this session minted a
+duplicate. We delete only the DUPLICATE measurement id's property. GA4 delete_property is
+a SOFT delete (moves to Trash, recoverable ~7 days), so this is reversible.
+
+Resolves measurement id -> property by listing properties under account 15714274 and each
+property's web data streams. Only deletes a property whose stream carries one of the DUP ids.
+"""
+from __future__ import annotations
+
+import sys
+from pathlib import Path
+
+SCRIPT_DIR = Path(__file__).parent
+SKILLS_DIR = Path.home() / ".claude" / "skills" / "analytics" / "scripts"
+sys.path.insert(0, str(SKILLS_DIR))
+
+from _auth import ensure_credentials  # noqa: E402
+from google.analytics.admin import AnalyticsAdminServiceClient  # noqa: E402
+
+ACCOUNT = "accounts/15714274"
+
+# domain -> the DUPLICATE (redundant) id to delete; the EXISTING id (kept) is in the comment.
+DUPS = {
+    "commercialdesignreps.com": "G-1C6HDZJT1T",   # keep G-PRJ51PTW5G
+    "designerrepresentatives.com": "G-X4WY2B9Z27", # keep G-MVZFTCQY3X
+    "designtradelive.com": "G-LCM67ZD8PS",         # keep G-E8R8V9RDXN
+    "wallsandfabrics.com": "G-7CTCV6VS2D",         # keep G-429EVNMHVY
+    "iwascute.com": "G-3084642QM4",                # keep G-2DHDBP8R78
+}
+TARGET_IDS = set(DUPS.values())
+
+
+def main() -> None:
+    apply = "--apply" in sys.argv
+    ensure_credentials()
+    client = AnalyticsAdminServiceClient()
+
+    # measurement_id -> (property_name, property_display_name)
+    mid_map: dict[str, tuple] = {}
+    for prop in client.list_properties(request={"filter": f"parent:{ACCOUNT}", "show_deleted": False}):
+        for ds in client.list_data_streams(request={"parent": prop.name}):
+            wsd = getattr(ds, "web_stream_data", None)
+            mid = getattr(wsd, "measurement_id", "") if wsd else ""
+            if mid:
+                mid_map[mid] = (prop.name, prop.display_name)
+
+    print(f"Account {ACCOUNT}: mapped {len(mid_map)} measurement ids to properties\n")
+    to_delete = []
+    for domain, dup in DUPS.items():
+        hit = mid_map.get(dup)
+        if hit:
+            print(f"  {domain:32} dup {dup} -> {hit[0]} (display: {hit[1]})")
+            to_delete.append((domain, dup, hit[0]))
+        else:
+            print(f"  {domain:32} dup {dup} -> NOT FOUND under this account (skip)")
+
+    if not to_delete:
+        print("\nNothing to delete.")
+        return
+
+    if not apply:
+        print(f"\nDRY RUN — {len(to_delete)} properties would be trashed. Re-run with --apply.")
+        return
+
+    print(f"\nDeleting (soft/trash) {len(to_delete)} duplicate properties …")
+    for domain, dup, name in to_delete:
+        try:
+            client.delete_property(request={"name": name})
+            print(f"  ✓ trashed {name}  ({domain} dup {dup})")
+        except Exception as exc:
+            print(f"  ✗ FAILED {name}: {exc}")
+
+
+if __name__ == "__main__":
+    main()

← 44c3dc4 GA4 absence-detector: park-aware + local-repo-aware, strict  ·  back to Ga Allsites  ·  chore: ruff autofixes on GA4 detector/mint/delete scripts (s 5598972 →