sync: Auto-sync from ACG-M-L5090 at 2026-03-10 19:11:00
Synced files: - Quote wizard frontend (all components, hooks, types, config) - API updates (config, models, routers, schemas, services) - Client work (bg-builders, gurushow) - Scripts (BGB Lesley termination, CIPP, Datto, migration) - Temp files (Bardach contacts, VWP investigation, misc) - Credentials and session logs - Email service, PHP API, session logs Machine: ACG-M-L5090 Timestamp: 2026-03-10 19:11:00 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
103
temp/bardach_pull_deleted.py
Normal file
103
temp/bardach_pull_deleted.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""Pull all deleted contacts from Barbara's mailbox."""
|
||||
import subprocess, json
|
||||
|
||||
TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
|
||||
CLAUDE_APP = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
CLAUDE_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
|
||||
USER = "barbara@bardach.net"
|
||||
|
||||
# Get token
|
||||
token_cmd = subprocess.run([
|
||||
'curl', '-s', '-X', 'POST',
|
||||
f'https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token',
|
||||
'-d', f'client_id={CLAUDE_APP}&client_secret={CLAUDE_SECRET}&scope=https://graph.microsoft.com/.default&grant_type=client_credentials'
|
||||
], capture_output=True, text=True)
|
||||
token = json.loads(token_cmd.stdout)['access_token']
|
||||
|
||||
# Page through all deleted contacts
|
||||
url = (f"https://graph.microsoft.com/beta/users/{USER}/contacts"
|
||||
f"?$filter=parentFolderId%20eq%20'deleteditems'"
|
||||
f"&$top=100"
|
||||
f"&$select=displayName,emailAddresses,companyName,lastModifiedDateTime")
|
||||
|
||||
all_deleted = []
|
||||
page = 0
|
||||
|
||||
while url:
|
||||
page += 1
|
||||
cmd = ['curl', '-s', '-H', f'Authorization: Bearer {token}', url]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
data = json.loads(result.stdout)
|
||||
|
||||
if 'error' in data:
|
||||
print(f"Error on page {page}: {data['error'].get('message','')[:200]}")
|
||||
break
|
||||
|
||||
items = data.get('value', [])
|
||||
all_deleted.extend(items)
|
||||
url = data.get('@odata.nextLink')
|
||||
|
||||
if page % 5 == 0:
|
||||
print(f" Page {page}: {len(all_deleted)} deleted contacts so far...")
|
||||
|
||||
if not items:
|
||||
break
|
||||
|
||||
print(f"\nTotal deleted contacts found: {len(all_deleted)}")
|
||||
|
||||
# Analyze
|
||||
from collections import Counter
|
||||
|
||||
if all_deleted:
|
||||
# Save full data
|
||||
with open('D:/ClaudeTools/temp/bardach_deleted_contacts.json', 'w') as f:
|
||||
json.dump(all_deleted, f, indent=2)
|
||||
|
||||
# Name analysis
|
||||
names = [c.get('displayName', '').strip() for c in all_deleted if c.get('displayName')]
|
||||
name_counts = Counter([n.lower() for n in names])
|
||||
dupes = {k: v for k, v in name_counts.items() if v > 1}
|
||||
|
||||
print(f"\nUnique names: {len(set(n.lower() for n in names))}")
|
||||
print(f"Names with duplicates: {len(dupes)}")
|
||||
|
||||
# Check overlap with active contacts
|
||||
active_file = 'D:/ClaudeTools/temp/bardach_contacts.json'
|
||||
try:
|
||||
with open(active_file) as f:
|
||||
active = json.load(f)
|
||||
active_names = set(c.get('displayName', '').strip().lower() for c in active if c.get('displayName'))
|
||||
deleted_names = set(n.lower() for n in names)
|
||||
|
||||
overlap = active_names & deleted_names
|
||||
only_deleted = deleted_names - active_names
|
||||
|
||||
print(f"\nActive contacts: {len(active_names)}")
|
||||
print(f"Deleted contacts (unique names): {len(deleted_names)}")
|
||||
print(f"Names in BOTH active and deleted: {len(overlap)}")
|
||||
print(f"Names ONLY in deleted (not in active): {len(only_deleted)}")
|
||||
|
||||
if only_deleted:
|
||||
print(f"\nSample of contacts only in Deleted Items (not in active contacts):")
|
||||
for name in sorted(only_deleted)[:50]:
|
||||
print(f" - {name}")
|
||||
if len(only_deleted) > 50:
|
||||
print(f" ... and {len(only_deleted) - 50} more")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("\n(Active contacts file not found for comparison)")
|
||||
|
||||
# Show date range
|
||||
dates = [c.get('lastModifiedDateTime', '')[:10] for c in all_deleted if c.get('lastModifiedDateTime')]
|
||||
if dates:
|
||||
print(f"\nDate range of deleted contacts: {min(dates)} to {max(dates)}")
|
||||
|
||||
# Sample
|
||||
print(f"\nFirst 30 deleted contacts:")
|
||||
for c in all_deleted[:30]:
|
||||
name = c.get('displayName', '(no name)')
|
||||
emails = ', '.join([e.get('address', '') for e in c.get('emailAddresses', [])])
|
||||
company = c.get('companyName', '')
|
||||
modified = c.get('lastModifiedDateTime', '?')[:10]
|
||||
detail = emails or company or ''
|
||||
print(f" {modified} - {name}" + (f" ({detail})" if detail else ""))
|
||||
Reference in New Issue
Block a user