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>
141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
"""
|
|
Final Verification: Count remaining Temp contacts and summarize all operations.
|
|
"""
|
|
import json
|
|
import subprocess
|
|
import urllib.parse
|
|
|
|
TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
|
|
CLIENT_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
|
|
CLIENT_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
|
|
SCOPE = "https://graph.microsoft.com/.default"
|
|
USER = "barbara@bardach.net"
|
|
|
|
TEMP_FOLDER_ID = None # Will be resolved dynamically
|
|
|
|
def get_token():
|
|
url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
|
|
data = (
|
|
f"client_id={CLIENT_ID}"
|
|
f"&scope={urllib.parse.quote(SCOPE)}"
|
|
f"&client_secret={urllib.parse.quote(CLIENT_SECRET)}"
|
|
f"&grant_type=client_credentials"
|
|
)
|
|
result = subprocess.run(
|
|
["curl", "-s", "-X", "POST", url,
|
|
"-H", "Content-Type: application/x-www-form-urlencoded",
|
|
"-d", data],
|
|
capture_output=True, text=True
|
|
)
|
|
resp = json.loads(result.stdout)
|
|
if "access_token" not in resp:
|
|
print(f"[ERROR] Token acquisition failed: {resp}")
|
|
raise Exception("Failed to get token")
|
|
return resp["access_token"]
|
|
|
|
def count_temp_contacts(token):
|
|
"""Count contacts in Temp folder using $count."""
|
|
url = (
|
|
f"https://graph.microsoft.com/v1.0/users/{USER}"
|
|
f"/contactFolders/{TEMP_FOLDER_ID}/contacts?$count=true&$top=1&$select=id"
|
|
)
|
|
result = subprocess.run(
|
|
["curl", "-s", "-X", "GET", url,
|
|
"-H", f"Authorization: Bearer {token}",
|
|
"-H", "ConsistencyLevel: eventual"],
|
|
capture_output=True, text=True
|
|
)
|
|
resp = json.loads(result.stdout)
|
|
count = resp.get("@odata.count")
|
|
if count is not None:
|
|
return count
|
|
# Fallback: page through all
|
|
print("[INFO] @odata.count not available, paging through contacts...")
|
|
total = 0
|
|
page_url = (
|
|
f"https://graph.microsoft.com/v1.0/users/{USER}"
|
|
f"/contactFolders/{TEMP_FOLDER_ID}/contacts?$top=100&$select=id"
|
|
)
|
|
while page_url:
|
|
result = subprocess.run(
|
|
["curl", "-s", "-X", "GET", page_url,
|
|
"-H", f"Authorization: Bearer {token}"],
|
|
capture_output=True, text=True
|
|
)
|
|
resp = json.loads(result.stdout)
|
|
contacts = resp.get("value", [])
|
|
total += len(contacts)
|
|
page_url = resp.get("@odata.nextLink")
|
|
if total % 500 == 0 and total > 0:
|
|
print(f" ...counted {total} so far")
|
|
return total
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("FINAL VERIFICATION")
|
|
print("=" * 60)
|
|
|
|
token = get_token()
|
|
print("[OK] Token acquired")
|
|
|
|
# Find Temp folder ID
|
|
global TEMP_FOLDER_ID
|
|
url = f"https://graph.microsoft.com/v1.0/users/{USER}/contactFolders"
|
|
result = subprocess.run(
|
|
["curl", "-s", "-X", "GET", url,
|
|
"-H", f"Authorization: Bearer {token}"],
|
|
capture_output=True, text=True
|
|
)
|
|
folders = json.loads(result.stdout).get("value", [])
|
|
for f in folders:
|
|
if "temp" in f.get("displayName", "").lower():
|
|
TEMP_FOLDER_ID = f["id"]
|
|
print(f"[INFO] Found Temp folder: '{f['displayName']}' -> {TEMP_FOLDER_ID[:40]}...")
|
|
break
|
|
if not TEMP_FOLDER_ID:
|
|
print("[ERROR] Could not find Temp contact folder!")
|
|
for f in folders:
|
|
print(f" Folder: {f.get('displayName')} -> {f['id'][:40]}...")
|
|
return
|
|
|
|
# Count remaining Temp contacts
|
|
print("\n[INFO] Counting remaining Temp contacts...")
|
|
remaining = count_temp_contacts(token)
|
|
print(f"[INFO] Remaining Temp contacts: {remaining}")
|
|
print(f"[INFO] Expected: ~3,888 (matches_with_extras)")
|
|
|
|
# Load operation results
|
|
print("\n--- OPERATION SUMMARIES ---")
|
|
for op_file, label in [
|
|
("D:/ClaudeTools/temp/bardach_op1_delete_exact.json", "Op1: Delete Exact Matches"),
|
|
("D:/ClaudeTools/temp/bardach_op2_move_unique.json", "Op2: Move Unique Contacts"),
|
|
("D:/ClaudeTools/temp/bardach_op3_delete_blank.json", "Op3: Delete Blank Contacts"),
|
|
]:
|
|
try:
|
|
with open(op_file, "r") as f:
|
|
r = json.load(f)
|
|
print(f"\n {label}:")
|
|
print(f" Total: {r['total']}")
|
|
print(f" Successes: {r['successes']}")
|
|
print(f" Failures: {r['failures']}")
|
|
if r.get("elapsed_seconds"):
|
|
print(f" Time: {r['elapsed_seconds']}s")
|
|
if r.get("method"):
|
|
print(f" Method: {r['method']}")
|
|
except FileNotFoundError:
|
|
print(f"\n {label}: [NOT FOUND] {op_file}")
|
|
except Exception as e:
|
|
print(f"\n {label}: [ERROR] {e}")
|
|
|
|
print(f"\n{'=' * 60}")
|
|
print(f"Remaining Temp contacts: {remaining}")
|
|
diff = remaining - 3888
|
|
if abs(diff) < 10:
|
|
print(f"[OK] Close to expected (~3,888), difference: {diff}")
|
|
else:
|
|
print(f"[WARNING] Difference from expected (3,888): {diff}")
|
|
print("=" * 60)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|