wxcli 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wxcli/__init__.py +4 -0
- wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
- wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
- wxcli/_playbook/.claude/rules/cleanup.md +29 -0
- wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
- wxcli/_playbook/.claude/rules/org-health.md +14 -0
- wxcli/_playbook/.claude/settings.json +8 -0
- wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
- wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
- wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
- wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
- wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
- wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
- wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
- wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
- wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
- wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
- wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
- wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
- wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
- wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
- wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
- wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
- wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
- wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
- wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
- wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
- wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
- wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
- wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
- wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
- wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
- wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
- wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
- wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
- wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
- wxcli/_playbook/CLAUDE.md +373 -0
- wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
- wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
- wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
- wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
- wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
- wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
- wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
- wxcli/_playbook/docs/reference/admin-partner.md +357 -0
- wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
- wxcli/_playbook/docs/reference/authentication.md +965 -0
- wxcli/_playbook/docs/reference/call-control.md +1205 -0
- wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
- wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
- wxcli/_playbook/docs/reference/call-routing.md +2304 -0
- wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
- wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
- wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
- wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
- wxcli/_playbook/docs/reference/devices-core.md +1869 -0
- wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
- wxcli/_playbook/docs/reference/devices-platform.md +467 -0
- wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
- wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
- wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
- wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
- wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
- wxcli/_playbook/docs/reference/meetings-content.md +473 -0
- wxcli/_playbook/docs/reference/meetings-core.md +903 -0
- wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
- wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
- wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
- wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
- wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
- wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
- wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
- wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
- wxcli/_playbook/docs/reference/provisioning.md +1366 -0
- wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
- wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
- wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
- wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
- wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
- wxcli/_version.py +24 -0
- wxcli/auth.py +137 -0
- wxcli/commands/__init__.py +0 -0
- wxcli/commands/_registry.py +181 -0
- wxcli/commands/activation_email.py +103 -0
- wxcli/commands/admin_recordings.py +585 -0
- wxcli/commands/analytics.py +105 -0
- wxcli/commands/announcement_playlists.py +219 -0
- wxcli/commands/announcements.py +519 -0
- wxcli/commands/archive_users.py +36 -0
- wxcli/commands/attachment_actions.py +60 -0
- wxcli/commands/audit_events.py +83 -0
- wxcli/commands/authorizations.py +113 -0
- wxcli/commands/auto_attendant.py +642 -0
- wxcli/commands/broadworks_billing_reports.py +126 -0
- wxcli/commands/broadworks_enterprises.py +160 -0
- wxcli/commands/broadworks_subscribers.py +319 -0
- wxcli/commands/broadworks_workspaces.py +108 -0
- wxcli/commands/call_controls.py +1304 -0
- wxcli/commands/call_park.py +494 -0
- wxcli/commands/call_pickup.py +229 -0
- wxcli/commands/call_queue.py +1878 -0
- wxcli/commands/call_recording.py +707 -0
- wxcli/commands/call_routing.py +1610 -0
- wxcli/commands/call_settings_for_me_phase_5.py +212 -0
- wxcli/commands/caller_reputation.py +169 -0
- wxcli/commands/calling_service.py +305 -0
- wxcli/commands/cc_address_book.py +770 -0
- wxcli/commands/cc_agent_greetings.py +242 -0
- wxcli/commands/cc_agent_summaries.py +39 -0
- wxcli/commands/cc_agent_wellbeing.py +216 -0
- wxcli/commands/cc_agents.py +425 -0
- wxcli/commands/cc_ai_assistant.py +55 -0
- wxcli/commands/cc_ai_feature.py +294 -0
- wxcli/commands/cc_audio_files.py +149 -0
- wxcli/commands/cc_auto_csat.py +311 -0
- wxcli/commands/cc_aux_code.py +458 -0
- wxcli/commands/cc_business_hour.py +353 -0
- wxcli/commands/cc_call_monitoring.py +228 -0
- wxcli/commands/cc_callbacks.py +194 -0
- wxcli/commands/cc_campaign.py +212 -0
- wxcli/commands/cc_captures.py +39 -0
- wxcli/commands/cc_contact_list.py +169 -0
- wxcli/commands/cc_contact_number.py +295 -0
- wxcli/commands/cc_data_sources.py +235 -0
- wxcli/commands/cc_desktop_layout.py +380 -0
- wxcli/commands/cc_desktop_profile.py +322 -0
- wxcli/commands/cc_dial_number.py +511 -0
- wxcli/commands/cc_dial_plan.py +347 -0
- wxcli/commands/cc_dnc.py +101 -0
- wxcli/commands/cc_entry_point.py +545 -0
- wxcli/commands/cc_ewt.py +51 -0
- wxcli/commands/cc_flow.py +477 -0
- wxcli/commands/cc_global_vars.py +430 -0
- wxcli/commands/cc_holiday_list.py +335 -0
- wxcli/commands/cc_journey.py +1261 -0
- wxcli/commands/cc_legacy_flows.py +49 -0
- wxcli/commands/cc_multimedia_profile.py +436 -0
- wxcli/commands/cc_notification.py +51 -0
- wxcli/commands/cc_outdial_ani.py +604 -0
- wxcli/commands/cc_overrides.py +341 -0
- wxcli/commands/cc_queue.py +1083 -0
- wxcli/commands/cc_queue_stats.py +55 -0
- wxcli/commands/cc_realtime.py +51 -0
- wxcli/commands/cc_resource_collection.py +312 -0
- wxcli/commands/cc_search.py +46 -0
- wxcli/commands/cc_site.py +400 -0
- wxcli/commands/cc_skill.py +350 -0
- wxcli/commands/cc_skill_profile.py +289 -0
- wxcli/commands/cc_subscriptions.py +426 -0
- wxcli/commands/cc_summaries.py +130 -0
- wxcli/commands/cc_tasks.py +844 -0
- wxcli/commands/cc_team.py +325 -0
- wxcli/commands/cc_user_profiles.py +349 -0
- wxcli/commands/cc_users.py +701 -0
- wxcli/commands/cc_work_types.py +363 -0
- wxcli/commands/cdr.py +89 -0
- wxcli/commands/classifications.py +38 -0
- wxcli/commands/cleanup.py +1427 -0
- wxcli/commands/client_settings.py +72 -0
- wxcli/commands/conference.py +328 -0
- wxcli/commands/configure.py +72 -0
- wxcli/commands/converged_recordings.py +347 -0
- wxcli/commands/converged_recordings_export.py +310 -0
- wxcli/commands/cq_playlists.py +42 -0
- wxcli/commands/cucm.py +2794 -0
- wxcli/commands/cucm_config.py +129 -0
- wxcli/commands/customer_assist.py +373 -0
- wxcli/commands/data_sources.py +227 -0
- wxcli/commands/dect_devices.py +787 -0
- wxcli/commands/device_configurations.py +77 -0
- wxcli/commands/device_dynamic_settings.py +320 -0
- wxcli/commands/device_settings.py +1489 -0
- wxcli/commands/devices.py +280 -0
- wxcli/commands/domains.py +142 -0
- wxcli/commands/ecm.py +178 -0
- wxcli/commands/emergency_services.py +827 -0
- wxcli/commands/events.py +83 -0
- wxcli/commands/external_voicemail.py +52 -0
- wxcli/commands/groups.py +206 -0
- wxcli/commands/guest_management.py +76 -0
- wxcli/commands/hds.py +340 -0
- wxcli/commands/hot_desk.py +67 -0
- wxcli/commands/hot_desking_members.py +118 -0
- wxcli/commands/hot_desking_portal.py +124 -0
- wxcli/commands/hunt_group.py +577 -0
- wxcli/commands/hybrid_clusters.py +71 -0
- wxcli/commands/hybrid_connectors.py +67 -0
- wxcli/commands/identity_org.py +159 -0
- wxcli/commands/init_playbook.py +155 -0
- wxcli/commands/licenses.py +106 -0
- wxcli/commands/live_monitoring.py +40 -0
- wxcli/commands/location_call_handling.py +577 -0
- wxcli/commands/location_schedules.py +341 -0
- wxcli/commands/location_settings.py +1297 -0
- wxcli/commands/location_voicemail.py +494 -0
- wxcli/commands/locations.py +336 -0
- wxcli/commands/meeting_captions.py +110 -0
- wxcli/commands/meeting_chats.py +63 -0
- wxcli/commands/meeting_invitees.py +234 -0
- wxcli/commands/meeting_messages.py +28 -0
- wxcli/commands/meeting_participants.py +278 -0
- wxcli/commands/meeting_polls.py +111 -0
- wxcli/commands/meeting_preferences.py +515 -0
- wxcli/commands/meeting_qa.py +78 -0
- wxcli/commands/meeting_qualities.py +43 -0
- wxcli/commands/meeting_reports.py +101 -0
- wxcli/commands/meeting_session_types.py +105 -0
- wxcli/commands/meeting_site.py +57 -0
- wxcli/commands/meeting_slido.py +44 -0
- wxcli/commands/meeting_summaries.py +92 -0
- wxcli/commands/meeting_tracking_codes.py +233 -0
- wxcli/commands/meeting_transcripts.py +230 -0
- wxcli/commands/meetings.py +1997 -0
- wxcli/commands/memberships.py +164 -0
- wxcli/commands/messages.py +213 -0
- wxcli/commands/mode_management.py +244 -0
- wxcli/commands/my_call_settings.py +3577 -0
- wxcli/commands/numbers.py +405 -0
- wxcli/commands/operating_modes.py +410 -0
- wxcli/commands/org_contacts.py +284 -0
- wxcli/commands/org_health_cli.py +55 -0
- wxcli/commands/org_settings.py +71 -0
- wxcli/commands/organizations.py +83 -0
- wxcli/commands/paging_group.py +257 -0
- wxcli/commands/partner_admins.py +141 -0
- wxcli/commands/partner_reports.py +188 -0
- wxcli/commands/partner_tags.py +216 -0
- wxcli/commands/people.py +279 -0
- wxcli/commands/person_call_settings.py +71 -0
- wxcli/commands/pstn.py +280 -0
- wxcli/commands/recording_report.py +154 -0
- wxcli/commands/report_templates.py +38 -0
- wxcli/commands/reports.py +144 -0
- wxcli/commands/resource_group_memberships.py +160 -0
- wxcli/commands/resource_groups.py +67 -0
- wxcli/commands/roles.py +63 -0
- wxcli/commands/room_tabs.py +160 -0
- wxcli/commands/rooms.py +225 -0
- wxcli/commands/scim_bulk.py +46 -0
- wxcli/commands/scim_groups.py +246 -0
- wxcli/commands/scim_schemas.py +82 -0
- wxcli/commands/scim_users.py +283 -0
- wxcli/commands/security_audit.py +54 -0
- wxcli/commands/service_apps.py +51 -0
- wxcli/commands/single_number_reach.py +209 -0
- wxcli/commands/team_memberships.py +155 -0
- wxcli/commands/teams.py +153 -0
- wxcli/commands/ucm_profile.py +42 -0
- wxcli/commands/update.py +219 -0
- wxcli/commands/user_settings.py +5116 -0
- wxcli/commands/video_mesh.py +1090 -0
- wxcli/commands/virtual_extensions.py +502 -0
- wxcli/commands/virtual_line_settings.py +2072 -0
- wxcli/commands/webhooks.py +176 -0
- wxcli/commands/wholesale_billing_reports.py +147 -0
- wxcli/commands/wholesale_provisioning.py +523 -0
- wxcli/commands/workspace_locations.py +339 -0
- wxcli/commands/workspace_metrics.py +103 -0
- wxcli/commands/workspace_personalization.py +70 -0
- wxcli/commands/workspace_settings.py +3347 -0
- wxcli/commands/workspaces.py +279 -0
- wxcli/commands/xapi.py +114 -0
- wxcli/config.py +139 -0
- wxcli/errors.py +77 -0
- wxcli/main.py +210 -0
- wxcli/migration/CLAUDE.md +106 -0
- wxcli/migration/__init__.py +1 -0
- wxcli/migration/advisory/CLAUDE.md +196 -0
- wxcli/migration/advisory/__init__.py +46 -0
- wxcli/migration/advisory/advisor.py +107 -0
- wxcli/migration/advisory/advisory_patterns.py +2552 -0
- wxcli/migration/advisory/recommendation_rules.py +835 -0
- wxcli/migration/cucm/CLAUDE.md +74 -0
- wxcli/migration/cucm/__init__.py +5 -0
- wxcli/migration/cucm/connection.py +265 -0
- wxcli/migration/cucm/discovery.py +225 -0
- wxcli/migration/cucm/extractors/__init__.py +1 -0
- wxcli/migration/cucm/extractors/announcements.py +92 -0
- wxcli/migration/cucm/extractors/base.py +90 -0
- wxcli/migration/cucm/extractors/device_profiles.py +98 -0
- wxcli/migration/cucm/extractors/devices.py +193 -0
- wxcli/migration/cucm/extractors/e911.py +103 -0
- wxcli/migration/cucm/extractors/features.py +309 -0
- wxcli/migration/cucm/extractors/helpers.py +73 -0
- wxcli/migration/cucm/extractors/informational.py +295 -0
- wxcli/migration/cucm/extractors/locations.py +221 -0
- wxcli/migration/cucm/extractors/moh.py +100 -0
- wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
- wxcli/migration/cucm/extractors/routing.py +493 -0
- wxcli/migration/cucm/extractors/shared_lines.py +146 -0
- wxcli/migration/cucm/extractors/templates.py +229 -0
- wxcli/migration/cucm/extractors/tier4.py +173 -0
- wxcli/migration/cucm/extractors/users.py +240 -0
- wxcli/migration/cucm/extractors/voicemail.py +243 -0
- wxcli/migration/cucm/extractors/workspaces.py +88 -0
- wxcli/migration/cucm/unity_connection.py +304 -0
- wxcli/migration/execute/CLAUDE.md +372 -0
- wxcli/migration/execute/__init__.py +303 -0
- wxcli/migration/execute/batch.py +277 -0
- wxcli/migration/execute/dependency.py +483 -0
- wxcli/migration/execute/engine.py +964 -0
- wxcli/migration/execute/handlers.py +2232 -0
- wxcli/migration/execute/planner.py +1939 -0
- wxcli/migration/execute/runtime.py +571 -0
- wxcli/migration/export/CLAUDE.md +49 -0
- wxcli/migration/export/__init__.py +8 -0
- wxcli/migration/export/csv_export.py +126 -0
- wxcli/migration/export/deployment_plan.py +479 -0
- wxcli/migration/export/json_export.py +60 -0
- wxcli/migration/models.py +941 -0
- wxcli/migration/phone_models.py +50 -0
- wxcli/migration/preflight/CLAUDE.md +52 -0
- wxcli/migration/preflight/__init__.py +89 -0
- wxcli/migration/preflight/checks.py +911 -0
- wxcli/migration/preflight/runner.py +293 -0
- wxcli/migration/rate_limiter.py +144 -0
- wxcli/migration/report/CLAUDE.md +245 -0
- wxcli/migration/report/__init__.py +7 -0
- wxcli/migration/report/appendix.py +2437 -0
- wxcli/migration/report/assembler.py +183 -0
- wxcli/migration/report/charts.py +263 -0
- wxcli/migration/report/executive.py +776 -0
- wxcli/migration/report/explainer.py +704 -0
- wxcli/migration/report/helpers.py +74 -0
- wxcli/migration/report/ingest.py +152 -0
- wxcli/migration/report/notice_templates.py +192 -0
- wxcli/migration/report/score.py +375 -0
- wxcli/migration/report/styles.py +1052 -0
- wxcli/migration/report/user_diff.py +1052 -0
- wxcli/migration/report/user_notice.py +425 -0
- wxcli/migration/state.py +139 -0
- wxcli/migration/store.py +860 -0
- wxcli/migration/transform/CLAUDE.md +107 -0
- wxcli/migration/transform/__init__.py +12 -0
- wxcli/migration/transform/analysis_pipeline.py +453 -0
- wxcli/migration/transform/analyzers/__init__.py +142 -0
- wxcli/migration/transform/analyzers/css_permission.py +189 -0
- wxcli/migration/transform/analyzers/css_routing.py +335 -0
- wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
- wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
- wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
- wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
- wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
- wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
- wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
- wxcli/migration/transform/analyzers/missing_data.py +300 -0
- wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
- wxcli/migration/transform/analyzers/shared_line.py +170 -0
- wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
- wxcli/migration/transform/analyzers/workspace_license.py +212 -0
- wxcli/migration/transform/cross_reference.py +1136 -0
- wxcli/migration/transform/cucm_pattern.py +246 -0
- wxcli/migration/transform/decisions.py +301 -0
- wxcli/migration/transform/e164.py +145 -0
- wxcli/migration/transform/engine.py +256 -0
- wxcli/migration/transform/mappers/CLAUDE.md +123 -0
- wxcli/migration/transform/mappers/__init__.py +76 -0
- wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
- wxcli/migration/transform/mappers/base.py +190 -0
- wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
- wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
- wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
- wxcli/migration/transform/mappers/css_mapper.py +868 -0
- wxcli/migration/transform/mappers/dect_mapper.py +367 -0
- wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
- wxcli/migration/transform/mappers/device_mapper.py +227 -0
- wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
- wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
- wxcli/migration/transform/mappers/e911_mapper.py +129 -0
- wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
- wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
- wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
- wxcli/migration/transform/mappers/line_mapper.py +310 -0
- wxcli/migration/transform/mappers/location_mapper.py +235 -0
- wxcli/migration/transform/mappers/moh_mapper.py +114 -0
- wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
- wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
- wxcli/migration/transform/mappers/routing_mapper.py +821 -0
- wxcli/migration/transform/mappers/snr_mapper.py +201 -0
- wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
- wxcli/migration/transform/mappers/user_mapper.py +264 -0
- wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
- wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
- wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
- wxcli/migration/transform/normalizers.py +2008 -0
- wxcli/migration/transform/pattern_converter.py +106 -0
- wxcli/migration/transform/pipeline.py +271 -0
- wxcli/migration/transform/rules.py +255 -0
- wxcli/org_health/CLAUDE.md +63 -0
- wxcli/org_health/__init__.py +7 -0
- wxcli/org_health/analyze.py +93 -0
- wxcli/org_health/checks.py +458 -0
- wxcli/org_health/collector.py +62 -0
- wxcli/org_health/models.py +78 -0
- wxcli/org_health/report.py +300 -0
- wxcli/output.py +101 -0
- wxcli-1.2.0.dist-info/METADATA +505 -0
- wxcli-1.2.0.dist-info/RECORD +413 -0
- wxcli-1.2.0.dist-info/WHEEL +5 -0
- wxcli-1.2.0.dist-info/entry_points.txt +2 -0
- wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
- wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
- wxcli-1.2.0.dist-info/scm_version.json +8 -0
- wxcli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Base extractor class and ExtractionResult.
|
|
2
|
+
|
|
3
|
+
All AXL extractors (§2.1-2.6 of 02b) inherit from BaseExtractor.
|
|
4
|
+
|
|
5
|
+
Sources:
|
|
6
|
+
- 02b-cucm-extraction.md §3 (base extractor class, ExtractionResult)
|
|
7
|
+
- 02b-cucm-extraction.md §1 (error handling strategy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from wxcli.migration.cucm.connection import AXLConnection
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ExtractionResult:
|
|
24
|
+
"""Summary of one extractor's run.
|
|
25
|
+
|
|
26
|
+
(from 02b §3)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
extractor: str
|
|
30
|
+
total: int = 0
|
|
31
|
+
failed: int = 0
|
|
32
|
+
errors: list[str] = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def success_count(self) -> int:
|
|
36
|
+
return self.total - self.failed
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ExtractionError(Exception):
|
|
40
|
+
"""Raised when an extractor encounters an unrecoverable error.
|
|
41
|
+
|
|
42
|
+
(from 02b §1: error handling)
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, extractor: str, method: str, message: str) -> None:
|
|
46
|
+
self.extractor = extractor
|
|
47
|
+
self.method = method
|
|
48
|
+
super().__init__(f"[{extractor}] {method}: {message}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class BaseExtractor(ABC):
|
|
52
|
+
"""Base class for all CUCM AXL extractors.
|
|
53
|
+
|
|
54
|
+
(from 02b §3)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
name: str = ""
|
|
58
|
+
page_size: int = 200 # Default AXL page size (from cucm-wxc-migration.md line 307)
|
|
59
|
+
|
|
60
|
+
def __init__(self, connection: AXLConnection) -> None:
|
|
61
|
+
self.conn = connection
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def extract(self) -> ExtractionResult:
|
|
65
|
+
"""Run extraction. Returns count of objects extracted and any errors."""
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
def paginated_list(
|
|
69
|
+
self,
|
|
70
|
+
method_name: str,
|
|
71
|
+
search_criteria: dict[str, str],
|
|
72
|
+
returned_tags: dict[str, str],
|
|
73
|
+
page_size: int | None = None,
|
|
74
|
+
) -> list[dict[str, Any]]:
|
|
75
|
+
"""Generic paginated AXL list operation.
|
|
76
|
+
|
|
77
|
+
Uses first/skip pagination (from cucm-wxc-migration.md line 307).
|
|
78
|
+
(from 02b §3)
|
|
79
|
+
"""
|
|
80
|
+
effective_page_size = page_size or self.page_size
|
|
81
|
+
return self.conn.paginated_list(
|
|
82
|
+
method_name, search_criteria, returned_tags, effective_page_size
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def get_detail(self, method_name: str, **kwargs: Any) -> dict[str, Any] | None:
|
|
86
|
+
"""Single-object get operation (e.g. getPhone by name or UUID).
|
|
87
|
+
|
|
88
|
+
(from 02b §3)
|
|
89
|
+
"""
|
|
90
|
+
return self.conn.get_detail(method_name, **kwargs)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Device Profile extractor for CUCM AXL.
|
|
2
|
+
|
|
3
|
+
Pulls Extension Mobility Device Profiles from CUCM.
|
|
4
|
+
|
|
5
|
+
Sources:
|
|
6
|
+
- tier2-enterprise-expansion.md §5 (Extension Mobility extraction)
|
|
7
|
+
- AXL WSDL: listDeviceProfile / getDeviceProfile
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from wxcli.migration.cucm.extractors.base import BaseExtractor, ExtractionResult
|
|
16
|
+
from wxcli.migration.cucm.extractors.helpers import to_list
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
DEVICE_PROFILE_LIST_RETURNED_TAGS = {
|
|
21
|
+
"name": "",
|
|
22
|
+
"product": "",
|
|
23
|
+
"protocol": "",
|
|
24
|
+
"class": "",
|
|
25
|
+
"phoneTemplateName": "",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeviceProfileExtractor(BaseExtractor):
|
|
30
|
+
"""Extract Extension Mobility Device Profiles from CUCM AXL.
|
|
31
|
+
|
|
32
|
+
Device Profiles are virtual phone configurations that users log into.
|
|
33
|
+
When a user logs into a physical phone, the device profile's lines/settings
|
|
34
|
+
replace the phone's default config.
|
|
35
|
+
|
|
36
|
+
(from tier2-enterprise-expansion.md §5)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
name = "device_profiles"
|
|
40
|
+
|
|
41
|
+
def __init__(self, connection) -> None:
|
|
42
|
+
super().__init__(connection)
|
|
43
|
+
self.results: dict[str, list[dict[str, Any]]] = {}
|
|
44
|
+
|
|
45
|
+
def extract(self) -> ExtractionResult:
|
|
46
|
+
result = ExtractionResult(extractor=self.name)
|
|
47
|
+
self.results["device_profiles"] = self._extract_device_profiles(result)
|
|
48
|
+
return result
|
|
49
|
+
|
|
50
|
+
def _extract_device_profiles(
|
|
51
|
+
self, result: ExtractionResult
|
|
52
|
+
) -> list[dict[str, Any]]:
|
|
53
|
+
"""List all device profiles, then get full detail for each."""
|
|
54
|
+
profiles: list[dict[str, Any]] = []
|
|
55
|
+
try:
|
|
56
|
+
summaries = self.paginated_list(
|
|
57
|
+
"listDeviceProfile",
|
|
58
|
+
{"name": "%"},
|
|
59
|
+
DEVICE_PROFILE_LIST_RETURNED_TAGS,
|
|
60
|
+
)
|
|
61
|
+
except Exception as exc:
|
|
62
|
+
msg = f"listDeviceProfile failed: {exc}"
|
|
63
|
+
logger.error("[%s] %s", self.name, msg)
|
|
64
|
+
result.errors.append(msg)
|
|
65
|
+
return profiles
|
|
66
|
+
|
|
67
|
+
for summary in summaries:
|
|
68
|
+
result.total += 1
|
|
69
|
+
profile_name = summary.get("name", "")
|
|
70
|
+
if not profile_name:
|
|
71
|
+
result.failed += 1
|
|
72
|
+
continue
|
|
73
|
+
try:
|
|
74
|
+
detail = self.get_detail("getDeviceProfile", name=profile_name)
|
|
75
|
+
except Exception as exc:
|
|
76
|
+
logger.warning(
|
|
77
|
+
"[%s] getDeviceProfile error for %s: %s",
|
|
78
|
+
self.name, profile_name, exc,
|
|
79
|
+
)
|
|
80
|
+
profiles.append(summary)
|
|
81
|
+
continue
|
|
82
|
+
if detail is None:
|
|
83
|
+
profiles.append(summary)
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
# Normalize lines
|
|
87
|
+
lines_raw = detail.get("lines") or {}
|
|
88
|
+
detail["lines"] = to_list(lines_raw, "line")
|
|
89
|
+
|
|
90
|
+
# Count speed dials and BLF entries
|
|
91
|
+
sd_raw = detail.get("speeddials") or {}
|
|
92
|
+
detail["speeddials"] = to_list(sd_raw, "speeddial")
|
|
93
|
+
blf_raw = detail.get("busyLampFields") or {}
|
|
94
|
+
detail["busyLampFields"] = to_list(blf_raw, "busyLampField")
|
|
95
|
+
|
|
96
|
+
profiles.append(detail)
|
|
97
|
+
|
|
98
|
+
return profiles
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Device extractor — phones with line appearances.
|
|
2
|
+
|
|
3
|
+
Two-step extraction: listPhone discovers devices, then getPhone fetches
|
|
4
|
+
full detail including nested line/speed-dial data per device.
|
|
5
|
+
|
|
6
|
+
Sources:
|
|
7
|
+
- 02b-cucm-extraction.md §2.3 (device extraction, line appearances)
|
|
8
|
+
- 02b-cucm-extraction.md §3 (base extractor, pagination)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
from wxcli.migration.cucm.connection import AXLConnection
|
|
19
|
+
from wxcli.migration.cucm.extractors.base import BaseExtractor, ExtractionResult
|
|
20
|
+
from wxcli.migration.cucm.extractors.helpers import ref_value, to_list
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
# Step 1: listPhone — lightweight discovery
|
|
26
|
+
PHONE_LIST_RETURNED_TAGS = {
|
|
27
|
+
'name': '', 'model': '', 'ownerUserName': '', 'devicePoolName': '',
|
|
28
|
+
'protocol': '', 'description': '',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Step 2: getPhone — full detail per device
|
|
32
|
+
PHONE_GET_RETURNED_TAGS = {
|
|
33
|
+
'name': '', 'model': '', 'description': '', 'ownerUserName': '',
|
|
34
|
+
'devicePoolName': '', 'protocol': '', 'callingSearchSpaceName': '',
|
|
35
|
+
'phoneTemplateName': '', 'softkeyTemplateName': '', 'deviceMobilityMode': '',
|
|
36
|
+
'product': '', 'class': '', 'lines': '',
|
|
37
|
+
'speeddials': '', 'busyLampFields': '',
|
|
38
|
+
# Device settings fields (for DeviceSettingsMapper)
|
|
39
|
+
'commonPhoneConfigName': '', 'productSpecificConfiguration': '',
|
|
40
|
+
'userLocale': '', 'networkLocale': '',
|
|
41
|
+
'dndOption': '', 'dndStatus': '', 'enableExtensionMobility': '',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DeviceExtractor(BaseExtractor):
|
|
46
|
+
"""Extract phones and their line appearances from CUCM.
|
|
47
|
+
|
|
48
|
+
(from 02b §2.3)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
name = "devices"
|
|
52
|
+
|
|
53
|
+
def __init__(self, connection: AXLConnection) -> None:
|
|
54
|
+
super().__init__(connection)
|
|
55
|
+
self.results: dict[str, list[dict[str, Any]]] = {}
|
|
56
|
+
|
|
57
|
+
def extract(self) -> ExtractionResult:
|
|
58
|
+
"""Run two-step phone extraction.
|
|
59
|
+
|
|
60
|
+
Step 1: listPhone with pagination to discover all devices.
|
|
61
|
+
Step 2: getPhone per device for full line appearance data.
|
|
62
|
+
"""
|
|
63
|
+
result = ExtractionResult(extractor=self.name)
|
|
64
|
+
|
|
65
|
+
# Step 1: discover all phones
|
|
66
|
+
logger.info("[%s] Listing phones...", self.name)
|
|
67
|
+
try:
|
|
68
|
+
phone_summaries = self.paginated_list(
|
|
69
|
+
method_name="listPhone",
|
|
70
|
+
search_criteria={"name": "%"},
|
|
71
|
+
returned_tags=PHONE_LIST_RETURNED_TAGS,
|
|
72
|
+
)
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
msg = f"listPhone failed: {exc}"
|
|
75
|
+
logger.error("[%s] %s", self.name, msg)
|
|
76
|
+
result.errors.append(msg)
|
|
77
|
+
self.results["phones"] = []
|
|
78
|
+
return result
|
|
79
|
+
logger.info("[%s] Found %d phones", self.name, len(phone_summaries))
|
|
80
|
+
result.total = len(phone_summaries)
|
|
81
|
+
|
|
82
|
+
# Step 2: get full detail per phone
|
|
83
|
+
phones: list[dict[str, Any]] = []
|
|
84
|
+
total = len(phone_summaries)
|
|
85
|
+
for i, summary in enumerate(phone_summaries, 1):
|
|
86
|
+
if i % 200 == 0:
|
|
87
|
+
console.print(f" phones: {i}/{total}...")
|
|
88
|
+
phone_name = ref_value(summary.get("name")) or summary.get("name")
|
|
89
|
+
if not phone_name:
|
|
90
|
+
result.failed += 1
|
|
91
|
+
result.errors.append("Phone with no name in listPhone result")
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
detail = self._get_phone_detail(phone_name)
|
|
95
|
+
if detail is None:
|
|
96
|
+
result.failed += 1
|
|
97
|
+
result.errors.append(f"getPhone failed for {phone_name}")
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
phones.append(detail)
|
|
101
|
+
|
|
102
|
+
self.results["phones"] = phones
|
|
103
|
+
logger.info(
|
|
104
|
+
"[%s] Extracted %d/%d phones (%d failed)",
|
|
105
|
+
self.name, result.success_count, result.total, result.failed,
|
|
106
|
+
)
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
def _get_phone_detail(self, phone_name: str) -> dict[str, Any] | None:
|
|
110
|
+
"""Fetch full phone detail and normalize line appearances.
|
|
111
|
+
|
|
112
|
+
Handles edge cases:
|
|
113
|
+
- lines may be empty dict or None
|
|
114
|
+
- lines.line may be a single dict, not a list
|
|
115
|
+
- Speed dials are interleaved with lines — filter to entries with ``dirn``
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
detail = self.get_detail("getPhone", name=phone_name)
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
logger.warning("[%s] getPhone error for %s: %s", self.name, phone_name, exc)
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
if detail is None:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
# Normalize lines — may be None, empty dict, or populated
|
|
127
|
+
raw_lines = detail.get("lines")
|
|
128
|
+
line_entries = to_list(raw_lines, "line")
|
|
129
|
+
|
|
130
|
+
# Filter: only entries with a ``dirn`` key are real line appearances.
|
|
131
|
+
# Speed dial entries are interleaved but lack ``dirn``.
|
|
132
|
+
line_appearances = []
|
|
133
|
+
for entry in line_entries:
|
|
134
|
+
if not isinstance(entry, dict):
|
|
135
|
+
continue
|
|
136
|
+
if entry.get("dirn") is not None:
|
|
137
|
+
line_appearances.append(entry)
|
|
138
|
+
|
|
139
|
+
detail["lines"] = line_appearances
|
|
140
|
+
|
|
141
|
+
# Enrich each line with call forwarding settings from getLine.
|
|
142
|
+
# getPhone line entries do NOT include callForwardAll/Busy/NoAnswer —
|
|
143
|
+
# those are only on the Line AXL object. The voicemail_mapper needs
|
|
144
|
+
# these to determine sendAllCalls/sendBusyCalls/sendUnansweredCalls.
|
|
145
|
+
# Verified against live CUCM 15.0 (2026-03-23): getLine response
|
|
146
|
+
# includes callForwardAll, callForwardBusy, callForwardNoAnswer with
|
|
147
|
+
# forwardToVoiceMail, destination, and duration fields.
|
|
148
|
+
for line_entry in line_appearances:
|
|
149
|
+
dirn = line_entry.get("dirn")
|
|
150
|
+
if not isinstance(dirn, dict):
|
|
151
|
+
continue
|
|
152
|
+
pattern = dirn.get("pattern")
|
|
153
|
+
partition = ref_value(dirn.get("routePartitionName"))
|
|
154
|
+
if not pattern:
|
|
155
|
+
continue
|
|
156
|
+
self._enrich_line_with_forwarding(line_entry, pattern, partition)
|
|
157
|
+
|
|
158
|
+
return detail
|
|
159
|
+
|
|
160
|
+
def _enrich_line_with_forwarding(
|
|
161
|
+
self, line_entry: dict[str, Any], pattern: str, partition: str | None,
|
|
162
|
+
) -> None:
|
|
163
|
+
"""Fetch getLine for a DN and merge call forwarding settings into the line entry."""
|
|
164
|
+
try:
|
|
165
|
+
kwargs: dict[str, Any] = {"pattern": pattern}
|
|
166
|
+
if partition:
|
|
167
|
+
kwargs["routePartitionName"] = partition
|
|
168
|
+
line_detail = self.get_detail("getLine", **kwargs)
|
|
169
|
+
except Exception as exc:
|
|
170
|
+
logger.debug(
|
|
171
|
+
"[%s] getLine(%s/%s) failed: %s", self.name, pattern, partition, exc,
|
|
172
|
+
)
|
|
173
|
+
return
|
|
174
|
+
if line_detail is None:
|
|
175
|
+
return
|
|
176
|
+
# Merge forwarding fields into the line entry
|
|
177
|
+
for fwd_key in (
|
|
178
|
+
"callForwardAll", "callForwardBusy", "callForwardBusyInt",
|
|
179
|
+
"callForwardNoAnswer", "callForwardNoAnswerInt",
|
|
180
|
+
"callForwardNoCoverage", "callForwardNoCoverageInt",
|
|
181
|
+
"callForwardNotRegistered", "callForwardNotRegisteredInt",
|
|
182
|
+
"callForwardOnFailure",
|
|
183
|
+
):
|
|
184
|
+
if fwd_key in line_detail:
|
|
185
|
+
line_entry[fwd_key] = line_detail[fwd_key]
|
|
186
|
+
# Also grab voiceMailProfileName if present on the Line
|
|
187
|
+
if "voiceMailProfileName" in line_detail:
|
|
188
|
+
line_entry["voiceMailProfileName"] = line_detail["voiceMailProfileName"]
|
|
189
|
+
# Grab recording fields if present on the Line
|
|
190
|
+
if "recordingProfileName" in line_detail:
|
|
191
|
+
line_entry["recordingProfileName"] = line_detail["recordingProfileName"]
|
|
192
|
+
if "recordingFlag" in line_detail:
|
|
193
|
+
line_entry["recordingFlag"] = line_detail["recordingFlag"]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""E911 extractor for CUCM AXL.
|
|
2
|
+
|
|
3
|
+
Pulls ELIN groups and geographic locations from CUCM for E911 advisory.
|
|
4
|
+
|
|
5
|
+
Sources:
|
|
6
|
+
- tier2-enterprise-expansion.md §6 (E911/ELIN extraction)
|
|
7
|
+
- AXL WSDL: listElinGroup / getElinGroup, listGeoLocation / getGeoLocation
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from wxcli.migration.cucm.extractors.base import BaseExtractor, ExtractionResult
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
ELIN_GROUP_LIST_RETURNED_TAGS = {
|
|
20
|
+
"name": "",
|
|
21
|
+
"elinNumbers": "",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
GEO_LOCATION_LIST_RETURNED_TAGS = {
|
|
25
|
+
"name": "",
|
|
26
|
+
"description": "",
|
|
27
|
+
"country": "",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class E911Extractor(BaseExtractor):
|
|
32
|
+
"""Extract E911 ELIN groups and geographic locations from CUCM AXL.
|
|
33
|
+
|
|
34
|
+
ELIN groups define E.164 numbers reserved for E911 callback.
|
|
35
|
+
Geographic locations define E911 routing by country/region.
|
|
36
|
+
CER (Cisco Emergency Responder) is out of scope — it has its own DB.
|
|
37
|
+
|
|
38
|
+
(from tier2-enterprise-expansion.md §6)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
name = "e911"
|
|
42
|
+
|
|
43
|
+
def __init__(self, connection) -> None:
|
|
44
|
+
super().__init__(connection)
|
|
45
|
+
self.results: dict[str, list[dict[str, Any]]] = {}
|
|
46
|
+
|
|
47
|
+
def extract(self) -> ExtractionResult:
|
|
48
|
+
result = ExtractionResult(extractor=self.name)
|
|
49
|
+
self.results["elin_groups"] = self._extract_elin_groups(result)
|
|
50
|
+
self.results["geo_locations"] = self._extract_geo_locations(result)
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
def _extract_elin_groups(self, result: ExtractionResult) -> list[dict[str, Any]]:
|
|
54
|
+
"""List all ELIN groups."""
|
|
55
|
+
groups: list[dict[str, Any]] = []
|
|
56
|
+
try:
|
|
57
|
+
summaries = self.paginated_list(
|
|
58
|
+
"listElinGroup",
|
|
59
|
+
{"name": "%"},
|
|
60
|
+
ELIN_GROUP_LIST_RETURNED_TAGS,
|
|
61
|
+
)
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
msg = f"listElinGroup failed: {exc}"
|
|
64
|
+
logger.error("[%s] %s", self.name, msg)
|
|
65
|
+
result.errors.append(msg)
|
|
66
|
+
return groups
|
|
67
|
+
|
|
68
|
+
for summary in summaries:
|
|
69
|
+
result.total += 1
|
|
70
|
+
name = summary.get("name", "")
|
|
71
|
+
if not name:
|
|
72
|
+
result.failed += 1
|
|
73
|
+
continue
|
|
74
|
+
try:
|
|
75
|
+
detail = self.get_detail("getElinGroup", name=name)
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
logger.warning("[%s] getElinGroup error for %s: %s", self.name, name, exc)
|
|
78
|
+
groups.append(summary)
|
|
79
|
+
continue
|
|
80
|
+
if detail is None:
|
|
81
|
+
groups.append(summary)
|
|
82
|
+
continue
|
|
83
|
+
groups.append({**summary, **detail})
|
|
84
|
+
|
|
85
|
+
return groups
|
|
86
|
+
|
|
87
|
+
def _extract_geo_locations(self, result: ExtractionResult) -> list[dict[str, Any]]:
|
|
88
|
+
"""List all geographic locations."""
|
|
89
|
+
locations: list[dict[str, Any]] = []
|
|
90
|
+
try:
|
|
91
|
+
summaries = self.paginated_list(
|
|
92
|
+
"listGeoLocation",
|
|
93
|
+
{"name": "%"},
|
|
94
|
+
GEO_LOCATION_LIST_RETURNED_TAGS,
|
|
95
|
+
)
|
|
96
|
+
locations.extend(summaries)
|
|
97
|
+
result.total += len(summaries)
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
msg = f"listGeoLocation failed: {exc}"
|
|
100
|
+
logger.error("[%s] %s", self.name, msg)
|
|
101
|
+
result.errors.append(msg)
|
|
102
|
+
|
|
103
|
+
return locations
|