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.
Files changed (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,514 @@
1
+ """Voicemail mapper: CUCM Unity Connection VM profiles -> Webex per-person voicemail.
2
+
3
+ Maps Unity Connection voicemail profiles (shared config) plus per-user VM settings
4
+ to Webex Calling per-person voicemail settings (CanonicalVoicemailProfile).
5
+
6
+ (from 03b-transform-mappers.md §9, voicemail_mapper)
7
+
8
+ Cross-ref reads:
9
+ user_has_voicemail_profile (#26) — EndUser -> VoicemailProfile
10
+ voicemail_profile_settings (#27) — VoicemailProfile -> settings
11
+
12
+ Per-user VM settings:
13
+ store.get_object(f"unity_vm:{userid}") — per-user Unity Connection settings
14
+ (from Phase 04 corrections: unity_vm objects for notifications, greetings, etc.)
15
+
16
+ Decisions generated:
17
+ VOICEMAIL_INCOMPATIBLE — UC features without Webex equivalent (13-row gap table §11)
18
+ MISSING_DATA — custom greeting audio files can't be extracted
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ from typing import Any
25
+
26
+ from wxcli.migration.models import (
27
+ CanonicalVoicemailProfile,
28
+ DecisionType,
29
+ MapperResult,
30
+ MigrationStatus,
31
+ )
32
+ from wxcli.migration.store import MigrationStore
33
+ from wxcli.migration.transform.mappers.base import (
34
+ Mapper,
35
+ accept_option,
36
+ decision_to_store_dict,
37
+ enrich_user,
38
+ extract_provenance,
39
+ hash_id,
40
+ manual_option,
41
+ skip_option,
42
+ )
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+ # Read-only fields that must be stripped from Webex voicemail PUT
47
+ # (from 03b-transform-mappers.md §9: "Read-only fields must be stripped")
48
+ # (from docs/reference/person-call-settings-media.md: voiceMessageForwardingEnabled,
49
+ # greetingUploaded, systemMaxNumberOfRings)
50
+ _READ_ONLY_FIELDS = frozenset([
51
+ "voiceMessageForwardingEnabled",
52
+ "voice_message_forwarding_enabled",
53
+ "greetingUploaded",
54
+ "greeting_uploaded",
55
+ "systemMaxNumberOfRings",
56
+ "system_max_number_of_rings",
57
+ ])
58
+
59
+ # Default number of rings if CFNA timeout can't be parsed
60
+ _DEFAULT_NUMBER_OF_RINGS = 3
61
+
62
+ # Max ring count (system max is typically 20)
63
+ # (from 03b-transform-mappers.md §9: "clamp to [1, systemMaxNumberOfRings]")
64
+ _MAX_NUMBER_OF_RINGS = 20
65
+
66
+ # Voicemail gap analysis: Unity Connection features to check
67
+ # (from 03b-transform-mappers.md §11: 13-row gap table)
68
+ # Each tuple: (check_key, description, condition_fn)
69
+ # condition_fn takes (profile_settings, unity_vm_settings) and returns True if gap detected
70
+ _VM_GAP_CHECKS: list[tuple[str, str, Any]] = [
71
+ (
72
+ "multiple_greetings",
73
+ "Multiple greetings per time-of-day (UC supports 6+; Webex has 2)",
74
+ lambda p, u: (
75
+ (p.get("alternateGreeting") is not None)
76
+ or (p.get("offHoursGreeting") is not None)
77
+ or (p.get("internalGreeting") is not None)
78
+ or (p.get("holidayGreeting") is not None)
79
+ or (u.get("alternateGreeting") is not None)
80
+ or (u.get("offHoursGreeting") is not None)
81
+ or (u.get("internalGreeting") is not None)
82
+ or (u.get("holidayGreeting") is not None)
83
+ ),
84
+ ),
85
+ (
86
+ "caller_input_rules",
87
+ "Caller input rules (press digits during greeting for routing)",
88
+ lambda p, u: bool(
89
+ p.get("callerInputRules") or u.get("callerInputRules")
90
+ ),
91
+ ),
92
+ (
93
+ "alternate_extensions",
94
+ "Alternate extensions (additional pilot numbers)",
95
+ lambda p, u: bool(
96
+ p.get("alternateExtensions") or u.get("alternateExtensions")
97
+ ),
98
+ ),
99
+ (
100
+ "message_expiry",
101
+ "Message expiry/retention policies",
102
+ lambda p, u: bool(
103
+ p.get("messageExpiry") or p.get("retentionPolicy")
104
+ or u.get("messageExpiry") or u.get("retentionPolicy")
105
+ ),
106
+ ),
107
+ (
108
+ "vpim_networking",
109
+ "VPIM networking (inter-system VM messaging)",
110
+ lambda p, u: bool(
111
+ p.get("vpimEnabled") or u.get("vpimEnabled")
112
+ ),
113
+ ),
114
+ (
115
+ "dispatch_messaging",
116
+ "Dispatch messaging (round-robin VM to group)",
117
+ lambda p, u: bool(
118
+ p.get("dispatchMessaging") or u.get("dispatchMessaging")
119
+ ),
120
+ ),
121
+ (
122
+ "secure_private_messages",
123
+ "Secure/private messages (encryption flag)",
124
+ lambda p, u: bool(
125
+ p.get("secureMessaging") or p.get("privateMessages")
126
+ or u.get("secureMessaging") or u.get("privateMessages")
127
+ ),
128
+ ),
129
+ (
130
+ "live_record",
131
+ "Live Record (record active call to voicemail inbox)",
132
+ lambda p, u: bool(
133
+ p.get("liveRecord") or u.get("liveRecord")
134
+ ),
135
+ ),
136
+ (
137
+ "pager_sms_notification",
138
+ "Pager/SMS notification (no Webex equivalent)",
139
+ lambda p, u: bool(
140
+ p.get("pagerNotification") or p.get("smsNotification")
141
+ or u.get("pagerNotification") or u.get("smsNotification")
142
+ ),
143
+ ),
144
+ (
145
+ "fax_internal_storage",
146
+ "Fax with internal storage (fax requires external storage in Webex)",
147
+ lambda p, u: (
148
+ (p.get("faxEnabled") or u.get("faxEnabled"))
149
+ and (p.get("storageType") or u.get("storageType", "INTERNAL")) != "EXTERNAL"
150
+ ),
151
+ ),
152
+ ]
153
+
154
+
155
+ class VoicemailMapper(Mapper):
156
+ """Maps CUCM Unity Connection voicemail profiles to Webex per-person VM settings.
157
+
158
+ Joins two data sources per user:
159
+ 1. voicemail_profile (shared config via cross-ref #26, #27)
160
+ 2. unity_vm:{userid} (per-user settings from Unity Connection)
161
+
162
+ (from 03b-transform-mappers.md §9)
163
+ """
164
+
165
+ name = "voicemail_mapper"
166
+ depends_on = ["user_mapper"]
167
+
168
+ def map(self, store: MigrationStore) -> MapperResult:
169
+ """Read CUCM users with voicemail profiles and produce VM settings."""
170
+ result = MapperResult()
171
+
172
+ for user_data in store.get_objects("user"):
173
+ user_id = user_data["canonical_id"]
174
+
175
+ # --- Resolve voicemail profile via cross-ref (#26) ---
176
+ vm_profile_refs = store.find_cross_refs(
177
+ user_id, "user_has_voicemail_profile"
178
+ )
179
+ if not vm_profile_refs:
180
+ # User has no voicemail profile — skip
181
+ continue
182
+
183
+ # --- Load shared profile settings via cross-ref (#27) ---
184
+ profile_id = vm_profile_refs[0]
185
+ profile_data = store.get_object(profile_id)
186
+ profile_state = (profile_data or {}).get("pre_migration_state") or {}
187
+
188
+ # Resolve detailed settings via voicemail_profile_settings cross-ref
189
+ settings_refs = store.find_cross_refs(
190
+ profile_id, "voicemail_profile_settings"
191
+ )
192
+ profile_settings: dict[str, Any] = {}
193
+ if settings_refs:
194
+ settings_data = store.get_object(settings_refs[0])
195
+ if settings_data:
196
+ profile_settings = (
197
+ settings_data.get("pre_migration_state") or {}
198
+ )
199
+
200
+ # Merge profile_state into profile_settings (profile_state overrides)
201
+ merged_profile = {**profile_settings, **profile_state}
202
+
203
+ # --- Load per-user Unity VM settings ---
204
+ # (from Phase 04 corrections: unity_vm:{userid} objects)
205
+ # Try top-level cucm_userid first (CanonicalUser after UserMapper),
206
+ # fall back to pre_migration_state (raw MigrationObject before UserMapper)
207
+ user_cucm_id = user_data.get("cucm_userid") or (
208
+ user_data.get("pre_migration_state") or {}
209
+ ).get("cucm_userid")
210
+ unity_vm_state: dict[str, Any] = {}
211
+ if user_cucm_id:
212
+ unity_vm_data = store.get_object(f"unity_vm:{user_cucm_id}")
213
+ if unity_vm_data:
214
+ unity_vm_state = (
215
+ unity_vm_data.get("pre_migration_state") or {}
216
+ )
217
+
218
+ # Also try the canonical_id-based key
219
+ if not unity_vm_state:
220
+ user_name = user_id.split(":", 1)[-1] if ":" in user_id else user_id
221
+ unity_vm_data = store.get_object(f"unity_vm:{user_name}")
222
+ if unity_vm_data:
223
+ unity_vm_state = (
224
+ unity_vm_data.get("pre_migration_state") or {}
225
+ )
226
+
227
+ # --- Build Webex voicemail settings ---
228
+ vm_profile = self._build_voicemail_profile(
229
+ store, user_id, user_data, merged_profile, unity_vm_state
230
+ )
231
+ store.upsert_object(vm_profile)
232
+ result.objects_created += 1
233
+
234
+ # --- Enrich user object with voicemail reference ---
235
+ # The planner checks user.voicemail_profile_id to decide whether
236
+ # to produce a configure_voicemail operation.
237
+ enrich_user(store, user_id, voicemail_profile_id=vm_profile.canonical_id)
238
+
239
+ # --- Gap analysis: check all 13 rows ---
240
+ # (from 03b-transform-mappers.md §11: Voicemail Gap Analysis)
241
+ gaps = self._detect_gaps(merged_profile, unity_vm_state)
242
+ if gaps:
243
+ gap_descriptions = [desc for _, desc in gaps]
244
+ decision = self._create_decision(
245
+ store=store,
246
+ decision_type=DecisionType.VOICEMAIL_INCOMPATIBLE,
247
+ severity="MEDIUM",
248
+ summary=(
249
+ f"User '{user_id.split(':', 1)[-1]}' uses "
250
+ f"{len(gaps)} Unity Connection feature(s) without "
251
+ f"Webex equivalent: {', '.join(k for k, _ in gaps)}"
252
+ ),
253
+ context={
254
+ "user_id": user_id,
255
+ "gaps": [
256
+ {"feature": key, "description": desc}
257
+ for key, desc in gaps
258
+ ],
259
+ "gap_count": len(gaps),
260
+ },
261
+ options=[
262
+ accept_option(
263
+ f"Accept loss of {len(gaps)} feature(s) — "
264
+ f"document what's lost"
265
+ ),
266
+ skip_option(
267
+ "Skip voicemail migration — configure manually"
268
+ ),
269
+ manual_option(
270
+ "Flag for post-migration manual configuration"
271
+ ),
272
+ ],
273
+ affected_objects=[user_id],
274
+ )
275
+ store.save_decision(decision_to_store_dict(decision))
276
+ result.decisions.append(decision)
277
+
278
+ # --- MISSING_DATA for custom greetings ---
279
+ has_custom_greeting = (
280
+ merged_profile.get("customBusyGreeting")
281
+ or merged_profile.get("customNoAnswerGreeting")
282
+ or unity_vm_state.get("customBusyGreeting")
283
+ or unity_vm_state.get("customNoAnswerGreeting")
284
+ )
285
+ if has_custom_greeting:
286
+ decision = self._create_decision(
287
+ store=store,
288
+ decision_type=DecisionType.MISSING_DATA,
289
+ severity="MEDIUM",
290
+ summary=(
291
+ f"Custom greeting audio for user "
292
+ f"'{user_id.split(':', 1)[-1]}' can't be extracted "
293
+ f"from Unity Connection"
294
+ ),
295
+ context={
296
+ "user_id": user_id,
297
+ "reason": "custom_greeting_not_extractable",
298
+ },
299
+ options=[
300
+ accept_option("Use DEFAULT greetings"),
301
+ manual_option(
302
+ "Admin provides audio files manually post-migration"
303
+ ),
304
+ ],
305
+ affected_objects=[user_id],
306
+ )
307
+ store.save_decision(decision_to_store_dict(decision))
308
+ result.decisions.append(decision)
309
+
310
+ return result
311
+
312
+ def _build_voicemail_profile(
313
+ self,
314
+ store: MigrationStore,
315
+ user_id: str,
316
+ user_data: dict[str, Any],
317
+ profile_settings: dict[str, Any],
318
+ unity_vm_state: dict[str, Any],
319
+ ) -> CanonicalVoicemailProfile:
320
+ """Build a CanonicalVoicemailProfile from profile + per-user settings.
321
+
322
+ (from 03b-transform-mappers.md §9: field mapping table)
323
+ """
324
+ # --- send_all_calls ---
325
+ # Check device line appearances for call_forward_all to VM
326
+ # (from Phase 04 corrections: call forwarding on line appearances)
327
+ send_all_enabled = bool(
328
+ profile_settings.get("callForwardAll")
329
+ or unity_vm_state.get("callForwardAll")
330
+ )
331
+ send_all_calls = {"enabled": send_all_enabled}
332
+
333
+ # --- send_busy_calls ---
334
+ send_busy_enabled = bool(
335
+ profile_settings.get("callForwardBusy")
336
+ or unity_vm_state.get("callForwardBusy")
337
+ )
338
+ busy_greeting = (
339
+ "CUSTOM"
340
+ if (
341
+ profile_settings.get("customBusyGreeting")
342
+ or unity_vm_state.get("customBusyGreeting")
343
+ )
344
+ else "DEFAULT"
345
+ )
346
+ send_busy_calls = {
347
+ "enabled": send_busy_enabled,
348
+ "greeting": busy_greeting,
349
+ }
350
+
351
+ # --- send_unanswered_calls ---
352
+ # Preserve explicit opt-out from CUCM; default to True (Webex default)
353
+ cfna_setting = (
354
+ profile_settings.get("callForwardNoAnswer")
355
+ or unity_vm_state.get("callForwardNoAnswer")
356
+ )
357
+ send_unanswered_enabled = cfna_setting if cfna_setting is not None else True
358
+ unanswered_greeting = (
359
+ "CUSTOM"
360
+ if (
361
+ profile_settings.get("customNoAnswerGreeting")
362
+ or unity_vm_state.get("customNoAnswerGreeting")
363
+ )
364
+ else "DEFAULT"
365
+ )
366
+
367
+ # --- CFNA timeout -> numberOfRings ---
368
+ # (from 03b-transform-mappers.md §9: "CUCM timeout in seconds ÷ 6")
369
+ cfna_timeout = (
370
+ unity_vm_state.get("cfnaTimeout")
371
+ or profile_settings.get("cfnaTimeout")
372
+ )
373
+ number_of_rings = _DEFAULT_NUMBER_OF_RINGS
374
+ if cfna_timeout is not None:
375
+ try:
376
+ computed_rings = max(1, int(cfna_timeout) // 6)
377
+ number_of_rings = min(computed_rings, _MAX_NUMBER_OF_RINGS)
378
+ except (ValueError, TypeError):
379
+ number_of_rings = _DEFAULT_NUMBER_OF_RINGS
380
+
381
+ send_unanswered_calls = {
382
+ "enabled": send_unanswered_enabled,
383
+ "greeting": unanswered_greeting,
384
+ "numberOfRings": number_of_rings,
385
+ }
386
+
387
+ # --- notifications ---
388
+ notification_enabled = bool(
389
+ profile_settings.get("notificationEnabled")
390
+ or unity_vm_state.get("notificationEnabled")
391
+ )
392
+ notification_dest = (
393
+ unity_vm_state.get("notificationEmail")
394
+ or profile_settings.get("notificationEmail")
395
+ )
396
+ notifications = {
397
+ "enabled": notification_enabled,
398
+ }
399
+ if notification_dest:
400
+ notifications["destination"] = notification_dest
401
+
402
+ # --- email_copy_of_message ---
403
+ email_copy_enabled = bool(
404
+ profile_settings.get("emailCopyEnabled")
405
+ or unity_vm_state.get("emailCopyEnabled")
406
+ )
407
+ email_copy_address = (
408
+ unity_vm_state.get("emailCopyAddress")
409
+ or profile_settings.get("emailCopyAddress")
410
+ )
411
+ email_copy = {"enabled": email_copy_enabled}
412
+ if email_copy_address:
413
+ email_copy["emailId"] = email_copy_address
414
+
415
+ # --- message_storage ---
416
+ storage_type = (
417
+ unity_vm_state.get("storageType")
418
+ or profile_settings.get("storageType")
419
+ or "INTERNAL"
420
+ )
421
+ mwi_enabled = unity_vm_state.get(
422
+ "mwiEnabled", profile_settings.get("mwiEnabled", True)
423
+ )
424
+ message_storage: dict[str, Any] = {
425
+ "storageType": storage_type,
426
+ "mwiEnabled": mwi_enabled,
427
+ }
428
+ if storage_type == "EXTERNAL":
429
+ external_email = (
430
+ unity_vm_state.get("externalEmail")
431
+ or profile_settings.get("externalEmail")
432
+ )
433
+ if external_email:
434
+ message_storage["externalEmail"] = external_email
435
+ else:
436
+ # Fall back to INTERNAL if no external email
437
+ # (from 03b-transform-mappers.md §9: edge case)
438
+ message_storage["storageType"] = "INTERNAL"
439
+
440
+ # --- fax_message ---
441
+ fax_enabled = bool(
442
+ profile_settings.get("faxEnabled")
443
+ or unity_vm_state.get("faxEnabled")
444
+ )
445
+ fax_message: dict[str, Any] = {"enabled": fax_enabled}
446
+ if fax_enabled:
447
+ fax_phone = (
448
+ unity_vm_state.get("faxPhoneNumber")
449
+ or profile_settings.get("faxPhoneNumber")
450
+ )
451
+ if fax_phone:
452
+ fax_message["phoneNumber"] = fax_phone
453
+ fax_ext = (
454
+ unity_vm_state.get("faxExtension")
455
+ or profile_settings.get("faxExtension")
456
+ )
457
+ if fax_ext:
458
+ fax_message["extension"] = fax_ext
459
+
460
+ # --- transfer_to_number ---
461
+ transfer_enabled = bool(
462
+ profile_settings.get("transferToZero")
463
+ or unity_vm_state.get("transferToZero")
464
+ )
465
+ transfer_dest = (
466
+ unity_vm_state.get("transferToZeroDestination")
467
+ or profile_settings.get("transferToZeroDestination")
468
+ )
469
+ transfer_to_number: dict[str, Any] = {"enabled": transfer_enabled}
470
+ if transfer_dest:
471
+ transfer_to_number["destination"] = transfer_dest
472
+
473
+ # --- Profile name ---
474
+ cucm_profile_name = profile_settings.get("voicemail_profile_name")
475
+
476
+ return CanonicalVoicemailProfile(
477
+ canonical_id=f"voicemail_profile:{hash_id(user_id)}",
478
+ provenance=extract_provenance(user_data),
479
+ status=MigrationStatus.ANALYZED,
480
+ enabled=True,
481
+ send_all_calls=send_all_calls,
482
+ send_busy_calls=send_busy_calls,
483
+ send_unanswered_calls=send_unanswered_calls,
484
+ notifications=notifications,
485
+ email_copy_of_message=email_copy,
486
+ message_storage=message_storage,
487
+ fax_message=fax_message,
488
+ transfer_to_number=transfer_to_number,
489
+ cucm_voicemail_profile_name=cucm_profile_name,
490
+ user_canonical_id=user_id,
491
+ )
492
+
493
+ def _detect_gaps(
494
+ self,
495
+ profile_settings: dict[str, Any],
496
+ unity_vm_state: dict[str, Any],
497
+ ) -> list[tuple[str, str]]:
498
+ """Scan for Unity Connection features without Webex equivalents.
499
+
500
+ Returns list of (gap_key, description) for each detected gap.
501
+
502
+ (from 03b-transform-mappers.md §11: Voicemail Gap Analysis table)
503
+ """
504
+ gaps: list[tuple[str, str]] = []
505
+ for key, description, check_fn in _VM_GAP_CHECKS:
506
+ try:
507
+ if check_fn(profile_settings, unity_vm_state):
508
+ gaps.append((key, description))
509
+ except (KeyError, TypeError, AttributeError):
510
+ # Gracefully handle missing keys in check functions
511
+ continue
512
+ return gaps
513
+
514
+