datex-studio-cli 0.4.1__tar.gz → 0.4.3__tar.gz
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.
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/PKG-INFO +1 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/pyproject.toml +1 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/scripts/analyze_corpus.py +12 -12
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/scripts/generate_rdlx_schema.py +42 -18
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/__init__.py +1 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/cli.py +141 -10
- datex_studio_cli-0.4.3/src/dxs/commands/api.py +236 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/auth.py +114 -35
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/branch.py +2 -5
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/config.py +7 -3
- datex_studio_cli-0.4.3/src/dxs/commands/configuration.py +250 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/crm.py +92 -3
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/datasource.py +41 -23
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/devops.py +15 -13
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/document.py +4 -6
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/explore.py +25 -27
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/function.py +1 -2
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/odata.py +26 -5
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/organization.py +3 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/release_notes.py +3 -8
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/__init__.py +6 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/add_cmds.py +30 -22
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/api.py +8 -15
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/create.py +3 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/data.py +11 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/dataset.py +3 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/folder.py +62 -20
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/schema.py +57 -27
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/source.py +33 -44
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/studio.py +0 -1
- datex_studio_cli-0.4.3/src/dxs/commands/telemetry.py +181 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/context.py +10 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/__init__.py +9 -2
- datex_studio_cli-0.4.3/src/dxs/core/api/app_config.py +76 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/endpoints.py +11 -17
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_generator.py +1 -3
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_models.py +1 -3
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/models.py +3 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/parsers.py +2 -6
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/models.py +12 -6
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/client.py +117 -2
- datex_studio_cli-0.4.3/src/dxs/core/dynamics/metadata_cache.py +133 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/csv_fmt.py +2 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/formatter.py +13 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/redaction.py +61 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/release_notes.py +2 -7
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/__init__.py +29 -0
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/config.py +141 -0
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/identity.py +89 -0
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/recorder.py +232 -0
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/scrubber.py +83 -0
- datex_studio_cli-0.4.3/src/dxs/core/telemetry/sender.py +215 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/engine.py +11 -6
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/folder.py +9 -20
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/preview_arjs.py +4 -2
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/schema.py +1 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/svg_renderer.py +8 -6
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/validator.py +10 -25
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/paths.py +11 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/resolvers.py +27 -11
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/update_check.py +31 -14
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/app.py +1 -1
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/design.py +15 -15
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/proxy_app.py +1 -3
- datex_studio_cli-0.4.1/src/dxs/commands/api.py +0 -147
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/.gitignore +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/README.md +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/report-creator.skill +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/__main__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/endpoint.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/env.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/marketplace.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/proxy.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/repo.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/batch.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/edit.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/preview.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/schema_cmds.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/servicepack.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/user.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/client.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/metadata_models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/decorators.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/msal_client.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/token_cache.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/cache.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/generator.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/parsers.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/resolver.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/type_def_parser.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/validator.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/flow_models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/client.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/helpers.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/client.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/edmx_parser.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/metadata.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/generator.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/validator.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/graph.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/json_fmt.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/yaml_fmt.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/responses.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/models/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/datasource_binding.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/field_parser.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/manifest.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/models.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/preview.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/validate_arjs.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/wrapper.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/click_options.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/config.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/errors.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/filtering.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/image.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/responses.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/restricted.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/sorting.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/__init__.py +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/404/index.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/404.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.txt +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.txt +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.txt +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/render-cli.js +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-preview.html +0 -0
- {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-validate.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datex-studio-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: CLI for Datex Studio low-code platform, designed for LLM-based AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/datex/datex-studio-cli
|
|
6
6
|
Project-URL: Documentation, https://github.com/datex/datex-studio-cli
|
|
@@ -226,39 +226,39 @@ def main() -> None:
|
|
|
226
226
|
for cat, cnt in category_counts.most_common():
|
|
227
227
|
print(f" {cat:30s} {cnt:4d}")
|
|
228
228
|
|
|
229
|
-
print(
|
|
229
|
+
print("\n## Page Size Distribution")
|
|
230
230
|
for size, cnt in page_sizes.most_common():
|
|
231
231
|
pct = cnt / total * 100
|
|
232
232
|
print(f" {size:30s} {cnt:4d} ({pct:.0f}%)")
|
|
233
233
|
|
|
234
|
-
print(
|
|
234
|
+
print("\n## Margin Distribution")
|
|
235
235
|
for margin, cnt in margin_counter.most_common(15):
|
|
236
236
|
pct = cnt / total * 100
|
|
237
237
|
print(f" {margin:55s} {cnt:4d} ({pct:.0f}%)")
|
|
238
238
|
|
|
239
|
-
print(
|
|
239
|
+
print("\n## Item Type Frequency")
|
|
240
240
|
for typ, cnt in item_type_counter.most_common():
|
|
241
241
|
print(f" {typ:25s} {cnt:5d}")
|
|
242
242
|
|
|
243
|
-
print(
|
|
243
|
+
print("\n## Barcode Symbology Frequency")
|
|
244
244
|
for sym, cnt in barcode_symbology_counter.most_common():
|
|
245
245
|
print(f" {sym:25s} {cnt:4d}")
|
|
246
246
|
|
|
247
|
-
print(
|
|
247
|
+
print("\n## Font Usage Frequency (top 20)")
|
|
248
248
|
for fnt, cnt in font_counter.most_common(20):
|
|
249
249
|
print(f" {fnt:30s} {cnt:5d}")
|
|
250
250
|
|
|
251
|
-
print(
|
|
251
|
+
print("\n## Expression Pattern Frequency")
|
|
252
252
|
for pat, cnt in expression_pattern_counter.most_common():
|
|
253
253
|
print(f" {pat:30s} {cnt:5d}")
|
|
254
254
|
|
|
255
|
-
print(
|
|
255
|
+
print("\n## Dataset Count per Report")
|
|
256
256
|
if dataset_counts:
|
|
257
257
|
print(f" Mean: {mean(dataset_counts):.1f}")
|
|
258
258
|
print(f" Median: {median(dataset_counts):.1f}")
|
|
259
259
|
print(f" Range: {min(dataset_counts)} - {max(dataset_counts)}")
|
|
260
260
|
|
|
261
|
-
print(
|
|
261
|
+
print("\n## Element Count per Report by Category (body-level items)")
|
|
262
262
|
for cat in sorted(element_counts_by_category):
|
|
263
263
|
counts = element_counts_by_category[cat]
|
|
264
264
|
if counts:
|
|
@@ -269,7 +269,7 @@ def main() -> None:
|
|
|
269
269
|
f"n={len(counts)}"
|
|
270
270
|
)
|
|
271
271
|
|
|
272
|
-
print(
|
|
272
|
+
print("\n## Common Field Names by Category (top 10)")
|
|
273
273
|
for cat in sorted(field_names_by_category):
|
|
274
274
|
top = field_names_by_category[cat].most_common(10)
|
|
275
275
|
if top:
|
|
@@ -277,9 +277,9 @@ def main() -> None:
|
|
|
277
277
|
for name, cnt in top:
|
|
278
278
|
print(f" {name:30s} {cnt:4d}")
|
|
279
279
|
|
|
280
|
-
print(
|
|
281
|
-
print(f" PageHeader: {page_header_count}/{total} ({page_header_count/total*100:.0f}%)")
|
|
282
|
-
print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count/total*100:.0f}%)")
|
|
280
|
+
print("\n## PageHeader / PageFooter Usage")
|
|
281
|
+
print(f" PageHeader: {page_header_count}/{total} ({page_header_count / total * 100:.0f}%)")
|
|
282
|
+
print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count / total * 100:.0f}%)")
|
|
283
283
|
|
|
284
284
|
|
|
285
285
|
if __name__ == "__main__":
|
|
@@ -241,9 +241,22 @@ def compare_schema(item_types: dict, items: list[dict]) -> dict:
|
|
|
241
241
|
all_schema_props = schema_props | schema_style_props
|
|
242
242
|
corpus_props = set(item_types[type_name]["properties"].keys())
|
|
243
243
|
# Exclude common layout props that aren't type-specific
|
|
244
|
-
common_layout = {
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
common_layout = {
|
|
245
|
+
"Type",
|
|
246
|
+
"Name",
|
|
247
|
+
"Left",
|
|
248
|
+
"Top",
|
|
249
|
+
"Width",
|
|
250
|
+
"Height",
|
|
251
|
+
"ZIndex",
|
|
252
|
+
"Visibility",
|
|
253
|
+
"Style",
|
|
254
|
+
"LayerName",
|
|
255
|
+
"OverflowName",
|
|
256
|
+
"Bookmark",
|
|
257
|
+
"Label",
|
|
258
|
+
"CustomProperties",
|
|
259
|
+
}
|
|
247
260
|
missing = sorted((corpus_props - all_schema_props) - common_layout)
|
|
248
261
|
if missing:
|
|
249
262
|
props_not_in_schema[type_name] = missing
|
|
@@ -296,13 +309,13 @@ def print_summary(schema: dict) -> None:
|
|
|
296
309
|
# Common properties
|
|
297
310
|
common = schema.get("common_properties", {})
|
|
298
311
|
if common:
|
|
299
|
-
print(
|
|
312
|
+
print("\n## Common Properties (shared across >=80% of item types)")
|
|
300
313
|
for prop, info in sorted(common.items()):
|
|
301
314
|
print(f" {prop:25s} {info['present_in']}")
|
|
302
315
|
|
|
303
316
|
# Top properties per type (top 5 by count, excluding common)
|
|
304
317
|
common_names = set(common.keys()) if common else set()
|
|
305
|
-
print(
|
|
318
|
+
print("\n## Type-Specific Properties (top 10, excluding common)")
|
|
306
319
|
for type_name, type_info in sorted(item_types.items(), key=lambda x: -x[1]["count"]):
|
|
307
320
|
props = type_info["properties"]
|
|
308
321
|
specific = [(k, v) for k, v in props.items() if k not in common_names]
|
|
@@ -322,41 +335,41 @@ def print_summary(schema: dict) -> None:
|
|
|
322
335
|
# Comparison
|
|
323
336
|
comp = schema.get("comparison", {})
|
|
324
337
|
if comp and "error" not in comp:
|
|
325
|
-
print(
|
|
338
|
+
print("\n## Schema Comparison (corpus vs schema.py)")
|
|
326
339
|
|
|
327
340
|
types_missing = comp.get("item_types_in_corpus_not_in_schema", [])
|
|
328
341
|
if types_missing:
|
|
329
|
-
print(
|
|
342
|
+
print(" Item types in corpus but NOT in schema.py:")
|
|
330
343
|
for t in types_missing:
|
|
331
344
|
count = item_types.get(t, {}).get("count", "?")
|
|
332
345
|
print(f" - {t} ({count} instances)")
|
|
333
346
|
|
|
334
347
|
types_extra = comp.get("item_types_in_schema_not_in_corpus", [])
|
|
335
348
|
if types_extra:
|
|
336
|
-
print(
|
|
349
|
+
print(" Item types in schema.py but NOT in corpus:")
|
|
337
350
|
for t in types_extra:
|
|
338
351
|
print(f" - {t}")
|
|
339
352
|
|
|
340
353
|
props_missing = comp.get("properties_in_corpus_not_in_schema", {})
|
|
341
354
|
if props_missing:
|
|
342
|
-
print(
|
|
355
|
+
print(" Properties in corpus but NOT in schema.py:")
|
|
343
356
|
for type_name, props in sorted(props_missing.items()):
|
|
344
357
|
print(f" {type_name}: {', '.join(props)}")
|
|
345
358
|
|
|
346
359
|
sym_missing = comp.get("symbologies_in_corpus_not_in_schema", [])
|
|
347
360
|
if sym_missing:
|
|
348
|
-
print(
|
|
361
|
+
print(" Barcode symbologies in corpus but NOT in schema.py:")
|
|
349
362
|
for s in sym_missing:
|
|
350
363
|
print(f" - {s}")
|
|
351
364
|
|
|
352
365
|
sym_extra = comp.get("symbologies_in_schema_not_in_corpus", [])
|
|
353
366
|
if sym_extra:
|
|
354
|
-
print(
|
|
367
|
+
print(" Barcode symbologies in schema.py but NOT in corpus:")
|
|
355
368
|
for s in sym_extra:
|
|
356
369
|
print(f" - {s}")
|
|
357
370
|
|
|
358
371
|
if not comp or all(not v for v in comp.values() if isinstance(v, (list, dict))):
|
|
359
|
-
print(
|
|
372
|
+
print("\n Schema comparison: no gaps found!")
|
|
360
373
|
|
|
361
374
|
|
|
362
375
|
# ── Main ─────────────────────────────────────────────────────────────────
|
|
@@ -364,12 +377,23 @@ def print_summary(schema: dict) -> None:
|
|
|
364
377
|
|
|
365
378
|
def main() -> None:
|
|
366
379
|
parser = argparse.ArgumentParser(description="Generate RDLX-JSON schema from report corpus")
|
|
367
|
-
parser.add_argument(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
380
|
+
parser.add_argument(
|
|
381
|
+
"directory",
|
|
382
|
+
nargs="?",
|
|
383
|
+
default="example-reports",
|
|
384
|
+
help="Directory containing .rdlx-json files (default: example-reports/)",
|
|
385
|
+
)
|
|
386
|
+
parser.add_argument(
|
|
387
|
+
"-o",
|
|
388
|
+
"--output",
|
|
389
|
+
default="corpus-schema.json",
|
|
390
|
+
help="Output JSON file path (default: corpus-schema.json)",
|
|
391
|
+
)
|
|
392
|
+
parser.add_argument(
|
|
393
|
+
"--summary-only",
|
|
394
|
+
action="store_true",
|
|
395
|
+
help="Print human-readable summary only, skip JSON output",
|
|
396
|
+
)
|
|
373
397
|
args = parser.parse_args()
|
|
374
398
|
|
|
375
399
|
root = Path(args.directory)
|
|
@@ -38,6 +38,37 @@ def set_last_output_format(fmt: OutputFormat) -> None:
|
|
|
38
38
|
_last_output_format = fmt
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _format_set_in_env_or_config() -> bool:
|
|
42
|
+
"""Whether ``default_output_format`` was set explicitly outside CLI flags.
|
|
43
|
+
|
|
44
|
+
Used to decide if TTY-auto-script mode should override the default format.
|
|
45
|
+
Returns True when either ``DXS_DEFAULT_OUTPUT_FORMAT`` is in the environment
|
|
46
|
+
or the config file contains a ``default_output_format`` entry.
|
|
47
|
+
"""
|
|
48
|
+
import os
|
|
49
|
+
|
|
50
|
+
from dxs.utils.config import load_config_file
|
|
51
|
+
|
|
52
|
+
if os.environ.get("DXS_DEFAULT_OUTPUT_FORMAT"):
|
|
53
|
+
return True
|
|
54
|
+
try:
|
|
55
|
+
return bool(load_config_file().get("default_output_format"))
|
|
56
|
+
except Exception: # noqa: BLE001 - config read should never block startup
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _stdout_is_pipe() -> bool:
|
|
61
|
+
"""Whether stdout looks like a non-interactive consumer (pipe/redirect).
|
|
62
|
+
|
|
63
|
+
Pulled into a helper so tests can patch it independently of Click's
|
|
64
|
+
CliRunner, which swaps ``sys.stdout`` with a non-TTY StringIO.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
return not sys.stdout.isatty()
|
|
68
|
+
except (AttributeError, ValueError):
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
41
72
|
# Global options shared across all commands
|
|
42
73
|
CONTEXT_SETTINGS = {
|
|
43
74
|
"help_option_names": ["-h", "--help"],
|
|
@@ -140,6 +171,13 @@ class DxsGroup(click.Group):
|
|
|
140
171
|
default=False,
|
|
141
172
|
help="Show full output including null values and verbose metadata",
|
|
142
173
|
)
|
|
174
|
+
@click.option(
|
|
175
|
+
"--flat-fv/--no-flat-fv",
|
|
176
|
+
"flat_fv",
|
|
177
|
+
default=None,
|
|
178
|
+
help="Flatten Dynamics @OData FormattedValue siblings into clean keys. "
|
|
179
|
+
"Defaults to on with --concise (the default mode); pass --no-flat-fv to opt out.",
|
|
180
|
+
)
|
|
143
181
|
@click.option(
|
|
144
182
|
"-s",
|
|
145
183
|
"--save",
|
|
@@ -181,6 +219,7 @@ def cli(
|
|
|
181
219
|
verbose: bool,
|
|
182
220
|
quiet: bool,
|
|
183
221
|
full: bool,
|
|
222
|
+
flat_fv: bool | None,
|
|
184
223
|
save_path: str | None,
|
|
185
224
|
force_overwrite: bool,
|
|
186
225
|
diagnose_auth: bool,
|
|
@@ -229,19 +268,40 @@ def cli(
|
|
|
229
268
|
# Load settings
|
|
230
269
|
settings = get_settings()
|
|
231
270
|
|
|
232
|
-
#
|
|
271
|
+
# When stdout is not a TTY (piped/redirected), assume a script consumer:
|
|
272
|
+
# default to JSON output and suppress progress banners unless the user
|
|
273
|
+
# has explicitly chosen otherwise via CLI flag, env var, or config file.
|
|
274
|
+
piped = _stdout_is_pipe()
|
|
275
|
+
click_ctx = click.get_current_context()
|
|
276
|
+
quiet_src = click_ctx.get_parameter_source("quiet")
|
|
277
|
+
fmt_explicit_in_env_or_config = _format_set_in_env_or_config()
|
|
278
|
+
|
|
279
|
+
# Set output format (CLI flag > env var > config > TTY auto > default)
|
|
233
280
|
if output_format:
|
|
234
281
|
dxs_ctx.output_format = OutputFormat(output_format.lower())
|
|
235
282
|
dxs_ctx.explicit_output_format = True
|
|
283
|
+
elif fmt_explicit_in_env_or_config and settings.default_output_format:
|
|
284
|
+
dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
|
|
285
|
+
elif piped:
|
|
286
|
+
dxs_ctx.output_format = OutputFormat.JSON
|
|
236
287
|
elif settings.default_output_format:
|
|
237
288
|
dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
|
|
238
289
|
set_last_output_format(dxs_ctx.output_format)
|
|
239
290
|
|
|
240
291
|
# Set verbosity (--verbose enables debug output, --quiet suppresses progress)
|
|
241
292
|
dxs_ctx.verbose = verbose
|
|
242
|
-
|
|
293
|
+
if quiet_src == click.core.ParameterSource.COMMANDLINE:
|
|
294
|
+
dxs_ctx.quiet = quiet
|
|
295
|
+
else:
|
|
296
|
+
dxs_ctx.quiet = quiet or piped
|
|
243
297
|
# Concise output is default; --full disables it
|
|
244
298
|
dxs_ctx.concise = not full
|
|
299
|
+
# Flatten Dynamics FormattedValue siblings: follow --concise unless
|
|
300
|
+
# --flat-fv / --no-flat-fv was passed explicitly.
|
|
301
|
+
if flat_fv is None:
|
|
302
|
+
dxs_ctx.flat_fv = dxs_ctx.concise
|
|
303
|
+
else:
|
|
304
|
+
dxs_ctx.flat_fv = flat_fv
|
|
245
305
|
|
|
246
306
|
# Set save options
|
|
247
307
|
dxs_ctx.save_path = save_path
|
|
@@ -260,6 +320,17 @@ def cli(
|
|
|
260
320
|
# Stash for the result_callback to honor --no-update-check
|
|
261
321
|
dxs_ctx.no_update_check = no_update_check
|
|
262
322
|
|
|
323
|
+
# Telemetry: record invocation start (must come before any subcommand runs).
|
|
324
|
+
# Wrapped so a telemetry bug can never block the CLI.
|
|
325
|
+
try:
|
|
326
|
+
from dxs.core.telemetry import maybe_show_first_run_notice, record_start
|
|
327
|
+
|
|
328
|
+
invoked = ctx.invoked_subcommand or "<root>"
|
|
329
|
+
record_start(argv=sys.argv[1:], command=invoked)
|
|
330
|
+
maybe_show_first_run_notice()
|
|
331
|
+
except Exception: # noqa: BLE001
|
|
332
|
+
pass
|
|
333
|
+
|
|
263
334
|
|
|
264
335
|
def _mask_token(token: str | None, visible_chars: int = 8) -> str:
|
|
265
336
|
"""Mask a token for display, showing only first/last few characters.
|
|
@@ -316,18 +387,30 @@ def handle_errors(f: F) -> F:
|
|
|
316
387
|
try:
|
|
317
388
|
return ctx.invoke(f, *args, **kwargs)
|
|
318
389
|
except DxsError as e:
|
|
390
|
+
_record_error_silently(e)
|
|
319
391
|
dxs_ctx.output_error(e)
|
|
320
392
|
sys.exit(1)
|
|
321
393
|
except click.ClickException:
|
|
322
394
|
# Let Click handle its own exceptions
|
|
323
395
|
raise
|
|
324
396
|
except Exception as e:
|
|
397
|
+
_record_error_silently(e)
|
|
325
398
|
dxs_ctx.output_error(e)
|
|
326
399
|
sys.exit(1)
|
|
327
400
|
|
|
328
401
|
return cast(F, wrapper)
|
|
329
402
|
|
|
330
403
|
|
|
404
|
+
def _record_error_silently(exc: BaseException) -> None:
|
|
405
|
+
"""Emit a telemetry error event; never raises."""
|
|
406
|
+
try:
|
|
407
|
+
from dxs.core.telemetry import record_error
|
|
408
|
+
|
|
409
|
+
record_error(exc)
|
|
410
|
+
except Exception: # noqa: BLE001
|
|
411
|
+
pass
|
|
412
|
+
|
|
413
|
+
|
|
331
414
|
# Import and register command groups
|
|
332
415
|
# These imports are here to avoid circular imports
|
|
333
416
|
def register_commands() -> None:
|
|
@@ -336,6 +419,7 @@ def register_commands() -> None:
|
|
|
336
419
|
api,
|
|
337
420
|
auth,
|
|
338
421
|
config,
|
|
422
|
+
configuration,
|
|
339
423
|
crm,
|
|
340
424
|
datasource,
|
|
341
425
|
devops,
|
|
@@ -355,6 +439,8 @@ def register_commands() -> None:
|
|
|
355
439
|
cli.add_command(api.api)
|
|
356
440
|
cli.add_command(auth.auth)
|
|
357
441
|
cli.add_command(config.config)
|
|
442
|
+
cli.add_command(config.config, name="settings")
|
|
443
|
+
cli.add_command(configuration.configuration)
|
|
358
444
|
cli.add_command(crm.crm)
|
|
359
445
|
cli.add_command(datasource.datasource)
|
|
360
446
|
cli.add_command(devops.devops)
|
|
@@ -376,14 +462,30 @@ def register_commands() -> None:
|
|
|
376
462
|
|
|
377
463
|
cli.add_command(schema.schema)
|
|
378
464
|
|
|
465
|
+
# Telemetry subcommands (status / enable / disable / test)
|
|
466
|
+
from dxs.commands import telemetry as telemetry_cmd
|
|
467
|
+
|
|
468
|
+
cli.add_command(telemetry_cmd.telemetry)
|
|
469
|
+
|
|
379
470
|
|
|
380
471
|
@cli.result_callback()
|
|
381
472
|
@click.pass_context
|
|
382
473
|
def _after_command(ctx: click.Context, result: Any, **kwargs: Any) -> Any:
|
|
383
|
-
"""Run post-command hooks (
|
|
474
|
+
"""Run post-command hooks (telemetry success event + PyPI update notice)."""
|
|
384
475
|
from dxs.utils.update_check import maybe_show_update_notice
|
|
385
476
|
|
|
386
477
|
dxs_ctx: DxsContext | None = ctx.find_object(DxsContext)
|
|
478
|
+
|
|
479
|
+
# Emit a success event. handle_errors-decorated commands never reach here
|
|
480
|
+
# on failure, so the only path through this branch is a successful run.
|
|
481
|
+
try:
|
|
482
|
+
from dxs.core.telemetry import record_end
|
|
483
|
+
|
|
484
|
+
ctx_extras = _ctx_extras(dxs_ctx)
|
|
485
|
+
record_end(exit_code=0, ctx_extras=ctx_extras)
|
|
486
|
+
except Exception: # noqa: BLE001
|
|
487
|
+
pass
|
|
488
|
+
|
|
387
489
|
skip = bool(dxs_ctx and dxs_ctx.no_update_check)
|
|
388
490
|
try:
|
|
389
491
|
maybe_show_update_notice(skip=skip)
|
|
@@ -392,6 +494,18 @@ def _after_command(ctx: click.Context, result: Any, **kwargs: Any) -> Any:
|
|
|
392
494
|
return result
|
|
393
495
|
|
|
394
496
|
|
|
497
|
+
def _ctx_extras(dxs_ctx: "DxsContext | None") -> dict[str, Any]:
|
|
498
|
+
"""Pluck branch/repo/org/env from DxsContext for telemetry events."""
|
|
499
|
+
if dxs_ctx is None:
|
|
500
|
+
return {}
|
|
501
|
+
return {
|
|
502
|
+
"branch_id": dxs_ctx.branch,
|
|
503
|
+
"repo_id": dxs_ctx.repo,
|
|
504
|
+
"org": dxs_ctx.org,
|
|
505
|
+
"env": dxs_ctx.env,
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
|
|
395
509
|
@cli.command("update")
|
|
396
510
|
@click.option(
|
|
397
511
|
"--check",
|
|
@@ -438,13 +552,30 @@ register_commands()
|
|
|
438
552
|
def main() -> None:
|
|
439
553
|
"""Main entry point for the CLI."""
|
|
440
554
|
try:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
555
|
+
try:
|
|
556
|
+
cli()
|
|
557
|
+
except DxsError as e:
|
|
558
|
+
_record_error_silently(e)
|
|
559
|
+
# Use the module-level format captured during CLI init
|
|
560
|
+
# (Click context is torn down before exceptions reach here)
|
|
561
|
+
formatted = format_error(e, _last_output_format)
|
|
562
|
+
click.echo(formatted, err=True)
|
|
563
|
+
sys.exit(1)
|
|
564
|
+
except SystemExit:
|
|
565
|
+
# SystemExit from sys.exit() inside handle_errors — telemetry already
|
|
566
|
+
# recorded an error there; just propagate.
|
|
567
|
+
raise
|
|
568
|
+
except BaseException as e:
|
|
569
|
+
# Anything else escaping cli() shouldn't take telemetry with it.
|
|
570
|
+
_record_error_silently(e)
|
|
571
|
+
raise
|
|
572
|
+
finally:
|
|
573
|
+
try:
|
|
574
|
+
from dxs.core.telemetry import flush
|
|
575
|
+
|
|
576
|
+
flush(timeout=0.5)
|
|
577
|
+
except Exception: # noqa: BLE001
|
|
578
|
+
pass
|
|
448
579
|
|
|
449
580
|
|
|
450
581
|
if __name__ == "__main__":
|