qwak-sdk 0.5.102__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.
- qwak_sdk/__init__.py +9 -0
- qwak_sdk/cli.py +79 -0
- qwak_sdk/commands/__init__.py +0 -0
- qwak_sdk/commands/_logic/__init__.py +0 -0
- qwak_sdk/commands/_logic/tools.py +6 -0
- qwak_sdk/commands/admin/__init__.py +0 -0
- qwak_sdk/commands/admin/admin_commands_group.py +17 -0
- qwak_sdk/commands/admin/apikeys/__init__.py +0 -0
- qwak_sdk/commands/admin/apikeys/api_keys_commands_group.py +17 -0
- qwak_sdk/commands/admin/apikeys/generate/__init__.py +0 -0
- qwak_sdk/commands/admin/apikeys/generate/_logic.py +21 -0
- qwak_sdk/commands/admin/apikeys/generate/ui.py +45 -0
- qwak_sdk/commands/admin/apikeys/revoke/__init__.py +0 -0
- qwak_sdk/commands/admin/apikeys/revoke/_logic.py +22 -0
- qwak_sdk/commands/admin/apikeys/revoke/ui.py +31 -0
- qwak_sdk/commands/alerts/__init__.py +0 -0
- qwak_sdk/commands/alerts/alerts_commnad_group.py +18 -0
- qwak_sdk/commands/alerts/delete/__init__.py +0 -0
- qwak_sdk/commands/alerts/delete/_logic.py +5 -0
- qwak_sdk/commands/alerts/delete/ui.py +10 -0
- qwak_sdk/commands/alerts/list/__init__.py +0 -0
- qwak_sdk/commands/alerts/list/_logic.py +23 -0
- qwak_sdk/commands/alerts/list/ui.py +17 -0
- qwak_sdk/commands/alerts/register/__init__.py +0 -0
- qwak_sdk/commands/alerts/register/_logic.py +72 -0
- qwak_sdk/commands/alerts/register/ui.py +30 -0
- qwak_sdk/commands/audience/__init__.py +0 -0
- qwak_sdk/commands/audience/_logic/__init__.py +0 -0
- qwak_sdk/commands/audience/_logic/config/__init__.py +0 -0
- qwak_sdk/commands/audience/_logic/config/config_base.py +15 -0
- qwak_sdk/commands/audience/_logic/config/parser.py +30 -0
- qwak_sdk/commands/audience/_logic/config/v1/__init__.py +0 -0
- qwak_sdk/commands/audience/_logic/config/v1/audience_config.py +26 -0
- qwak_sdk/commands/audience/_logic/config/v1/conditions_config.py +59 -0
- qwak_sdk/commands/audience/_logic/config/v1/config_v1.py +23 -0
- qwak_sdk/commands/audience/_logic/config/v1/route_config.py +15 -0
- qwak_sdk/commands/audience/_logic/config/v1/spec.py +9 -0
- qwak_sdk/commands/audience/audience_api_dump.py +86 -0
- qwak_sdk/commands/audience/audience_commands_group.py +30 -0
- qwak_sdk/commands/audience/create/__init__.py +0 -0
- qwak_sdk/commands/audience/create/logic.py +41 -0
- qwak_sdk/commands/audience/create/ui.py +21 -0
- qwak_sdk/commands/audience/delete/__init__.py +0 -0
- qwak_sdk/commands/audience/delete/logic.py +13 -0
- qwak_sdk/commands/audience/delete/ui.py +17 -0
- qwak_sdk/commands/audience/get/__init__.py +0 -0
- qwak_sdk/commands/audience/get/logic.py +14 -0
- qwak_sdk/commands/audience/get/ui.py +25 -0
- qwak_sdk/commands/audience/list/__init__.py +0 -0
- qwak_sdk/commands/audience/list/logic.py +16 -0
- qwak_sdk/commands/audience/list/ui.py +26 -0
- qwak_sdk/commands/audience/update/__init__.py +0 -0
- qwak_sdk/commands/audience/update/logic.py +37 -0
- qwak_sdk/commands/audience/update/ui.py +26 -0
- qwak_sdk/commands/auto_scalling/__init__.py +0 -0
- qwak_sdk/commands/auto_scalling/_logic/__init__.py +0 -0
- qwak_sdk/commands/auto_scalling/_logic/config/__init__.py +3 -0
- qwak_sdk/commands/auto_scalling/_logic/config/config.py +152 -0
- qwak_sdk/commands/auto_scalling/_logic/config/parser.py +21 -0
- qwak_sdk/commands/auto_scalling/attach/__init__.py +0 -0
- qwak_sdk/commands/auto_scalling/attach/_logic.py +43 -0
- qwak_sdk/commands/auto_scalling/attach/ui.py +21 -0
- qwak_sdk/commands/auto_scalling/autoscaling_commands_group.py +15 -0
- qwak_sdk/commands/automations/__init__.py +0 -0
- qwak_sdk/commands/automations/automations_commands_group.py +30 -0
- qwak_sdk/commands/automations/delete/__init__.py +0 -0
- qwak_sdk/commands/automations/delete/_logic.py +6 -0
- qwak_sdk/commands/automations/delete/ui.py +23 -0
- qwak_sdk/commands/automations/executions/__init__.py +0 -0
- qwak_sdk/commands/automations/executions/executions_commands_group.py +14 -0
- qwak_sdk/commands/automations/executions/list/__init__.py +0 -0
- qwak_sdk/commands/automations/executions/list/_logic.py +8 -0
- qwak_sdk/commands/automations/executions/list/ui.py +25 -0
- qwak_sdk/commands/automations/list/__init__.py +0 -0
- qwak_sdk/commands/automations/list/_logic.py +36 -0
- qwak_sdk/commands/automations/list/ui.py +21 -0
- qwak_sdk/commands/automations/register/__init__.py +0 -0
- qwak_sdk/commands/automations/register/_logic.py +43 -0
- qwak_sdk/commands/automations/register/ui.py +44 -0
- qwak_sdk/commands/feature_store/__init__.py +0 -0
- qwak_sdk/commands/feature_store/backfill/__init__.py +0 -0
- qwak_sdk/commands/feature_store/backfill/_logic.py +140 -0
- qwak_sdk/commands/feature_store/backfill/streaming/__init__.py +0 -0
- qwak_sdk/commands/feature_store/backfill/streaming/_logic.py +50 -0
- qwak_sdk/commands/feature_store/backfill/streaming/ui.py +67 -0
- qwak_sdk/commands/feature_store/backfill/ui.py +146 -0
- qwak_sdk/commands/feature_store/delete/__init__.py +0 -0
- qwak_sdk/commands/feature_store/delete/_logic.py +104 -0
- qwak_sdk/commands/feature_store/delete/ui.py +40 -0
- qwak_sdk/commands/feature_store/execution/__init__.py +0 -0
- qwak_sdk/commands/feature_store/execution/ui.py +19 -0
- qwak_sdk/commands/feature_store/feature_store_command_group.py +29 -0
- qwak_sdk/commands/feature_store/list/__init__.py +0 -0
- qwak_sdk/commands/feature_store/list/ui.py +140 -0
- qwak_sdk/commands/feature_store/pause/__init__.py +0 -0
- qwak_sdk/commands/feature_store/pause/ui.py +18 -0
- qwak_sdk/commands/feature_store/register/__init__.py +0 -0
- qwak_sdk/commands/feature_store/register/_logic.py +367 -0
- qwak_sdk/commands/feature_store/register/ui.py +111 -0
- qwak_sdk/commands/feature_store/resume/__init__.py +0 -0
- qwak_sdk/commands/feature_store/resume/ui.py +18 -0
- qwak_sdk/commands/feature_store/trigger/__init__.py +0 -0
- qwak_sdk/commands/feature_store/trigger/ui.py +39 -0
- qwak_sdk/commands/models/__init__.py +0 -0
- qwak_sdk/commands/models/build/__init__.py +0 -0
- qwak_sdk/commands/models/build/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/build/_logic/build_steps.py +42 -0
- qwak_sdk/commands/models/build/_logic/client_logs/__init__.py +0 -0
- qwak_sdk/commands/models/build/_logic/client_logs/cli_phase_run_handler.py +123 -0
- qwak_sdk/commands/models/build/_logic/client_logs/cli_trigger_build_logger.py +19 -0
- qwak_sdk/commands/models/build/_logic/client_logs/logger.py +88 -0
- qwak_sdk/commands/models/build/_logic/client_logs/messages.py +36 -0
- qwak_sdk/commands/models/build/_logic/client_logs/spinner.py +14 -0
- qwak_sdk/commands/models/build/_logic/client_logs/trigger_build_logger.py +54 -0
- qwak_sdk/commands/models/build/_logic/client_logs/utils.py +12 -0
- qwak_sdk/commands/models/build/_logic/phase/__init__.py +0 -0
- qwak_sdk/commands/models/build/_logic/phase/a_fetch_model_code/__init__.py +20 -0
- qwak_sdk/commands/models/build/_logic/phase/a_fetch_model_code/get_sdk_version_step.py +14 -0
- qwak_sdk/commands/models/build/_logic/phase/b_remote_register_qwak_build/__init__.py +16 -0
- qwak_sdk/commands/models/build/_logic/phase/c_deploy/__init__.py +6 -0
- qwak_sdk/commands/models/build/_logic/phase/c_deploy/build_polling_status.py +55 -0
- qwak_sdk/commands/models/build/_logic/phase/c_deploy/deploy_build.py +61 -0
- qwak_sdk/commands/models/build/_logic/util/__init__.py +0 -0
- qwak_sdk/commands/models/build/_logic/util/protobuf_factory.py +45 -0
- qwak_sdk/commands/models/build/_logic/util/step_decorator.py +60 -0
- qwak_sdk/commands/models/build/_logic/util/text.py +9 -0
- qwak_sdk/commands/models/build/_logic/wait_until_finished.py +27 -0
- qwak_sdk/commands/models/build/ui.py +337 -0
- qwak_sdk/commands/models/builds/__init__.py +0 -0
- qwak_sdk/commands/models/builds/builds_commands_group.py +16 -0
- qwak_sdk/commands/models/builds/cancel/__init__.py +0 -0
- qwak_sdk/commands/models/builds/cancel/_logic.py +5 -0
- qwak_sdk/commands/models/builds/cancel/ui.py +15 -0
- qwak_sdk/commands/models/builds/logs/__init__.py +0 -0
- qwak_sdk/commands/models/builds/logs/ui.py +35 -0
- qwak_sdk/commands/models/builds/status/__init__.py +0 -0
- qwak_sdk/commands/models/builds/status/_logic.py +6 -0
- qwak_sdk/commands/models/builds/status/ui.py +39 -0
- qwak_sdk/commands/models/create/__init__.py +0 -0
- qwak_sdk/commands/models/create/_logic.py +36 -0
- qwak_sdk/commands/models/create/ui.py +43 -0
- qwak_sdk/commands/models/delete/__init__.py +0 -0
- qwak_sdk/commands/models/delete/_logic.py +5 -0
- qwak_sdk/commands/models/delete/ui.py +25 -0
- qwak_sdk/commands/models/deployments/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/advance_deployment_options_handler.py +31 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/base_deploy_executor.py +68 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/deploy_config.py +261 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment.py +405 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment_message_helpers.py +114 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment_response_handler.py +156 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment_size_mapper.py +96 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/get_latest_successful_build.py +28 -0
- qwak_sdk/commands/models/deployments/deploy/_logic/local_deployment.py +193 -0
- qwak_sdk/commands/models/deployments/deploy/batch/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/batch/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/batch/_logic/advanced_deployment_mapper.py +15 -0
- qwak_sdk/commands/models/deployments/deploy/batch/_logic/deploy_executor.py +24 -0
- qwak_sdk/commands/models/deployments/deploy/batch/ui.py +126 -0
- qwak_sdk/commands/models/deployments/deploy/deploy_commands_group.py +19 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/advanced_deployment_mapper.py +21 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py +24 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py +75 -0
- qwak_sdk/commands/models/deployments/deploy/realtime/ui.py +209 -0
- qwak_sdk/commands/models/deployments/deploy/streaming/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/streaming/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py +24 -0
- qwak_sdk/commands/models/deployments/deploy/streaming/_logic/serving_strategy_mapper.py +38 -0
- qwak_sdk/commands/models/deployments/deploy/streaming/ui.py +213 -0
- qwak_sdk/commands/models/deployments/undeploy/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/undeploy/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/deployments/undeploy/_logic/request_undeploy.py +249 -0
- qwak_sdk/commands/models/deployments/undeploy/ui.py +72 -0
- qwak_sdk/commands/models/describe/__init__.py +0 -0
- qwak_sdk/commands/models/describe/_logic.py +169 -0
- qwak_sdk/commands/models/describe/ui.py +35 -0
- qwak_sdk/commands/models/executions/__init__.py +0 -0
- qwak_sdk/commands/models/executions/cancel/__init__.py +0 -0
- qwak_sdk/commands/models/executions/cancel/_logic.py +9 -0
- qwak_sdk/commands/models/executions/cancel/ui.py +27 -0
- qwak_sdk/commands/models/executions/execution_commands_group.py +24 -0
- qwak_sdk/commands/models/executions/report/__init__.py +0 -0
- qwak_sdk/commands/models/executions/report/_logic.py +14 -0
- qwak_sdk/commands/models/executions/report/ui.py +43 -0
- qwak_sdk/commands/models/executions/start/__init__.py +0 -0
- qwak_sdk/commands/models/executions/start/_logic.py +81 -0
- qwak_sdk/commands/models/executions/start/ui.py +208 -0
- qwak_sdk/commands/models/executions/status/__init__.py +0 -0
- qwak_sdk/commands/models/executions/status/_logic.py +13 -0
- qwak_sdk/commands/models/executions/status/ui.py +27 -0
- qwak_sdk/commands/models/init/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/initialize_model_structure.py +40 -0
- qwak_sdk/commands/models/init/_logic/template/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/churn/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/churn/cookiecutter.json +3 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/conda.yml +10 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/model.py +95 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/test_churn.py +32 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/cookiecutter.json +3 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
- qwak_sdk/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/model.py +108 -0
- qwak_sdk/commands/models/init/_logic/template/general/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/general/cookiecutter.json +6 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/__init__.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/conda.yml +8 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/model.py +66 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/test_qwak_model.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/cookiecutter.json +3 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/model.py +98 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +24 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/cookiecutter.json +3 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/model.py +98 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/pyproject.toml +20 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
- qwak_sdk/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +25 -0
- qwak_sdk/commands/models/init/ui.py +61 -0
- qwak_sdk/commands/models/list/__init__.py +0 -0
- qwak_sdk/commands/models/list/_logic.py +5 -0
- qwak_sdk/commands/models/list/ui.py +40 -0
- qwak_sdk/commands/models/list_models/__init__.py +0 -0
- qwak_sdk/commands/models/list_models/_logic.py +5 -0
- qwak_sdk/commands/models/list_models/ui.py +60 -0
- qwak_sdk/commands/models/metadata/__init__.py +0 -0
- qwak_sdk/commands/models/metadata/_logic.py +5 -0
- qwak_sdk/commands/models/metadata/ui.py +60 -0
- qwak_sdk/commands/models/models_command_group.py +44 -0
- qwak_sdk/commands/models/runtime/__init__.py +0 -0
- qwak_sdk/commands/models/runtime/logs/__init__.py +0 -0
- qwak_sdk/commands/models/runtime/logs/ui.py +63 -0
- qwak_sdk/commands/models/runtime/runtime_commands_group.py +17 -0
- qwak_sdk/commands/models/runtime/update/__init__.py +0 -0
- qwak_sdk/commands/models/runtime/update/_logic.py +9 -0
- qwak_sdk/commands/models/runtime/update/ui.py +15 -0
- qwak_sdk/commands/projects/__init__.py +0 -0
- qwak_sdk/commands/projects/create/__init__.py +0 -0
- qwak_sdk/commands/projects/create/_logic.py +9 -0
- qwak_sdk/commands/projects/create/ui.py +68 -0
- qwak_sdk/commands/projects/delete/__init__.py +0 -0
- qwak_sdk/commands/projects/delete/_logic.py +6 -0
- qwak_sdk/commands/projects/delete/ui.py +24 -0
- qwak_sdk/commands/projects/list/__init__.py +0 -0
- qwak_sdk/commands/projects/list/_logic.py +6 -0
- qwak_sdk/commands/projects/list/ui.py +45 -0
- qwak_sdk/commands/projects/projects_command_group.py +19 -0
- qwak_sdk/commands/secrets/__init__.py +0 -0
- qwak_sdk/commands/secrets/delete/__init__.py +0 -0
- qwak_sdk/commands/secrets/delete/_logic.py +5 -0
- qwak_sdk/commands/secrets/delete/ui.py +21 -0
- qwak_sdk/commands/secrets/get/__init__.py +0 -0
- qwak_sdk/commands/secrets/get/_logic.py +5 -0
- qwak_sdk/commands/secrets/get/ui.py +17 -0
- qwak_sdk/commands/secrets/secrets_commands_group.py +19 -0
- qwak_sdk/commands/secrets/set/__init__.py +0 -0
- qwak_sdk/commands/secrets/set/_logic.py +5 -0
- qwak_sdk/commands/secrets/set/ui.py +16 -0
- qwak_sdk/commands/ui_tools.py +18 -0
- qwak_sdk/commands/workspaces/__init__.py +0 -0
- qwak_sdk/commands/workspaces/_logic/__init__.py +0 -0
- qwak_sdk/commands/workspaces/_logic/tools.py +44 -0
- qwak_sdk/commands/workspaces/_logic/workspace_validations.py +41 -0
- qwak_sdk/commands/workspaces/config/__init__.py +0 -0
- qwak_sdk/commands/workspaces/config/workspace_config.py +35 -0
- qwak_sdk/commands/workspaces/create/__init__.py +0 -0
- qwak_sdk/commands/workspaces/create/_logic.py +54 -0
- qwak_sdk/commands/workspaces/create/ui.py +48 -0
- qwak_sdk/commands/workspaces/delete/__init__.py +0 -0
- qwak_sdk/commands/workspaces/delete/_logic.py +30 -0
- qwak_sdk/commands/workspaces/delete/ui.py +23 -0
- qwak_sdk/commands/workspaces/start/__init__.py +0 -0
- qwak_sdk/commands/workspaces/start/_logic.py +30 -0
- qwak_sdk/commands/workspaces/start/ui.py +23 -0
- qwak_sdk/commands/workspaces/stop/__init__.py +0 -0
- qwak_sdk/commands/workspaces/stop/_logic.py +30 -0
- qwak_sdk/commands/workspaces/stop/ui.py +23 -0
- qwak_sdk/commands/workspaces/update/__init__.py +0 -0
- qwak_sdk/commands/workspaces/update/_logic.py +82 -0
- qwak_sdk/commands/workspaces/update/ui.py +57 -0
- qwak_sdk/commands/workspaces/workspaces_commands_group.py +23 -0
- qwak_sdk/exceptions/__init__.py +11 -0
- qwak_sdk/exceptions/qwak_command_exception.py +2 -0
- qwak_sdk/exceptions/qwak_deploy_new_build_failed.py +5 -0
- qwak_sdk/exceptions/qwak_resource_not_found.py +2 -0
- qwak_sdk/inner/__init__.py +0 -0
- qwak_sdk/inner/file_registry.py +98 -0
- qwak_sdk/inner/tools/__init__.py +0 -0
- qwak_sdk/inner/tools/cli_tools.py +220 -0
- qwak_sdk/inner/tools/config_handler.py +27 -0
- qwak_sdk/inner/tools/dataclasses_utils.py +21 -0
- qwak_sdk/inner/tools/logger/__init__.py +3 -0
- qwak_sdk/inner/tools/logger/logger.py +269 -0
- qwak_sdk/inner/tools/logger/logging.yml +79 -0
- qwak_sdk/inner/tools/tracking.py +47 -0
- qwak_sdk/main.py +9 -0
- qwak_sdk/tools/__init__.py +0 -0
- qwak_sdk/tools/colors.py +13 -0
- qwak_sdk/tools/files.py +63 -0
- qwak_sdk/tools/log_handling.py +159 -0
- qwak_sdk/tools/utils.py +42 -0
- qwak_sdk-0.5.102.dist-info/METADATA +51 -0
- qwak_sdk-0.5.102.dist-info/RECORD +328 -0
- qwak_sdk-0.5.102.dist-info/WHEEL +4 -0
- qwak_sdk-0.5.102.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from qwak_sdk.commands.automations.delete.ui import delete
|
|
6
|
+
from qwak_sdk.commands.automations.executions.executions_commands_group import (
|
|
7
|
+
executions_command_group,
|
|
8
|
+
)
|
|
9
|
+
from qwak_sdk.commands.automations.list.ui import list_automations
|
|
10
|
+
from qwak_sdk.commands.automations.register.ui import register
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
DELIMITER = "----------------------------------------"
|
|
14
|
+
|
|
15
|
+
AUTOMATION = "automation"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group(
|
|
19
|
+
name="automations",
|
|
20
|
+
help="Commands for interacting with the Qwak Automations",
|
|
21
|
+
)
|
|
22
|
+
def automations_commands_group():
|
|
23
|
+
# Click commands group injection
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
automations_commands_group.add_command(delete)
|
|
28
|
+
automations_commands_group.add_command(list_automations)
|
|
29
|
+
automations_commands_group.add_command(register)
|
|
30
|
+
automations_commands_group.add_command(executions_command_group)
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from qwak_sdk.commands.automations.delete._logic import delete_automation
|
|
4
|
+
from qwak_sdk.inner.tools.cli_tools import QwakCommand
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command(
|
|
8
|
+
"delete",
|
|
9
|
+
help="Delete an automation object",
|
|
10
|
+
cls=QwakCommand,
|
|
11
|
+
)
|
|
12
|
+
@click.option(
|
|
13
|
+
"--automation-id",
|
|
14
|
+
type=str,
|
|
15
|
+
metavar="ID",
|
|
16
|
+
help="The automation id to delete",
|
|
17
|
+
)
|
|
18
|
+
def delete(automation_id: str, **kwargs):
|
|
19
|
+
deleted = delete_automation(automation_id=automation_id)
|
|
20
|
+
if deleted:
|
|
21
|
+
print(f"Automation {automation_id} deleted successfully")
|
|
22
|
+
else:
|
|
23
|
+
print(f"Automation {automation_id} was not found")
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from qwak_sdk.commands.automations.executions.list.ui import list_executions
|
|
4
|
+
|
|
5
|
+
DELIMITER = "----------------------------------------"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group(name="executions", help=" automation executions")
|
|
9
|
+
def executions_command_group():
|
|
10
|
+
# Click commands group injection
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
executions_command_group.add_command(list_executions)
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from qwak.automations.automation_executions import AutomationExecution
|
|
4
|
+
from qwak.clients.automation_management.client import AutomationsManagementClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def execute_list_executions(automation_id: str) -> List[AutomationExecution]:
|
|
8
|
+
return AutomationsManagementClient().list_executions(automation_id)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from qwak_sdk.commands.automations.executions.list._logic import execute_list_executions
|
|
4
|
+
from qwak_sdk.inner.tools.cli_tools import QwakCommand
|
|
5
|
+
|
|
6
|
+
DELIMITER = "----------------------------------------"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command(
|
|
10
|
+
"list",
|
|
11
|
+
help="List all executions for a specific automation",
|
|
12
|
+
cls=QwakCommand,
|
|
13
|
+
)
|
|
14
|
+
@click.option(
|
|
15
|
+
"--automation-id",
|
|
16
|
+
type=str,
|
|
17
|
+
metavar="ID",
|
|
18
|
+
required=True,
|
|
19
|
+
help="The automation id to list its executions",
|
|
20
|
+
)
|
|
21
|
+
def list_executions(automation_id: str, **kwargs):
|
|
22
|
+
executions_list = execute_list_executions(automation_id)
|
|
23
|
+
for execution in executions_list:
|
|
24
|
+
print(execution)
|
|
25
|
+
print(DELIMITER)
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from qwak.clients.automation_management.client import AutomationsManagementClient
|
|
2
|
+
from tabulate import tabulate
|
|
3
|
+
|
|
4
|
+
from qwak_sdk.commands._logic.tools import list_of_messages_to_json_str
|
|
5
|
+
|
|
6
|
+
COLUMNS = ["Id", "Name", "Model id", "Type", "Status", "Last updated", "Updated by"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def execute_list_automations():
|
|
10
|
+
client = AutomationsManagementClient()
|
|
11
|
+
automations_list = client.list_automations()
|
|
12
|
+
data = []
|
|
13
|
+
for automation in automations_list:
|
|
14
|
+
data.append(
|
|
15
|
+
[
|
|
16
|
+
automation.id,
|
|
17
|
+
automation.name,
|
|
18
|
+
automation.model_id,
|
|
19
|
+
automation.action.__class__.__name__,
|
|
20
|
+
_map_automation_status(automation.is_enabled),
|
|
21
|
+
automation.create_audit.date,
|
|
22
|
+
automation.create_audit.user_id,
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return tabulate(data, headers=COLUMNS)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def execute_list_json_automations() -> str:
|
|
30
|
+
client = AutomationsManagementClient()
|
|
31
|
+
automations = client.get_list_automations_from_server()
|
|
32
|
+
return list_of_messages_to_json_str(automations)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _map_automation_status(status: bool) -> str:
|
|
36
|
+
return "Active" if status else "Inactive"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from qwak_sdk.commands.automations.list._logic import (
|
|
4
|
+
execute_list_automations,
|
|
5
|
+
execute_list_json_automations,
|
|
6
|
+
)
|
|
7
|
+
from qwak_sdk.inner.tools.cli_tools import QwakCommand
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command(
|
|
11
|
+
"list",
|
|
12
|
+
help="List all automations",
|
|
13
|
+
cls=QwakCommand,
|
|
14
|
+
)
|
|
15
|
+
@click.option("--json-format", is_flag=True, type=bool)
|
|
16
|
+
def list_automations(json_format: bool, **kwargs):
|
|
17
|
+
if json_format:
|
|
18
|
+
click.echo(execute_list_json_automations())
|
|
19
|
+
|
|
20
|
+
else:
|
|
21
|
+
click.echo(execute_list_automations())
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from qwak.automations import Automation
|
|
4
|
+
from qwak.clients.automation_management.client import AutomationsManagementClient
|
|
5
|
+
|
|
6
|
+
from qwak_sdk.inner.file_registry import extract_class_objects
|
|
7
|
+
from qwak_sdk.inner.tools.cli_tools import ask_yesno
|
|
8
|
+
from qwak_sdk.tools.utils import qwak_spinner
|
|
9
|
+
|
|
10
|
+
DELIMITER = "----------------------------------------"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def register_automations(qwak_python_files: List[str], force: bool):
|
|
14
|
+
"""
|
|
15
|
+
Register Automation Entities Objects
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
qwak_python_files: a list of python files containing qwak package imports
|
|
19
|
+
force: to force
|
|
20
|
+
"""
|
|
21
|
+
with qwak_spinner(
|
|
22
|
+
begin_text="Finding Automations to register", print_callback=print
|
|
23
|
+
):
|
|
24
|
+
qwak_automations: List[Automation] = extract_class_objects(
|
|
25
|
+
qwak_python_files, Automation
|
|
26
|
+
)
|
|
27
|
+
client = AutomationsManagementClient()
|
|
28
|
+
print(f"Found {len(qwak_automations)} Automations")
|
|
29
|
+
for automation, source_file_path in qwak_automations:
|
|
30
|
+
existing_automation = client.get_automation_by_name(automation.name)
|
|
31
|
+
if existing_automation:
|
|
32
|
+
if ask_yesno(
|
|
33
|
+
f"Update existing Automation '{automation.name}' from source file '{source_file_path}'?",
|
|
34
|
+
force,
|
|
35
|
+
):
|
|
36
|
+
client.update_automation(existing_automation.id, automation.to_proto())
|
|
37
|
+
else:
|
|
38
|
+
if ask_yesno(
|
|
39
|
+
f"Create new Automation '{automation.name}' from source file '{source_file_path}'?",
|
|
40
|
+
force,
|
|
41
|
+
):
|
|
42
|
+
client.create_automation(automation.to_proto())
|
|
43
|
+
print(DELIMITER)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from qwak_sdk.commands.automations.register._logic import register_automations
|
|
7
|
+
from qwak_sdk.inner.file_registry import list_qwak_python_files
|
|
8
|
+
from qwak_sdk.inner.tools.cli_tools import QwakCommand
|
|
9
|
+
from qwak_sdk.tools.utils import qwak_spinner
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command(
|
|
13
|
+
"register",
|
|
14
|
+
help="Register all automations object under the given path. Registered "
|
|
15
|
+
"automations will be visible on the Qwak management platform after registration",
|
|
16
|
+
cls=QwakCommand,
|
|
17
|
+
)
|
|
18
|
+
@click.option(
|
|
19
|
+
"--path",
|
|
20
|
+
"-p",
|
|
21
|
+
type=click.Path(exists=True),
|
|
22
|
+
metavar="PATH",
|
|
23
|
+
help="Directory / module where qwak automations objects are stored",
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--force",
|
|
27
|
+
"-f",
|
|
28
|
+
default=False,
|
|
29
|
+
is_flag=True,
|
|
30
|
+
metavar="FLAG",
|
|
31
|
+
help="Force register all found qwak automations Store objects",
|
|
32
|
+
)
|
|
33
|
+
def register(path: Path, force: bool, **kwargs):
|
|
34
|
+
path = Path(path) if path else Path.cwd()
|
|
35
|
+
if path.is_file():
|
|
36
|
+
qwak_python_files = [(str(path), os.path.abspath(path))]
|
|
37
|
+
elif Path.is_dir(path):
|
|
38
|
+
with qwak_spinner(
|
|
39
|
+
begin_text="Recursively looking for python files in input dir",
|
|
40
|
+
print_callback=print,
|
|
41
|
+
) as sp:
|
|
42
|
+
qwak_python_files = list_qwak_python_files(path, sp)
|
|
43
|
+
|
|
44
|
+
register_automations(qwak_python_files, force)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from _qwak_proto.qwak.feature_store.features.feature_set_pb2 import FeatureSet
|
|
5
|
+
from _qwak_proto.qwak.feature_store.features.feature_set_service_pb2 import (
|
|
6
|
+
GetFeatureSetByNameResponse,
|
|
7
|
+
)
|
|
8
|
+
from croniter import croniter
|
|
9
|
+
from qwak.clients.feature_store import FeatureRegistryClient
|
|
10
|
+
from qwak.feature_store.execution.backfill import Backfill
|
|
11
|
+
from qwak.feature_store.feature_sets.batch import BatchFeatureSet
|
|
12
|
+
|
|
13
|
+
from qwak_sdk.inner.tools.cli_tools import ask_yesno
|
|
14
|
+
from qwak_sdk.tools.colors import Color
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _BackfillLogic:
|
|
18
|
+
featureset: FeatureSet
|
|
19
|
+
batch_featureset: BatchFeatureSet
|
|
20
|
+
|
|
21
|
+
def configure_window_backfill(
|
|
22
|
+
self,
|
|
23
|
+
featureset_name: str,
|
|
24
|
+
start_time: Optional[datetime] = None,
|
|
25
|
+
stop_time: Optional[datetime] = None,
|
|
26
|
+
comment: str = None,
|
|
27
|
+
cluster_template: str = None,
|
|
28
|
+
) -> Optional[Backfill]:
|
|
29
|
+
"""
|
|
30
|
+
Configures a window back-fill object. This type of backfill will not reset the entire featureset, but will be
|
|
31
|
+
performed from the requested start time to the requested stop time in ticks calculated according to the
|
|
32
|
+
feature sets scheduling policy.
|
|
33
|
+
@param featureset_name: feature set name to perform the backfill job for
|
|
34
|
+
@type featureset_name: str
|
|
35
|
+
@param start_time: backfill requested start date
|
|
36
|
+
@type start_time: optional[datetime]
|
|
37
|
+
@param stop_time:
|
|
38
|
+
@type stop_time: optional[datetime]
|
|
39
|
+
@param comment:
|
|
40
|
+
@type comment: str
|
|
41
|
+
@param cluster_template:
|
|
42
|
+
@type cluster_template: str
|
|
43
|
+
@return:
|
|
44
|
+
@rtype:
|
|
45
|
+
"""
|
|
46
|
+
print(
|
|
47
|
+
f"{Color.BLUE} Backfilling from {start_time} to {stop_time}, "
|
|
48
|
+
f"the following ticks are going to be performed {Color.END}"
|
|
49
|
+
)
|
|
50
|
+
zipped_tick_strs = Backfill.generate_expected_ticks_repr(
|
|
51
|
+
scheduling_policy=self.batch_featureset.scheduling_policy,
|
|
52
|
+
start_time=start_time,
|
|
53
|
+
stop_time=stop_time,
|
|
54
|
+
)
|
|
55
|
+
print("\n".join(zipped_tick_strs))
|
|
56
|
+
if ask_yesno("", force=False):
|
|
57
|
+
backfill_execution = Backfill(
|
|
58
|
+
featureset_name=featureset_name,
|
|
59
|
+
comment=comment,
|
|
60
|
+
cluster_template=cluster_template,
|
|
61
|
+
start_time=start_time,
|
|
62
|
+
stop_time=stop_time,
|
|
63
|
+
)
|
|
64
|
+
return backfill_execution
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _get_featureset_by_name(featureset_name: str) -> GetFeatureSetByNameResponse:
|
|
69
|
+
registry_client = FeatureRegistryClient()
|
|
70
|
+
|
|
71
|
+
return registry_client.get_feature_set_by_name(feature_set_name=featureset_name)
|
|
72
|
+
|
|
73
|
+
def get_start_stop_times(
|
|
74
|
+
self,
|
|
75
|
+
requested_start_time: Optional[datetime] = None,
|
|
76
|
+
requested_stop_time: Optional[datetime] = None,
|
|
77
|
+
) -> tuple:
|
|
78
|
+
start_time: datetime = (
|
|
79
|
+
requested_start_time or self.batch_featureset.backfill.start_date
|
|
80
|
+
).replace(tzinfo=timezone.utc)
|
|
81
|
+
stop_time: datetime
|
|
82
|
+
|
|
83
|
+
if start_time and start_time > datetime.now().replace(tzinfo=timezone.utc):
|
|
84
|
+
stop_time = datetime.utcnow().replace(tzinfo=timezone.utc)
|
|
85
|
+
else:
|
|
86
|
+
stop_time = (
|
|
87
|
+
requested_stop_time or datetime.utcnow().replace(tzinfo=timezone.utc)
|
|
88
|
+
).replace(tzinfo=timezone.utc)
|
|
89
|
+
|
|
90
|
+
return start_time, stop_time
|
|
91
|
+
|
|
92
|
+
def is_valid_input(
|
|
93
|
+
self,
|
|
94
|
+
reset_backfill: bool,
|
|
95
|
+
start_time: Optional[datetime],
|
|
96
|
+
stop_time: Optional[datetime],
|
|
97
|
+
featureset_name: str,
|
|
98
|
+
) -> bool:
|
|
99
|
+
# Validate reset_backfill or start and stop times
|
|
100
|
+
if (not reset_backfill and not start_time and not stop_time) or (
|
|
101
|
+
reset_backfill and (start_time or stop_time)
|
|
102
|
+
):
|
|
103
|
+
print(
|
|
104
|
+
f"{Color.RED} please specify either --reset-backfill or one or more of --start-time/--stop-time"
|
|
105
|
+
)
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
# Validate featureset exists
|
|
109
|
+
get_featureset_resp = self._get_featureset_by_name(
|
|
110
|
+
featureset_name=featureset_name
|
|
111
|
+
)
|
|
112
|
+
if not get_featureset_resp or not get_featureset_resp.feature_set:
|
|
113
|
+
print(f"{Color.RED} Failed to retrieve featureset {featureset_name}")
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
self.featureset = get_featureset_resp.feature_set
|
|
117
|
+
fs_type_attr: str = self.featureset.feature_set_definition.feature_set_spec.feature_set_type.WhichOneof(
|
|
118
|
+
"set_type"
|
|
119
|
+
)
|
|
120
|
+
# Validate featureset is batch v1
|
|
121
|
+
if fs_type_attr != "batch_feature_set_v1":
|
|
122
|
+
print(
|
|
123
|
+
f"{Color.RED} Feature set {featureset_name} is of type {fs_type_attr}. Only BatchV1 is supported."
|
|
124
|
+
)
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
self.batch_featureset = BatchFeatureSet._from_proto(
|
|
128
|
+
self.featureset.feature_set_definition.feature_set_spec
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Validate scheduling policy
|
|
132
|
+
if not self.batch_featureset.scheduling_policy or not croniter.is_valid(
|
|
133
|
+
self.batch_featureset.scheduling_policy
|
|
134
|
+
):
|
|
135
|
+
print(
|
|
136
|
+
f"{Color.RED} Feature set {featureset_name} has an invalid scheduling policy {self.batch_featureset.scheduling_policy}."
|
|
137
|
+
)
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
return True
|
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
from qwak.feature_store.execution.streaming_backfill import StreamingAggregationBackfill
|
|
5
|
+
from qwak.feature_store.feature_sets.streaming_backfill import StreamingBackfill
|
|
6
|
+
|
|
7
|
+
from qwak_sdk.inner.file_registry import extract_class_objects
|
|
8
|
+
from qwak_sdk.tools.utils import qwak_spinner
|
|
9
|
+
|
|
10
|
+
DELIMITER = "----------------------------------------"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _trigger_streaming_backfills(backfill_definition_files: List[Tuple[str, str]]):
|
|
14
|
+
"""
|
|
15
|
+
Trigger streaming aggregation backfills from Python files.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
backfill_definition_files: List of tuples (module_name, file_path) containing qwak imports
|
|
19
|
+
"""
|
|
20
|
+
with qwak_spinner(
|
|
21
|
+
begin_text="Finding streaming backfill definitions", print_callback=print
|
|
22
|
+
):
|
|
23
|
+
streaming_backfills: List[
|
|
24
|
+
Tuple[StreamingBackfill, str]
|
|
25
|
+
] = extract_class_objects(backfill_definition_files, StreamingBackfill)
|
|
26
|
+
|
|
27
|
+
print(f"👀 Found {len(streaming_backfills)} streaming backfill definition(s)")
|
|
28
|
+
|
|
29
|
+
print(DELIMITER)
|
|
30
|
+
|
|
31
|
+
# Trigger each backfill
|
|
32
|
+
for backfill_spec, source_file_path in streaming_backfills:
|
|
33
|
+
print(
|
|
34
|
+
f"Triggering backfill for featureset '{backfill_spec.featureset_name}' from '{source_file_path}'"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
# Create executor and trigger
|
|
39
|
+
executor = StreamingAggregationBackfill(
|
|
40
|
+
backfill_spec, Path(source_file_path)
|
|
41
|
+
)
|
|
42
|
+
execution_id = executor.trigger()
|
|
43
|
+
|
|
44
|
+
print(f"✅ Execution ID: {execution_id}")
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"❌ Failed: {str(e)}")
|
|
48
|
+
exit(1)
|
|
49
|
+
|
|
50
|
+
print(DELIMITER)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from qwak_sdk.commands.feature_store.backfill.streaming._logic import (
|
|
7
|
+
_trigger_streaming_backfills,
|
|
8
|
+
)
|
|
9
|
+
from qwak_sdk.inner.file_registry import list_qwak_python_files
|
|
10
|
+
from qwak_sdk.inner.tools.cli_tools import QwakCommand
|
|
11
|
+
from qwak_sdk.tools.utils import qwak_spinner
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.command(
|
|
15
|
+
"streaming",
|
|
16
|
+
cls=QwakCommand,
|
|
17
|
+
help="Trigger backfills for all streaming aggregation backfill definitions under the given path",
|
|
18
|
+
)
|
|
19
|
+
@click.option(
|
|
20
|
+
"--path",
|
|
21
|
+
"-p",
|
|
22
|
+
type=click.Path(exists=True, path_type=Path),
|
|
23
|
+
required=True,
|
|
24
|
+
help="Directory or file path containing backfill definitions (decorated with @streaming.backfill)",
|
|
25
|
+
)
|
|
26
|
+
def backfill_streaming(path: Path, **kwargs):
|
|
27
|
+
"""
|
|
28
|
+
Trigger streaming aggregation backfills from a definition file.
|
|
29
|
+
|
|
30
|
+
The file should contain functions decorated with @streaming.backfill
|
|
31
|
+
that defines the backfill transformation and configuration.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
An example file can look like this:
|
|
35
|
+
from qwak.feature_store.feature_sets import streaming
|
|
36
|
+
from qwak.feature_store.feature_sets.streaming_backfill import BackfillDataSource
|
|
37
|
+
from datetime import datetime
|
|
38
|
+
|
|
39
|
+
@streaming.backfill(
|
|
40
|
+
feature_set_name="my_streaming_fs",
|
|
41
|
+
start_date=datetime(2023, 1, 1),
|
|
42
|
+
end_date=datetime(2023, 12, 1),
|
|
43
|
+
data_sources=[BackfillDataSource(data_source_name="batch_source")]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def my_backfill():
|
|
47
|
+
return SparkSqlTransformation("SELECT * FROM batch_source")
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
qwak features backfill streaming --path backfill_definition.py
|
|
51
|
+
"""
|
|
52
|
+
backfill_definition_files: List[Tuple[str, str]]
|
|
53
|
+
|
|
54
|
+
if Path.is_dir(path):
|
|
55
|
+
backfill_definition_files = _handle_directory(path)
|
|
56
|
+
else:
|
|
57
|
+
backfill_definition_files = [(str(path), str(path.absolute()))]
|
|
58
|
+
|
|
59
|
+
_trigger_streaming_backfills(backfill_definition_files)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _handle_directory(path: Path) -> list[tuple[str, str]]:
|
|
63
|
+
with qwak_spinner(
|
|
64
|
+
begin_text="Recursively looking for python files in input dir",
|
|
65
|
+
print_callback=print,
|
|
66
|
+
) as sp:
|
|
67
|
+
return list_qwak_python_files(path, sp)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from qwak.feature_store.execution.backfill import Backfill
|
|
6
|
+
|
|
7
|
+
from qwak_sdk.commands.feature_store.backfill._logic import _BackfillLogic
|
|
8
|
+
from qwak_sdk.commands.feature_store.backfill.streaming.ui import backfill_streaming
|
|
9
|
+
from qwak_sdk.inner.tools.cli_tools import DefaultCommandGroup, QwakCommand, ask_yesno
|
|
10
|
+
from qwak_sdk.tools.colors import Color
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(
|
|
14
|
+
cls=DefaultCommandGroup,
|
|
15
|
+
name="backfill",
|
|
16
|
+
default="batch",
|
|
17
|
+
help="Trigger a backfill process for Featuresets. If no command is provided, the default `batch` command is executed.",
|
|
18
|
+
)
|
|
19
|
+
def backfill():
|
|
20
|
+
"""Backfill group with default batch_backfill command."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Default Command
|
|
25
|
+
@click.command(
|
|
26
|
+
"batch", cls=QwakCommand, help="Trigger a backfill process for a Feature Set"
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--feature-set",
|
|
30
|
+
"--name",
|
|
31
|
+
help="Feature Set name to perform the backfill process for",
|
|
32
|
+
required=True,
|
|
33
|
+
type=click.STRING,
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--reset-backfill",
|
|
37
|
+
"--reset",
|
|
38
|
+
is_flag=True,
|
|
39
|
+
metavar="FLAG",
|
|
40
|
+
default=False,
|
|
41
|
+
help="Perform a complete reset of the featuresets data. "
|
|
42
|
+
"This will result in the deletion of the current existing data.",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--start-time",
|
|
46
|
+
type=click.DateTime(),
|
|
47
|
+
default=None,
|
|
48
|
+
required=False,
|
|
49
|
+
help="The time from which the featuresets data should be backfilled in UTC. "
|
|
50
|
+
"Defaults to the featuresets configured backfill start time.",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--stop-time",
|
|
54
|
+
type=click.DateTime(),
|
|
55
|
+
default=None,
|
|
56
|
+
required=False,
|
|
57
|
+
help="The time up until the featuresets data should be backfilled in UTC. Defaults to the current timestamp. "
|
|
58
|
+
"If the time provided is in the future, stop time will be rounded down to the current time. ",
|
|
59
|
+
)
|
|
60
|
+
@click.option(
|
|
61
|
+
"--cluster-template",
|
|
62
|
+
type=str,
|
|
63
|
+
default=None,
|
|
64
|
+
required=False,
|
|
65
|
+
help="Backfill resource configuration, expects a ClusterType size. "
|
|
66
|
+
"Defaults to the featureset resource configuration",
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
"--comment",
|
|
70
|
+
type=str,
|
|
71
|
+
default=None,
|
|
72
|
+
required=False,
|
|
73
|
+
help="Backfill job optional comment tag line",
|
|
74
|
+
)
|
|
75
|
+
def backfill_batch(
|
|
76
|
+
feature_set: str,
|
|
77
|
+
reset_backfill: bool,
|
|
78
|
+
start_time: Optional[datetime],
|
|
79
|
+
stop_time: Optional[datetime],
|
|
80
|
+
cluster_template: Optional[str],
|
|
81
|
+
comment: Optional[str],
|
|
82
|
+
**kwargs,
|
|
83
|
+
):
|
|
84
|
+
backill_logic = _BackfillLogic()
|
|
85
|
+
if not backill_logic.is_valid_input(
|
|
86
|
+
reset_backfill=reset_backfill,
|
|
87
|
+
start_time=start_time,
|
|
88
|
+
stop_time=stop_time,
|
|
89
|
+
featureset_name=feature_set,
|
|
90
|
+
):
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
if reset_backfill:
|
|
94
|
+
backfill_execution = _configure_backfill_triggered(
|
|
95
|
+
featureset_name=feature_set,
|
|
96
|
+
comment=comment,
|
|
97
|
+
cluster_template=cluster_template,
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
start, stop = backill_logic.get_start_stop_times(
|
|
101
|
+
requested_start_time=start_time, requested_stop_time=stop_time
|
|
102
|
+
)
|
|
103
|
+
if start >= stop:
|
|
104
|
+
print(
|
|
105
|
+
f"{Color.RED} Stop time {stop.isoformat()} must be after start time {start.isoformat()}"
|
|
106
|
+
)
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
backfill_execution = backill_logic.configure_window_backfill(
|
|
110
|
+
featureset_name=feature_set,
|
|
111
|
+
start_time=start,
|
|
112
|
+
stop_time=stop,
|
|
113
|
+
comment=comment,
|
|
114
|
+
cluster_template=cluster_template,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if backfill_execution:
|
|
118
|
+
execution_id: str = backfill_execution.trigger_batch_backfill()
|
|
119
|
+
print(
|
|
120
|
+
f"{Color.BLUE}✅ Triggered backfill execution for featureset {feature_set}, "
|
|
121
|
+
f"execution id for follow up on is {execution_id}."
|
|
122
|
+
)
|
|
123
|
+
print(
|
|
124
|
+
f"To inquire the status of the execution run "
|
|
125
|
+
f"'qwak features execution-status --execution-id {execution_id}'"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _configure_backfill_triggered(
|
|
130
|
+
featureset_name: str, comment: str, cluster_template: str
|
|
131
|
+
) -> Optional[Backfill]:
|
|
132
|
+
if ask_yesno(
|
|
133
|
+
f"You are about to remove and re-populate all data in {featureset_name}",
|
|
134
|
+
force=False,
|
|
135
|
+
):
|
|
136
|
+
print(f"{Color.RED} - A backfill reset was triggered")
|
|
137
|
+
return Backfill(
|
|
138
|
+
featureset_name=featureset_name,
|
|
139
|
+
comment=comment,
|
|
140
|
+
cluster_template=cluster_template,
|
|
141
|
+
)
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
backfill.add_command(backfill_batch)
|
|
146
|
+
backfill.add_command(backfill_streaming)
|