← back to Ga Allsites
scripts/delete_dup_properties.py
80 lines
#!/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
from google.analytics.admin import AnalyticsAdminServiceClient
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()