Session log: Bardach contact cleanup, Dataforth MFA/auth policy fix, ACE Portables Bitdefender
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
75
temp/bardach_fix_blank_names.py
Normal file
75
temp/bardach_fix_blank_names.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import urllib.request, urllib.parse, json, os
|
||||
from collections import defaultdict
|
||||
|
||||
APP_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
|
||||
CLIENT_SECRET = os.environ["CLIENT_SECRET"]
|
||||
USER_ID = "41d14430-feb4-4ae2-aed6-2bd4e6384ca7"
|
||||
|
||||
token_data = urllib.parse.urlencode({
|
||||
'client_id': APP_ID, 'client_secret': CLIENT_SECRET,
|
||||
'scope': 'https://graph.microsoft.com/.default', 'grant_type': 'client_credentials'
|
||||
}).encode()
|
||||
req = urllib.request.Request(f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data=token_data, method='POST')
|
||||
with urllib.request.urlopen(req) as r:
|
||||
token = json.loads(r.read())['access_token']
|
||||
|
||||
base = f'https://graph.microsoft.com/v1.0/users/{USER_ID}/contacts'
|
||||
|
||||
def patch_contact(cid, data):
|
||||
body = json.dumps(data).encode()
|
||||
req = urllib.request.Request(f'{base}/{cid}', data=body, method='PATCH',
|
||||
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'})
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return r.status
|
||||
|
||||
# Fetch all contacts with blank displayName
|
||||
url = f'{base}?$select=id,displayName,givenName,surname,companyName,emailAddresses,businessPhones,mobilePhone&$top=999'
|
||||
blanks = []
|
||||
while url:
|
||||
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
|
||||
with urllib.request.urlopen(req) as r:
|
||||
data = json.loads(r.read())
|
||||
for c in data.get('value', []):
|
||||
dn = (c.get('displayName') or '').strip()
|
||||
if not dn:
|
||||
blanks.append(c)
|
||||
url = data.get('@odata.nextLink')
|
||||
|
||||
print(f'Found {len(blanks)} contacts with blank displayName\n')
|
||||
|
||||
fixed = 0
|
||||
skipped = 0
|
||||
for c in blanks:
|
||||
given = (c.get('givenName') or '').strip()
|
||||
surname = (c.get('surname') or '').strip()
|
||||
company = (c.get('companyName') or '').strip()
|
||||
emails = [e.get('address', '') for e in c.get('emailAddresses', []) if e.get('address', '').strip()]
|
||||
phones = list(filter(None, (c.get('businessPhones') or []) + [c.get('mobilePhone')]))
|
||||
|
||||
# Build display name from best available info
|
||||
if given and surname:
|
||||
new_name = f'{given} {surname}'
|
||||
elif given:
|
||||
new_name = given
|
||||
elif surname:
|
||||
new_name = surname
|
||||
elif company:
|
||||
new_name = company
|
||||
elif emails:
|
||||
# Use email local part as name
|
||||
new_name = emails[0].split('@')[0].replace('.', ' ').replace('_', ' ').title()
|
||||
else:
|
||||
# Nothing useful - skip
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
status = patch_contact(c['id'], {'displayName': new_name})
|
||||
src = 'name' if (given or surname) else ('company' if company else 'email')
|
||||
print(f' [OK] "{new_name}" (from {src}, status {status})')
|
||||
fixed += 1
|
||||
except Exception as e:
|
||||
print(f' [ERROR] {new_name}: {e}')
|
||||
|
||||
print(f'\n=== DONE: Fixed {fixed}, Skipped {skipped} (no usable data) ===')
|
||||
Reference in New Issue
Block a user