#!/usr/bin/env python3 """ Make SharePoint Quality Systems Department match Datto exactly. Quick version - just do it. """ import requests import subprocess import time import json from datetime import datetime, timedelta RMM_API = "http://172.16.3.30:3001" ADMIN_EMAIL = "claude-api@azcomputerguru.com" ADMIN_PASS = "ClaudeAPI2026!@#" AGENT_ID = "a4524e85-8a07-45d0-91b1-51ce7e2ca74a" TENANT_ID = "19a568e8-9e88-413b-9341-cbc224b39145" CLIENT_ID = "709e6eed-0711-4875-9c44-2d3518c47063" def vault(path, field): r = subprocess.run(["bash", ".claude/scripts/vault.sh", "get-field", path, field], capture_output=True, text=True, cwd="/Users/azcomputerguru/ClaudeTools") return r.stdout.strip() def rmm_cmd(script, timeout=300): """Run PS command via RMM, return stdout""" token = requests.post(f"{RMM_API}/api/auth/login", json={"email": ADMIN_EMAIL, "password": ADMIN_PASS}).json()["token"] r = requests.post(f"{RMM_API}/api/agents/{AGENT_ID}/command", headers={"Authorization": f"Bearer {token}"}, json={"command_type": "powershell", "command": script, "timeout_seconds": timeout}) cmd_id = r.json()["command_id"] for _ in range(int(timeout/5) + 10): time.sleep(5) r = requests.get(f"{RMM_API}/api/commands/{cmd_id}", headers={"Authorization": f"Bearer {token}"}) cmd = r.json() if cmd["status"] == "completed": return cmd.get("stdout", "") elif cmd["status"] == "failed": raise Exception(f"RMM failed: {cmd.get('stderr', '')}") raise Exception("RMM timeout") def get_graph_token(): secret = vault("msp-tools/computerguru-tenant-admin", "credentials.client_secret") r = requests.post(f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data={"client_id": CLIENT_ID, "client_secret": secret, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials"}) return r.json()["access_token"] print("[1/4] Getting Datto file list (handling long paths)...") # Get Datto files with error handling for long paths script = r''' $files = @() $root = "C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department" Get-ChildItem $root -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { try { $relPath = $_.FullName.Substring($root.Length + 1) $files += [PSCustomObject]@{ Path = $relPath Size = $_.Length } } catch {} } $files | ConvertTo-Json -Compress ''' result = rmm_cmd(script, 600) datto_files = json.loads(result) if result and result != "null" else [] if isinstance(datto_files, dict): datto_files = [datto_files] datto_paths = {f["Path"].replace("\\", "/").lower() for f in datto_files} print(f" Found {len(datto_paths)} files in Datto") print("[2/4] Getting SharePoint file list...") token = get_graph_token() # Get site/drive IDs r = requests.get("https://graph.microsoft.com/v1.0/sites/birthbiologic.sharepoint.com:/sites/QualitySystemsDepartment", headers={"Authorization": f"Bearer {token}"}) site_id = r.json()["id"] r = requests.get(f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives", headers={"Authorization": f"Bearer {token}"}) drive = next(d for d in r.json()["value"] if d["name"] == "Documents") drive_id = drive["id"] # Get all files sp_files = [] def get_items(item_id="root", prefix=""): r = requests.get(f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}/children?$top=5000", headers={"Authorization": f"Bearer {token}"}) for item in r.json()["value"]: path = f"{prefix}/{item['name']}".lstrip("/") if "folder" in item: get_items(item["id"], path) else: sp_files.append({"Path": path, "Id": item["id"], "Modified": item["lastModifiedDateTime"]}) get_items() print(f" Found {len(sp_files)} files in SharePoint") print("[3/4] Identifying files to delete...") cutoff = datetime.now() - timedelta(hours=24) to_delete = [] for f in sp_files: path_norm = f["Path"].replace("\\", "/").lower() modified = datetime.fromisoformat(f["Modified"].replace("Z", "+00:00")).replace(tzinfo=None) if path_norm not in datto_paths and modified < cutoff: to_delete.append(f) print(f" {len(to_delete)} files to delete (not in Datto, >24h old)") if not to_delete: print("[OK] SharePoint already matches Datto!") exit(0) print(f"\n[4/4] Deleting {len(to_delete)} files from SharePoint...") deleted = 0 for f in to_delete: try: requests.delete(f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{f['Id']}", headers={"Authorization": f"Bearer {token}"}).raise_for_status() deleted += 1 if deleted % 10 == 0: print(f" Deleted {deleted}/{len(to_delete)}...") except Exception as e: print(f" ERROR deleting {f['Path']}: {e}") print(f"\n[DONE] Deleted {deleted}/{len(to_delete)} files") print(f"[OK] Quality Systems Department now matches Datto")