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,941 @@
1
+ """Canonical data models for CUCM-to-Webex migration.
2
+
3
+ All types use canonical field names only — no CUCM or Webex API field names.
4
+ Fields prefixed with cucm_ preserve CUCM-specific metadata for decision context.
5
+
6
+ Sources:
7
+ - MigrationStatus, Provenance, MigrationObject: cucm-wxc-migration.md lines 113-143
8
+ - Concrete types: cucm-wxc-migration.md lines 145-153
9
+ - Field details: 03b-transform-mappers.md mapper field tables
10
+ - Object status progression: 07-idempotency-resumability.md lines 149-160
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from datetime import datetime
16
+ from enum import Enum
17
+ from typing import Any
18
+
19
+ from pydantic import BaseModel, Field
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Enums
24
+ # ---------------------------------------------------------------------------
25
+
26
+ class MigrationStatus(str, Enum):
27
+ """Object-level migration status.
28
+
29
+ Four-state progression for analysis: discovered -> normalized -> analyzed -> planned
30
+ Full lifecycle includes execution states.
31
+ (from cucm-wxc-migration.md lines 113-123, 07-idempotency-resumability.md lines 149-160)
32
+ """
33
+ DISCOVERED = "discovered"
34
+ NORMALIZED = "normalized" # (from 07-idempotency-resumability.md line 158)
35
+ ANALYZED = "analyzed"
36
+ NEEDS_DECISION = "needs_decision"
37
+ PLANNED = "planned"
38
+ PREFLIGHT_PASSED = "preflight_passed"
39
+ EXECUTING = "executing"
40
+ COMPLETED = "completed"
41
+ FAILED = "failed"
42
+ SKIPPED = "skipped"
43
+ ROLLED_BACK = "rolled_back"
44
+ STALE = "stale" # (from 07-idempotency-resumability.md line 179)
45
+
46
+
47
+ class OpStatus(str, Enum):
48
+ """Canonical execution-op status (plan_operations.status).
49
+
50
+ Mirrors the existing raw-string literals used across runtime.py, engine.py,
51
+ and store queries — this enum is the single source of truth going forward.
52
+ Existing raw-string call sites do not need migration because the string
53
+ values are identical; only new code should reference ``OpStatus.X.value``.
54
+ """
55
+ PENDING = "pending"
56
+ IN_PROGRESS = "in_progress"
57
+ COMPLETED = "completed"
58
+ SKIPPED = "skipped"
59
+ FAILED = "failed"
60
+
61
+
62
+ class LineClassification(str, Enum):
63
+ """DN classification result from E.164 normalization algorithm.
64
+ (from 03b-transform-mappers.md, line_mapper E.164 algorithm lines 208-211)
65
+ """
66
+ EXTENSION = "EXTENSION"
67
+ NATIONAL = "NATIONAL"
68
+ E164 = "E164"
69
+ AMBIGUOUS = "AMBIGUOUS"
70
+
71
+
72
+ class DeviceCompatibilityTier(str, Enum):
73
+ """Six-tier device model compatibility classification.
74
+ (from 03b-transform-mappers.md, device_mapper compatibility table lines 270-274)
75
+ """
76
+ NATIVE_MPP = "native_mpp"
77
+ CONVERTIBLE = "convertible"
78
+ WEBEX_APP = "webex_app"
79
+ INFRASTRUCTURE = "infrastructure"
80
+ INCOMPATIBLE = "incompatible"
81
+ DECT = "dect"
82
+
83
+
84
+ class DecisionType(str, Enum):
85
+ """Types of migration decisions requiring user input.
86
+ (from 03b-transform-mappers.md, all mapper decision tables)
87
+ (from 03-conflict-detection-engine.md, analyzer decision tables)
88
+ """
89
+ EXTENSION_CONFLICT = "EXTENSION_CONFLICT"
90
+ DN_AMBIGUOUS = "DN_AMBIGUOUS"
91
+ DEVICE_INCOMPATIBLE = "DEVICE_INCOMPATIBLE"
92
+ # DEPRECATED 2026-04-15: convertibility is now a device classification
93
+ # (tier='convertible'), not an operator choice. The planner emits the
94
+ # create_activation_code op unconditionally for convertible devices.
95
+ # Enum retained so legacy stored projects still deserialize; no code
96
+ # path emits new decisions of this type.
97
+ DEVICE_FIRMWARE_CONVERTIBLE = "DEVICE_FIRMWARE_CONVERTIBLE"
98
+ SHARED_LINE_COMPLEX = "SHARED_LINE_COMPLEX"
99
+ CSS_ROUTING_MISMATCH = "CSS_ROUTING_MISMATCH"
100
+ CALLING_PERMISSION_MISMATCH = "CALLING_PERMISSION_MISMATCH"
101
+ LOCATION_AMBIGUOUS = "LOCATION_AMBIGUOUS"
102
+ DUPLICATE_USER = "DUPLICATE_USER"
103
+ VOICEMAIL_INCOMPATIBLE = "VOICEMAIL_INCOMPATIBLE"
104
+ WORKSPACE_LICENSE_TIER = "WORKSPACE_LICENSE_TIER"
105
+ WORKSPACE_TYPE_UNCERTAIN = "WORKSPACE_TYPE_UNCERTAIN"
106
+ HOTDESK_DN_CONFLICT = "HOTDESK_DN_CONFLICT"
107
+ FEATURE_APPROXIMATION = "FEATURE_APPROXIMATION"
108
+ MISSING_DATA = "MISSING_DATA"
109
+ # Preflight-only decision types (from 05a-preflight-checks.md)
110
+ NUMBER_CONFLICT = "NUMBER_CONFLICT"
111
+ # Advisory system (from migration-advisory-design.md §3.1)
112
+ ARCHITECTURE_ADVISORY = "ARCHITECTURE_ADVISORY"
113
+ # Tier 2 decision types
114
+ FORWARDING_LOSSY = "FORWARDING_LOSSY"
115
+ SNR_LOSSY = "SNR_LOSSY"
116
+ AUDIO_ASSET_MANUAL = "AUDIO_ASSET_MANUAL"
117
+ BUTTON_UNMAPPABLE = "BUTTON_UNMAPPABLE"
118
+ DEVICE_SETTINGS_LOSSY = "DEVICE_SETTINGS_LOSSY"
119
+ # E911/ECBN execution (from 2026-04-10-e911-ecbn-execution.md §4.3)
120
+ E911_ECBN_AMBIGUOUS = "E911_ECBN_AMBIGUOUS"
121
+ E911_LOCATION_MISMATCH = "E911_LOCATION_MISMATCH"
122
+ # Workspace call settings (from 2026-04-10-workspace-call-settings.md §5)
123
+ WORKSPACE_SETTINGS_PROFESSIONAL_REQUIRED = "WORKSPACE_SETTINGS_PROFESSIONAL_REQUIRED"
124
+ # DECT Phase 2 decision types
125
+ DECT_NETWORK_DESIGN = "DECT_NETWORK_DESIGN"
126
+ DECT_HANDSET_ASSIGNMENT = "DECT_HANDSET_ASSIGNMENT"
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Base types
131
+ # ---------------------------------------------------------------------------
132
+
133
+ class Provenance(BaseModel):
134
+ """Tracks where a canonical object came from.
135
+ (from cucm-wxc-migration.md lines 125-131)
136
+ """
137
+ source_system: str # "cucm" or "webex"
138
+ source_id: str # CUCM pkid or Webex UUID
139
+ source_name: str
140
+ cluster: str | None = None
141
+ extracted_at: datetime
142
+ cucm_version: str | None = None
143
+
144
+
145
+ class MigrationObject(BaseModel):
146
+ """Base for all migratable objects.
147
+ (from cucm-wxc-migration.md lines 133-143)
148
+ """
149
+ canonical_id: str
150
+ provenance: Provenance
151
+ status: MigrationStatus = MigrationStatus.DISCOVERED
152
+ webex_id: str | None = None
153
+ pre_migration_state: dict[str, Any] | None = None
154
+ errors: list[str] = Field(default_factory=list)
155
+ warnings: list[str] = Field(default_factory=list)
156
+ depends_on: list[str] = Field(default_factory=list)
157
+ batch: str | None = None
158
+
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # Decision types — used by mappers and analyzers
162
+ # ---------------------------------------------------------------------------
163
+
164
+ class DecisionOption(BaseModel):
165
+ """One selectable resolution for a Decision.
166
+ (from 03b-transform-mappers.md, shared patterns — Decision Option Builder)
167
+ """
168
+ id: str # "skip", "virtual_line", "convert", etc.
169
+ label: str # Human-readable: "Virtual Line"
170
+ impact: str # "1 virtual line + 4 line assignments"
171
+
172
+
173
+ class Decision(BaseModel):
174
+ """A migration decision requiring user (or auto-rule) resolution.
175
+ (from 03b-transform-mappers.md, shared patterns — _create_decision helper)
176
+ (from 01-data-representation.md, decisions table lines 63-77)
177
+ """
178
+ decision_id: str # Auto-incrementing via store.next_decision_id()
179
+ type: DecisionType # Enum of 20 decision types
180
+ severity: str # "LOW", "MEDIUM", "HIGH", "CRITICAL"
181
+ summary: str # Human-readable one-liner
182
+ context: dict[str, Any] # JSON blob with full details
183
+ options: list[DecisionOption] # Available resolution options
184
+ chosen_option: str | None = None # Set when resolved
185
+ resolved_at: str | None = None
186
+ # Production writers set one of: "user" (interactive CLI resolve),
187
+ # "auto_rule" (unified auto-matcher), "batch" (batch --choice resolve),
188
+ # "stale" (superseded by a fingerprint re-run). Historical stored rows
189
+ # from projects predating the Bug F unification may still carry the
190
+ # legacy "auto_apply" marker — new filter/report code should treat it
191
+ # as equivalent to "auto_rule" for backwards compatibility.
192
+ resolved_by: str | None = None
193
+ fingerprint: str # Hash of causal data
194
+ run_id: str # Analysis run identifier
195
+ affected_objects: list[str] = Field(default_factory=list) # canonical_ids affected
196
+ recommendation: str | None = None
197
+ recommendation_reasoning: str | None = None
198
+
199
+
200
+ class MapperResult(BaseModel):
201
+ """Return type for Mapper.map().
202
+ (from 03b-transform-mappers.md, mapper contract)
203
+ """
204
+ objects_created: int = 0
205
+ objects_updated: int = 0
206
+ decisions: list[Decision] = Field(default_factory=list)
207
+
208
+
209
+ class MapperError(BaseModel):
210
+ """Records a mapper-level error for TransformResult.
211
+ (from 03b-transform-mappers.md, shared patterns — MapperError)
212
+ """
213
+ mapper_name: str
214
+ error_message: str
215
+ traceback: str | None = None
216
+
217
+
218
+ class TransformResult(BaseModel):
219
+ """Aggregate result from running all mappers in the transform engine.
220
+ (from 03b-transform-mappers.md, shared patterns — TransformResult)
221
+ """
222
+ decisions: list[Decision] = Field(default_factory=list)
223
+ errors: list[MapperError] = Field(default_factory=list)
224
+
225
+
226
+ # ---------------------------------------------------------------------------
227
+ # Canonical types — Location & Routing infrastructure
228
+ # ---------------------------------------------------------------------------
229
+
230
+ class LocationAddress(BaseModel):
231
+ """Address fields for a Webex location.
232
+ (from 03b-transform-mappers.md, location_mapper field table lines 80-84)
233
+ """
234
+ address1: str | None = None
235
+ city: str | None = None
236
+ state: str | None = None
237
+ postal_code: str | None = None
238
+ country: str | None = None
239
+
240
+
241
+ class CanonicalLocation(MigrationObject):
242
+ """CUCM Device Pool -> Webex Calling Location.
243
+ (from 03b-transform-mappers.md, location_mapper section lines 66-121)
244
+ """
245
+ name: str | None = None
246
+ time_zone: str | None = None
247
+ preferred_language: str | None = None
248
+ announcement_language: str | None = None
249
+ address: LocationAddress = Field(default_factory=LocationAddress)
250
+ routing_prefix: str | None = None
251
+ outside_dial_digit: str | None = None
252
+ calling_enabled: bool = True
253
+ main_number: str | None = None
254
+ # CUCM metadata
255
+ cucm_device_pool_names: list[str] = Field(default_factory=list)
256
+ cucm_datetime_group_name: str | None = None
257
+ cucm_location_name: str | None = None
258
+
259
+
260
+ class TrunkGatewayRef(BaseModel):
261
+ """Reference to a trunk within a route group, with priority.
262
+ (from 03b-transform-mappers.md, routing_mapper route group field table line 387)
263
+ """
264
+ trunk_canonical_id: str
265
+ priority: int = 1
266
+
267
+
268
+ class CanonicalTrunk(MigrationObject):
269
+ """CUCM Gateway/SIP Trunk -> Webex Calling Trunk.
270
+ (from 03b-transform-mappers.md, routing_mapper trunk field table lines 366-380)
271
+ """
272
+ name: str | None = None
273
+ location_id: str | None = None
274
+ trunk_type: str | None = None # REGISTERING or CERTIFICATE_BASED
275
+ address: str | None = None # FQDN or SRV record
276
+ domain: str | None = None
277
+ port: int | None = None
278
+ max_concurrent_calls: int | None = None
279
+ password: str | None = None # generated placeholder
280
+ dual_identity_support_enabled: bool | None = None
281
+ device_type: str | None = None # immutable after creation
282
+ p_charge_info_support_policy: str | None = None
283
+ # SIP/Security profile detail (from Tier 2 expansion)
284
+ sip_profile_early_offer: bool | None = None
285
+ sip_profile_srtp_fallback: bool | None = None
286
+ security_mode: str | None = None # NON_SECURE, TLS, IPSEC
287
+ security_digest_auth: bool | None = None
288
+
289
+
290
+ class CanonicalRouteGroup(MigrationObject):
291
+ """CUCM Route Group -> Webex Calling Route Group.
292
+ (from 03b-transform-mappers.md, routing_mapper route group field table lines 382-387)
293
+ """
294
+ name: str | None = None
295
+ local_gateways: list[TrunkGatewayRef] = Field(default_factory=list)
296
+
297
+
298
+ class CanonicalRouteList(MigrationObject):
299
+ """CUCM Route List -> Webex Calling Route List.
300
+
301
+ Webex route lists bind to exactly ONE route group (unlike CUCM which allows
302
+ multiple). Dial plans point to route groups, not route lists, because Webex
303
+ RouteType enum = ROUTE_GROUP | TRUNK only.
304
+ """
305
+ name: str = ""
306
+ location_id: str | None = None
307
+ route_group_id: str | None = None
308
+ numbers: list[str] = Field(default_factory=list)
309
+ cucm_route_list_name: str = ""
310
+ cucm_route_groups: list[str] = Field(default_factory=list)
311
+
312
+
313
+
314
+ class CanonicalDialPlan(MigrationObject):
315
+ """CUCM Route Patterns / CSS routing scope -> Webex Calling Dial Plan.
316
+ (from 03b-transform-mappers.md, routing_mapper dial plan lines 389-395
317
+ and css_mapper dial plan lines 469-475)
318
+ """
319
+ name: str | None = None
320
+ dial_patterns: list[str] = Field(default_factory=list)
321
+ route_id: str | None = None
322
+ route_type: str | None = None # TRUNK or ROUTE_GROUP
323
+
324
+
325
+ class CanonicalTranslationPattern(MigrationObject):
326
+ """CUCM Translation Pattern -> Webex Calling Translation Pattern.
327
+ (from 03b-transform-mappers.md, routing_mapper translation pattern lines 411-417)
328
+ """
329
+ name: str | None = None
330
+ matching_pattern: str | None = None
331
+ replacement_pattern: str | None = None
332
+
333
+
334
+ # ---------------------------------------------------------------------------
335
+ # Canonical types — Users, Lines, Devices, Workspaces
336
+ # ---------------------------------------------------------------------------
337
+
338
+ class CanonicalUser(MigrationObject):
339
+ """CUCM End User -> Webex Calling Person.
340
+ (from 03b-transform-mappers.md, user_mapper field table lines 130-148)
341
+ """
342
+ emails: list[str] = Field(default_factory=list)
343
+ first_name: str | None = None
344
+ last_name: str | None = None
345
+ display_name: str | None = None
346
+ location_id: str | None = None # canonical_id of CanonicalLocation
347
+ extension: str | None = None
348
+ phone_numbers: list[dict[str, str]] = Field(default_factory=list)
349
+ department: str | None = None
350
+ title: str | None = None
351
+ create_method: str | None = None # "scim" or "people_api"
352
+ calling_data: bool = True # must pass callingData=true in API calls
353
+ # CUCM metadata
354
+ cucm_manager_user_id: str | None = None
355
+ cucm_userid: str | None = None
356
+ cucm_mailid: str | None = None
357
+ # Enrichment fields (set by mappers, consumed by planner)
358
+ voicemail_profile_id: str | None = None
359
+ call_settings: dict[str, Any] | None = None
360
+
361
+
362
+ class CanonicalLine(MigrationObject):
363
+ """CUCM DN/Line -> Webex Phone Number + Extension.
364
+ (from 03b-transform-mappers.md, line_mapper field table lines 190-199)
365
+ """
366
+ extension: str | None = None
367
+ e164: str | None = None
368
+ classification: LineClassification | None = None
369
+ shared: bool = False
370
+ # CUCM metadata
371
+ cucm_pattern: str | None = None
372
+ route_partition_name: str | None = None
373
+
374
+
375
+ class CanonicalDevice(MigrationObject):
376
+ """CUCM Phone -> Webex Calling Device.
377
+ (from 03b-transform-mappers.md, device_mapper field table lines 252-266)
378
+ """
379
+ mac: str | None = None
380
+ model: str | None = None
381
+ compatibility_tier: DeviceCompatibilityTier | None = None
382
+ display_name: str | None = None
383
+ owner_canonical_id: str | None = None # canonical_id of CanonicalUser
384
+ location_canonical_id: str | None = None
385
+ line_appearances: list[dict[str, Any]] = Field(default_factory=list)
386
+ # CUCM metadata
387
+ cucm_protocol: str | None = None # SIP or SCCP
388
+ cucm_device_name: str | None = None
389
+ device_id_surface: str = "telephony" # "telephony" (MPP) or "cloud" (9800/8875/PhoneOS)
390
+
391
+
392
+ class CanonicalWorkspace(MigrationObject):
393
+ """CUCM Common-area Phone -> Webex Workspace + Device.
394
+ (from 03b-transform-mappers.md, workspace_mapper field table lines 314-328)
395
+ """
396
+ display_name: str | None = None
397
+ location_id: str | None = None
398
+ supported_devices: str | None = None # "phones" or "collaborationDevices"
399
+ extension: str | None = None
400
+ phone_number: str | None = None
401
+ calling_type: str = "webexCalling"
402
+ workspace_type: str | None = None # meetingRoom, desk, other
403
+ hotdesking_status: str | None = None # on or off
404
+ is_common_area: bool = True
405
+ license_tier: str | None = None # Workspace or Professional Workspace
406
+ call_settings: dict[str, Any] | None = None
407
+
408
+
409
+ # ---------------------------------------------------------------------------
410
+ # Canonical types — Call Features
411
+ # ---------------------------------------------------------------------------
412
+
413
+ class CanonicalHuntGroup(MigrationObject):
414
+ """CUCM Hunt Pilot + Hunt List + Line Group -> Webex Hunt Group.
415
+ (from 03b-transform-mappers.md, feature_mapper HG field table lines 601-614)
416
+ """
417
+ name: str | None = None
418
+ extension: str | None = None
419
+ phone_number: str | None = None
420
+ policy: str | None = None # REGULAR, CIRCULAR, UNIFORM, SIMULTANEOUS
421
+ agents: list[str] = Field(default_factory=list)
422
+ no_answer_rings: int | None = None
423
+ enabled: bool = True
424
+ location_id: str | None = None
425
+ # Forwarding configuration (from 2026-04-10-feature-forwarding-night-service.md)
426
+ forward_always_enabled: bool = False
427
+ forward_always_destination: str | None = None
428
+ forward_busy_enabled: bool = False
429
+ forward_busy_destination: str | None = None
430
+ forward_no_answer_enabled: bool = False
431
+ forward_no_answer_destination: str | None = None
432
+
433
+
434
+ class CanonicalCallQueue(MigrationObject):
435
+ """Queue-style CUCM Hunt Pilot -> Webex Call Queue.
436
+ (from 03b-transform-mappers.md, feature_mapper CQ field table lines 616-627)
437
+ """
438
+ name: str | None = None
439
+ extension: str | None = None
440
+ phone_number: str | None = None
441
+ policy: str | None = None # REGULAR, CIRCULAR, UNIFORM
442
+ routing_type: str = "PRIORITY_BASED"
443
+ agents: list[str] = Field(default_factory=list)
444
+ queue_size: int = 25
445
+ enabled: bool = True
446
+ location_id: str | None = None
447
+ # Forwarding configuration (from 2026-04-10-feature-forwarding-night-service.md)
448
+ forward_always_enabled: bool = False
449
+ forward_always_destination: str | None = None
450
+ # Overflow destinations from CUCM queueCalls
451
+ queue_full_destination: str | None = None
452
+ max_wait_time_destination: str | None = None
453
+ max_wait_time: int | None = None
454
+ no_agent_destination: str | None = None
455
+ # Holiday service
456
+ holiday_service_enabled: bool = False
457
+ holiday_schedule_name: str | None = None
458
+ holiday_schedule_level: str = "LOCATION"
459
+ holiday_action: str = "BUSY"
460
+ holiday_transfer_number: str | None = None
461
+ # Night service
462
+ night_service_enabled: bool = False
463
+ night_business_hours_name: str | None = None
464
+ night_business_hours_level: str = "LOCATION"
465
+ night_action: str = "TRANSFER"
466
+ night_transfer_number: str | None = None
467
+ # Forced forward (currently unused — left for future spec)
468
+ forced_forward_enabled: bool = False
469
+ forced_forward_number: str | None = None
470
+
471
+
472
+ class CanonicalAutoAttendant(MigrationObject):
473
+ """CUCM CTI Route Point + Script -> Webex Auto Attendant.
474
+ (from 03b-transform-mappers.md, feature_mapper AA field table lines 629-638)
475
+ """
476
+ name: str | None = None
477
+ extension: str | None = None
478
+ phone_number: str | None = None
479
+ business_schedule: str | None = None
480
+ business_hours_menu: dict[str, Any] | None = None
481
+ after_hours_menu: dict[str, Any] | None = None
482
+ location_id: str | None = None
483
+ # Forwarding configuration (from 2026-04-10-feature-forwarding-night-service.md)
484
+ forward_always_enabled: bool = False
485
+ forward_always_destination: str | None = None
486
+
487
+
488
+ class CanonicalCallPark(MigrationObject):
489
+ """CUCM Call Park Number -> Webex Call Park Extension.
490
+ (from 03b-transform-mappers.md, feature_mapper simple features line 644)
491
+ """
492
+ name: str | None = None
493
+ extension: str | None = None
494
+ location_id: str | None = None
495
+
496
+
497
+ class CanonicalPickupGroup(MigrationObject):
498
+ """CUCM Pickup Group -> Webex Call Pickup.
499
+ (from 03b-transform-mappers.md, feature_mapper simple features line 645)
500
+ """
501
+ name: str | None = None
502
+ agents: list[str] = Field(default_factory=list)
503
+ location_id: str | None = None
504
+
505
+
506
+ class CanonicalPagingGroup(MigrationObject):
507
+ """CUCM Paging -> Webex Paging Group.
508
+ (from 03b-transform-mappers.md, feature_mapper simple features line 646)
509
+ """
510
+ name: str | None = None
511
+ extension: str | None = None
512
+ targets: list[str] = Field(default_factory=list)
513
+ originators: list[str] = Field(default_factory=list)
514
+
515
+
516
+ class CanonicalOperatingMode(MigrationObject):
517
+ """CUCM Time Period + Time Schedule -> Webex Operating Mode.
518
+ (from 03b-transform-mappers.md, feature_mapper simple features line 647)
519
+ """
520
+ name: str | None = None
521
+ level: str = "ORGANIZATION"
522
+ schedule_type: str | None = None # SAME_HOURS_DAILY, DIFFERENT_HOURS_DAILY, HOLIDAY
523
+ location_id: str | None = None
524
+ same_hours_daily: dict[str, Any] | None = None
525
+ different_hours_daily: dict[str, Any] | None = None
526
+ holidays: list[dict[str, Any]] = Field(default_factory=list)
527
+
528
+
529
+ class CanonicalLocationSchedule(MigrationObject):
530
+ """CUCM Time Schedule -> Webex Location Schedule.
531
+ Created for schedules referenced by AAs, CQs, or HGs.
532
+ (from fix-schedule-mapping-and-skill-gaps.md Fix 1)
533
+ """
534
+ name: str | None = None
535
+ schedule_type: str | None = None # businessHours or holidays
536
+ location_id: str | None = None
537
+ events: list[dict[str, Any]] = Field(default_factory=list)
538
+ # Reference back to the operating mode (same CUCM source)
539
+ operating_mode_canonical_id: str | None = None
540
+
541
+
542
+ # ---------------------------------------------------------------------------
543
+ # Canonical types — Voicemail, Shared Lines, Virtual Lines, Permissions
544
+ # ---------------------------------------------------------------------------
545
+
546
+ class CanonicalVoicemailProfile(MigrationObject):
547
+ """CUCM Unity Connection VM Profile -> Webex per-person voicemail settings.
548
+ (from 03b-transform-mappers.md, voicemail_mapper field table lines 699-724)
549
+ """
550
+ enabled: bool = True
551
+ send_all_calls: dict[str, Any] | None = None
552
+ send_busy_calls: dict[str, Any] | None = None
553
+ send_unanswered_calls: dict[str, Any] | None = None
554
+ notifications: dict[str, Any] | None = None
555
+ email_copy_of_message: dict[str, Any] | None = None
556
+ message_storage: dict[str, Any] | None = None
557
+ fax_message: dict[str, Any] | None = None
558
+ transfer_to_number: dict[str, Any] | None = None
559
+ # CUCM metadata
560
+ cucm_voicemail_profile_name: str | None = None
561
+ user_canonical_id: str | None = None
562
+
563
+
564
+ class CanonicalVoicemailGroup(MigrationObject):
565
+ """Unity Connection shared/group mailbox -> Webex Voicemail Group.
566
+
567
+ Produced by VoicemailGroupMapper. Location-scoped. Consumed by
568
+ handle_voicemail_group_create which POSTs to
569
+ /telephony/config/locations/{locationId}/voicemailGroups.
570
+ (from docs/superpowers/specs/2026-04-10-voicemail-groups.md)
571
+ """
572
+ name: str | None = None
573
+ extension: str | None = None
574
+ phone_number: str | None = None
575
+ location_id: str | None = None # canonical_id of CanonicalLocation
576
+ language_code: str = "en_us"
577
+ enabled: bool = True
578
+ passcode: str | None = None # Generated by mapper (CUCM PINs not extractable)
579
+ greeting_type: str = "DEFAULT" # DEFAULT or CUSTOM
580
+ caller_id_name: str | None = None
581
+ # Feature settings (match Webex API shapes)
582
+ message_storage: dict[str, Any] = Field(
583
+ default_factory=lambda: {"storageType": "INTERNAL"}
584
+ )
585
+ notifications: dict[str, Any] = Field(
586
+ default_factory=lambda: {"enabled": False}
587
+ )
588
+ fax_message: dict[str, Any] = Field(
589
+ default_factory=lambda: {"enabled": False}
590
+ )
591
+ transfer_to_number: dict[str, Any] = Field(
592
+ default_factory=lambda: {"enabled": False}
593
+ )
594
+ email_copy_of_message: dict[str, Any] = Field(
595
+ default_factory=lambda: {"enabled": False}
596
+ )
597
+ # CUCM metadata
598
+ cucm_object_id: str | None = None # Unity Connection ObjectId
599
+ cucm_display_name: str | None = None
600
+ # Cross-reference: hunt groups/call queues that overflow to this mailbox
601
+ referring_features: list[str] = Field(default_factory=list)
602
+
603
+
604
+ class CanonicalSharedLine(MigrationObject):
605
+ """Shared line appearance — DN shared across multiple devices/users.
606
+ (from cucm-wxc-migration.md line 185, cucm-wxc-migration.md CUCM mapping table line 204)
607
+ """
608
+ dn_canonical_id: str | None = None # canonical_id of the shared DN
609
+ owner_canonical_ids: list[str] = Field(default_factory=list)
610
+ device_canonical_ids: list[str] = Field(default_factory=list)
611
+
612
+
613
+ class CanonicalVirtualLine(MigrationObject):
614
+ """Virtual line — Webex Virtual Line for shared line migration.
615
+ (from cucm-wxc-migration.md line 186)
616
+ """
617
+ extension: str | None = None
618
+ phone_number: str | None = None
619
+ location_id: str | None = None
620
+ display_name: str | None = None
621
+ dn_canonical_id: str | None = None
622
+
623
+
624
+ class CanonicalExecutiveAssistant(MigrationObject):
625
+ """CUCM Executive/Assistant pairing -> Webex Executive/Assistant config.
626
+ (from executive-assistant-migration spec §4c)
627
+ """
628
+ executive_canonical_id: str | None = None
629
+ assistant_canonical_ids: list[str] = Field(default_factory=list)
630
+ alerting_mode: str = "SIMULTANEOUS" # SEQUENTIAL or SIMULTANEOUS
631
+ filter_enabled: bool = False
632
+ filter_type: str = "ALL_CALLS" # ALL_CALLS, ALL_INTERNAL_CALLS, ALL_EXTERNAL_CALLS
633
+ screening_enabled: bool = False
634
+
635
+
636
+ class CallingPermissionEntry(BaseModel):
637
+ """Single call type permission entry.
638
+ (from 03b-transform-mappers.md, css_mapper field table lines 477-486)
639
+ """
640
+ call_type: str # e.g. INTERNAL_CALL, NATIONAL, INTERNATIONAL
641
+ action: str # ALLOW or BLOCK
642
+ transfer_enabled: bool = True
643
+
644
+
645
+ class CanonicalCallingPermission(MigrationObject):
646
+ """CSS blocking partitions -> Webex per-user Outgoing Calling Permissions.
647
+ (from 03b-transform-mappers.md, css_mapper field table lines 477-486)
648
+ """
649
+ calling_permissions: list[CallingPermissionEntry] = Field(default_factory=list)
650
+ assigned_users: list[str] = Field(default_factory=list)
651
+ use_custom_enabled: bool = True
652
+ use_custom_permissions: bool = True
653
+
654
+
655
+ # ---------------------------------------------------------------------------
656
+ # Canonical types — Tier 2: Call Forwarding, Monitoring
657
+ # ---------------------------------------------------------------------------
658
+
659
+ class CanonicalCallForwarding(MigrationObject):
660
+ """CUCM per-line forwarding → Webex per-person call forwarding."""
661
+ user_canonical_id: str | None = None
662
+ always_enabled: bool = False
663
+ always_destination: str | None = None
664
+ always_to_voicemail: bool = False
665
+ busy_enabled: bool = False
666
+ busy_destination: str | None = None
667
+ busy_to_voicemail: bool = False
668
+ no_answer_enabled: bool = False
669
+ no_answer_destination: str | None = None
670
+ no_answer_to_voicemail: bool = False
671
+ no_answer_ring_count: int | None = None
672
+ busy_internal_enabled: bool = False
673
+ busy_internal_destination: str | None = None
674
+ no_answer_internal_enabled: bool = False
675
+ no_answer_internal_destination: str | None = None
676
+ no_coverage_enabled: bool = False
677
+ no_coverage_destination: str | None = None
678
+ on_failure_enabled: bool = False
679
+ on_failure_destination: str | None = None
680
+ not_registered_enabled: bool = False
681
+ not_registered_destination: str | None = None
682
+
683
+
684
+ class CanonicalMonitoringList(MigrationObject):
685
+ """CUCM BLF → Webex per-person monitoring list."""
686
+ user_canonical_id: str | None = None
687
+ call_park_notification_enabled: bool = False
688
+ monitored_members: list[dict[str, Any]] = Field(default_factory=list)
689
+
690
+
691
+ class CanonicalLineKeyTemplate(MigrationObject):
692
+ """CUCM Phone Button Template → Webex Line Key Template."""
693
+ name: str | None = None
694
+ cucm_template_name: str | None = None
695
+ device_model: str | None = None
696
+ line_keys: list[dict[str, Any]] = Field(default_factory=list)
697
+ kem_module_type: str | None = None
698
+ kem_keys: list[dict[str, Any]] = Field(default_factory=list)
699
+ unmapped_buttons: list[dict[str, Any]] = Field(default_factory=list)
700
+ phones_using: int = 0
701
+
702
+
703
+ class CanonicalDeviceLayout(MigrationObject):
704
+ """Per-device resolved line key + KEM layout."""
705
+ device_canonical_id: str | None = None
706
+ template_canonical_id: str | None = None
707
+ owner_canonical_id: str | None = None
708
+ line_members: list[dict[str, Any]] = Field(default_factory=list)
709
+ resolved_line_keys: list[dict[str, Any]] = Field(default_factory=list)
710
+ resolved_kem_keys: list[dict[str, Any]] = Field(default_factory=list)
711
+ speed_dials: list[dict[str, Any]] = Field(default_factory=list)
712
+ unmapped_buttons: list[dict[str, Any]] = Field(default_factory=list)
713
+ device_id_surface: str = "telephony" # Copied from associated device at map time
714
+
715
+
716
+ class CanonicalMusicOnHold(MigrationObject):
717
+ """CUCM MOH Audio Source → Webex per-location MOH."""
718
+ location_canonical_id: str | None = None
719
+ source_name: str | None = None
720
+ source_file_name: str | None = None
721
+ is_default: bool = False
722
+ cucm_source_id: str | None = None
723
+
724
+
725
+ class CanonicalAnnouncement(MigrationObject):
726
+ """CUCM Announcement / Unity greeting → Webex announcement repo."""
727
+ name: str | None = None
728
+ location_canonical_id: str | None = None
729
+ file_name: str | None = None
730
+ media_type: str | None = None
731
+ source_system: str | None = None
732
+ usage: str | None = None
733
+ associated_feature_canonical_id: str | None = None
734
+
735
+
736
+ class CanonicalDeviceProfile(MigrationObject):
737
+ """CUCM Device Profile → informs Webex hot desking decisions."""
738
+ profile_name: str | None = None
739
+ user_canonical_id: str | None = None
740
+ model: str | None = None
741
+ protocol: str | None = None
742
+ lines: list[dict[str, Any]] = Field(default_factory=list)
743
+ device_pool_name: str | None = None
744
+ speed_dial_count: int = 0
745
+ blf_count: int = 0
746
+ # Execution-ready hoteling fields (from hoteling-migration spec §6a)
747
+ hoteling_guest_enabled: bool = False
748
+ host_device_canonical_ids: list[str] = Field(default_factory=list)
749
+ auto_logout_minutes: int = 0
750
+ location_canonical_id: str | None = None
751
+
752
+
753
+ class CanonicalReceptionistConfig(MigrationObject):
754
+ """Receptionist configuration detected from CUCM phone layout signals."""
755
+ user_canonical_id: str = ""
756
+ location_canonical_id: str = ""
757
+ blf_count: int = 0
758
+ has_kem: bool = False
759
+ kem_key_count: int = 0
760
+ template_name: str = ""
761
+ detection_score: int = 0
762
+ detection_reasons: list[str] = Field(default_factory=list)
763
+ monitored_members: list[str] = Field(default_factory=list)
764
+ is_main_number_holder: bool = False
765
+
766
+
767
+ class CanonicalE911Config(MigrationObject):
768
+ """CUCM ELIN/GeoLocation → Webex E911 advisory."""
769
+ location_canonical_id: str | None = None
770
+ elin_group_name: str | None = None
771
+ elin_numbers: list[str] = Field(default_factory=list)
772
+ geo_location_name: str | None = None
773
+ geo_country: str | None = None
774
+ has_emergency_route_pattern: bool = False
775
+
776
+
777
+ class CanonicalEcbnConfig(MigrationObject):
778
+ """Per-entity ECBN configuration for Webex.
779
+
780
+ One object per user, workspace, or virtual line. Produced by EcbnMapper
781
+ from existing canonical data (no new AXL queries).
782
+ (from 2026-04-10-e911-ecbn-execution.md §4.2)
783
+ """
784
+ entity_type: str = "" # "user" | "workspace" | "virtual_line"
785
+ entity_canonical_id: str = "" # canonical_id of the user/workspace/VL
786
+ location_canonical_id: str | None = None
787
+ ecbn_selection: str = "" # "DIRECT_LINE" | "LOCATION_ECBN" | "LOCATION_MEMBER_NUMBER"
788
+ did_numbers: list[str] = Field(default_factory=list) # all DIDs on this entity
789
+ primary_did: str | None = None # best candidate DID for ECBN
790
+ needs_location_ecbn: bool = False # True if extension-only; location ECBN must exist
791
+
792
+
793
+ class CanonicalSingleNumberReach(MigrationObject):
794
+ """CUCM Remote Destination → Webex Single Number Reach."""
795
+ user_canonical_id: str | None = None
796
+ enabled: bool = True
797
+ alert_click_to_dial: bool = False
798
+ numbers: list[dict[str, Any]] = Field(default_factory=list)
799
+ # Each entry: {phone_number, enabled, name, answer_confirmation,
800
+ # cucm_answer_too_soon, cucm_answer_too_late}
801
+
802
+
803
+ class CanonicalSoftkeyConfig(MigrationObject):
804
+ """CUCM Softkey Template → Webex PSK config (9800/8875) or report flag (classic MPP)."""
805
+ cucm_template_name: str | None = None
806
+ is_psk_target: bool = False
807
+ psk_mappings: list[dict[str, Any]] = Field(default_factory=list)
808
+ state_key_lists: dict[str, list[str]] = Field(default_factory=dict)
809
+ unmapped_softkeys: list[dict[str, Any]] = Field(default_factory=list)
810
+ phones_using: int = 0
811
+ device_canonical_id: str | None = None # Per-device PSK config: links to device
812
+
813
+
814
+ class CanonicalDeviceSettingsTemplate(MigrationObject):
815
+ """CUCM device settings -> Webex device settings template.
816
+
817
+ One template per (model_family, location) group. Settings are the
818
+ majority-vote values across all phones in the group. Per-device overrides
819
+ list phones that differ from the majority.
820
+ """
821
+ model_family: str | None = None # "9800", "8875", "78xx", "68xx"
822
+ location_canonical_id: str | None = None
823
+ settings: dict[str, Any] = Field(default_factory=dict)
824
+ per_device_overrides: list[dict[str, Any]] = Field(default_factory=list)
825
+ unmappable_settings: list[str] = Field(default_factory=list)
826
+ phones_using: int = 0
827
+ custom_backgrounds: list[str] = Field(default_factory=list)
828
+
829
+
830
+ # ---------------------------------------------------------------------------
831
+ # Canonical types — DECT Networks
832
+ # ---------------------------------------------------------------------------
833
+
834
+ class CanonicalDECTNetwork(MigrationObject):
835
+ """CUCM DECT infrastructure → Webex DECT Network + Base Stations + Handsets.
836
+
837
+ Produced by DECTMapper (Phase 2). One object per CUCM DECT network (identified
838
+ by access_code + base station cluster). Consumed by DECT execution handlers
839
+ which POST to /telephony/config/locations/{locationId}/dectNetworks.
840
+ """
841
+ location_canonical_id: str | None = None # canonical_id of CanonicalLocation
842
+ network_name: str | None = None # Internal name for the network
843
+ display_name: str | None = None # User-visible display name
844
+ model: str | None = None # DECT network model (e.g., "DBS-110", "DBS-210")
845
+ access_code: str | None = None # DECT system access code
846
+ base_stations: list[dict[str, Any]] = Field(default_factory=list)
847
+ # Each base station entry: {mac, display_name, location_canonical_id, cucm_device_name}
848
+ handset_assignments: list[dict[str, Any]] = Field(default_factory=list)
849
+ # Each handset entry: {index, line1_canonical_id, line2_canonical_id,
850
+ # display_name, user_canonical_id}
851
+ pre_migration_state: dict[str, Any] | None = None
852
+
853
+
854
+ # ---------------------------------------------------------------------------
855
+ # Inventory — aggregates all canonical types
856
+ # ---------------------------------------------------------------------------
857
+
858
+ class MigrationInventory(BaseModel):
859
+ """Single source of truth for the migration.
860
+ (from cucm-wxc-migration.md lines 165-189)
861
+
862
+ Note: The design spec also defines ``unmapped`` and ``pending_decisions``
863
+ fields. These are intentionally omitted here because the SQLite store
864
+ (MigrationStore) is the authoritative source for decisions and unmapped
865
+ objects are tracked via MigrationStatus. The ``wxcli cucm export`` command
866
+ populates these on-demand from the store when generating JSON views.
867
+ """
868
+ project_id: str
869
+ cucm_cluster: str
870
+ locations: list[CanonicalLocation] = Field(default_factory=list)
871
+ users: list[CanonicalUser] = Field(default_factory=list)
872
+ devices: list[CanonicalDevice] = Field(default_factory=list)
873
+ lines: list[CanonicalLine] = Field(default_factory=list)
874
+ hunt_groups: list[CanonicalHuntGroup] = Field(default_factory=list)
875
+ call_queues: list[CanonicalCallQueue] = Field(default_factory=list)
876
+ auto_attendants: list[CanonicalAutoAttendant] = Field(default_factory=list)
877
+ trunks: list[CanonicalTrunk] = Field(default_factory=list)
878
+ dial_plans: list[CanonicalDialPlan] = Field(default_factory=list)
879
+ route_groups: list[CanonicalRouteGroup] = Field(default_factory=list)
880
+ translation_patterns: list[CanonicalTranslationPattern] = Field(default_factory=list)
881
+ operating_modes: list[CanonicalOperatingMode] = Field(default_factory=list)
882
+ call_parks: list[CanonicalCallPark] = Field(default_factory=list)
883
+ pickup_groups: list[CanonicalPickupGroup] = Field(default_factory=list)
884
+ paging_groups: list[CanonicalPagingGroup] = Field(default_factory=list)
885
+ voicemail_profiles: list[CanonicalVoicemailProfile] = Field(default_factory=list)
886
+ shared_lines: list[CanonicalSharedLine] = Field(default_factory=list)
887
+ virtual_lines: list[CanonicalVirtualLine] = Field(default_factory=list)
888
+ workspaces: list[CanonicalWorkspace] = Field(default_factory=list)
889
+ calling_permissions: list[CanonicalCallingPermission] = Field(default_factory=list)
890
+ schedules: list[CanonicalLocationSchedule] = Field(default_factory=list)
891
+
892
+
893
+ # ---------------------------------------------------------------------------
894
+ # Type registry — maps object_type strings to classes for deserialization
895
+ # ---------------------------------------------------------------------------
896
+
897
+ CANONICAL_TYPE_REGISTRY: dict[str, type[MigrationObject]] = {
898
+ "location": CanonicalLocation,
899
+ "user": CanonicalUser,
900
+ "device": CanonicalDevice,
901
+ "line": CanonicalLine,
902
+ "hunt_group": CanonicalHuntGroup,
903
+ "call_queue": CanonicalCallQueue,
904
+ "auto_attendant": CanonicalAutoAttendant,
905
+ "trunk": CanonicalTrunk,
906
+ "dial_plan": CanonicalDialPlan,
907
+ "route_group": CanonicalRouteGroup,
908
+ "route_list": CanonicalRouteList,
909
+ "translation_pattern": CanonicalTranslationPattern,
910
+ "operating_mode": CanonicalOperatingMode,
911
+ "call_park": CanonicalCallPark,
912
+ "pickup_group": CanonicalPickupGroup,
913
+ "paging_group": CanonicalPagingGroup,
914
+ "voicemail_profile": CanonicalVoicemailProfile,
915
+ "voicemail_group": CanonicalVoicemailGroup,
916
+ "shared_line": CanonicalSharedLine,
917
+ "virtual_line": CanonicalVirtualLine,
918
+ "workspace": CanonicalWorkspace,
919
+ "calling_permission": CanonicalCallingPermission,
920
+ "schedule": CanonicalLocationSchedule,
921
+ "call_forwarding": CanonicalCallForwarding,
922
+ "monitoring_list": CanonicalMonitoringList,
923
+ "line_key_template": CanonicalLineKeyTemplate,
924
+ "device_layout": CanonicalDeviceLayout,
925
+ "softkey_config": CanonicalSoftkeyConfig,
926
+ "single_number_reach": CanonicalSingleNumberReach,
927
+ "e911_config": CanonicalE911Config,
928
+ "ecbn_config": CanonicalEcbnConfig,
929
+ "device_profile": CanonicalDeviceProfile,
930
+ "receptionist_config": CanonicalReceptionistConfig,
931
+ "music_on_hold": CanonicalMusicOnHold,
932
+ "announcement": CanonicalAnnouncement,
933
+ "device_settings_template": CanonicalDeviceSettingsTemplate,
934
+ "executive_assistant": CanonicalExecutiveAssistant,
935
+ "dect_network": CanonicalDECTNetwork,
936
+ }
937
+
938
+ # Reverse lookup: class -> type name string (O(1) for _object_type_for)
939
+ CANONICAL_CLASS_TO_TYPE: dict[type[MigrationObject], str] = {
940
+ cls: name for name, cls in CANONICAL_TYPE_REGISTRY.items()
941
+ }