diff --git a/bitbylaw/steps/advoware_cal_sync/audit_calendar_sync.py b/bitbylaw/steps/advoware_cal_sync/audit_calendar_sync.py index ad72afa4..000a23f4 100644 --- a/bitbylaw/steps/advoware_cal_sync/audit_calendar_sync.py +++ b/bitbylaw/steps/advoware_cal_sync/audit_calendar_sync.py @@ -505,6 +505,171 @@ async def cleanup_orphaned_calendars(service, orphaned): print(f"\nTotal orphaned calendars deleted: {total_deleted}") +async def query_frnr(frnr): + """Query sync information for a specific Advoware frNr.""" + conn = await connect_db() + try: + # Find all sync entries for this frNr + rows = await conn.fetch( + """ + SELECT sync_id, employee_kuerzel, advoware_frnr, google_event_id, + source_system, sync_strategy, sync_status, last_sync, created_at, updated_at + FROM calendar_sync + WHERE advoware_frnr = $1 + ORDER BY sync_id + """, + str(frnr) + ) + + if not rows: + print(f"\nNo sync entries found for frNr: {frnr}") + return + + print(f"\n=== Sync Information for frNr: {frnr} ===") + print(f"Found {len(rows)} sync entr{'y' if len(rows) == 1 else 'ies'}") + + for row in rows: + print(f"\nSync ID: {row['sync_id']}") + print(f" Employee: {row['employee_kuerzel']}") + print(f" Advoware frNr: {row['advoware_frnr']}") + print(f" Google Event ID: {row['google_event_id']}") + print(f" Source System: {row['source_system']}") + print(f" Sync Strategy: {row['sync_strategy']}") + print(f" Sync Status: {row['sync_status']}") + print(f" Last Sync: {row['last_sync']}") + print(f" Created: {row['created_at']}") + print(f" Updated: {row['updated_at']}") + + finally: + await conn.close() + +async def query_event(event_id): + """Query sync information for a specific Google Event ID.""" + conn = await connect_db() + try: + # Find sync entry for this event ID + row = await conn.fetchrow( + """ + SELECT sync_id, employee_kuerzel, advoware_frnr, google_event_id, + source_system, sync_strategy, sync_status, last_sync, created_at, updated_at + FROM calendar_sync + WHERE google_event_id = $1 + """, + event_id + ) + + if not row: + print(f"\nNo sync entry found for Google Event ID: {event_id}") + return + + print(f"\n=== Sync Information for Google Event ID: {event_id} ===") + print(f"Sync ID: {row['sync_id']}") + print(f" Employee: {row['employee_kuerzel']}") + print(f" Advoware frNr: {row['advoware_frnr']}") + print(f" Google Event ID: {row['google_event_id']}") + print(f" Source System: {row['source_system']}") + print(f" Sync Strategy: {row['sync_strategy']}") + print(f" Sync Status: {row['sync_status']}") + print(f" Last Sync: {row['last_sync']}") + print(f" Created: {row['created_at']}") + print(f" Updated: {row['updated_at']}") + + finally: + await conn.close() + +async def verify_sync(frnr, service, advoware_api): + """Verify sync status for a frNr by checking both systems.""" + conn = await connect_db() + try: + # Get sync entry + row = await conn.fetchrow( + """ + SELECT sync_id, employee_kuerzel, advoware_frnr, google_event_id, + source_system, sync_strategy, sync_status, last_sync + FROM calendar_sync + WHERE advoware_frnr = $1 AND deleted = FALSE + """, + str(frnr) + ) + + if not row: + print(f"\nNo active sync entry found for frNr: {frnr}") + return + + employee_kuerzel = row['employee_kuerzel'] + google_event_id = row['google_event_id'] + + print(f"\n=== Sync Verification for frNr: {frnr} ===") + print(f"Employee: {employee_kuerzel}") + print(f"Sync Status: {row['sync_status']}") + print(f"Last Sync: {row['last_sync']}") + + # Check Advoware + print(f"\n--- Checking Advoware ---") + try: + advoware_result = await advoware_api.api_call( + 'api/v1/advonet/Termine', + method='GET', + params={ + 'kuerzel': employee_kuerzel, + 'frNr': str(frnr) + } + ) + if advoware_result and len(advoware_result) > 0: + appointment = advoware_result[0] + print("✅ Found in Advoware:") + print(f" Subject: {appointment.get('betreff', 'N/A')}") + print(f" Date: {appointment.get('datum', 'N/A')}") + print(f" Time: {appointment.get('uhrzeit', 'N/A')}") + advoware_exists = True + else: + print("❌ Not found in Advoware") + advoware_exists = False + except Exception as e: + print(f"❌ Error checking Advoware: {e}") + advoware_exists = False + + # Check Google Calendar + print(f"\n--- Checking Google Calendar ---") + try: + # Find the calendar + calendar_id = await ensure_google_calendar(service, employee_kuerzel) + if not calendar_id: + print(f"❌ Google calendar for {employee_kuerzel} not found") + google_exists = False + else: + # Get the event + event = service.events().get(calendarId=calendar_id, eventId=google_event_id).execute() + print("✅ Found in Google Calendar:") + print(f" Summary: {event.get('summary', 'N/A')}") + print(f" Start: {event.get('start', {}).get('dateTime', event.get('start', {}).get('date', 'N/A'))}") + print(f" End: {event.get('end', {}).get('dateTime', event.get('end', {}).get('date', 'N/A'))}") + google_exists = True + except HttpError as e: + if e.resp.status == 404: + print("❌ Not found in Google Calendar") + google_exists = False + else: + print(f"❌ Error checking Google Calendar: {e}") + google_exists = False + except Exception as e: + print(f"❌ Error checking Google Calendar: {e}") + google_exists = False + + # Summary + print(f"\n--- Sync Status Summary ---") + if advoware_exists and google_exists: + print("✅ Synchronized: Exists in both systems") + elif advoware_exists and not google_exists: + print("⚠️ Out of sync: Exists in Advoware, missing in Google") + elif not advoware_exists and google_exists: + print("⚠️ Out of sync: Exists in Google, missing in Advoware") + else: + print("❌ Orphaned: Missing in both systems") + + finally: + await conn.close() + async def main(): if len(sys.argv) < 2: print("Usage: python audit_calendar_sync.py [options]") @@ -523,6 +688,12 @@ async def main(): print(" Find AW-* calendars without corresponding employees in DB") print(" cleanup-orphaned") print(" Find and delete orphaned AW-* calendars") + print(" query-frnr ") + print(" Show sync information for a specific Advoware frNr") + print(" query-event ") + print(" Show sync information for a specific Google Event ID") + print(" verify-sync ") + print(" Verify sync status by checking both Advoware and Google") print("\nOptions:") print(" --delete-orphaned-google: Delete Google events that exist in Google but not in the DB (for audit command)") print("\nExamples:") @@ -533,6 +704,9 @@ async def main(): print(" python audit_calendar_sync.py delete-duplicates") print(" python audit_calendar_sync.py find-orphaned") print(" python audit_calendar_sync.py cleanup-orphaned") + print(" python audit_calendar_sync.py query-frnr 12345") + print(" python audit_calendar_sync.py query-event abc123@google.com") + print(" python audit_calendar_sync.py verify-sync 12345") sys.exit(1) command = sys.argv[1].lower() @@ -583,6 +757,28 @@ async def main(): else: print("No orphaned calendars to delete.") + elif command == 'query-frnr': + if len(sys.argv) < 3: + print("Usage: python audit_calendar_sync.py query-frnr ") + sys.exit(1) + frnr = sys.argv[2] + await query_frnr(frnr) + + elif command == 'query-event': + if len(sys.argv) < 3: + print("Usage: python audit_calendar_sync.py query-event ") + sys.exit(1) + event_id = sys.argv[2] + await query_event(event_id) + + elif command == 'verify-sync': + if len(sys.argv) < 3: + print("Usage: python audit_calendar_sync.py verify-sync ") + sys.exit(1) + frnr = sys.argv[2] + advoware_api = AdvowareAPI({}) + await verify_sync(frnr, service, advoware_api) + else: print(f"Unknown command: {command}") sys.exit(1)