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,938 @@
1
+ <!-- Not directly verified via CLI — documents wxcadm library (not generated CLI commands) -->
2
+ <!-- wxcadm v4.6.1 review 2026-03-27: All v4.5.0–v4.6.1 changes verified present. LocationList(org) signature, Location(org=) constructor, logitude→longitude typo fix, Location.set_default_moh/upload_moh_file removal, workspace_location removal, external_caller_id_name Optional[str] return type — all accurately documented. No discrepancies found. -->
3
+ # wxcadm Location Management
4
+
5
+ Reference for location CRUD, calling enablement, schedules, and location-level features in `wxcadm`.
6
+
7
+ Source files: `wxcadm/location.py`, `wxcadm/location_features.py`
8
+
9
+ ---
10
+
11
+ <!-- Updated by playbook session 2026-03-18 -->
12
+ ## When to Use wxcadm vs Raw HTTP
13
+
14
+ | Use wxcadm when | Use raw HTTP when |
15
+ |---|---|
16
+ | You need **batch operations across all locations** — `LocationList` caches the full list and provides filtered lookups without repeated API calls | You're doing CRUD on a single known location |
17
+ | You want **`enable_webex_calling()`** which orchestrates multiple API calls (PSTN, zone assignment, phone number assignment) in one method | You need to configure a specific location setting (internal dialing, voicemail policy, etc.) |
18
+ | You need **location-scoped feature lists** — `location.call_queues`, `location.hunt_groups`, etc. auto-filter to that location | You want direct control over which endpoints are called |
19
+ | You want **schedule management** via `LocationSchedule` with event CRUD | You're building automation against well-known location endpoints |
20
+
21
+ > **Note:** The playbook uses raw HTTP via `api.session.rest_*()` for location CRUD and settings configuration. Location endpoints are straightforward REST resources. wxcadm's main value here is batch discovery (iterating all locations with cached data) and the `enable_webex_calling()` orchestration method, which bundles multiple setup steps into one call.
22
+
23
+ ---
24
+
25
+ ## Table of Contents
26
+
27
+ 1. [LocationList](#locationlist)
28
+ 2. [Location](#location)
29
+ - [Core Properties](#core-properties)
30
+ - [Calling Enablement](#calling-enablement)
31
+ - [Calling Configuration](#calling-configuration)
32
+ - [Number Management](#number-management)
33
+ - [Emergency Services](#emergency-services)
34
+ - [Schedules](#schedules)
35
+ - [Outgoing Call Permissions](#outgoing-call-permissions)
36
+ - [Feature Access Properties](#feature-access-properties)
37
+ - [Floor Management](#floor-management)
38
+ - [Monitoring](#monitoring)
39
+ - [Delete](#delete)
40
+ 3. [Location Features](#location-features)
41
+ - [LocationSchedule](#locationschedule)
42
+ - [VoicePortal](#voiceportal)
43
+ - [CallParkExtension](#callparkextension)
44
+ - [PagingGroup](#paginggroup)
45
+ - [OutgoingPermissionDigitPattern](#outgoingpermissiondigitpattern)
46
+ - [VoicemailGroup](#voicemailgroup)
47
+ 4. [wxcadm vs wxc_sdk](#wxcadm-vs-wxc_sdk)
48
+
49
+ ---
50
+
51
+ ## LocationList
52
+
53
+ `LocationList` extends `collections.UserList`. It is created automatically when you access `org.locations` and fetches all locations from the Webex `v1/locations` API on initialization.
54
+
55
+ ### Constructor
56
+
57
+ ```python
58
+ LocationList(org: wxcadm.Org)
59
+ ```
60
+
61
+ Fetches all locations on init. The list is iterable and indexable like a normal Python list.
62
+
63
+ ### Methods
64
+
65
+ #### `refresh()`
66
+
67
+ ```python
68
+ def refresh(self) -> None
69
+ ```
70
+
71
+ Re-fetches the full location list from Webex. Call this after creating or deleting locations outside the list.
72
+
73
+ ---
74
+
75
+ #### `get(id=None, name=None, spark_id=None)`
76
+
77
+ ```python
78
+ def get(self, id: str = None, name: str = None, spark_id: str = None) -> Optional[Location]
79
+ ```
80
+
81
+ Find a Location by ID, name, or Spark ID. Searches in that priority order. Returns `None` if no match.
82
+
83
+ Raises `ValueError` if called with no arguments.
84
+
85
+ ```python
86
+ loc = org.locations.get(name="Main Office")
87
+ loc = org.locations.get(id="Y2lzY29...")
88
+ ```
89
+
90
+ ---
91
+
92
+ #### `create(...)`
93
+
94
+ ```python
95
+ def create(self,
96
+ name: str,
97
+ time_zone: str,
98
+ preferred_language: str,
99
+ address: dict,
100
+ latitude: Optional[str] = None,
101
+ longitude: Optional[str] = None) -> Location
102
+ ```
103
+
104
+ Creates a new location via `POST v1/locations`. Automatically refreshes the list and returns the new `Location` instance.
105
+
106
+ **Address dict format:**
107
+
108
+ ```python
109
+ {
110
+ "address1": "100 N. Main",
111
+ "address2": "Suite 200", # optional
112
+ "city": "Houston",
113
+ "state": "TX",
114
+ "postalCode": "32123",
115
+ "country": "US"
116
+ }
117
+ ```
118
+
119
+ ```python
120
+ loc = org.locations.create(
121
+ name="Branch Office",
122
+ time_zone="America/New_York",
123
+ preferred_language="en_US",
124
+ address={"address1": "50 Elm St", "city": "Raleigh", "state": "NC", "postalCode": "27601", "country": "US"}
125
+ )
126
+ ```
127
+
128
+ ---
129
+
130
+ #### `webex_calling(enabled=True, single=False)`
131
+
132
+ ```python
133
+ def webex_calling(self, enabled: bool = True, single: bool = False) -> Location | list[Location]
134
+ ```
135
+
136
+ Filters locations by Webex Calling enablement status. Uses the bulk `v1/telephony/config/locations` API (added in v4.3.9) instead of per-location calls.
137
+
138
+ - `enabled=True` (default): returns locations that have Webex Calling
139
+ - `enabled=False`: returns locations that do NOT have Webex Calling
140
+ - `single=True`: returns just the first matching Location (useful for API calls that only need one WxC location)
141
+
142
+ ```python
143
+ wxc_locations = org.locations.webex_calling()
144
+ non_wxc = org.locations.webex_calling(enabled=False)
145
+ one_loc = org.locations.webex_calling(single=True)
146
+ ```
147
+
148
+ ---
149
+
150
+ #### `with_pstn(has_pstn=True)`
151
+
152
+ ```python
153
+ def with_pstn(self, has_pstn: bool = True) -> list[Location]
154
+ ```
155
+
156
+ Filters Webex Calling locations by whether they have a PSTN provider configured. Calls `webex_calling(enabled=True)` internally, then checks `loc.pstn.provider`.
157
+
158
+ #### `without_pstn()`
159
+
160
+ ```python
161
+ def without_pstn(self) -> list[Location]
162
+ ```
163
+
164
+ Alias for `with_pstn(has_pstn=False)`.
165
+
166
+ ---
167
+
168
+ ## Location
169
+
170
+ The `Location` class represents a single Webex location. Instances are typically created by `LocationList`, not manually.
171
+
172
+ ### Constructor
173
+
174
+ ```python
175
+ Location(org: wxcadm.Org,
176
+ location_id: str,
177
+ name: str,
178
+ time_zone: str,
179
+ preferred_language: str,
180
+ announcement_language: str = None,
181
+ address: dict = None)
182
+ ```
183
+
184
+ ### Core Properties
185
+
186
+ | Property | Type | Description |
187
+ |----------|------|-------------|
188
+ | `id` | `str` | Webex ID of the location |
189
+ | `name` | `str` | Location name |
190
+ | `address` | `dict` | Address dictionary |
191
+ | `time_zone` | `str` | Timezone string (e.g. `America/Chicago`) |
192
+ | `preferred_language` | `str` | Language for phones/menus (e.g. `en_US`) |
193
+ | `announcement_language` | `str` | Language for audio announcements |
194
+ | `org_id` | `str` | Org ID (delegates to `self.org.id`) |
195
+ | `spark_id` | `str` | Decoded Spark ID (underlying service ID) |
196
+
197
+ ---
198
+
199
+ ### Calling Enablement
200
+
201
+ #### `calling_enabled` (property + setter)
202
+
203
+ ```python
204
+ @property
205
+ def calling_enabled(self) -> bool
206
+
207
+ @calling_enabled.setter
208
+ def calling_enabled(self, value: bool)
209
+ ```
210
+
211
+ Checks whether the location has Webex Calling by hitting `GET v1/telephony/config/locations/{id}`. The result is cached after the first call. The setter allows manual override without an API call (used internally by `LocationList.webex_calling()`).
212
+
213
+ #### `enable_webex_calling()`
214
+
215
+ ```python
216
+ def enable_webex_calling(self) -> bool
217
+ ```
218
+
219
+ Enables Webex Calling on the location via `PUT v1/telephony/config/locations`. No-ops if already enabled. Returns `True` on success.
220
+
221
+ **Gotcha:** If `address2` is an empty string `''`, the API will reject it. This method cleans it to `None` before sending.
222
+
223
+ #### `calling_config` (property)
224
+
225
+ ```python
226
+ @property
227
+ def calling_config(self) -> Optional[dict]
228
+ ```
229
+
230
+ Returns the full Webex Calling configuration dict for the location. Cached after first access. Returns `None` if location is not Calling-enabled.
231
+
232
+ ---
233
+
234
+ ### Calling Configuration
235
+
236
+ #### `external_caller_id_name` (property)
237
+
238
+ ```python
239
+ @property
240
+ def external_caller_id_name(self) -> Optional[str]
241
+ ```
242
+
243
+ The default external Caller ID name for the location.
244
+
245
+ #### `set_external_caller_id_name(name)`
246
+
247
+ ```python
248
+ def set_external_caller_id_name(self, name: str) -> bool
249
+ ```
250
+
251
+ Sets the default external Caller ID name. Clears the cached `_calling_config` so the next access re-fetches.
252
+
253
+ #### `routing_prefix` (property)
254
+
255
+ ```python
256
+ @property
257
+ def routing_prefix(self) -> Optional[str]
258
+ ```
259
+
260
+ The routing prefix (location code) for the location. Derived from `calling_config`.
261
+
262
+ #### `set_routing_prefix(prefix)`
263
+
264
+ ```python
265
+ def set_routing_prefix(self, prefix: str) -> bool
266
+ ```
267
+
268
+ Sets the routing prefix via `PUT v1/telephony/config/locations/{id}`.
269
+
270
+ #### `set_announcement_language(language, update_users=False, update_features=False)`
271
+
272
+ ```python
273
+ def set_announcement_language(self,
274
+ language: str,
275
+ update_users: bool = False,
276
+ update_features: bool = False) -> bool
277
+ ```
278
+
279
+ Sets the announcement language for the location. With optional flags to propagate to existing users/workspaces and features.
280
+
281
+ **Important:** Calling without `update_users`/`update_features` only changes the default for *new* users/features. Existing ones keep their current language unless you pass `True`.
282
+
283
+ ```python
284
+ # Change default only
285
+ loc.set_announcement_language("es_ES")
286
+
287
+ # Change and update all existing users and features
288
+ loc.set_announcement_language("es_ES", update_users=True, update_features=True)
289
+ ```
290
+
291
+ #### `unknown_extension_policy` (property)
292
+
293
+ ```python
294
+ @property
295
+ def unknown_extension_policy(self) -> dict
296
+ ```
297
+
298
+ Returns `{'enabled': bool, 'route': Trunk | RouteGroup | None}`. Controls whether unknown extensions are routed to premises via a Trunk or Route Group.
299
+
300
+ #### `set_unknown_extension_policy(route)`
301
+
302
+ ```python
303
+ def set_unknown_extension_policy(self, route: Union[wxcadm.Trunk, wxcadm.RouteGroup, str]) -> bool
304
+ ```
305
+
306
+ Pass a `Trunk` or `RouteGroup` to enable, or the string `'disabled'` to disable.
307
+
308
+ ```python
309
+ trunk = org.call_routing.trunks.get(name="PBX Trunk")
310
+ loc.set_unknown_extension_policy(trunk)
311
+
312
+ # To disable:
313
+ loc.set_unknown_extension_policy('disabled')
314
+ ```
315
+
316
+ ---
317
+
318
+ ### Number Management
319
+
320
+ #### `numbers` (property)
321
+
322
+ ```python
323
+ @property
324
+ def numbers(self) -> Optional[NumberList]
325
+ ```
326
+
327
+ All numbers assigned to this location. Returns `None` if not a Calling location.
328
+
329
+ #### `available_numbers` (property)
330
+
331
+ ```python
332
+ @property
333
+ def available_numbers(self) -> Optional[list[dict]]
334
+ ```
335
+
336
+ Numbers that are available (unassigned) at this location. Includes both Active and Inactive numbers so numbers can be assigned prior to activation/porting.
337
+
338
+ #### `main_number` (property)
339
+
340
+ ```python
341
+ @property
342
+ def main_number(self) -> Number
343
+ ```
344
+
345
+ The main number for the location. Derived from `calling_config['callingLineId']['phoneNumber']`.
346
+
347
+ #### `set_main_number(number)`
348
+
349
+ ```python
350
+ def set_main_number(self, number: Union[Number, str]) -> bool
351
+ ```
352
+
353
+ Sets the main number. Accepts a `Number` instance or a phone number string.
354
+
355
+ Raises `wxcadm.APIError` if the number is rejected by Webex.
356
+
357
+ ---
358
+
359
+ ### Emergency Services
360
+
361
+ #### `ecbn` (property)
362
+
363
+ ```python
364
+ @property
365
+ def ecbn(self) -> dict
366
+ ```
367
+
368
+ Emergency Callback Number settings for the location.
369
+
370
+ #### `set_ecbn(value)`
371
+
372
+ ```python
373
+ def set_ecbn(self, value: Union[str, Person, Workspace, VirtualLine, HuntGroup]) -> bool
374
+ ```
375
+
376
+ Set the ECBN. Pass the string `'location'` to use the location number, or a `Person`, `Workspace`, `VirtualLine`, or `HuntGroup` to use a member's number.
377
+
378
+ ```python
379
+ loc.set_ecbn('location')
380
+ loc.set_ecbn(org.people.get(email="jane@example.com"))
381
+ ```
382
+
383
+ #### `enhanced_emergency_calling` (property)
384
+
385
+ ```python
386
+ @property
387
+ def enhanced_emergency_calling(self) -> LocationEmergencySettings
388
+ ```
389
+
390
+ Returns a `LocationEmergencySettings` object with boolean attributes `integration` and `routing` (RedSky integration status).
391
+
392
+ #### `set_enhanced_emergency_calling(mode)`
393
+
394
+ ```python
395
+ def set_enhanced_emergency_calling(self, mode: str) -> dict
396
+ ```
397
+
398
+ Set the RedSky Enhanced Emergency Calling mode. Valid modes:
399
+
400
+ | Mode | Effect |
401
+ |------|--------|
402
+ | `'none'` | Opt out of enhanced emergency calling |
403
+ | `'integration'` | Integrate with RedSky (HELD data + 933 test calls) |
404
+ | `'routing'` | Route 911 calls through RedSky |
405
+
406
+ **Known issue (from source):** Running this method with any value will cause the location to show a compliance warning in Control Hub. The config is correct but the warning can only be cleared manually in Control Hub.
407
+
408
+ ---
409
+
410
+ ### Schedules
411
+
412
+ #### `schedules` (property)
413
+
414
+ ```python
415
+ @property
416
+ def schedules(self) -> Optional[list[LocationSchedule]]
417
+ ```
418
+
419
+ Returns a list of `LocationSchedule` instances (business hours and holidays). Returns `None` if not a Calling location.
420
+
421
+ See [LocationSchedule](#locationschedule) below for full schedule management.
422
+
423
+ ---
424
+
425
+ ### Outgoing Call Permissions
426
+
427
+ #### `outgoing_call_permissions` (property)
428
+
429
+ ```python
430
+ @property
431
+ def outgoing_call_permissions(self) -> Optional[list]
432
+ ```
433
+
434
+ The `callingPermissions` list for the location. Each entry is a dict with call type and permission settings.
435
+
436
+ #### `set_outgoing_call_permissions(outgoing_call_permissions)`
437
+
438
+ ```python
439
+ def set_outgoing_call_permissions(self, outgoing_call_permissions: list) -> Optional[bool]
440
+ ```
441
+
442
+ Set outgoing call permissions. The easiest approach is to read the current list, modify it, and pass it back.
443
+
444
+ ```python
445
+ perms = loc.outgoing_call_permissions
446
+ # Modify perms...
447
+ loc.set_outgoing_call_permissions(perms)
448
+ ```
449
+
450
+ Raises `ValueError` if the argument is not a list.
451
+
452
+ #### `outgoing_permission_digit_patterns` (property)
453
+
454
+ ```python
455
+ @property
456
+ def outgoing_permission_digit_patterns(self) -> OutgoingPermissionDigitPatternList
457
+ ```
458
+
459
+ Returns an `OutgoingPermissionDigitPatternList` for creating/managing digit pattern-based outgoing call rules. See [OutgoingPermissionDigitPattern](#outgoingpermissiondigitpattern) below.
460
+
461
+ ---
462
+
463
+ ### Feature Access Properties
464
+
465
+ These properties return feature-specific list objects scoped to the location. All return `None` if the location is not Webex Calling enabled (except where noted).
466
+
467
+ | Property | Return Type | Description |
468
+ |----------|-------------|-------------|
469
+ | `hunt_groups` | `HuntGroupList` | Hunt groups at this location |
470
+ | `call_queues` | `CallQueueList` | Call queues at this location |
471
+ | `auto_attendants` | `AutoAttendantList` | Auto attendants at this location |
472
+ | `pickup_groups` | `PickupGroupList` | Pickup groups at this location |
473
+ | `park_extensions` | `list[CallParkExtension]` | Call park extensions at this location |
474
+ | `dect_networks` | `DECTNetworkList` | DECT networks at this location |
475
+ | `virtual_lines` | `VirtualLineList` | Virtual lines at this location |
476
+ | `translation_patterns` | `TranslationPatternList` | Translation patterns at this location |
477
+ | `workspaces` | `WorkspaceList` | Workspaces at this location |
478
+ | `people` | `list` | People assigned to this location (delegates to `org.people.get(location=self)`) |
479
+ | `announcements` | `list[Announcement]` | Location-level announcements (excludes org-level) |
480
+ | `voice_portal` | `VoicePortal` | Voice portal for this location |
481
+ | `recording_vendor` | `LocationRecordingVendorSelection` | Recording vendor selection |
482
+ | `pstn` | `LocationPSTN` | PSTN configuration for this location |
483
+
484
+ #### `create_park_extension(name, extension)`
485
+
486
+ ```python
487
+ def create_park_extension(self, name: str, extension: str) -> str
488
+ ```
489
+
490
+ Creates a new Call Park Extension. Returns the ID of the created extension.
491
+
492
+ ---
493
+
494
+ ### Floor Management
495
+
496
+ #### `floors` (property)
497
+
498
+ ```python
499
+ @property
500
+ def floors(self) -> LocationFloorList
501
+ ```
502
+
503
+ Returns a `LocationFloorList` (extends `UserList`) of `LocationFloor` instances.
504
+
505
+ #### LocationFloorList Methods
506
+
507
+ ```python
508
+ def refresh(self) -> None # Re-fetch from API
509
+ def create(self, floor_number: int, name: str) -> LocationFloor # Create a floor
510
+ ```
511
+
512
+ `create()` raises `ValueError` if the floor number already exists.
513
+
514
+ #### LocationFloor
515
+
516
+ | Attribute | Type | Description |
517
+ |-----------|------|-------------|
518
+ | `id` | `str` | Floor ID |
519
+ | `floor_number` | `int` | Numeric floor within the building |
520
+ | `name` | `str` | Display name (e.g. "Basement", "2nd Floor") |
521
+
522
+ ```python
523
+ def update(self, floor_number: Optional[int] = None, name: Optional[str] = None) -> bool
524
+ def delete(self) -> bool
525
+ ```
526
+
527
+ ---
528
+
529
+ ### Monitoring
530
+
531
+ #### `get_all_monitoring()`
532
+
533
+ ```python
534
+ def get_all_monitoring(self) -> dict
535
+ ```
536
+
537
+ Returns a dict showing who is monitoring whom at this location:
538
+
539
+ ```python
540
+ {
541
+ 'people': { person_instance: [list_of_monitors] },
542
+ 'workspaces': { workspace_instance: [list_of_monitors] }
543
+ }
544
+ ```
545
+
546
+ Iterates over all people and Webex Calling workspaces at the location, collecting their monitored elements.
547
+
548
+ ---
549
+
550
+ ### Delete
551
+
552
+ #### `delete()`
553
+
554
+ ```python
555
+ def delete(self) -> bool
556
+ ```
557
+
558
+ **Always returns `False`.** Location deletion is not supported via the Webex API. This stub exists so users are not searching for a missing method. Must be done in Control Hub.
559
+
560
+ ---
561
+
562
+ ## Location Features
563
+
564
+ Defined in `wxcadm/location_features.py`. These classes represent features that are scoped to a specific location.
565
+
566
+ ### LocationSchedule
567
+
568
+ ```python
569
+ @dataclass
570
+ class LocationSchedule:
571
+ location: wxcadm.Location
572
+ id: str
573
+ name: str
574
+ type: str # 'businessHours' or 'holidays'
575
+ config: dict # auto-populated on init
576
+ ```
577
+
578
+ Fetches its full configuration from `v1/telephony/config/locations/{loc_id}/schedules/{type}/{id}` on initialization.
579
+
580
+ #### `refresh_config()`
581
+
582
+ ```python
583
+ def refresh_config(self) -> None
584
+ ```
585
+
586
+ Re-fetches the schedule config from the API.
587
+
588
+ #### `add_holiday(name, date, recur=False, recurrence=None)`
589
+
590
+ ```python
591
+ def add_holiday(self, name: str, date: str, recur: bool = False, recurrence: Optional[dict] = None) -> bool
592
+ ```
593
+
594
+ Adds an all-day holiday event. Only works on `holidays` type schedules (raises `TypeError` otherwise).
595
+
596
+ - `date` format: `YYYY-MM-DD`
597
+ - If `recur=True` and no `recurrence` dict is given, defaults to yearly recurrence on the same month/day (works for fixed-date holidays like Christmas)
598
+ - For floating holidays (e.g. Thanksgiving), pass a custom recurrence dict:
599
+
600
+ ```python
601
+ recurrence = {
602
+ 'recurForEver': True,
603
+ 'recurYearlyByDay': {
604
+ 'day': 'THURSDAY',
605
+ 'week': 'FOURTH',
606
+ 'month': 'NOVEMBER'
607
+ }
608
+ }
609
+ schedule.add_holiday("Thanksgiving", "2026-11-26", recur=True, recurrence=recurrence)
610
+ ```
611
+
612
+ #### `create_event(name, start_date, end_date, ...)`
613
+
614
+ ```python
615
+ def create_event(self,
616
+ name: str,
617
+ start_date: str,
618
+ end_date: str,
619
+ start_time: str = "",
620
+ end_time: str = "",
621
+ all_day: bool = False,
622
+ recurrence: Optional[dict] = None) -> bool
623
+ ```
624
+
625
+ Generic event creation for either schedule type. If `all_day=False`, both `start_time` and `end_time` are required (raises `ValueError` otherwise).
626
+
627
+ Recurrence dict example (every weekday):
628
+
629
+ ```python
630
+ recurrence = {
631
+ 'recurForEver': True,
632
+ 'recurWeekly': {
633
+ 'monday': True
634
+ }
635
+ }
636
+ ```
637
+
638
+ #### `update_event(id, ...)`
639
+
640
+ ```python
641
+ def update_event(self,
642
+ id: str,
643
+ name: str = None,
644
+ start_date: str = None,
645
+ end_date: str = None,
646
+ start_time: str = None,
647
+ end_time: str = None,
648
+ all_day: bool = None,
649
+ recurrence: dict = None) -> bool
650
+ ```
651
+
652
+ Updates an existing event by ID. Fetches the current config for the event, merges in provided values, and PUTs it back.
653
+
654
+ **Known bug in wxcadm:** The `update_event()` method's API URL is truncated — it PUTs to `v1/telephony/config/` instead of the full schedule/event path (`v1/telephony/config/locations/{loc_id}/schedules/{type}/{id}/events/{event_id}`). This means updates will fail with an API error. Workaround: use raw HTTP via `api.session.rest_put()` with the correct URL.
655
+
656
+ #### `delete_event(event)`
657
+
658
+ ```python
659
+ def delete_event(self, event: str) -> bool
660
+ ```
661
+
662
+ Deletes an event by name or ID (both are unique within a schedule).
663
+
664
+ #### `get_event_config_by_id(id)`
665
+
666
+ ```python
667
+ def get_event_config_by_id(self, id: str) -> Optional[dict]
668
+ ```
669
+
670
+ Returns the raw event dict from the schedule config for a given event ID. Useful for inspecting or modifying event data.
671
+
672
+ #### `clone(target_location=None, name=None)`
673
+
674
+ ```python
675
+ def clone(self,
676
+ target_location: Optional[wxcadm.Location] = None,
677
+ name: Optional[str] = None) -> str
678
+ ```
679
+
680
+ Clones the schedule. You must provide at least one of `target_location` or `name`.
681
+
682
+ - To clone to another location (same name): `schedule.clone(target_location=other_loc)`
683
+ - To clone within the same location (new name): `schedule.clone(name="After Hours v2")`
684
+ - Both: `schedule.clone(target_location=other_loc, name="Custom Name")`
685
+
686
+ Returns the ID of the newly created schedule.
687
+
688
+ ---
689
+
690
+ ### VoicePortal
691
+
692
+ ```python
693
+ @dataclass
694
+ class VoicePortal(RealtimeClass):
695
+ location: wxcadm.Location
696
+ id: str # auto-populated
697
+ language: str
698
+ language_code: str
699
+ extension: str
700
+ phone_number: str
701
+ first_name: str
702
+ last_name: str
703
+ ```
704
+
705
+ Extends `RealtimeClass` -- changing attributes directly pushes updates to Webex immediately (unlike other wxcadm classes that require explicit save calls).
706
+
707
+ **How auto-push works:** `_api_fields` is initialized with `['phone_number', 'extension']`, but `RealtimeClass.__init__` appends ALL fields from the API GET response to the same list. The `__setattr__` override fires on ANY attribute change after initialization, PUTting the full `api_fields` payload back. So all API-returned fields (language, language_code, first_name, last_name, etc.) are also auto-synced — not just phone_number and extension. If the PUT fails, the attribute is reverted to its old value and a `PutError` is raised.
708
+
709
+ #### `copy_config(target_location, phone_number=None, passcode=None)`
710
+
711
+ ```python
712
+ def copy_config(self,
713
+ target_location: wxcadm.Location,
714
+ phone_number: str = None,
715
+ passcode: str = None) -> bool
716
+ ```
717
+
718
+ Copies the Voice Portal settings (language, extension, name) to another Calling-enabled location. Phone number and passcode are NOT copied unless explicitly provided.
719
+
720
+ Raises `ValueError` if the target location is not Webex Calling enabled.
721
+
722
+ ---
723
+
724
+ ### CallParkExtension
725
+
726
+ ```python
727
+ @dataclass
728
+ class CallParkExtension:
729
+ location: wxcadm.Location
730
+ id: str
731
+ name: str
732
+ extension: str
733
+ ```
734
+
735
+ Created via `loc.park_extensions` (property) or `loc.create_park_extension(name, extension)`.
736
+
737
+ #### `get_monitored_by()`
738
+
739
+ ```python
740
+ def get_monitored_by(self) -> Optional[list]
741
+ ```
742
+
743
+ Returns a list of users/workspaces monitoring this park extension. Uses the org-level `get_all_monitoring()` method.
744
+
745
+ ---
746
+
747
+ ### PagingGroup
748
+
749
+ ```python
750
+ @dataclass
751
+ class PagingGroup:
752
+ location: wxcadm.Location
753
+ id: str
754
+ name: str
755
+ spark_id: str # auto-computed
756
+ config: dict # auto-populated on init
757
+ ```
758
+
759
+ #### `refresh_config()`
760
+
761
+ ```python
762
+ def refresh_config(self) -> None
763
+ ```
764
+
765
+ Re-fetches the paging group configuration from `v1/telephony/config/locations/{loc_id}/paging/{id}`.
766
+
767
+ **Access pattern:** PagingGroup instances are accessed at the **org level** via `org.paging_groups` (a lazily-loaded list property that fetches from `v1/telephony/config/paging`) and `org.get_paging_group(id=, name=, spark_id=)`. They are NOT exposed as a Location property despite being location-scoped.
768
+
769
+ ---
770
+
771
+ ### OutgoingPermissionDigitPattern
772
+
773
+ Accessed via `loc.outgoing_permission_digit_patterns`.
774
+
775
+ #### OutgoingPermissionDigitPatternList
776
+
777
+ ```python
778
+ class OutgoingPermissionDigitPatternList:
779
+ patterns: list[OutgoingPermissionDigitPattern]
780
+ ```
781
+
782
+ Methods:
783
+
784
+ ```python
785
+ def refresh(self) -> self
786
+ def create(self, name: str, pattern: str, action: str, transfer_enabled: Optional[bool] = False) -> OutgoingPermissionDigitPattern
787
+ def delete_all(self) -> bool
788
+ ```
789
+
790
+ **Valid `action` values:** `'ALLOW'`, `'BLOCK'`, `'AUTH_CODE'`, `'TRANSFER_NUMBER_1'`, `'TRANSFER_NUMBER_2'`, `'TRANSFER_NUMBER_3'`
791
+
792
+ ```python
793
+ patterns = loc.outgoing_permission_digit_patterns
794
+ new = patterns.create(name="Block 900", pattern="1900XXXXXXX", action="BLOCK")
795
+ patterns.delete_all() # remove all patterns at the location
796
+ ```
797
+
798
+ #### OutgoingPermissionDigitPattern Instance
799
+
800
+ | Attribute | Type | Description |
801
+ |-----------|------|-------------|
802
+ | `id` | `str` | Pattern ID |
803
+ | `name` | `str` | Pattern name |
804
+ | `pattern` | `str` | The digit pattern |
805
+ | `action` | `str` | Action taken (ALLOW, BLOCK, etc.) |
806
+ | `transfer_enabled` | `bool` | Whether applied to transferred/forwarded calls |
807
+
808
+ ```python
809
+ def update(self, name=None, pattern=None, action=None, transfer_enabled=None) -> OutgoingPermissionDigitPattern
810
+ def delete(self) -> bool
811
+ ```
812
+
813
+ Only pass the values you want to change to `update()`.
814
+
815
+ ---
816
+
817
+ ### VoicemailGroup
818
+
819
+ Managed at the **org level** via `org.voicemail_groups` (a `VoicemailGroupList`), but scoped to a location.
820
+
821
+ #### VoicemailGroupList
822
+
823
+ ```python
824
+ class VoicemailGroupList(UserList):
825
+ def __init__(self, org: wxcadm.Org)
826
+ ```
827
+
828
+ Methods:
829
+
830
+ ```python
831
+ def refresh(self) -> self
832
+ def get(self, name: str = None, id: str = None) -> Optional[VoicemailGroup]
833
+ def create(self,
834
+ location: wxcadm.Location,
835
+ name: str,
836
+ extension: str,
837
+ passcode: str,
838
+ phone_number: Optional[str] = None,
839
+ first_name: Optional[str] = None,
840
+ last_name: Optional[str] = None,
841
+ language_code: Optional[str] = 'en_us',
842
+ message_storage: Optional[dict] = None,
843
+ notifications: Optional[dict] = None,
844
+ fax_message: Optional[dict] = None,
845
+ transfer_to_number: Optional[dict] = None,
846
+ email_copy_of_message: Optional[dict] = None) -> VoicemailGroup
847
+ ```
848
+
849
+ Default values for `create()` if not provided:
850
+ - `message_storage`: `{'storageType': 'INTERNAL'}`
851
+ - `notifications`, `fax_message`, `transfer_to_number`, `email_copy_of_message`: `{'enabled': False}`
852
+
853
+ #### VoicemailGroup Instance
854
+
855
+ Core attributes (available immediately):
856
+
857
+ | Attribute | Type | Description |
858
+ |-----------|------|-------------|
859
+ | `id` | `str` | Voicemail Group ID |
860
+ | `name` | `str` | Group name |
861
+ | `location_name` | `str` | Location name |
862
+ | `location_id` | `str` | Location ID |
863
+ | `extension` | `str` | Extension number |
864
+ | `enabled` | `bool` | Whether the group is enabled |
865
+ | `esn` | `Optional[str]` | ESN |
866
+
867
+ Lazy-loaded properties (trigger an API call on first access):
868
+
869
+ | Property | Type | Description |
870
+ |----------|------|-------------|
871
+ | `phone_number` | `str` | DID |
872
+ | `first_name` | `str` | Directory first name |
873
+ | `last_name` | `str` | Directory last name |
874
+ | `language_code` | `str` | Language code |
875
+ | `greeting_type` | `str` | `'DEFAULT'` or `'CUSTOM'` |
876
+ | `greeting_uploaded` | `bool` | Whether a custom greeting was uploaded |
877
+ | `greeting_description` | `str` | Custom greeting description |
878
+ | `message_storage` | `dict` | Storage config |
879
+ | `notifications` | `dict` | Notification settings |
880
+ | `fax_message` | `dict` | Fax settings |
881
+ | `transfer_to_number` | `dict` | Transfer settings |
882
+ | `email_copy_of_message` | `dict` | Email copy settings |
883
+ | `message_forwarding_enabled` | `bool` | Voice message forwarding status |
884
+
885
+ Methods:
886
+
887
+ ```python
888
+ def update(self, name=None, phone_number=None, extension=None, first_name=None,
889
+ last_name=None, enabled=None, language_code=None, greeting_type=None,
890
+ greeting_description=None, message_storage=None, notifications=None,
891
+ fax_message=None, transfer_to_number=None, email_copy_of_message=None) -> VoicemailGroup
892
+ def delete(self) -> bool
893
+ def enable_email_copy(self, email: str) -> VoicemailGroup # shortcut for update()
894
+ ```
895
+
896
+ ---
897
+
898
+ ## wxcadm vs wxc_sdk
899
+
900
+ ### Location access patterns
901
+
902
+ | Operation | wxcadm | wxc_sdk |
903
+ |-----------|--------|---------|
904
+ | List locations | `org.locations` (auto-fetches) | `api.locations.list()` (returns generator) |
905
+ | Get by name | `org.locations.get(name="...")` | `api.locations.list(name="...")` then iterate |
906
+ | Get by ID | `org.locations.get(id="...")` | `api.locations.details(location_id="...")` |
907
+ | Create | `org.locations.create(...)` returns `Location` | `api.locations.create(...)` returns ID string |
908
+ | Delete | Not supported (returns `False`) | `api.locations.delete(location_id)` |
909
+
910
+ ### Calling enablement
911
+
912
+ | Operation | wxcadm | wxc_sdk |
913
+ |-----------|--------|---------|
914
+ | Check if Calling-enabled | `loc.calling_enabled` (bool property) | Check via `api.telephony.location.details()` |
915
+ | Enable Calling | `loc.enable_webex_calling()` | `api.telephony.location.enable_for_calling(location)` |
916
+ | Filter Calling locations | `org.locations.webex_calling()` (bulk API) | Manual filtering required |
917
+ | Filter by PSTN | `org.locations.with_pstn()` / `without_pstn()` | Manual filtering required |
918
+
919
+ ### Key architectural differences
920
+
921
+ 1. **Caching**: wxcadm caches calling config, numbers, and feature lists as instance attributes. wxc_sdk generally does not cache and makes a fresh API call each time.
922
+
923
+ 2. **Object model**: wxcadm uses a `Location` object with rich properties that lazily load sub-features (hunt groups, call queues, schedules, etc.). wxc_sdk uses separate API method groups (`api.telephony.callqueue`, `api.telephony.huntgroup`, etc.) that take a `location_id` parameter.
924
+
925
+ 3. **List classes**: wxcadm wraps results in `UserList` subclasses (e.g., `LocationList`, `CallQueueList`) with `.get()` and `.refresh()` methods. wxc_sdk returns plain generators or lists.
926
+
927
+ 4. **Setter pattern**: wxcadm uses separate `set_*` methods (e.g., `set_ecbn()`, `set_routing_prefix()`) rather than property setters that auto-push. The exception is `VoicePortal` which extends `RealtimeClass` for auto-push behavior.
928
+
929
+ 5. **Schedule management**: wxcadm exposes `LocationSchedule` objects with `add_holiday()`, `create_event()`, `clone()`, etc. wxc_sdk uses `api.telephony.schedules` (a `ScheduleApi` instance) with `list()`, `details()`, `create()`, `update()`, `delete()`, and event-level methods.
930
+
931
+ 6. **Bulk operations**: wxcadm's `LocationList.webex_calling()` uses a single bulk API call (`v1/telephony/config/locations`) for efficiency (added in v4.3.9). Earlier versions made per-location calls.
932
+
933
+ ---
934
+
935
+ ## See Also
936
+
937
+ - [provisioning.md](provisioning.md) — wxc_sdk location provisioning patterns (creation, calling enablement, number assignment)
938
+ - [location-calling-core.md](location-calling-core.md) — wxc_sdk location-level call settings (caller ID, voicemail, music on hold, outgoing permissions)