prophecy-libs 2.0.0.dev5__tar.gz → 2.0.0.dev7__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.
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/PKG-INFO +1 -1
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/componentruns/component_runs_dao.py +13 -11
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/delta_metrics_storage_initializer.py +1 -1
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/in_memory_store.py +226 -171
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/package.py +28 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/pipelineruns/pipeline_run_service.py +50 -48
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/schemas/em.py +41 -2
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/schemas/external.py +8 -0
- prophecy-libs-2.0.0.dev7/prophecy/executionmetrics/workflow_parser.py +151 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/jsonrpc/models.py +4 -1
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/pipeline_monitoring.py +4 -38
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/utils.py +88 -37
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/PKG-INFO +1 -1
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/SOURCES.txt +1 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/setup.py +1 -1
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/README.md +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/config/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/config/config_base.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/config/config_base_test.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/config/utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/componentruns/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/componentruns/component_run_service.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/hive_parquet_storage_initializer.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/metrics_storage_initializer.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/models.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/evolutions/package.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/execution_metrics_handler.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/interims/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/interims/delta_interims.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/interims/hive_parquet_interims.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/interims/interims_table.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/logging_spark_session.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/models.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/pipelineruns/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/pipelineruns/pipeline_runs_dao.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/schemas/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/utils/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/utils/common.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/utils/constants.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/utils/external.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/utils/interim_store.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/zip_file.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/jsonrpc/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/libs/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/libs/uc_shared_utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/libs/utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/lookups/LookupsBase.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/lookups/LookupsNative.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/lookups/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/random_data_creator.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/streaming/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/streaming/delta_lake_utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/test/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/test/base_test_case.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/test/utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/udfs/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/udfs/rest_api_udf.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/udfs/sample_udf.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/udfs/scala_udf_wrapper.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/constants.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/data_profiler.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/datasampleloader.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/diff.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/__diff.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/_dill.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/_objects.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/_shims.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/detect.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/logger.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/objtypes.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/pointers.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/session.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/settings.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/source.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/dill/temp.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/__main__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/cli.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/config.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/custom_typing.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/decode/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/decode/codes.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/documentor.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/exceptions.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/factory.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/generator.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/address/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/address/en/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/address/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/date_time/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/date_time/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/internet/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/internet/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/misc/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/misc/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/person/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/person/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/phone_number/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/phone_number/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/python/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/providers/python/en_US/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/proxy.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/checksums.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/datasets.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/decorators.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/distribution.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/loading.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/faker/utils/text.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/functions.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/gems_utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/httpclient.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/initial_python_code.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/json_rpc_layer.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/metagem_utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/monitoring_utils.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/request_processor.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/secrets.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/synthetic_data_generator.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/__init__.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/abi_base.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/abi_core_fcns.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/abi_fcn_wrapper.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/dataframe_fcns.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/dml_schema.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/utils/transpiler/fixed_file_schema.py +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/dependency_links.txt +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/not-zip-safe +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/requires.txt +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy_libs.egg-info/top_level.txt +0 -0
- {prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/setup.cfg +0 -0
|
@@ -693,8 +693,8 @@ class ComponentRunsDAO(ExecutionMetricsDAO):
|
|
|
693
693
|
def _row_to_component_runs_with_status(self, row: Row) -> ComponentRunsWithStatus:
|
|
694
694
|
"""Convert Row to ComponentRunsWithStatus."""
|
|
695
695
|
base = self._row_to_component_runs(row)
|
|
696
|
-
return ComponentRunsWithStatus(
|
|
697
|
-
|
|
696
|
+
return ComponentRunsWithStatus.from_component_runs(
|
|
697
|
+
base, row[STATUS] if STATUS in row else None
|
|
698
698
|
)
|
|
699
699
|
|
|
700
700
|
def _row_to_interim_response(self, row: Row) -> InterimResponse:
|
|
@@ -817,18 +817,20 @@ class ComponentRunsDAO(ExecutionMetricsDAO):
|
|
|
817
817
|
"""Convert Row exception to StoredSerializableException."""
|
|
818
818
|
return StoredSerializableException(
|
|
819
819
|
exception_type=exception_row[EXCEPTION_TYPE],
|
|
820
|
-
|
|
821
|
-
|
|
820
|
+
message=exception_row[MSG],
|
|
821
|
+
cause_message=exception_row[CAUSE_MSG],
|
|
822
822
|
stack_trace=exception_row[STACK_TRACE],
|
|
823
823
|
time=exception_row[TIME],
|
|
824
824
|
)
|
|
825
825
|
|
|
826
826
|
def _convert_exception_to_row(self, exception: StoredSerializableException) -> Row:
|
|
827
827
|
"""Convert StoredSerializableException to Row."""
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
828
|
+
row_data = {
|
|
829
|
+
EXCEPTION_TYPE: exception.exception_type,
|
|
830
|
+
MSG: exception.message,
|
|
831
|
+
CAUSE_MSG: exception.cause_message,
|
|
832
|
+
STACK_TRACE: exception.stack_trace,
|
|
833
|
+
TIME: exception.time,
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return Row(**row_data)
|
|
@@ -104,7 +104,7 @@ def base_location(spark: SparkSession, suffix: str) -> str:
|
|
|
104
104
|
spark_delta_path_prefix = "spark.prophecy.delta.path.prefix"
|
|
105
105
|
default_path = "/prophecy/metadata/executionmetrics"
|
|
106
106
|
try:
|
|
107
|
-
base_path = spark.conf.get(spark_delta_path_prefix)
|
|
107
|
+
base_path = spark.conf.get(spark_delta_path_prefix, default_path)
|
|
108
108
|
except Exception as e:
|
|
109
109
|
base_path = default_path
|
|
110
110
|
return f"{base_path}{suffix}"
|
{prophecy-libs-2.0.0.dev5 → prophecy-libs-2.0.0.dev7}/prophecy/executionmetrics/in_memory_store.py
RENAMED
|
@@ -45,70 +45,16 @@ from prophecy.executionmetrics.evolutions.metrics_storage_initializer import (
|
|
|
45
45
|
StorageMetadata,
|
|
46
46
|
create_storage_metadata,
|
|
47
47
|
)
|
|
48
|
-
from prophecy.executionmetrics.utils.common import is_databricks_environment
|
|
48
|
+
from prophecy.executionmetrics.utils.common import is_databricks_environment, now_millis
|
|
49
49
|
from prophecy.executionmetrics.utils.external import compress
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@dataclass
|
|
59
|
-
class WorkflowNode:
|
|
60
|
-
"""Workflow node representation."""
|
|
61
|
-
|
|
62
|
-
id: str
|
|
63
|
-
component: str
|
|
64
|
-
metadata: Dict[str, Any]
|
|
65
|
-
ports: Dict[str, Any]
|
|
66
|
-
properties: Dict[str, Any] = field(default_factory=dict)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@dataclass
|
|
70
|
-
class WorkflowEdge:
|
|
71
|
-
"""Workflow edge representation."""
|
|
72
|
-
|
|
73
|
-
source: str
|
|
74
|
-
source_port: str
|
|
75
|
-
target: str
|
|
76
|
-
target_port: str
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@dataclass
|
|
80
|
-
class WorkflowGroup(WorkflowNode, HasProcessesConnectionsPorts):
|
|
81
|
-
"""Workflow group node."""
|
|
82
|
-
|
|
83
|
-
processes: Dict[str, "WorkflowNode"] = field(default_factory=dict)
|
|
84
|
-
connections: List["WorkflowEdge"] = field(default_factory=list)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@dataclass
|
|
88
|
-
class WorkflowGraph(HasProcessesConnectionsPorts):
|
|
89
|
-
"""Workflow graph representation."""
|
|
90
|
-
|
|
91
|
-
processes: Dict[str, WorkflowNode]
|
|
92
|
-
connections: List[WorkflowEdge]
|
|
93
|
-
ports: Dict[str, Any] = field(default_factory=dict)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_workflow_graph(code: Optional[Dict[str, str]]) -> Optional[WorkflowGraph]:
|
|
97
|
-
"""Extract workflow graph from code dictionary."""
|
|
98
|
-
if not code:
|
|
99
|
-
return None
|
|
100
|
-
|
|
101
|
-
workflow_content = None
|
|
102
|
-
for path, content in code.items():
|
|
103
|
-
if path.endswith(".prophecy/workflow.latest.json"):
|
|
104
|
-
workflow_content = content
|
|
105
|
-
break
|
|
106
|
-
|
|
107
|
-
if workflow_content:
|
|
108
|
-
data = json.loads(workflow_content)
|
|
109
|
-
return _parse_workflow_graph(data)
|
|
110
|
-
|
|
111
|
-
return None
|
|
50
|
+
from prophecy.executionmetrics.workflow_parser import (
|
|
51
|
+
HasProcessesConnectionsPorts,
|
|
52
|
+
WorkflowEdge,
|
|
53
|
+
WorkflowGraph,
|
|
54
|
+
WorkflowGroup,
|
|
55
|
+
WorkflowNode,
|
|
56
|
+
get_workflow_graph,
|
|
57
|
+
)
|
|
112
58
|
|
|
113
59
|
|
|
114
60
|
def get_slug_to_process_map(code: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]:
|
|
@@ -134,61 +80,15 @@ def extract_slug_to_process_mapping(
|
|
|
134
80
|
slug_to_process[current_slug] = node.id
|
|
135
81
|
|
|
136
82
|
if isinstance(node, WorkflowGroup):
|
|
137
|
-
for
|
|
83
|
+
for _, child_node in node.processes.items():
|
|
138
84
|
extract_from_node(child_node, current_slug)
|
|
139
85
|
|
|
140
|
-
for
|
|
86
|
+
for _, node in processes.items():
|
|
141
87
|
extract_from_node(node)
|
|
142
88
|
|
|
143
89
|
return slug_to_process
|
|
144
90
|
|
|
145
91
|
|
|
146
|
-
def _parse_workflow_nodes(data: Dict[str, Any]) -> Dict[str, "WorkflowNode"]:
|
|
147
|
-
processes = {}
|
|
148
|
-
for proc_id, proc_data in data.get("processes", {}).items():
|
|
149
|
-
if proc_data.get("component") == "Subgraph":
|
|
150
|
-
node = WorkflowGroup(
|
|
151
|
-
id=proc_id,
|
|
152
|
-
component=proc_data.get("component"),
|
|
153
|
-
metadata=proc_data.get("metadata", {}),
|
|
154
|
-
ports=proc_data.get("ports", {}),
|
|
155
|
-
properties=proc_data.get("properties", {}),
|
|
156
|
-
processes=_parse_workflow_nodes(proc_data),
|
|
157
|
-
connections=proc_data.get("connections", []),
|
|
158
|
-
)
|
|
159
|
-
else:
|
|
160
|
-
node = WorkflowNode(
|
|
161
|
-
id=proc_id,
|
|
162
|
-
component=proc_data.get("component"),
|
|
163
|
-
metadata=proc_data.get("metadata", {}),
|
|
164
|
-
ports=proc_data.get("ports", {}),
|
|
165
|
-
properties=proc_data.get("properties", {}),
|
|
166
|
-
)
|
|
167
|
-
processes[proc_id] = node
|
|
168
|
-
|
|
169
|
-
return processes
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def _parse_workflow_graph(data: Dict[str, Any]) -> WorkflowGraph:
|
|
173
|
-
"""Parse workflow graph from dictionary."""
|
|
174
|
-
processes = _parse_workflow_nodes(data)
|
|
175
|
-
|
|
176
|
-
connections = []
|
|
177
|
-
for conn_data in data.get("connections", []):
|
|
178
|
-
connections.append(
|
|
179
|
-
WorkflowEdge(
|
|
180
|
-
source=conn_data["source"],
|
|
181
|
-
source_port=conn_data["sourcePort"],
|
|
182
|
-
target=conn_data["target"],
|
|
183
|
-
target_port=conn_data["targetPort"],
|
|
184
|
-
)
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
return WorkflowGraph(
|
|
188
|
-
processes=processes, connections=connections, ports=data.get("ports", {})
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
|
|
192
92
|
class InMemoryStore:
|
|
193
93
|
"""
|
|
194
94
|
In-memory store for pipeline execution metrics.
|
|
@@ -215,6 +115,7 @@ class InMemoryStore:
|
|
|
215
115
|
self._status: str = PipelineStatus.STARTED
|
|
216
116
|
self._fabric_uid: str = ""
|
|
217
117
|
self._time_taken: Optional[int] = None
|
|
118
|
+
self._time_started: int = now_millis()
|
|
218
119
|
self._rows_read: Optional[int] = None
|
|
219
120
|
self._rows_written: Optional[int] = None
|
|
220
121
|
self._run_type: str = ""
|
|
@@ -282,6 +183,7 @@ class InMemoryStore:
|
|
|
282
183
|
)
|
|
283
184
|
|
|
284
185
|
# Set pipeline execution state
|
|
186
|
+
self._time_started = now_millis()
|
|
285
187
|
self._pipeline_uri = pipeline_uri
|
|
286
188
|
self._job_uri = job_uri
|
|
287
189
|
self._fabric_uid = fabric_uid
|
|
@@ -411,59 +313,208 @@ class InMemoryStore:
|
|
|
411
313
|
collect_processes(graph.processes)
|
|
412
314
|
return all_procs
|
|
413
315
|
|
|
316
|
+
def _all_connections(self) -> List[WorkflowEdge]:
|
|
317
|
+
graph = self._graph
|
|
318
|
+
all_conns = []
|
|
319
|
+
if not graph:
|
|
320
|
+
return all_conns
|
|
321
|
+
|
|
322
|
+
def collect_connections(
|
|
323
|
+
connections: List[WorkflowEdge], processes: Dict[str, WorkflowNode]
|
|
324
|
+
):
|
|
325
|
+
all_conns.extend(connections)
|
|
326
|
+
# Check for subgraphs and collect their connections
|
|
327
|
+
for node in processes.values():
|
|
328
|
+
if isinstance(node, WorkflowGroup):
|
|
329
|
+
collect_connections(node.connections, node.processes)
|
|
330
|
+
|
|
331
|
+
collect_connections(graph.connections, graph.processes)
|
|
332
|
+
return all_conns
|
|
333
|
+
|
|
414
334
|
def _extract_process_to_gem_map(self) -> Dict[str, GemProgress]:
|
|
415
335
|
"""Extract mapping from process ID to GemProgress."""
|
|
416
|
-
|
|
336
|
+
graph = self._graph
|
|
337
|
+
processes = self._processes
|
|
338
|
+
if graph is None or processes is None:
|
|
417
339
|
return {}
|
|
418
340
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
for
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
341
|
+
slug_to_process_map = extract_slug_to_process_mapping(graph.processes)
|
|
342
|
+
process_to_slug_map = {v: k for k, v in slug_to_process_map.items()}
|
|
343
|
+
|
|
344
|
+
connections = self._all_connections()
|
|
345
|
+
target_port_to_connection_map = {c.target_port: c for c in connections}
|
|
346
|
+
|
|
347
|
+
# Group connections by source port
|
|
348
|
+
source_port_to_connection_multi_map: Dict[str, List[WorkflowEdge]] = {}
|
|
349
|
+
for conn in connections:
|
|
350
|
+
if conn.source_port not in source_port_to_connection_multi_map:
|
|
351
|
+
source_port_to_connection_multi_map[conn.source_port] = []
|
|
352
|
+
source_port_to_connection_multi_map[conn.source_port].append(conn)
|
|
353
|
+
|
|
354
|
+
def fetch_output_details(out_port: str) -> List[Tuple[str, str]]:
|
|
355
|
+
"""
|
|
356
|
+
Fetch output details for a given output port.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
out_port: The output port ID
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
List of tuples (target process ID, target port ID)
|
|
363
|
+
"""
|
|
364
|
+
connections_list = source_port_to_connection_multi_map.get(out_port, [])
|
|
365
|
+
target_connection_to_node = {
|
|
366
|
+
c: processes.get(c.target) for c in connections_list
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
result = []
|
|
370
|
+
for connection, node in target_connection_to_node.items():
|
|
371
|
+
if node and node.component == "Subgraph":
|
|
372
|
+
# Recursively fetch output details for subgraph
|
|
373
|
+
result.extend(fetch_output_details(connection.target_port))
|
|
374
|
+
elif node:
|
|
375
|
+
result.append((connection.target, connection.target_port))
|
|
376
|
+
|
|
377
|
+
return result
|
|
378
|
+
|
|
379
|
+
def fetch_input_details(in_port: str) -> Tuple[Optional[str], Optional[str]]:
|
|
380
|
+
"""
|
|
381
|
+
Fetch input details for a given input port.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
in_port: The input port ID
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
Tuple of (source process ID, source port ID)
|
|
388
|
+
"""
|
|
389
|
+
connection = target_port_to_connection_map.get(in_port)
|
|
390
|
+
if not connection:
|
|
391
|
+
return (None, None)
|
|
392
|
+
|
|
393
|
+
source_node = processes.get(connection.source)
|
|
394
|
+
if source_node and source_node.component == "Subgraph":
|
|
395
|
+
# Recursively fetch input details for subgraph
|
|
396
|
+
return fetch_input_details(connection.source_port)
|
|
397
|
+
else:
|
|
398
|
+
return (
|
|
399
|
+
connection.source if connection else None,
|
|
400
|
+
connection.source_port if connection else None,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def extract_process_to_gem_map_inner() -> Dict[str, GemProgress]:
|
|
404
|
+
"""
|
|
405
|
+
Internal function to extract process to gem mapping.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
List of tuples (process ID, GemProgress)
|
|
409
|
+
"""
|
|
410
|
+
result = {}
|
|
411
|
+
|
|
412
|
+
for process_id, current_process in processes.items():
|
|
413
|
+
gem_name = process_to_slug_map.get(
|
|
414
|
+
current_process.id
|
|
415
|
+
) or current_process.metadata.get("slug", "")
|
|
416
|
+
|
|
417
|
+
out_ports = [port.slug for port in current_process.ports.outputs]
|
|
418
|
+
in_ports = [port.slug for port in current_process.ports.inputs]
|
|
419
|
+
|
|
420
|
+
# Get input process details
|
|
421
|
+
input_process_details = []
|
|
422
|
+
for current_process_in_port in current_process.ports.inputs:
|
|
423
|
+
input_process, source_port_id = fetch_input_details(
|
|
424
|
+
current_process_in_port.id
|
|
425
|
+
)
|
|
426
|
+
input_process_details.append(
|
|
427
|
+
(input_process, source_port_id, current_process_in_port.slug)
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# Get output process details
|
|
431
|
+
output_process_details = []
|
|
432
|
+
for current_process_out_port in current_process.ports.outputs:
|
|
433
|
+
output_details = fetch_output_details(current_process_out_port.id)
|
|
434
|
+
for output_process, target_port_id in output_details:
|
|
435
|
+
output_process_details.append(
|
|
436
|
+
(
|
|
437
|
+
output_process,
|
|
438
|
+
current_process_out_port.slug,
|
|
439
|
+
target_port_id,
|
|
443
440
|
)
|
|
444
441
|
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
442
|
+
|
|
443
|
+
# Create input gem edges
|
|
444
|
+
input_gem_edges = []
|
|
445
|
+
for (
|
|
446
|
+
process_id_opt,
|
|
447
|
+
from_port_id_opt,
|
|
448
|
+
to_port_slug,
|
|
449
|
+
) in input_process_details:
|
|
450
|
+
gem_name_opt = (
|
|
451
|
+
process_to_slug_map.get(process_id_opt)
|
|
452
|
+
if process_id_opt
|
|
453
|
+
else None
|
|
454
|
+
)
|
|
455
|
+
input_process = (
|
|
456
|
+
processes.get(process_id_opt) if process_id_opt else None
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
from_port_slug = None
|
|
460
|
+
if from_port_id_opt and input_process:
|
|
461
|
+
for port in input_process.ports.outputs:
|
|
462
|
+
if port.id == from_port_id_opt:
|
|
463
|
+
from_port_slug = port.slug
|
|
464
|
+
break
|
|
465
|
+
|
|
466
|
+
input_gem_edges.append(
|
|
467
|
+
GemEdge(
|
|
468
|
+
gem_name=gem_name_opt,
|
|
469
|
+
from_port=from_port_slug,
|
|
470
|
+
to_port=to_port_slug,
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Create output gem edges
|
|
475
|
+
output_gem_edges = []
|
|
476
|
+
for process_id, from_port_slug, to_port_id in output_process_details:
|
|
477
|
+
gem_name_opt = process_to_slug_map.get(process_id)
|
|
478
|
+
output_process = processes.get(process_id)
|
|
479
|
+
|
|
480
|
+
to_port_slug = None
|
|
481
|
+
if output_process:
|
|
482
|
+
for port in output_process.ports.inputs:
|
|
483
|
+
if port.id == to_port_id:
|
|
484
|
+
to_port_slug = port.slug
|
|
485
|
+
break
|
|
486
|
+
|
|
487
|
+
output_gem_edges.append(
|
|
488
|
+
GemEdge(
|
|
489
|
+
gem_name=gem_name_opt,
|
|
490
|
+
from_port=from_port_slug,
|
|
491
|
+
to_port=to_port_slug,
|
|
454
492
|
)
|
|
493
|
+
)
|
|
455
494
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
495
|
+
# Create GemProgress
|
|
496
|
+
gem_progress = GemProgress(
|
|
497
|
+
gem_name=gem_name,
|
|
498
|
+
process_id=current_process.id,
|
|
499
|
+
gem_type=current_process.component,
|
|
500
|
+
input_gems=input_gem_edges,
|
|
501
|
+
output_gems=output_gem_edges,
|
|
502
|
+
in_ports=in_ports,
|
|
503
|
+
out_ports=out_ports,
|
|
504
|
+
num_rows_output=None,
|
|
505
|
+
stdout=None,
|
|
506
|
+
stderr=None,
|
|
507
|
+
start_time=None,
|
|
508
|
+
end_time=None,
|
|
509
|
+
state=None,
|
|
510
|
+
exception=None,
|
|
511
|
+
)
|
|
465
512
|
|
|
466
|
-
|
|
513
|
+
result[current_process.id] = gem_progress
|
|
514
|
+
|
|
515
|
+
return result
|
|
516
|
+
|
|
517
|
+
return extract_process_to_gem_map_inner()
|
|
467
518
|
|
|
468
519
|
def update_run_uid(self, job_run_uid: str, task_run_uid: str) -> None:
|
|
469
520
|
"""Update job and task run UIDs."""
|
|
@@ -516,8 +567,8 @@ class InMemoryStore:
|
|
|
516
567
|
def update_gem_progress(
|
|
517
568
|
self,
|
|
518
569
|
process: str,
|
|
519
|
-
start_time:
|
|
520
|
-
end_time: Optional[
|
|
570
|
+
start_time: str,
|
|
571
|
+
end_time: Optional[str],
|
|
521
572
|
stdout: Optional[List[TimestampedOutput]],
|
|
522
573
|
stderr: Optional[List[TimestampedOutput]],
|
|
523
574
|
state: str,
|
|
@@ -530,13 +581,15 @@ class InMemoryStore:
|
|
|
530
581
|
|
|
531
582
|
if process in self._process_to_gem_map:
|
|
532
583
|
gem_progress = self._process_to_gem_map[process]
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
584
|
+
updated_gem_progress = gem_progress.update_progress(
|
|
585
|
+
start_time=int(start_time),
|
|
586
|
+
end_time=int(end_time) if end_time else None,
|
|
587
|
+
stdout=stdout,
|
|
588
|
+
stderr=stderr,
|
|
589
|
+
state=state,
|
|
590
|
+
exception=exception,
|
|
591
|
+
)
|
|
592
|
+
self._process_to_gem_map[process] = updated_gem_progress
|
|
540
593
|
|
|
541
594
|
def _update_connecting_gem_rows(
|
|
542
595
|
self, interim_process: str, interim: LInterimContent
|
|
@@ -609,7 +662,9 @@ class InMemoryStore:
|
|
|
609
662
|
gem.num_rows = num_records
|
|
610
663
|
|
|
611
664
|
def offload(
|
|
612
|
-
self,
|
|
665
|
+
self,
|
|
666
|
+
pipeline_status: str,
|
|
667
|
+
interim_details: List[Tuple[InterimKey, DataFrame]] = None,
|
|
613
668
|
) -> Tuple[List["ComponentRuns"], List[str]]:
|
|
614
669
|
"""
|
|
615
670
|
Offload metrics to persistent storage.
|
|
@@ -718,10 +773,10 @@ class InMemoryStore:
|
|
|
718
773
|
job_uri=self._job_uri,
|
|
719
774
|
job_run_uid=self._job_run_uid,
|
|
720
775
|
task_run_uid=self._task_run_uid,
|
|
721
|
-
status=
|
|
776
|
+
status=pipeline_status,
|
|
722
777
|
submission_time=self._submission_time,
|
|
723
778
|
fabric_uid=self._fabric_uid,
|
|
724
|
-
time_taken=self.
|
|
779
|
+
time_taken=now_millis() - self._time_started,
|
|
725
780
|
rows_read=self._rows_read,
|
|
726
781
|
rows_written=self._rows_written,
|
|
727
782
|
run_type=self._run_type,
|
|
@@ -783,7 +838,7 @@ class InMemoryStore:
|
|
|
783
838
|
component_name = node.metadata.get("slug", "")
|
|
784
839
|
component_type = node.component
|
|
785
840
|
|
|
786
|
-
interim_ports = [p
|
|
841
|
+
interim_ports = [p.id for p in node.ports.outputs]
|
|
787
842
|
gem_progress = (
|
|
788
843
|
self._process_to_gem_map.get(process_id)
|
|
789
844
|
if self._process_to_gem_map
|
|
@@ -832,11 +887,11 @@ class InMemoryStore:
|
|
|
832
887
|
# Default interim details
|
|
833
888
|
interim_subgraph = subgraph_name
|
|
834
889
|
interim_node = node
|
|
835
|
-
interim_ports = [p
|
|
890
|
+
interim_ports = [p.id for p in node.ports.outputs]
|
|
836
891
|
|
|
837
892
|
# Special handling for Target nodes
|
|
838
|
-
if component_type == "Target" and node.ports.
|
|
839
|
-
first_input_port = node.ports
|
|
893
|
+
if component_type == "Target" and node.ports.inputs:
|
|
894
|
+
first_input_port = node.ports.inputs[0].id
|
|
840
895
|
connection, parent_graph, target_subgraph = (
|
|
841
896
|
self._fetch_target_component_source_port(
|
|
842
897
|
target_port_connection_map,
|
|
@@ -316,6 +316,34 @@ class ComponentRunsWithStatus(ExecutionMetricsEntity):
|
|
|
316
316
|
"branch": self.branch,
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
@classmethod
|
|
320
|
+
def from_component_runs(
|
|
321
|
+
cls, component_run: ComponentRuns, status: Optional[str]
|
|
322
|
+
) -> "ComponentRunsWithStatus":
|
|
323
|
+
return ComponentRunsWithStatus(
|
|
324
|
+
uid=component_run.uid,
|
|
325
|
+
component_uri=component_run.component_uri,
|
|
326
|
+
pipeline_run_uid=component_run.pipeline_run_uid,
|
|
327
|
+
pipeline_uri=component_run.pipeline_uri,
|
|
328
|
+
fabric_uid=component_run.fabric_uid,
|
|
329
|
+
component_name=component_run.component_name,
|
|
330
|
+
interim_component_name=component_run.interim_component_name,
|
|
331
|
+
component_type=component_run.component_type,
|
|
332
|
+
interim_out_port=component_run.interim_out_port,
|
|
333
|
+
interim_subgraph_name=component_run.interim_subgraph_name,
|
|
334
|
+
interim_process_id=component_run.interim_process_id,
|
|
335
|
+
created_by=component_run.created_by,
|
|
336
|
+
records=component_run.records,
|
|
337
|
+
bytes=component_run.bytes,
|
|
338
|
+
partitions=component_run.partitions,
|
|
339
|
+
created_at=component_run.created_at,
|
|
340
|
+
expired=component_run.expired,
|
|
341
|
+
status=status, # This will be set separately
|
|
342
|
+
job_uri=component_run.job_uri,
|
|
343
|
+
run_type=component_run.run_type,
|
|
344
|
+
branch=component_run.branch,
|
|
345
|
+
)
|
|
346
|
+
|
|
319
347
|
|
|
320
348
|
@dataclass
|
|
321
349
|
class ComponentRunsWithStatusAndInterims(ExecutionMetricsEntity):
|