pam-python 0.2.10__tar.gz → 0.2.12__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.
- {pam_python-0.2.10/pam_python.egg-info → pam_python-0.2.12}/PKG-INFO +1 -1
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/__init__.py +1 -1
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/interface_task_manager.py +1 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/__init__.py +2 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/area.py +10 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/bar.py +10 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/definition.py +16 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/donut.py +6 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/line.py +10 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/manager.py +30 -2
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/metric.py +10 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/pie.py +6 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/polar_area.py +6 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/radial_bar.py +10 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/store.py +53 -10
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/table.py +10 -0
- pam_python-0.2.12/pam/reports/temporal.py +55 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/transport.py +1 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/task_manager.py +2 -1
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/AGENT.md +112 -9
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/REPORTS.md +152 -17
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/tester_task.py +22 -1
- {pam_python-0.2.10 → pam_python-0.2.12/pam_python.egg-info}/PKG-INFO +1 -1
- {pam_python-0.2.10 → pam_python-0.2.12}/pam_python.egg-info/SOURCES.txt +2 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_cli_templates.py +40 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_report_store.py +40 -6
- pam_python-0.2.12/tests/test_temporal_mode.py +116 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/LICENSE.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/README.md +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/api.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/cli.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/datetime_utils.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/logger.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/models/__init__.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/models/data_input_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/models/request_command.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/models/sqlite_download.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/axis.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/box_plot.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/bubble.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/candlestick.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/collection.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/errors.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/fields.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/heatmap.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/radar.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/range_area.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/range_bar.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/range_transform.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/record.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/scatter.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/schema.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/reports/treemap.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/request_file_format.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/result_batch_uploader.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/server.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/service.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/sqlite.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/temp_file_utils.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/buildcmd/pamb +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/buildcmd/pamb-base.sh +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/dockerignore.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/gitignore.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/main.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/pylintrc.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/run_unit_test.bat +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/run_unit_test.ps1 +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/init/run_unit_test.sh +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/project/pip/Dockerfile +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/project/pip/requirements.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/project/uv/Dockerfile +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/project/uv/pyproject.toml +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/project/uv/python-version +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/common/service.yaml +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/pandas/functions.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/pandas/service.test.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/pandas/service_class.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/polars/functions.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/polars/service.test.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/templates/service/polars/service_class.tmpl +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam/utils.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam_python.egg-info/dependency_links.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam_python.egg-info/entry_points.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam_python.egg-info/requires.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pam_python.egg-info/top_level.txt +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/pyproject.toml +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/setup.cfg +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_api.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_data_error_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_data_input_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_data_timeout_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_datetime_utils.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_range_transform.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_request_data_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_result_batch_uploader.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_service_dataframe_output.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_sqlite_download_v2.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_temp_file_utils.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_area_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_bar_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_box_plot_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_bubble_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_candlestick_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_donut_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_heatmap_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_line_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_metric_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_pie_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_polar_area_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_radar_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_radial_bar_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_range_area_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_range_bar_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_scatter_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_table_report.py +0 -0
- {pam_python-0.2.10 → pam_python-0.2.12}/tests/test_typed_treemap_report.py +0 -0
|
@@ -31,6 +31,7 @@ from pam.reports.range_bar import RangeBarChartReport
|
|
|
31
31
|
from pam.reports.scatter import ScatterChartReport
|
|
32
32
|
from pam.reports.store import ReportStore
|
|
33
33
|
from pam.reports.table import TableReport
|
|
34
|
+
from pam.reports.temporal import TemporalMode
|
|
34
35
|
from pam.reports.treemap import TreemapReport
|
|
35
36
|
|
|
36
37
|
__all__ = [
|
|
@@ -68,6 +69,7 @@ __all__ = [
|
|
|
68
69
|
"ScatterChartReport",
|
|
69
70
|
"Sum",
|
|
70
71
|
"TableReport",
|
|
72
|
+
"TemporalMode",
|
|
71
73
|
"TimeRangeTransform",
|
|
72
74
|
"TreemapReport",
|
|
73
75
|
]
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
AREA_RENDERER = "area"
|
|
@@ -50,6 +51,7 @@ class AreaChartReport(TypedCollectionReport):
|
|
|
50
51
|
series: Optional[Dimension] = None,
|
|
51
52
|
stacked: bool = False,
|
|
52
53
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
54
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
53
55
|
):
|
|
54
56
|
if not isinstance(stacked, bool):
|
|
55
57
|
raise ReportStoreValidationError("Area stacked must be boolean")
|
|
@@ -61,6 +63,13 @@ class AreaChartReport(TypedCollectionReport):
|
|
|
61
63
|
normalized_range_transform = normalize_range_transform(
|
|
62
64
|
range_transform, granularity, normalized_values, "Area Chart"
|
|
63
65
|
)
|
|
66
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
67
|
+
temporal_mode,
|
|
68
|
+
granularity,
|
|
69
|
+
normalized_values,
|
|
70
|
+
"Area Chart",
|
|
71
|
+
range_transform,
|
|
72
|
+
)
|
|
64
73
|
definition = ReportDefinition.create(
|
|
65
74
|
report_type="area",
|
|
66
75
|
contract_version=AREA_CONTRACT_VERSION,
|
|
@@ -72,6 +81,7 @@ class AreaChartReport(TypedCollectionReport):
|
|
|
72
81
|
},
|
|
73
82
|
options={"stacked": stacked},
|
|
74
83
|
range_transform=normalized_range_transform,
|
|
84
|
+
temporal_mode=normalized_temporal_mode,
|
|
75
85
|
)
|
|
76
86
|
report = store._declare_typed_report(
|
|
77
87
|
key=key,
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
BAR_RENDERER = "bar"
|
|
@@ -56,6 +57,7 @@ class BarChartReport(TypedCollectionReport):
|
|
|
56
57
|
orientation: str = "vertical",
|
|
57
58
|
stacked: bool = False,
|
|
58
59
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
60
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
59
61
|
):
|
|
60
62
|
if not isinstance(category, Dimension):
|
|
61
63
|
raise ReportStoreValidationError(
|
|
@@ -110,6 +112,13 @@ class BarChartReport(TypedCollectionReport):
|
|
|
110
112
|
normalized_range_transform = normalize_range_transform(
|
|
111
113
|
range_transform, granularity, normalized_values, "Bar Chart"
|
|
112
114
|
)
|
|
115
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
116
|
+
temporal_mode,
|
|
117
|
+
granularity,
|
|
118
|
+
normalized_values,
|
|
119
|
+
"Bar Chart",
|
|
120
|
+
range_transform,
|
|
121
|
+
)
|
|
113
122
|
definition = ReportDefinition.create(
|
|
114
123
|
report_type="bar",
|
|
115
124
|
contract_version=BAR_CONTRACT_VERSION,
|
|
@@ -121,6 +130,7 @@ class BarChartReport(TypedCollectionReport):
|
|
|
121
130
|
},
|
|
122
131
|
options={"orientation": orientation, "stacked": stacked},
|
|
123
132
|
range_transform=normalized_range_transform,
|
|
133
|
+
temporal_mode=normalized_temporal_mode,
|
|
124
134
|
)
|
|
125
135
|
report = store._declare_typed_report(
|
|
126
136
|
key=key,
|
|
@@ -18,6 +18,7 @@ class ReportDefinition:
|
|
|
18
18
|
binding: Dict[str, Any]
|
|
19
19
|
options: Dict[str, Any] = field(default_factory=dict)
|
|
20
20
|
range_transform: Optional[Dict[str, Any]] = None
|
|
21
|
+
temporal_mode: Optional[str] = None
|
|
21
22
|
|
|
22
23
|
def __post_init__(self) -> None:
|
|
23
24
|
if not isinstance(self.report_type, str) or not self.report_type:
|
|
@@ -41,6 +42,17 @@ class ReportDefinition:
|
|
|
41
42
|
raise ReportStoreValidationError(
|
|
42
43
|
"range_transform must be a dictionary or None"
|
|
43
44
|
)
|
|
45
|
+
if self.temporal_mode is not None and self.temporal_mode not in {
|
|
46
|
+
"AGGREGATE",
|
|
47
|
+
"SNAPSHOT",
|
|
48
|
+
}:
|
|
49
|
+
raise ReportStoreValidationError(
|
|
50
|
+
"temporal_mode must be AGGREGATE, SNAPSHOT, or None"
|
|
51
|
+
)
|
|
52
|
+
if self.temporal_mode is not None and self.range_transform is not None:
|
|
53
|
+
raise ReportStoreValidationError(
|
|
54
|
+
"temporal_mode and range_transform cannot be declared together"
|
|
55
|
+
)
|
|
44
56
|
|
|
45
57
|
# Validate serializability while the declaration is still close to its caller.
|
|
46
58
|
canonical_json(self.to_dict())
|
|
@@ -54,6 +66,7 @@ class ReportDefinition:
|
|
|
54
66
|
binding: Dict[str, Any],
|
|
55
67
|
options: Dict[str, Any] = None,
|
|
56
68
|
range_transform: Dict[str, Any] = None,
|
|
69
|
+
temporal_mode: str = None,
|
|
57
70
|
):
|
|
58
71
|
return cls(
|
|
59
72
|
report_type=report_type,
|
|
@@ -64,6 +77,7 @@ class ReportDefinition:
|
|
|
64
77
|
range_transform=(
|
|
65
78
|
dict(range_transform) if range_transform is not None else None
|
|
66
79
|
),
|
|
80
|
+
temporal_mode=temporal_mode,
|
|
67
81
|
)
|
|
68
82
|
|
|
69
83
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -76,6 +90,8 @@ class ReportDefinition:
|
|
|
76
90
|
}
|
|
77
91
|
if self.range_transform is not None:
|
|
78
92
|
definition["range_transform"] = self.range_transform
|
|
93
|
+
if self.temporal_mode is not None:
|
|
94
|
+
definition["temporal_mode"] = self.temporal_mode
|
|
79
95
|
return definition
|
|
80
96
|
|
|
81
97
|
def to_json(self) -> str:
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
DONUT_RENDERER = "donut"
|
|
@@ -51,6 +52,7 @@ class DonutChartReport(TypedCollectionReport):
|
|
|
51
52
|
category: Dimension,
|
|
52
53
|
value: Measure,
|
|
53
54
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
55
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
54
56
|
):
|
|
55
57
|
if not isinstance(category, Dimension):
|
|
56
58
|
raise ReportStoreValidationError(
|
|
@@ -80,12 +82,16 @@ class DonutChartReport(TypedCollectionReport):
|
|
|
80
82
|
normalized_range_transform = normalize_range_transform(
|
|
81
83
|
range_transform, granularity, (value,), "Donut"
|
|
82
84
|
)
|
|
85
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
86
|
+
temporal_mode, granularity, (value,), "Donut", range_transform
|
|
87
|
+
)
|
|
83
88
|
definition = ReportDefinition.create(
|
|
84
89
|
report_type="donut",
|
|
85
90
|
contract_version=DONUT_CONTRACT_VERSION,
|
|
86
91
|
fields=(category, value),
|
|
87
92
|
binding={"category": category.name, "value": value.name},
|
|
88
93
|
range_transform=normalized_range_transform,
|
|
94
|
+
temporal_mode=normalized_temporal_mode,
|
|
89
95
|
)
|
|
90
96
|
report = store._declare_typed_report(
|
|
91
97
|
key=key,
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
LINE_RENDERER = "line"
|
|
@@ -46,6 +47,7 @@ class LineChartReport(TypedCollectionReport):
|
|
|
46
47
|
values: Iterable[Measure],
|
|
47
48
|
series: Optional[Dimension] = None,
|
|
48
49
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
50
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
49
51
|
):
|
|
50
52
|
if granularity == "NONE":
|
|
51
53
|
raise ReportStoreValidationError("Line granularity must be DAY or MONTH")
|
|
@@ -55,6 +57,13 @@ class LineChartReport(TypedCollectionReport):
|
|
|
55
57
|
normalized_range_transform = normalize_range_transform(
|
|
56
58
|
range_transform, granularity, normalized_values, "Line"
|
|
57
59
|
)
|
|
60
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
61
|
+
temporal_mode,
|
|
62
|
+
granularity,
|
|
63
|
+
normalized_values,
|
|
64
|
+
"Line",
|
|
65
|
+
range_transform,
|
|
66
|
+
)
|
|
58
67
|
definition = ReportDefinition.create(
|
|
59
68
|
report_type="line",
|
|
60
69
|
contract_version=LINE_CONTRACT_VERSION,
|
|
@@ -65,6 +74,7 @@ class LineChartReport(TypedCollectionReport):
|
|
|
65
74
|
"values": [value.name for value in normalized_values],
|
|
66
75
|
},
|
|
67
76
|
range_transform=normalized_range_transform,
|
|
77
|
+
temporal_mode=normalized_temporal_mode,
|
|
68
78
|
)
|
|
69
79
|
report = store._declare_typed_report(
|
|
70
80
|
key=key,
|
|
@@ -31,9 +31,15 @@ class ReportStoreManager:
|
|
|
31
31
|
self._sleep = sleep_fn
|
|
32
32
|
self._stores = {}
|
|
33
33
|
|
|
34
|
-
def create(
|
|
34
|
+
def create(
|
|
35
|
+
self,
|
|
36
|
+
file_name: str,
|
|
37
|
+
shared: bool = False,
|
|
38
|
+
tracker_name: str | None = None,
|
|
39
|
+
) -> ReportStore:
|
|
35
40
|
"""Start a fresh local file without performing a download."""
|
|
36
41
|
self._validate_file_name(file_name)
|
|
42
|
+
tracker_name = self._resolve_tracker_name(tracker_name)
|
|
37
43
|
session_key = (file_name, shared)
|
|
38
44
|
self._assert_not_open(session_key)
|
|
39
45
|
path = self._new_local_path(file_name)
|
|
@@ -41,18 +47,29 @@ class ReportStoreManager:
|
|
|
41
47
|
path,
|
|
42
48
|
file_name,
|
|
43
49
|
shared,
|
|
50
|
+
tracker_name=tracker_name,
|
|
44
51
|
transport=self._transport,
|
|
45
52
|
cleanup_on_close=True,
|
|
46
53
|
)
|
|
47
54
|
self._stores[session_key] = store
|
|
48
55
|
return store
|
|
49
56
|
|
|
50
|
-
def open(
|
|
57
|
+
def open(
|
|
58
|
+
self,
|
|
59
|
+
file_name: str,
|
|
60
|
+
shared: bool = False,
|
|
61
|
+
tracker_name: str | None = None,
|
|
62
|
+
) -> ReportStore:
|
|
51
63
|
"""Resume remote state, or create only after an authoritative 404."""
|
|
52
64
|
self._validate_file_name(file_name)
|
|
65
|
+
tracker_name = self._resolve_tracker_name(tracker_name)
|
|
53
66
|
session_key = (file_name, shared)
|
|
54
67
|
existing = self._stores.get(session_key)
|
|
55
68
|
if existing is not None:
|
|
69
|
+
if existing.tracker_name != tracker_name:
|
|
70
|
+
raise ReportStoreLocalIOError(
|
|
71
|
+
"This report store is already open with a different tracker_name"
|
|
72
|
+
)
|
|
56
73
|
return existing
|
|
57
74
|
|
|
58
75
|
path = self._new_local_path(file_name)
|
|
@@ -64,6 +81,7 @@ class ReportStoreManager:
|
|
|
64
81
|
downloaded_path,
|
|
65
82
|
file_name,
|
|
66
83
|
shared,
|
|
84
|
+
tracker_name=tracker_name,
|
|
67
85
|
transport=self._transport,
|
|
68
86
|
cleanup_on_close=True,
|
|
69
87
|
)
|
|
@@ -75,6 +93,7 @@ class ReportStoreManager:
|
|
|
75
93
|
path,
|
|
76
94
|
file_name,
|
|
77
95
|
shared,
|
|
96
|
+
tracker_name=tracker_name,
|
|
78
97
|
transport=self._transport,
|
|
79
98
|
cleanup_on_close=True,
|
|
80
99
|
)
|
|
@@ -138,6 +157,15 @@ class ReportStoreManager:
|
|
|
138
157
|
"This report store is already open for the current service run"
|
|
139
158
|
)
|
|
140
159
|
|
|
160
|
+
def _resolve_tracker_name(self, tracker_name: str | None) -> str:
|
|
161
|
+
if tracker_name is None:
|
|
162
|
+
tracker_name = self._service.request.service_name
|
|
163
|
+
if not isinstance(tracker_name, str) or not tracker_name.strip():
|
|
164
|
+
raise ReportStoreLocalIOError(
|
|
165
|
+
"tracker_name must be a non-empty string"
|
|
166
|
+
)
|
|
167
|
+
return tracker_name.strip()
|
|
168
|
+
|
|
141
169
|
@staticmethod
|
|
142
170
|
def _validate_file_name(file_name: str) -> None:
|
|
143
171
|
if not isinstance(file_name, str) or not file_name.strip():
|
|
@@ -7,6 +7,7 @@ from pam.reports.errors import ReportStoreValidationError
|
|
|
7
7
|
from pam.reports.fields import Measure
|
|
8
8
|
from pam.reports.record import ReportRecord, build_record_key, validate_partition_key
|
|
9
9
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
10
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
METRIC_RENDERER = "metric"
|
|
@@ -31,6 +32,7 @@ class MetricReport:
|
|
|
31
32
|
granularity: str,
|
|
32
33
|
metrics: Iterable[Measure],
|
|
33
34
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
35
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
34
36
|
):
|
|
35
37
|
if isinstance(metrics, (str, bytes)):
|
|
36
38
|
raise ReportStoreValidationError("metrics must contain Measure declarations")
|
|
@@ -50,6 +52,13 @@ class MetricReport:
|
|
|
50
52
|
normalized_range_transform = normalize_range_transform(
|
|
51
53
|
range_transform, granularity, normalized_metrics, "Metric"
|
|
52
54
|
)
|
|
55
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
56
|
+
temporal_mode,
|
|
57
|
+
granularity,
|
|
58
|
+
normalized_metrics,
|
|
59
|
+
"Metric",
|
|
60
|
+
range_transform,
|
|
61
|
+
)
|
|
53
62
|
|
|
54
63
|
definition = ReportDefinition.create(
|
|
55
64
|
report_type="metric",
|
|
@@ -57,6 +66,7 @@ class MetricReport:
|
|
|
57
66
|
fields=normalized_metrics,
|
|
58
67
|
binding={"metrics": [metric.name for metric in normalized_metrics]},
|
|
59
68
|
range_transform=normalized_range_transform,
|
|
69
|
+
temporal_mode=normalized_temporal_mode,
|
|
60
70
|
)
|
|
61
71
|
report = store._declare_typed_report(
|
|
62
72
|
key=key,
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
PIE_RENDERER = "pie"
|
|
@@ -51,6 +52,7 @@ class PieChartReport(TypedCollectionReport):
|
|
|
51
52
|
category: Dimension,
|
|
52
53
|
value: Measure,
|
|
53
54
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
55
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
54
56
|
):
|
|
55
57
|
if not isinstance(category, Dimension):
|
|
56
58
|
raise ReportStoreValidationError(
|
|
@@ -80,12 +82,16 @@ class PieChartReport(TypedCollectionReport):
|
|
|
80
82
|
normalized_range_transform = normalize_range_transform(
|
|
81
83
|
range_transform, granularity, (value,), "Pie"
|
|
82
84
|
)
|
|
85
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
86
|
+
temporal_mode, granularity, (value,), "Pie", range_transform
|
|
87
|
+
)
|
|
83
88
|
definition = ReportDefinition.create(
|
|
84
89
|
report_type="pie",
|
|
85
90
|
contract_version=PIE_CONTRACT_VERSION,
|
|
86
91
|
fields=(category, value),
|
|
87
92
|
binding={"category": category.name, "value": value.name},
|
|
88
93
|
range_transform=normalized_range_transform,
|
|
94
|
+
temporal_mode=normalized_temporal_mode,
|
|
89
95
|
)
|
|
90
96
|
report = store._declare_typed_report(
|
|
91
97
|
key=key,
|
|
@@ -8,6 +8,7 @@ from pam.reports.definition import ReportDefinition
|
|
|
8
8
|
from pam.reports.errors import ReportStoreValidationError
|
|
9
9
|
from pam.reports.fields import Dimension, Measure
|
|
10
10
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
11
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
POLAR_AREA_RENDERER = "polar_area"
|
|
@@ -52,6 +53,7 @@ class PolarAreaChartReport(TypedCollectionReport):
|
|
|
52
53
|
category: Dimension,
|
|
53
54
|
value: Measure,
|
|
54
55
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
56
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
55
57
|
):
|
|
56
58
|
if not isinstance(category, Dimension):
|
|
57
59
|
raise ReportStoreValidationError(
|
|
@@ -81,12 +83,16 @@ class PolarAreaChartReport(TypedCollectionReport):
|
|
|
81
83
|
normalized_range_transform = normalize_range_transform(
|
|
82
84
|
range_transform, granularity, (value,), "Polar Area"
|
|
83
85
|
)
|
|
86
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
87
|
+
temporal_mode, granularity, (value,), "Polar Area", range_transform
|
|
88
|
+
)
|
|
84
89
|
definition = ReportDefinition.create(
|
|
85
90
|
report_type="polar_area",
|
|
86
91
|
contract_version=POLAR_AREA_CONTRACT_VERSION,
|
|
87
92
|
fields=(category, value),
|
|
88
93
|
binding={"category": category.name, "value": value.name},
|
|
89
94
|
range_transform=normalized_range_transform,
|
|
95
|
+
temporal_mode=normalized_temporal_mode,
|
|
90
96
|
)
|
|
91
97
|
report = store._declare_typed_report(
|
|
92
98
|
key=key,
|
|
@@ -9,6 +9,7 @@ from pam.reports.errors import ReportStoreValidationError
|
|
|
9
9
|
from pam.reports.fields import Measure
|
|
10
10
|
from pam.reports.record import ReportRecord, build_record_key, validate_partition_key
|
|
11
11
|
from pam.reports.range_transform import TimeRangeTransform, normalize_range_transform
|
|
12
|
+
from pam.reports.temporal import TemporalMode, normalize_temporal_mode
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
RADIAL_BAR_RENDERER = "radial_bar"
|
|
@@ -32,6 +33,7 @@ class RadialBarChartReport:
|
|
|
32
33
|
cls, store, key: str, granularity: str, metrics: Iterable[Measure],
|
|
33
34
|
min_value: float, max_value: float,
|
|
34
35
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
36
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
35
37
|
):
|
|
36
38
|
try:
|
|
37
39
|
normalized_metrics = tuple(metrics)
|
|
@@ -66,6 +68,13 @@ class RadialBarChartReport:
|
|
|
66
68
|
normalized_transform = normalize_range_transform(
|
|
67
69
|
range_transform, granularity, normalized_metrics, "Radial Bar"
|
|
68
70
|
)
|
|
71
|
+
normalized_temporal_mode = normalize_temporal_mode(
|
|
72
|
+
temporal_mode,
|
|
73
|
+
granularity,
|
|
74
|
+
normalized_metrics,
|
|
75
|
+
"Radial Bar",
|
|
76
|
+
range_transform,
|
|
77
|
+
)
|
|
69
78
|
definition = ReportDefinition.create(
|
|
70
79
|
report_type="radial_bar",
|
|
71
80
|
contract_version=RADIAL_BAR_CONTRACT_VERSION,
|
|
@@ -73,6 +82,7 @@ class RadialBarChartReport:
|
|
|
73
82
|
binding={"metrics": [metric.name for metric in normalized_metrics]},
|
|
74
83
|
options={"min_value": min_value, "max_value": max_value},
|
|
75
84
|
range_transform=normalized_transform,
|
|
85
|
+
temporal_mode=normalized_temporal_mode,
|
|
76
86
|
)
|
|
77
87
|
report = store._declare_typed_report(
|
|
78
88
|
key, RADIAL_BAR_RENDERER, RADIAL_BAR_SCHEMA, granularity,
|
|
@@ -45,6 +45,7 @@ from pam.reports.schema import (
|
|
|
45
45
|
STORE_VERSION,
|
|
46
46
|
)
|
|
47
47
|
from pam.reports.table import TableReport
|
|
48
|
+
from pam.reports.temporal import TemporalMode
|
|
48
49
|
from pam.reports.treemap import TreemapReport
|
|
49
50
|
|
|
50
51
|
|
|
@@ -60,12 +61,14 @@ class ReportStore:
|
|
|
60
61
|
path: Path,
|
|
61
62
|
file_name: str,
|
|
62
63
|
shared: bool,
|
|
64
|
+
tracker_name: str = "",
|
|
63
65
|
transport=None,
|
|
64
66
|
cleanup_on_close: bool = False,
|
|
65
67
|
):
|
|
66
68
|
self.path = Path(path)
|
|
67
69
|
self.file_name = file_name
|
|
68
70
|
self.shared = shared
|
|
71
|
+
self.tracker_name = tracker_name
|
|
69
72
|
self._transport = transport
|
|
70
73
|
self._cleanup_on_close = cleanup_on_close
|
|
71
74
|
self._lock = threading.RLock()
|
|
@@ -87,6 +90,7 @@ class ReportStore:
|
|
|
87
90
|
path: Path,
|
|
88
91
|
file_name: str,
|
|
89
92
|
shared: bool,
|
|
93
|
+
tracker_name: str = "",
|
|
90
94
|
transport=None,
|
|
91
95
|
cleanup_on_close: bool = False,
|
|
92
96
|
):
|
|
@@ -97,7 +101,14 @@ class ReportStore:
|
|
|
97
101
|
except OSError as exc:
|
|
98
102
|
raise ReportStoreLocalIOError(str(exc)) from exc
|
|
99
103
|
|
|
100
|
-
store = cls(
|
|
104
|
+
store = cls(
|
|
105
|
+
path,
|
|
106
|
+
file_name,
|
|
107
|
+
shared,
|
|
108
|
+
tracker_name=tracker_name,
|
|
109
|
+
transport=transport,
|
|
110
|
+
cleanup_on_close=cleanup_on_close,
|
|
111
|
+
)
|
|
101
112
|
try:
|
|
102
113
|
with store._connection:
|
|
103
114
|
store._connection.executescript(CREATE_SCHEMA_SQL)
|
|
@@ -116,10 +127,18 @@ class ReportStore:
|
|
|
116
127
|
path: Path,
|
|
117
128
|
file_name: str,
|
|
118
129
|
shared: bool,
|
|
130
|
+
tracker_name: str = "",
|
|
119
131
|
transport=None,
|
|
120
132
|
cleanup_on_close: bool = False,
|
|
121
133
|
):
|
|
122
|
-
store = cls(
|
|
134
|
+
store = cls(
|
|
135
|
+
path,
|
|
136
|
+
file_name,
|
|
137
|
+
shared,
|
|
138
|
+
tracker_name=tracker_name,
|
|
139
|
+
transport=transport,
|
|
140
|
+
cleanup_on_close=cleanup_on_close,
|
|
141
|
+
)
|
|
123
142
|
try:
|
|
124
143
|
store._migrate_compatible_schema()
|
|
125
144
|
store.validate()
|
|
@@ -137,9 +156,10 @@ class ReportStore:
|
|
|
137
156
|
key: str,
|
|
138
157
|
granularity: str,
|
|
139
158
|
columns: Iterable[Field],
|
|
159
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
140
160
|
) -> TableReport:
|
|
141
161
|
"""Declare a typed Table report."""
|
|
142
|
-
return TableReport.declare(self, key, granularity, columns)
|
|
162
|
+
return TableReport.declare(self, key, granularity, columns, temporal_mode)
|
|
143
163
|
|
|
144
164
|
def metric(
|
|
145
165
|
self,
|
|
@@ -147,10 +167,11 @@ class ReportStore:
|
|
|
147
167
|
granularity: str,
|
|
148
168
|
metrics: Iterable[Measure],
|
|
149
169
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
170
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
150
171
|
) -> MetricReport:
|
|
151
172
|
"""Declare a typed Metric report."""
|
|
152
173
|
return MetricReport.declare(
|
|
153
|
-
self, key, granularity, metrics, range_transform
|
|
174
|
+
self, key, granularity, metrics, range_transform, temporal_mode
|
|
154
175
|
)
|
|
155
176
|
|
|
156
177
|
def line_chart(
|
|
@@ -160,10 +181,11 @@ class ReportStore:
|
|
|
160
181
|
values: Iterable[Measure],
|
|
161
182
|
series: Optional[Dimension] = None,
|
|
162
183
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
184
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
163
185
|
) -> LineChartReport:
|
|
164
186
|
"""Declare a typed Line Chart report."""
|
|
165
187
|
return LineChartReport.declare(
|
|
166
|
-
self, key, granularity, values, series, range_transform
|
|
188
|
+
self, key, granularity, values, series, range_transform, temporal_mode
|
|
167
189
|
)
|
|
168
190
|
|
|
169
191
|
def area_chart(
|
|
@@ -174,10 +196,18 @@ class ReportStore:
|
|
|
174
196
|
series: Optional[Dimension] = None,
|
|
175
197
|
stacked: bool = False,
|
|
176
198
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
199
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
177
200
|
) -> AreaChartReport:
|
|
178
201
|
"""Declare a typed Area Chart report."""
|
|
179
202
|
return AreaChartReport.declare(
|
|
180
|
-
self,
|
|
203
|
+
self,
|
|
204
|
+
key,
|
|
205
|
+
granularity,
|
|
206
|
+
values,
|
|
207
|
+
series,
|
|
208
|
+
stacked,
|
|
209
|
+
range_transform,
|
|
210
|
+
temporal_mode,
|
|
181
211
|
)
|
|
182
212
|
|
|
183
213
|
def bar_chart(
|
|
@@ -190,6 +220,7 @@ class ReportStore:
|
|
|
190
220
|
orientation: str = "vertical",
|
|
191
221
|
stacked: bool = False,
|
|
192
222
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
223
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
193
224
|
) -> BarChartReport:
|
|
194
225
|
"""Declare a typed Bar/Column Chart report."""
|
|
195
226
|
return BarChartReport.declare(
|
|
@@ -202,6 +233,7 @@ class ReportStore:
|
|
|
202
233
|
orientation,
|
|
203
234
|
stacked,
|
|
204
235
|
range_transform,
|
|
236
|
+
temporal_mode,
|
|
205
237
|
)
|
|
206
238
|
|
|
207
239
|
def pie_chart(
|
|
@@ -211,10 +243,11 @@ class ReportStore:
|
|
|
211
243
|
category: Dimension,
|
|
212
244
|
value: Measure,
|
|
213
245
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
246
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
214
247
|
) -> PieChartReport:
|
|
215
248
|
"""Declare a typed Pie Chart report."""
|
|
216
249
|
return PieChartReport.declare(
|
|
217
|
-
self, key, granularity, category, value, range_transform
|
|
250
|
+
self, key, granularity, category, value, range_transform, temporal_mode
|
|
218
251
|
)
|
|
219
252
|
|
|
220
253
|
def donut_chart(
|
|
@@ -224,10 +257,11 @@ class ReportStore:
|
|
|
224
257
|
category: Dimension,
|
|
225
258
|
value: Measure,
|
|
226
259
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
260
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
227
261
|
) -> DonutChartReport:
|
|
228
262
|
"""Declare a typed Donut Chart report."""
|
|
229
263
|
return DonutChartReport.declare(
|
|
230
|
-
self, key, granularity, category, value, range_transform
|
|
264
|
+
self, key, granularity, category, value, range_transform, temporal_mode
|
|
231
265
|
)
|
|
232
266
|
|
|
233
267
|
def radial_bar_chart(
|
|
@@ -238,10 +272,18 @@ class ReportStore:
|
|
|
238
272
|
min_value: float,
|
|
239
273
|
max_value: float,
|
|
240
274
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
275
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
241
276
|
) -> RadialBarChartReport:
|
|
242
277
|
"""Declare a typed Radial Bar Chart report."""
|
|
243
278
|
return RadialBarChartReport.declare(
|
|
244
|
-
self,
|
|
279
|
+
self,
|
|
280
|
+
key,
|
|
281
|
+
granularity,
|
|
282
|
+
metrics,
|
|
283
|
+
min_value,
|
|
284
|
+
max_value,
|
|
285
|
+
range_transform,
|
|
286
|
+
temporal_mode,
|
|
245
287
|
)
|
|
246
288
|
|
|
247
289
|
def polar_area_chart(
|
|
@@ -251,10 +293,11 @@ class ReportStore:
|
|
|
251
293
|
category: Dimension,
|
|
252
294
|
value: Measure,
|
|
253
295
|
range_transform: Optional[TimeRangeTransform] = None,
|
|
296
|
+
temporal_mode: Optional[TemporalMode] = None,
|
|
254
297
|
) -> PolarAreaChartReport:
|
|
255
298
|
"""Declare a typed Polar Area Chart report."""
|
|
256
299
|
return PolarAreaChartReport.declare(
|
|
257
|
-
self, key, granularity, category, value, range_transform
|
|
300
|
+
self, key, granularity, category, value, range_transform, temporal_mode
|
|
258
301
|
)
|
|
259
302
|
|
|
260
303
|
def scatter_chart(
|