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,464 @@
1
+ """Workspace mapper: CUCM Common-area Phones -> Webex Workspaces.
2
+
3
+ Maps CUCM common-area phones (conference rooms, lobby phones, break rooms) to
4
+ CanonicalWorkspace objects with associated device references.
5
+
6
+ (from 03b-transform-mappers.md §5, workspace_mapper)
7
+
8
+ Cross-ref reads:
9
+ common_area_device_in_pool (Common-area device -> DevicePool)
10
+ device_pool_to_location (DevicePool -> Location)
11
+ device_has_dn (Device -> DN)
12
+
13
+ Decisions generated:
14
+ WORKSPACE_LICENSE_TIER — Workspace vs Professional Workspace license determination
15
+ WORKSPACE_TYPE_UNCERTAIN — ambiguous workspace classification
16
+ HOTDESK_DN_CONFLICT — device has both DN and hoteling enabled
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ from typing import Any
23
+
24
+ from wxcli.migration.models import (
25
+ CanonicalWorkspace,
26
+ DecisionType,
27
+ DeviceCompatibilityTier,
28
+ MapperResult,
29
+ MigrationStatus,
30
+ )
31
+ from wxcli.migration.store import MigrationStore
32
+ from wxcli.migration.transform.cross_reference import classify_phone_model
33
+ from wxcli.migration.transform.mappers.base import (
34
+ Mapper,
35
+ accept_option,
36
+ decision_to_store_dict,
37
+ extract_provenance,
38
+ manual_option,
39
+ skip_option,
40
+ )
41
+ from wxcli.migration.cucm.extractors.helpers import ref_value
42
+ from wxcli.migration.transform.mappers.call_forwarding_mapper import _duration_to_rings
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+ # Keywords that hint at workspace type classification
47
+ # (from 03b-transform-mappers.md §5: heuristic for type inference)
48
+ _MEETING_ROOM_HINTS = {"conf", "conference", "meeting", "room", "boardroom", "huddle"}
49
+ _DESK_HINTS = {"desk", "hotdesk", "hot-desk", "shared", "hoteling"}
50
+
51
+
52
+ class WorkspaceMapper(Mapper):
53
+ """Maps CUCM common-area phones to Webex Workspaces.
54
+
55
+ Only processes phones/devices flagged as is_common_area=True by the normalizer.
56
+
57
+ (from 03b-transform-mappers.md §5)
58
+ """
59
+
60
+ name = "workspace_mapper"
61
+ depends_on = ["location_mapper"]
62
+
63
+ def map(self, store: MigrationStore) -> MapperResult:
64
+ """Read common-area phone objects and produce CanonicalWorkspace objects."""
65
+ result = MapperResult()
66
+
67
+ for phone_data in store.get_objects("phone"):
68
+ phone_id = phone_data["canonical_id"]
69
+ # pre_migration_state here is the raw AXL getPhone dict (preserved by pipeline.py
70
+ # alongside the CanonicalDevice). Field names use original CUCM casing, not the
71
+ # normalized CanonicalDevice field names.
72
+ state = phone_data.get("pre_migration_state") or {}
73
+
74
+ # Only process common-area phones
75
+ # (from 03b-transform-mappers.md §5: "only processes phones flagged as is_common_area=true")
76
+ if not state.get("is_common_area", False):
77
+ continue
78
+
79
+ device_name = state.get("name", phone_id.replace("phone:", ""))
80
+ model = state.get("model")
81
+
82
+ # --- Location resolution ---
83
+ # Chain: common-area device -> common_area_device_in_pool -> device_pool_to_location
84
+ # (from 03b-transform-mappers.md §5: cross-ref dependencies)
85
+ location_id = store.resolve_chain(
86
+ phone_id, "common_area_device_in_pool", "device_pool_to_location"
87
+ )
88
+
89
+ # --- Extension / phone number resolution ---
90
+ # Primary line (line 1) from device_has_dn cross-ref
91
+ # (from 03b-transform-mappers.md §5: "DN from device line 1")
92
+ extension = None
93
+ phone_number = None
94
+ dn_refs = store.find_cross_refs(phone_id, "device_has_dn")
95
+ if dn_refs:
96
+ # Use the first DN (primary line)
97
+ dn_id = dn_refs[0]
98
+ # DN IDs are formatted as "dn:pattern:partition"
99
+ parts = dn_id.split(":", 2)
100
+ if len(parts) >= 2:
101
+ dn_pattern = parts[1]
102
+ # If it looks like E.164 (starts with +), it's a phone_number
103
+ if dn_pattern.startswith("+"):
104
+ phone_number = dn_pattern
105
+ else:
106
+ extension = dn_pattern
107
+
108
+ # Check if there's e164 enrichment data in the store
109
+ dn_data = store.get_object(dn_id)
110
+ if dn_data:
111
+ dn_state = dn_data.get("pre_migration_state") or {}
112
+ e164_result = dn_state.get("e164_result")
113
+ if e164_result:
114
+ if e164_result.get("e164"):
115
+ phone_number = e164_result["e164"]
116
+ if e164_result.get("extension"):
117
+ extension = e164_result["extension"]
118
+
119
+ # --- Display name ---
120
+ # (from 03b-transform-mappers.md §5: "description, fallback to device name")
121
+ display_name = state.get("description") or device_name
122
+
123
+ # --- Supported devices ---
124
+ # (from 03b-transform-mappers.md §5: "phones for MPP, collaborationDevices for Room/Board/Desk")
125
+ supported_devices = _infer_supported_devices(model)
126
+
127
+ # --- Hotdesking status ---
128
+ # enableExtensionMobility is the CUCM field (not 'hotelingEnabled')
129
+ # (from 02b-cucm-extraction.md §2.5: enableExtensionMobility on Phone)
130
+ em_val = state.get("enableExtensionMobility", False)
131
+ hotdesking_status = "on" if em_val in (True, "true", "True") else "off"
132
+
133
+ # --- Workspace type inference ---
134
+ # (from 03b-transform-mappers.md §5: heuristic from device pool name/description)
135
+ dp_name = state.get("cucm_device_pool", "")
136
+ workspace_type = _infer_workspace_type(display_name, dp_name)
137
+
138
+ # --- License tier inference ---
139
+ # (from 03b-transform-mappers.md §5: Workspace vs Professional Workspace)
140
+ # Default to Workspace; Professional Workspace if advanced settings are needed
141
+ license_tier = _infer_license_tier(state)
142
+
143
+ # --- Hotdesk + DN conflict check ---
144
+ # (from 03b-transform-mappers.md §5: "Webex hot desk workspaces cannot have extension")
145
+ has_dn = extension is not None or phone_number is not None
146
+ is_hotdesk = hotdesking_status == "on"
147
+
148
+ if has_dn and is_hotdesk:
149
+ decision = self._create_decision(
150
+ store=store,
151
+ decision_type=DecisionType.HOTDESK_DN_CONFLICT,
152
+ severity="MEDIUM",
153
+ summary=(
154
+ f"Common-area device '{device_name}' has both a DN and "
155
+ f"hoteling enabled — Webex hot desk workspaces cannot have "
156
+ f"extension or phoneNumber"
157
+ ),
158
+ context={
159
+ "device_name": device_name,
160
+ "extension": extension,
161
+ "phone_number": phone_number,
162
+ "hotdesking_status": hotdesking_status,
163
+ },
164
+ options=[
165
+ accept_option("Preserve extension — disable hot desking"),
166
+ accept_option("Preserve hot desking — drop the DN"),
167
+ manual_option("Admin decides how to resolve this conflict"),
168
+ ],
169
+ affected_objects=[phone_id],
170
+ )
171
+ store.save_decision(decision_to_store_dict(decision))
172
+ result.decisions.append(decision)
173
+
174
+ # --- Workspace type uncertain ---
175
+ if workspace_type is None:
176
+ workspace_type = "other" # default fallback
177
+ decision = self._create_decision(
178
+ store=store,
179
+ decision_type=DecisionType.WORKSPACE_TYPE_UNCERTAIN,
180
+ severity="LOW",
181
+ summary=(
182
+ f"Common-area device '{device_name}' has ambiguous workspace "
183
+ f"classification — device pool '{dp_name}' does not clearly "
184
+ f"indicate room type"
185
+ ),
186
+ context={
187
+ "device_name": device_name,
188
+ "device_pool": dp_name,
189
+ "display_name": display_name,
190
+ },
191
+ options=[
192
+ accept_option("Use 'meetingRoom' — conference room equipment"),
193
+ accept_option("Use 'desk' — shared desk / hot desk"),
194
+ accept_option("Use 'other' — lobby phone, break room, etc."),
195
+ ],
196
+ affected_objects=[phone_id],
197
+ )
198
+ store.save_decision(decision_to_store_dict(decision))
199
+ result.decisions.append(decision)
200
+
201
+ # --- License tier decision ---
202
+ # Always produce this decision so admin can confirm
203
+ # (from 03b-transform-mappers.md §5: WORKSPACE_LICENSE_TIER)
204
+ decision = self._create_decision(
205
+ store=store,
206
+ decision_type=DecisionType.WORKSPACE_LICENSE_TIER,
207
+ severity="LOW",
208
+ summary=(
209
+ f"Workspace '{display_name}' inferred as {license_tier} license tier"
210
+ ),
211
+ context={
212
+ "device_name": device_name,
213
+ "display_name": display_name,
214
+ "inferred_tier": license_tier,
215
+ "model": model,
216
+ },
217
+ options=[
218
+ accept_option(
219
+ f"Workspace — most /telephony/config/workspaces/ settings return 405, "
220
+ f"only musicOnHold and doNotDisturb work"
221
+ ),
222
+ accept_option(
223
+ "Professional Workspace — full settings access, higher license cost"
224
+ ),
225
+ ],
226
+ affected_objects=[phone_id],
227
+ )
228
+ store.save_decision(decision_to_store_dict(decision))
229
+ result.decisions.append(decision)
230
+
231
+ # --- Call settings extraction ---
232
+ call_settings = _extract_workspace_call_settings(state, license_tier)
233
+
234
+ # --- Decision: Professional-only settings dropped by license gate ---
235
+ if license_tier == "Workspace":
236
+ ungated = _extract_workspace_call_settings(state, "Professional Workspace")
237
+ ungated_keys = set(ungated.keys()) if ungated else set()
238
+ gated_keys = set(call_settings.keys()) if call_settings else set()
239
+ dropped = sorted(ungated_keys - gated_keys)
240
+ # Don't flag voicemail:{enabled:false} — that's a Webex default
241
+ # concern (VM on by default), not an active CUCM feature being lost.
242
+ # Handle at org/location level instead of per-phone decision.
243
+ dropped = [
244
+ k for k in dropped
245
+ if not (k == "voicemail"
246
+ and ungated and ungated.get(k) == {"enabled": False})
247
+ ]
248
+ if dropped:
249
+ decision = self._create_decision(
250
+ store=store,
251
+ decision_type=DecisionType.WORKSPACE_SETTINGS_PROFESSIONAL_REQUIRED,
252
+ severity="MEDIUM",
253
+ summary=(
254
+ f"Workspace '{display_name}' has {', '.join(dropped)} settings "
255
+ f"that require Professional Workspace license"
256
+ ),
257
+ context={
258
+ "device_name": device_name,
259
+ "display_name": display_name,
260
+ "license_tier": license_tier,
261
+ "dropped_settings": dropped,
262
+ },
263
+ options=[
264
+ accept_option(
265
+ "Upgrade to Professional Workspace — preserves all settings"
266
+ ),
267
+ accept_option(
268
+ "Accept loss — keep basic Workspace license, lose these settings"
269
+ ),
270
+ manual_option(
271
+ "Configure manually post-migration"
272
+ ),
273
+ ],
274
+ affected_objects=[phone_id],
275
+ )
276
+ store.save_decision(decision_to_store_dict(decision))
277
+ result.decisions.append(decision)
278
+
279
+ # --- Build CanonicalWorkspace ---
280
+ workspace = CanonicalWorkspace(
281
+ canonical_id=f"workspace:{device_name}",
282
+ provenance=extract_provenance(phone_data),
283
+ status=MigrationStatus.ANALYZED,
284
+ display_name=display_name,
285
+ location_id=location_id,
286
+ supported_devices=supported_devices,
287
+ extension=extension,
288
+ phone_number=phone_number,
289
+ calling_type="webexCalling",
290
+ workspace_type=workspace_type,
291
+ hotdesking_status=hotdesking_status,
292
+ is_common_area=True,
293
+ license_tier=license_tier,
294
+ call_settings=call_settings,
295
+ )
296
+
297
+ store.upsert_object(workspace)
298
+ result.objects_created += 1
299
+
300
+ return result
301
+
302
+
303
+ def _infer_supported_devices(model: str | None) -> str:
304
+ """Infer supportedDevices value from phone model.
305
+
306
+ (from 03b-transform-mappers.md §5: "phones for MPP, collaborationDevices for Room/Board/Desk")
307
+ """
308
+ if not model:
309
+ return "phones"
310
+
311
+ model_upper = model.upper()
312
+ # Room, Board, Desk series are collaboration devices
313
+ for keyword in ("ROOM", "BOARD", "DESK", "WEBEX BOARD", "WEBEX ROOM", "WEBEX DESK"):
314
+ if keyword in model_upper:
315
+ return "collaborationDevices"
316
+
317
+ return "phones"
318
+
319
+
320
+ def _infer_workspace_type(display_name: str, device_pool_name: str) -> str | None:
321
+ """Infer workspace type from display name and device pool name.
322
+
323
+ Returns 'meetingRoom', 'desk', or None if ambiguous.
324
+ (from 03b-transform-mappers.md §5: heuristic)
325
+ """
326
+ combined = f"{display_name} {device_pool_name}".lower()
327
+
328
+ for hint in _MEETING_ROOM_HINTS:
329
+ if hint in combined:
330
+ return "meetingRoom"
331
+
332
+ for hint in _DESK_HINTS:
333
+ if hint in combined:
334
+ return "desk"
335
+
336
+ return None
337
+
338
+
339
+ def _infer_license_tier(state: dict[str, Any]) -> str:
340
+ """Infer Workspace vs Professional Workspace license tier from CUCM settings.
341
+
342
+ Professional Workspace is needed if the device has:
343
+ - Call recording enabled
344
+ - Voicemail with custom greetings
345
+ - Outgoing call permissions
346
+ Otherwise Workspace license suffices.
347
+
348
+ (from 03b-transform-mappers.md §5: WORKSPACE_LICENSE_TIER decision)
349
+ """
350
+ if state.get("call_recording_enabled"):
351
+ return "Professional Workspace"
352
+ if state.get("voicemail_enabled") and state.get("custom_greetings"):
353
+ return "Professional Workspace"
354
+ if state.get("outgoing_call_permissions"):
355
+ return "Professional Workspace"
356
+ return "Workspace"
357
+
358
+
359
+ def _extract_workspace_call_settings(
360
+ state: dict[str, Any],
361
+ license_tier: str,
362
+ ) -> dict[str, Any] | None:
363
+ """Translate common-area phone state into a Webex workspace call_settings dict.
364
+
365
+ Returns a dict keyed by /telephony/config/workspaces/{id}/{key} path suffix.
366
+ Keys emitted depend on ``license_tier``:
367
+ - Workspace (basic): only ``doNotDisturb`` (Webex returns 405 on others for Basic).
368
+ - Professional Workspace: all detected settings.
369
+ Returns None if nothing worth writing.
370
+ """
371
+ settings: dict[str, Any] = {}
372
+
373
+ # --- doNotDisturb (both tiers) ---
374
+ dnd_status = state.get("dndStatus")
375
+ if dnd_status is not None:
376
+ enabled = dnd_status in ("true", True, "1")
377
+ ring_splash = enabled and state.get("dndOption") == "Ringer Off"
378
+ settings["doNotDisturb"] = {
379
+ "enabled": enabled,
380
+ "ringSplashEnabled": ring_splash,
381
+ }
382
+
383
+ # --- voicemail (Professional-only; license gate drops it for Workspace tier) ---
384
+ # voiceMailProfileName is extracted per-line via getLine enrichment. Live AXL
385
+ # returns it as a zeep reference dict: {"_value_1": "name", "uuid": "..."},
386
+ # so unwrap with ref_value().
387
+ first_line = _get_first_line(state)
388
+ vm_profile = ref_value(first_line.get("voiceMailProfileName")) if first_line else None
389
+ has_vm_profile = bool(vm_profile) and vm_profile.lower() not in ("none", "")
390
+ if has_vm_profile:
391
+ vm_body: dict[str, Any] = {"enabled": True}
392
+ # Pull CFNA ring timing + VM flag from line 1
393
+ if first_line:
394
+ cfna = first_line.get("callForwardNoAnswer") or {}
395
+ if cfna.get("forwardToVoiceMail") in ("true", True):
396
+ duration = cfna.get("duration")
397
+ rings = _duration_to_rings(duration) or 3
398
+ vm_body["sendUnansweredCalls"] = {
399
+ "enabled": True,
400
+ "numberOfRings": rings,
401
+ }
402
+ cfb = first_line.get("callForwardBusy") or {}
403
+ if cfb.get("forwardToVoiceMail") in ("true", True):
404
+ vm_body["sendBusyCalls"] = {"enabled": True}
405
+ settings["voicemail"] = vm_body
406
+ else:
407
+ # Webex defaults VM to enabled; common-area phones almost always want it OFF
408
+ settings["voicemail"] = {"enabled": False}
409
+
410
+ # --- callForwarding (Professional-only) ---
411
+ line1 = _get_first_line(state)
412
+ if line1:
413
+ # Only emit callForwarding if there's an explicit destination. When CUCM
414
+ # has forwardToVoiceMail=true with destination=None (forward to the phone's
415
+ # voicemail), we intentionally skip callForwarding — the voicemail block
416
+ # above already captures the user intent. Webex /telephony/config/.../callForwarding
417
+ # doesn't accept a "forward to voicemail" sentinel; that requires the voicemail endpoint.
418
+ cfa = (line1.get("callForwardAll") or {}).get("destination")
419
+ cfb = (line1.get("callForwardBusy") or {}).get("destination")
420
+ cfna_raw = line1.get("callForwardNoAnswer") or {}
421
+ cfna_dest = cfna_raw.get("destination")
422
+ if cfa or cfb or cfna_dest:
423
+ cf_body: dict[str, Any] = {
424
+ "always": {"enabled": bool(cfa)},
425
+ "busy": {"enabled": bool(cfb)},
426
+ "noAnswer": {"enabled": bool(cfna_dest)},
427
+ }
428
+ if cfa:
429
+ cf_body["always"]["destination"] = cfa
430
+ if cfb:
431
+ cf_body["busy"]["destination"] = cfb
432
+ if cfna_dest:
433
+ cf_body["noAnswer"]["destination"] = cfna_dest
434
+ duration = cfna_raw.get("duration")
435
+ cf_body["noAnswer"]["numberOfRings"] = _duration_to_rings(duration) or 3
436
+ settings["callForwarding"] = cf_body
437
+
438
+ # --- privacy (Professional-only) ---
439
+ # Real CUCM AXL field name is callInfoPrivacyStatus. Values observed on live
440
+ # CUCM 14.0 dCloud: "On" (privacy enabled), "Default" (inherits from common
441
+ # phone profile), or None (no owner — unowned phones don't get this field).
442
+ privacy = state.get("callInfoPrivacyStatus")
443
+ if privacy == "On":
444
+ settings["privacy"] = {"enabled": True}
445
+
446
+ # License tier gating: Workspace-tier only supports DND + MOH at /telephony/config/
447
+ # (from docs/reference/devices-workspaces.md license tier access matrix)
448
+ if license_tier == "Workspace":
449
+ allowed = {"doNotDisturb", "musicOnHold"}
450
+ settings = {k: v for k, v in settings.items() if k in allowed}
451
+
452
+ return settings or None
453
+
454
+
455
+ def _get_first_line(state: dict[str, Any]) -> dict | None:
456
+ """Return the first line entry from a raw phone state, or None if missing."""
457
+ lines = state.get("lines") or []
458
+ if isinstance(lines, dict):
459
+ return lines.get(1) or lines.get("1")
460
+ if isinstance(lines, list) and lines and isinstance(lines[0], dict):
461
+ return lines[0]
462
+ return None
463
+
464
+