rushti 2.2.0__tar.gz → 2.2.2__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.
- {rushti-2.2.0/src/rushti.egg-info → rushti-2.2.2}/PKG-INFO +1 -1
- {rushti-2.2.0 → rushti-2.2.2}/pyproject.toml +1 -1
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/__init__.py +1 -1
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/cli.py +43 -63
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/__init__.py +3 -3
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/push.py +12 -2
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/settings.py +71 -5
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/taskfile.py +2 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/tm1_build.py +11 -4
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/tm1_objects.py +181 -3
- {rushti-2.2.0 → rushti-2.2.2/src/rushti.egg-info}/PKG-INFO +1 -1
- {rushti-2.2.0 → rushti-2.2.2}/LICENSE +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/README.md +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/setup.cfg +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/_shlex_utils.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/app_paths.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/checkpoint.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/__init__.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/build.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/db.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/resume.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/__init__.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/analyze.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/export.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/list.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/optimize.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/stats/visualize.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/expand.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/export.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/validate.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/commands/tasks/visualize.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/contention_analyzer.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/dag.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/dashboard.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/db_admin.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/exclusive.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/execution.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/logging.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/logging_setup.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/messages.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/optimization_report.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/optimizer.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/parsing.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/results_writer.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/__init__.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/dynamodb.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/paths.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/repository.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/signature.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/stats/sqlite.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/task.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/taskfile_ops.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/templates/__init__.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/templates/visualization.html +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/tm1_integration.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/utils.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti/visualization_template.py +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti.egg-info/SOURCES.txt +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti.egg-info/dependency_links.txt +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti.egg-info/entry_points.txt +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti.egg-info/requires.txt +0 -0
- {rushti-2.2.0 → rushti-2.2.2}/src/rushti.egg-info/top_level.txt +0 -0
|
@@ -5,7 +5,7 @@ This package provides tools for executing TM1 TI processes in parallel
|
|
|
5
5
|
with dependency management, checkpoint/resume support, and execution logging.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "2.2.
|
|
8
|
+
__version__ = "2.2.2"
|
|
9
9
|
__app_name__ = "RushTI"
|
|
10
10
|
|
|
11
11
|
# Core exports for programmatic use
|
|
@@ -29,7 +29,7 @@ from rushti.logging_setup import (
|
|
|
29
29
|
)
|
|
30
30
|
from rushti.results_writer import create_results_file
|
|
31
31
|
from rushti.task import ExecutionMode
|
|
32
|
-
from rushti.settings import load_settings,
|
|
32
|
+
from rushti.settings import get_effective_settings, load_settings, resolve_tm1_instance
|
|
33
33
|
from rushti.taskfile import (
|
|
34
34
|
detect_file_type,
|
|
35
35
|
parse_json_taskfile,
|
|
@@ -629,42 +629,6 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
629
629
|
# Load settings from settings.ini
|
|
630
630
|
settings = load_settings(cli_args.get("settings_file"))
|
|
631
631
|
|
|
632
|
-
# Apply CLI overrides to settings
|
|
633
|
-
settings = get_effective_settings(settings, cli_args=cli_args)
|
|
634
|
-
|
|
635
|
-
if settings.tm1_integration.detailed_results:
|
|
636
|
-
logging.info(
|
|
637
|
-
"Detailed results enabled: one cube row per executed TI. "
|
|
638
|
-
"If you upgraded RushTI from an earlier release, run "
|
|
639
|
-
"'rushti build --tm1-instance <instance>' once to add the "
|
|
640
|
-
"'original_task_id' measure to the rushti_measure dimension. "
|
|
641
|
-
"The build is non-destructive — existing cube data is preserved."
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
# Extract final values (CLI overrides settings.ini)
|
|
645
|
-
max_workers = (
|
|
646
|
-
cli_args["max_workers"]
|
|
647
|
-
if cli_args.get("max_workers") is not None
|
|
648
|
-
else settings.defaults.max_workers
|
|
649
|
-
)
|
|
650
|
-
process_execution_retries = (
|
|
651
|
-
cli_args["retries"] if cli_args.get("retries") is not None else settings.defaults.retries
|
|
652
|
-
)
|
|
653
|
-
result_file = (
|
|
654
|
-
cli_args["output_file"]
|
|
655
|
-
if cli_args.get("output_file") is not None
|
|
656
|
-
else settings.defaults.result_file
|
|
657
|
-
)
|
|
658
|
-
# Resolve result file path relative to application directory (skip if empty/None)
|
|
659
|
-
if result_file:
|
|
660
|
-
from rushti.utils import resolve_app_path
|
|
661
|
-
|
|
662
|
-
result_file = resolve_app_path(result_file)
|
|
663
|
-
|
|
664
|
-
logger.debug(
|
|
665
|
-
f"Effective settings: max_workers={max_workers}, retries={process_execution_retries}, result_file={result_file}"
|
|
666
|
-
)
|
|
667
|
-
|
|
668
632
|
# Handle TM1 source if specified
|
|
669
633
|
tm1_instance = cli_args.get("tm1_instance")
|
|
670
634
|
workflow = cli_args.get("workflow")
|
|
@@ -701,6 +665,7 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
701
665
|
|
|
702
666
|
# Determine workflow and exclusive mode from task file
|
|
703
667
|
# Parse task file early to get metadata for session context
|
|
668
|
+
taskfile_preview = None
|
|
704
669
|
if tm1_taskfile:
|
|
705
670
|
# Using TM1 source
|
|
706
671
|
file_type = "json" # TM1 taskfiles are treated as JSON format
|
|
@@ -727,6 +692,36 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
727
692
|
logger.error(str(e))
|
|
728
693
|
sys.exit(1)
|
|
729
694
|
|
|
695
|
+
# Merge taskfile JSON settings (tier 2) into the effective settings
|
|
696
|
+
# before any setting is read. CLI args overlay JSON, which overlays
|
|
697
|
+
# settings.ini, which overlays built-in defaults.
|
|
698
|
+
effective_taskfile = tm1_taskfile or taskfile_preview
|
|
699
|
+
json_settings_dict = effective_taskfile.settings.to_dict() if effective_taskfile else {}
|
|
700
|
+
settings = get_effective_settings(settings, json_settings=json_settings_dict, cli_args=cli_args)
|
|
701
|
+
|
|
702
|
+
if settings.tm1_integration.detailed_results:
|
|
703
|
+
logging.info(
|
|
704
|
+
"Detailed results enabled: one cube row per executed TI. "
|
|
705
|
+
"If you upgraded RushTI from an earlier release, run "
|
|
706
|
+
"'rushti build --tm1-instance <instance>' once to add the "
|
|
707
|
+
"'original_task_id' measure to the rushti_measure dimension. "
|
|
708
|
+
"The build is non-destructive — existing cube data is preserved."
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
# Extract effective execution params (CLI > taskfile JSON > settings.ini > defaults)
|
|
712
|
+
max_workers = settings.defaults.max_workers
|
|
713
|
+
process_execution_retries = settings.defaults.retries
|
|
714
|
+
result_file = settings.defaults.result_file
|
|
715
|
+
if result_file:
|
|
716
|
+
from rushti.utils import resolve_app_path
|
|
717
|
+
|
|
718
|
+
result_file = resolve_app_path(result_file)
|
|
719
|
+
|
|
720
|
+
logger.debug(
|
|
721
|
+
f"Effective settings: max_workers={max_workers}, "
|
|
722
|
+
f"retries={process_execution_retries}, result_file={result_file}"
|
|
723
|
+
)
|
|
724
|
+
|
|
730
725
|
# Apply exclusive from settings.ini if not set via CLI or JSON
|
|
731
726
|
if exclusive_mode is None:
|
|
732
727
|
exclusive_mode = settings.exclusive_mode.enabled
|
|
@@ -824,16 +819,6 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
824
819
|
)
|
|
825
820
|
dag.validate()
|
|
826
821
|
taskfile = tm1_taskfile
|
|
827
|
-
# Apply settings from TM1 taskfile (CLI still overrides)
|
|
828
|
-
if taskfile.settings.max_workers and cli_args.get("max_workers") is None:
|
|
829
|
-
max_workers = taskfile.settings.max_workers
|
|
830
|
-
if taskfile.settings.retries is not None and cli_args.get("retries") is None:
|
|
831
|
-
process_execution_retries = taskfile.settings.retries
|
|
832
|
-
if taskfile.settings.result_file and cli_args.get("output_file") is None:
|
|
833
|
-
result_file = resolve_app_path(taskfile.settings.result_file)
|
|
834
|
-
logger.debug(
|
|
835
|
-
f"Applied TM1 taskfile settings: max_workers={max_workers}, retries={process_execution_retries}"
|
|
836
|
-
)
|
|
837
822
|
else:
|
|
838
823
|
# Build DAG from file with dependency validation
|
|
839
824
|
dag_result = build_dag(
|
|
@@ -846,16 +831,6 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
846
831
|
taskfile = None
|
|
847
832
|
if isinstance(dag_result, tuple):
|
|
848
833
|
dag, taskfile = dag_result
|
|
849
|
-
# Apply JSON settings (CLI still overrides)
|
|
850
|
-
if taskfile.settings.max_workers and cli_args.get("max_workers") is None:
|
|
851
|
-
max_workers = taskfile.settings.max_workers
|
|
852
|
-
if taskfile.settings.retries is not None and cli_args.get("retries") is None:
|
|
853
|
-
process_execution_retries = taskfile.settings.retries
|
|
854
|
-
if taskfile.settings.result_file and cli_args.get("output_file") is None:
|
|
855
|
-
result_file = resolve_app_path(taskfile.settings.result_file)
|
|
856
|
-
logger.debug(
|
|
857
|
-
f"Applied JSON settings: max_workers={max_workers}, retries={process_execution_retries}"
|
|
858
|
-
)
|
|
859
834
|
else:
|
|
860
835
|
dag = dag_result
|
|
861
836
|
# TXT source: convert to Taskfile for archiving
|
|
@@ -1117,12 +1092,15 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
1117
1092
|
assign_unique_task_ids,
|
|
1118
1093
|
)
|
|
1119
1094
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
# when CLI overrides default.
|
|
1124
|
-
upload_instance = tm1_instance or settings.tm1_integration.default_tm1_instance
|
|
1095
|
+
upload_instance, source_tier = resolve_tm1_instance(
|
|
1096
|
+
tm1_instance, settings, json_settings_dict
|
|
1097
|
+
)
|
|
1125
1098
|
if upload_instance:
|
|
1099
|
+
logger.info(
|
|
1100
|
+
"Result upload target: %s (source: %s)",
|
|
1101
|
+
upload_instance,
|
|
1102
|
+
source_tier,
|
|
1103
|
+
)
|
|
1126
1104
|
tm1_upload = connect_to_tm1_instance(
|
|
1127
1105
|
upload_instance,
|
|
1128
1106
|
CONFIG,
|
|
@@ -1173,7 +1151,9 @@ Use '{APP_NAME} <command> --help' for command-specific options and examples.
|
|
|
1173
1151
|
tm1_upload.logout()
|
|
1174
1152
|
else:
|
|
1175
1153
|
logger.warning(
|
|
1176
|
-
"push_results enabled but
|
|
1154
|
+
"push_results enabled but no TM1 instance configured "
|
|
1155
|
+
"(CLI --tm1-instance, taskfile settings.tm1_instance, "
|
|
1156
|
+
"or settings.ini tm1_instance)."
|
|
1177
1157
|
)
|
|
1178
1158
|
except Exception as e:
|
|
1179
1159
|
logger.warning(f"Failed to upload results to TM1 (non-blocking): {e}")
|
|
@@ -108,10 +108,10 @@ Examples:
|
|
|
108
108
|
add_taskfile_source_args(push_parser, required=False, include_settings=True)
|
|
109
109
|
push_parser.add_argument(
|
|
110
110
|
"--target-tm1-instance",
|
|
111
|
-
dest="
|
|
112
|
-
default=
|
|
111
|
+
dest="tm1_instance",
|
|
112
|
+
default=argparse.SUPPRESS,
|
|
113
113
|
metavar="INSTANCE",
|
|
114
|
-
help="
|
|
114
|
+
help="[DEPRECATED] Alias for --tm1-instance. Will be removed in a future major version.",
|
|
115
115
|
)
|
|
116
116
|
push_parser.add_argument(
|
|
117
117
|
"--mode",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""rushti tasks push — upload JSON taskfile to TM1 as a file."""
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
def handle_tasks_push(args, config_path: str) -> None:
|
|
9
12
|
"""Handle tasks push action.
|
|
@@ -11,6 +14,13 @@ def handle_tasks_push(args, config_path: str) -> None:
|
|
|
11
14
|
:param args: Parsed arguments
|
|
12
15
|
:param config_path: Path to config.ini
|
|
13
16
|
"""
|
|
17
|
+
if "--target-tm1-instance" in sys.argv[1:]:
|
|
18
|
+
logger.warning(
|
|
19
|
+
"DEPRECATION: --target-tm1-instance is deprecated. "
|
|
20
|
+
"Use --tm1-instance instead. The deprecated flag will continue "
|
|
21
|
+
"to work but may be removed in a future major version."
|
|
22
|
+
)
|
|
23
|
+
|
|
14
24
|
if not args.taskfile_path:
|
|
15
25
|
print("Error: --tasks is required for --push (must be a local JSON file)")
|
|
16
26
|
sys.exit(1)
|
|
@@ -19,9 +29,9 @@ def handle_tasks_push(args, config_path: str) -> None:
|
|
|
19
29
|
print(f"Error: Input file not found: {args.taskfile_path}")
|
|
20
30
|
sys.exit(1)
|
|
21
31
|
|
|
22
|
-
target_instance = args.
|
|
32
|
+
target_instance = args.tm1_instance
|
|
23
33
|
if not target_instance:
|
|
24
|
-
print("Error: --tm1-instance
|
|
34
|
+
print("Error: --tm1-instance is required for --push")
|
|
25
35
|
sys.exit(1)
|
|
26
36
|
|
|
27
37
|
try:
|
|
@@ -69,6 +69,7 @@ class TM1IntegrationSettings:
|
|
|
69
69
|
push_results: bool = False
|
|
70
70
|
auto_load_results: bool = False
|
|
71
71
|
detailed_results: bool = False
|
|
72
|
+
tm1_instance: Optional[str] = None
|
|
72
73
|
default_tm1_instance: Optional[str] = None
|
|
73
74
|
default_rushti_cube: str = "rushti"
|
|
74
75
|
default_workflow_dim: str = "rushti_workflow"
|
|
@@ -155,6 +156,7 @@ SETTINGS_SCHEMA = {
|
|
|
155
156
|
"push_results": bool,
|
|
156
157
|
"auto_load_results": bool,
|
|
157
158
|
"detailed_results": bool,
|
|
159
|
+
"tm1_instance": str,
|
|
158
160
|
"default_tm1_instance": str,
|
|
159
161
|
"default_rushti_cube": str,
|
|
160
162
|
"default_workflow_dim": str,
|
|
@@ -365,9 +367,67 @@ def load_settings(settings_path: Optional[str] = None) -> Settings:
|
|
|
365
367
|
# Resolve paths relative to application directory
|
|
366
368
|
settings.resume.checkpoint_dir = resolve_app_path(settings.resume.checkpoint_dir)
|
|
367
369
|
|
|
370
|
+
if not settings.tm1_integration.tm1_instance and settings.tm1_integration.default_tm1_instance:
|
|
371
|
+
logger.warning(
|
|
372
|
+
"DEPRECATION: [tm1_integration].default_tm1_instance is deprecated. "
|
|
373
|
+
"Rename to tm1_instance. The deprecated key will continue to work "
|
|
374
|
+
"but may be removed in a future major version."
|
|
375
|
+
)
|
|
376
|
+
|
|
368
377
|
return settings
|
|
369
378
|
|
|
370
379
|
|
|
380
|
+
def resolve_tm1_instance(
|
|
381
|
+
tm1_instance: Optional[str],
|
|
382
|
+
settings: Settings,
|
|
383
|
+
json_settings: Optional[Dict[str, Any]] = None,
|
|
384
|
+
) -> tuple[Optional[str], str]:
|
|
385
|
+
"""Resolve the effective TM1 instance for results-push, with source tier.
|
|
386
|
+
|
|
387
|
+
Walks the 4-tier precedence chain (highest to lowest), returning the
|
|
388
|
+
first non-empty value alongside a label identifying the tier:
|
|
389
|
+
|
|
390
|
+
1. CLI argument (``tm1_instance``) → label ``"cli"``
|
|
391
|
+
2. Taskfile JSON ``settings.tm1_instance`` → label ``"taskfile"``
|
|
392
|
+
3. ``settings.ini [tm1_integration].tm1_instance`` → label
|
|
393
|
+
``"settings.tm1_instance"``
|
|
394
|
+
4. ``settings.ini [tm1_integration].default_tm1_instance`` (deprecated)
|
|
395
|
+
→ label ``"settings.default_tm1_instance"``
|
|
396
|
+
|
|
397
|
+
Empty strings at any tier are treated as unset (fall through).
|
|
398
|
+
|
|
399
|
+
``json_settings`` is the taskfile JSON ``settings`` dict (as returned by
|
|
400
|
+
``TaskfileSettings.to_dict``). It is consulted separately so the
|
|
401
|
+
resolver can distinguish tier 2 from tier 3 — by the time
|
|
402
|
+
``get_effective_settings`` has been applied, the JSON-sourced
|
|
403
|
+
``tm1_instance`` has already overwritten the settings.ini value in the
|
|
404
|
+
merged ``Settings`` object, and the provenance is otherwise lost.
|
|
405
|
+
|
|
406
|
+
:param tm1_instance: Value from the CLI (e.g. ``run --tm1-instance``).
|
|
407
|
+
:param settings: Settings object (post-merge).
|
|
408
|
+
:param json_settings: Taskfile JSON settings dict; tier 2 lookup.
|
|
409
|
+
:return: ``(value, source_label)``; ``(None, "none")`` when all tiers
|
|
410
|
+
are empty.
|
|
411
|
+
"""
|
|
412
|
+
if tm1_instance:
|
|
413
|
+
return tm1_instance, "cli"
|
|
414
|
+
|
|
415
|
+
if json_settings:
|
|
416
|
+
taskfile_value = json_settings.get("tm1_instance")
|
|
417
|
+
if taskfile_value:
|
|
418
|
+
return taskfile_value, "taskfile"
|
|
419
|
+
|
|
420
|
+
settings_value = settings.tm1_integration.tm1_instance
|
|
421
|
+
if settings_value:
|
|
422
|
+
return settings_value, "settings.tm1_instance"
|
|
423
|
+
|
|
424
|
+
deprecated_value = settings.tm1_integration.default_tm1_instance
|
|
425
|
+
if deprecated_value:
|
|
426
|
+
return deprecated_value, "settings.default_tm1_instance"
|
|
427
|
+
|
|
428
|
+
return None, "none"
|
|
429
|
+
|
|
430
|
+
|
|
371
431
|
def get_effective_settings(
|
|
372
432
|
settings: Settings,
|
|
373
433
|
cli_args: Optional[Dict[str, Any]] = None,
|
|
@@ -419,14 +479,20 @@ def _apply_json_settings(settings: Settings, json_settings: Dict[str, Any]) -> N
|
|
|
419
479
|
"push_results": ("tm1_integration", "push_results"),
|
|
420
480
|
"auto_load_results": ("tm1_integration", "auto_load_results"),
|
|
421
481
|
"detailed_results": ("tm1_integration", "detailed_results"),
|
|
482
|
+
"tm1_instance": ("tm1_integration", "tm1_instance"),
|
|
422
483
|
}
|
|
423
484
|
|
|
424
485
|
for json_key, (section, attr) in json_to_settings.items():
|
|
425
|
-
if json_key in json_settings
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
486
|
+
if json_key not in json_settings:
|
|
487
|
+
continue
|
|
488
|
+
value = json_settings[json_key]
|
|
489
|
+
if value is None:
|
|
490
|
+
continue
|
|
491
|
+
if isinstance(value, str) and value == "":
|
|
492
|
+
continue
|
|
493
|
+
section_obj = getattr(settings, section)
|
|
494
|
+
setattr(section_obj, attr, value)
|
|
495
|
+
logger.debug(f"JSON override: {section}.{attr} = {value}")
|
|
430
496
|
|
|
431
497
|
|
|
432
498
|
def _apply_cli_args(settings: Settings, cli_args: Dict[str, Any]) -> None:
|
|
@@ -83,6 +83,7 @@ class TaskfileSettings:
|
|
|
83
83
|
optimization_algorithm: Optional[str] = None
|
|
84
84
|
push_results: Optional[bool] = None
|
|
85
85
|
auto_load_results: Optional[bool] = None
|
|
86
|
+
tm1_instance: Optional[str] = None
|
|
86
87
|
stage_order: Optional[List[str]] = None
|
|
87
88
|
stage_workers: Optional[Dict[str, int]] = None
|
|
88
89
|
|
|
@@ -103,6 +104,7 @@ class TaskfileSettings:
|
|
|
103
104
|
optimization_algorithm=data.get("optimization_algorithm"),
|
|
104
105
|
push_results=data.get("push_results"),
|
|
105
106
|
auto_load_results=data.get("auto_load_results"),
|
|
107
|
+
tm1_instance=data.get("tm1_instance"),
|
|
106
108
|
stage_order=data.get("stage_order"),
|
|
107
109
|
stage_workers=data.get("stage_workers"),
|
|
108
110
|
)
|
|
@@ -32,16 +32,19 @@ from TM1py.Objects import (
|
|
|
32
32
|
Process,
|
|
33
33
|
Subset,
|
|
34
34
|
)
|
|
35
|
+
from TM1py.Utils import integerize_version
|
|
35
36
|
|
|
36
37
|
from rushti.tm1_objects import (
|
|
37
38
|
MEASURE_ATTRIBUTES,
|
|
38
39
|
MEASURE_ELEMENTS,
|
|
39
40
|
PROCESS_DATA,
|
|
40
41
|
PROCESS_DATASOURCE,
|
|
41
|
-
|
|
42
|
+
PROCESS_EPILOG_V11,
|
|
43
|
+
PROCESS_EPILOG_V12,
|
|
42
44
|
PROCESS_METADATA,
|
|
43
45
|
PROCESS_PARAMETERS,
|
|
44
|
-
|
|
46
|
+
PROCESS_PROLOG_V11,
|
|
47
|
+
PROCESS_PROLOG_V12,
|
|
45
48
|
PROCESS_VARIABLES,
|
|
46
49
|
RUN_ID_SEED_ELEMENTS,
|
|
47
50
|
SAMPLE_DATA,
|
|
@@ -558,12 +561,16 @@ def _create_process(tm1: TM1Service, process_name: str, force: bool = False) ->
|
|
|
558
561
|
logger.info(f"Replacing existing process: {process_name}")
|
|
559
562
|
tm1.processes.delete(process_name)
|
|
560
563
|
|
|
564
|
+
is_v12 = integerize_version(tm1.version, 2) >= 12
|
|
565
|
+
prolog = PROCESS_PROLOG_V12 if is_v12 else PROCESS_PROLOG_V11
|
|
566
|
+
epilog = PROCESS_EPILOG_V12 if is_v12 else PROCESS_EPILOG_V11
|
|
567
|
+
|
|
561
568
|
process = Process(
|
|
562
569
|
name=process_name,
|
|
563
|
-
prolog_procedure=
|
|
570
|
+
prolog_procedure=prolog,
|
|
564
571
|
metadata_procedure=PROCESS_METADATA,
|
|
565
572
|
data_procedure=PROCESS_DATA,
|
|
566
|
-
epilog_procedure=
|
|
573
|
+
epilog_procedure=epilog,
|
|
567
574
|
datasource_type=PROCESS_DATASOURCE["Type"],
|
|
568
575
|
datasource_ascii_decimal_separator=PROCESS_DATASOURCE["asciiDecimalSeparator"],
|
|
569
576
|
datasource_ascii_delimiter_char=PROCESS_DATASOURCE["asciiDelimiterChar"],
|
|
@@ -10,8 +10,14 @@ Object Definitions:
|
|
|
10
10
|
- TASK_ID_ELEMENT_COUNT: Number of elements to generate for task_id dimension (1..N)
|
|
11
11
|
- RUN_ID_SEED_ELEMENTS: Seed elements for run_id dimension
|
|
12
12
|
- WORKFLOW_SEED_ELEMENTS: Seed elements for workflow dimension
|
|
13
|
-
-
|
|
13
|
+
- PROCESS_PROLOG_V11 / PROCESS_PROLOG_V12: Prolog body per TM1 major version
|
|
14
|
+
- PROCESS_EPILOG_V11 / PROCESS_EPILOG_V12: Epilog body per TM1 major version
|
|
14
15
|
- SAMPLE_DATA: Sample cube data for demonstration taskfiles
|
|
16
|
+
|
|
17
|
+
The v12 variants drop calls to functions that were removed or are unsupported
|
|
18
|
+
on TM1 v12 (``CubeGetLogChanges`` / ``CubeSetLogChanges`` and
|
|
19
|
+
``ExecuteCommand``). Source-file cleanup on v12 is handled by the TM1-native
|
|
20
|
+
``ASCIIDelete`` instead of shelling out to ``cmd /c del``.
|
|
15
21
|
"""
|
|
16
22
|
|
|
17
23
|
# ---------------------------------------------------------------------------
|
|
@@ -85,7 +91,7 @@ WORKFLOW_SEED_ELEMENTS = ["Sample_Stage_Mode", "Sample_Optimal_Mode"]
|
|
|
85
91
|
# TI Process: }rushti.load.results
|
|
86
92
|
# ---------------------------------------------------------------------------
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
PROCESS_PROLOG_V11 = r"""#################################################################################################
|
|
89
95
|
##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0~~##
|
|
90
96
|
#################################################################################################
|
|
91
97
|
|
|
@@ -208,6 +214,142 @@ CubeSetLogChanges( cCubeTgt, 0 );
|
|
|
208
214
|
#Region - DataSource
|
|
209
215
|
|
|
210
216
|
|
|
217
|
+
### Assign data source
|
|
218
|
+
If( nErrors = 0 );
|
|
219
|
+
DatasourceType = 'CHARACTERDELIMITED';
|
|
220
|
+
DatasourceNameForServer = cFileSrc;
|
|
221
|
+
DatasourceASCIIHeaderRecords = 1;
|
|
222
|
+
DatasourceASCIIDelimiter=',';
|
|
223
|
+
DatasourceASCIIDecimalSeparator='.';
|
|
224
|
+
DatasourceASCIIThousandSeparator='';
|
|
225
|
+
EndIf;
|
|
226
|
+
|
|
227
|
+
#EndRegion - DataSource
|
|
228
|
+
#################################################################################################
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
# v12 prolog: identical to v11 except the CubeGetLogChanges / CubeSetLogChanges
|
|
232
|
+
# block is removed. Those functions are unsupported on TM1 v12 and would cause
|
|
233
|
+
# the TI to fail at compile time.
|
|
234
|
+
PROCESS_PROLOG_V12 = r"""#################################################################################################
|
|
235
|
+
##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0~~##
|
|
236
|
+
#################################################################################################
|
|
237
|
+
|
|
238
|
+
#****Begin: Generated Statements***
|
|
239
|
+
#****End: Generated Statements****
|
|
240
|
+
|
|
241
|
+
#################################################################################################
|
|
242
|
+
### CHANGE HISTORY:
|
|
243
|
+
### MODIFICATION DATE CHANGED BY COMMENT
|
|
244
|
+
### 2026-01-15 Nicolas Bisurgi Creation of Process
|
|
245
|
+
### YYYY-MM-DD Developer Name Reason for modification here
|
|
246
|
+
#################################################################################################
|
|
247
|
+
#Region @DOC
|
|
248
|
+
# Description:
|
|
249
|
+
# This process will load RushTI result files into a TM1 cube
|
|
250
|
+
|
|
251
|
+
# Use case:
|
|
252
|
+
# Storing RushTI execution stats in a TM1 Cube
|
|
253
|
+
|
|
254
|
+
# Note:
|
|
255
|
+
# * This process assumes a there is a rushti compliant cube and dimensions. If you don't have it
|
|
256
|
+
# * please run: rushti.py build --tm1-instance tm1srv01
|
|
257
|
+
#EndRegion @DOC
|
|
258
|
+
#################################################################################################
|
|
259
|
+
|
|
260
|
+
#################################################################################################
|
|
261
|
+
#Region Process Declarations
|
|
262
|
+
### Process Parameters
|
|
263
|
+
# a short description of what the process does goes here in cAction variable, e.g. "copied data from cube A to cube B". This will be written to the message log if pLogOutput=1
|
|
264
|
+
cAction = 'loading rushti stats into ' | pTargetCube;
|
|
265
|
+
cParamArray = '';
|
|
266
|
+
# to use the parameter array remove the line above and uncomment the line below, adding the needed parameters in the provided format
|
|
267
|
+
#cParamArray = 'pLogOutput:%pLogOutput%, pTemp:%pTemp%';
|
|
268
|
+
|
|
269
|
+
### Global Variables
|
|
270
|
+
StringGlobalVariable('sProcessReturnCode');
|
|
271
|
+
NumericGlobalVariable('nProcessReturnCode');
|
|
272
|
+
|
|
273
|
+
### Standard Constants
|
|
274
|
+
cThisProcName = GetProcessName();
|
|
275
|
+
cUserName = TM1User();
|
|
276
|
+
cTimeStamp = TimSt( Now, '\Y\m\d\h\i\s' );
|
|
277
|
+
cRandomInt = NumberToString( INT( RAND( ) * 1000 ));
|
|
278
|
+
cTempObjName = Expand('%cThisProcName%_%cTimeStamp%_%cRandomInt%');
|
|
279
|
+
cViewClr = '}bedrock_clear_' | cTempObjName;
|
|
280
|
+
cViewSrc = '}bedrock_source_' | cTempObjName;
|
|
281
|
+
cMsgErrorLevel = 'ERROR';
|
|
282
|
+
cMsgErrorContent = 'Process:%cThisProcName% ErrorMsg:%sMessage%';
|
|
283
|
+
cLogInfo = 'Process:%cThisProcName% run with parameters %cParamArray%';
|
|
284
|
+
sDelimEleStart = '¦';
|
|
285
|
+
sDelimDim = '&';
|
|
286
|
+
sDelimEle = '+';
|
|
287
|
+
nProcessReturnCode = 0;
|
|
288
|
+
nErrors = 0;
|
|
289
|
+
nMetadataRecordCount= 0;
|
|
290
|
+
nDataRecordCount = 0;
|
|
291
|
+
|
|
292
|
+
### Process Specific Constants
|
|
293
|
+
cFileSrc = pSourceFile;
|
|
294
|
+
cCubeTgt = pTargetCube;
|
|
295
|
+
|
|
296
|
+
#EndRegion Process Declarations
|
|
297
|
+
#################################################################################################
|
|
298
|
+
|
|
299
|
+
### LogOutput parameters
|
|
300
|
+
IF( pLogoutput = 1 );
|
|
301
|
+
LogOutput('INFO', Expand( cLogInfo ) );
|
|
302
|
+
ENDIF;
|
|
303
|
+
|
|
304
|
+
#################################################################################################
|
|
305
|
+
#Region Validate Parameters
|
|
306
|
+
|
|
307
|
+
# pLogOutput
|
|
308
|
+
If( pLogOutput >= 1 );
|
|
309
|
+
pLogOutput = 1;
|
|
310
|
+
Else;
|
|
311
|
+
pLogOutput = 0;
|
|
312
|
+
EndIf;
|
|
313
|
+
|
|
314
|
+
# Validate source file
|
|
315
|
+
If( Trim( cFileSrc ) @= '' );
|
|
316
|
+
nErrors = nErrors + 1;
|
|
317
|
+
sMessage = 'No source cfileube specified.';
|
|
318
|
+
LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) );
|
|
319
|
+
ElseIf( FileExists( cFileSrc ) = 0 );
|
|
320
|
+
nErrors = nErrors + 1;
|
|
321
|
+
sMessage = Expand( 'Invalid source file specified: %cFileSrc%.');
|
|
322
|
+
LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) );
|
|
323
|
+
EndIf;
|
|
324
|
+
|
|
325
|
+
# Validate target cube
|
|
326
|
+
If( Trim( cCubeTgt ) @= '' );
|
|
327
|
+
nErrors = nErrors + 1;
|
|
328
|
+
sMessage = 'No target cube specified.';
|
|
329
|
+
LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) );
|
|
330
|
+
ElseIf( CubeExists( cCubeTgt ) = 0 );
|
|
331
|
+
nErrors = nErrors + 1;
|
|
332
|
+
sMessage = Expand( 'Invalid target cube specified: %cCubeTgt%.');
|
|
333
|
+
LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) );
|
|
334
|
+
EndIf;
|
|
335
|
+
|
|
336
|
+
# If any parameters fail validation then set data source of process to null and go directly to epilog
|
|
337
|
+
If( nErrors > 0 );
|
|
338
|
+
DataSourceType = 'NULL';
|
|
339
|
+
If( pStrictErrorHandling = 1 );
|
|
340
|
+
ProcessQuit;
|
|
341
|
+
Else;
|
|
342
|
+
ProcessBreak;
|
|
343
|
+
EndIf;
|
|
344
|
+
EndIf;
|
|
345
|
+
|
|
346
|
+
#################################################################################################
|
|
347
|
+
#EndRegion Validate Parameters
|
|
348
|
+
|
|
349
|
+
#################################################################################################
|
|
350
|
+
#Region - DataSource
|
|
351
|
+
|
|
352
|
+
|
|
211
353
|
### Assign data source
|
|
212
354
|
If( nErrors = 0 );
|
|
213
355
|
DatasourceType = 'CHARACTERDELIMITED';
|
|
@@ -271,7 +413,7 @@ CellPutS(vsucceed_on_minor_errors, pTargetCube, vworkflow, vrun_id, vtask_id, 's
|
|
|
271
413
|
CellPutS(voriginal_task_id, pTargetCube, vworkflow, vrun_id, vtask_id, 'original_task_id');
|
|
272
414
|
"""
|
|
273
415
|
|
|
274
|
-
|
|
416
|
+
PROCESS_EPILOG_V11 = r"""#################################################################################################
|
|
275
417
|
##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0~~##
|
|
276
418
|
#################################################################################################
|
|
277
419
|
|
|
@@ -308,6 +450,42 @@ EndIf;
|
|
|
308
450
|
|
|
309
451
|
### End Epilog ###"""
|
|
310
452
|
|
|
453
|
+
# v12 epilog: source-file cleanup uses TM1-native ASCIIDelete (no shell-out),
|
|
454
|
+
# and the CubeSetLogChanges restore is omitted because the v12 prolog does not
|
|
455
|
+
# toggle transaction logging.
|
|
456
|
+
PROCESS_EPILOG_V12 = r"""#################################################################################################
|
|
457
|
+
##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0~~##
|
|
458
|
+
#################################################################################################
|
|
459
|
+
|
|
460
|
+
#****Begin: Generated Statements***
|
|
461
|
+
#****End: Generated Statements****
|
|
462
|
+
|
|
463
|
+
### If required delete the source file
|
|
464
|
+
if(pDeleteSourceFile=1);
|
|
465
|
+
Sleep(1000);
|
|
466
|
+
ASCIIDelete(pSourceFile);
|
|
467
|
+
endif;
|
|
468
|
+
|
|
469
|
+
### Return code & final error message handling
|
|
470
|
+
If( nErrors > 0 );
|
|
471
|
+
sMessage = 'the process incurred at least 1 error. Please see above lines in this file for more details.';
|
|
472
|
+
nProcessReturnCode = 0;
|
|
473
|
+
LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) );
|
|
474
|
+
sProcessReturnCode = Expand( '%sProcessReturnCode% Process:%cThisProcName% completed with errors. Check tm1server.log for details.' );
|
|
475
|
+
If( pStrictErrorHandling = 1 );
|
|
476
|
+
ProcessQuit;
|
|
477
|
+
EndIf;
|
|
478
|
+
Else;
|
|
479
|
+
sProcessAction = Expand( 'Process:%cThisProcName% successfully %cAction%. %nDataRecordCount% records processed.' );
|
|
480
|
+
sProcessReturnCode = Expand( '%sProcessReturnCode% %sProcessAction%' );
|
|
481
|
+
nProcessReturnCode = 1;
|
|
482
|
+
If( pLogoutput = 1 );
|
|
483
|
+
LogOutput('INFO', Expand( sProcessAction ) );
|
|
484
|
+
EndIf;
|
|
485
|
+
EndIf;
|
|
486
|
+
|
|
487
|
+
### End Epilog ###"""
|
|
488
|
+
|
|
311
489
|
PROCESS_PARAMETERS = [
|
|
312
490
|
{
|
|
313
491
|
"Name": "pSourceFile",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|