pam-python 0.2.18__tar.gz → 0.2.19__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.18/pam_python.egg-info → pam_python-0.2.19}/PKG-INFO +1 -1
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/__init__.py +1 -1
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/store.py +5 -2
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/table.py +54 -2
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/REPORTS-th.md +3 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/REPORTS.md +7 -1
- {pam_python-0.2.18 → pam_python-0.2.19/pam_python.egg-info}/PKG-INFO +1 -1
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_table_report.py +39 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/LICENSE.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/README.md +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/api.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/cli.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/datetime_utils.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/interface_task_manager.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/logger.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/models/__init__.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/models/data_input_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/models/request_command.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/models/sqlite_download.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/__init__.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/area.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/axis.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/bar.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/box_plot.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/bubble.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/candlestick.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/collection.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/definition.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/donut.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/errors.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/fields.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/heatmap.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/line.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/manager.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/metric.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/pie.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/polar_area.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/radar.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/radial_bar.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/range_area.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/range_bar.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/range_transform.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/record.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/scatter.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/schema.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/temporal.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/transport.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/reports/treemap.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/request_file_format.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/result_batch_uploader.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/server.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/service.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/sqlite.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/task_manager.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/temp_file_utils.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/buildcmd/pamb +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/buildcmd/pamb-base.sh +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/AGENT.md +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/dockerignore.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/gitignore.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/main.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/pylintrc.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/run_unit_test.bat +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/run_unit_test.ps1 +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/init/run_unit_test.sh +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/project/pip/Dockerfile +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/project/pip/requirements.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/project/uv/Dockerfile +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/project/uv/pyproject.toml +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/project/uv/python-version +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/common/service.yaml +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/pandas/functions.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/pandas/service.test.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/pandas/service_class.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/polars/functions.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/polars/service.test.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/templates/service/polars/service_class.tmpl +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/tester_task.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam/utils.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam_python.egg-info/SOURCES.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam_python.egg-info/dependency_links.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam_python.egg-info/entry_points.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam_python.egg-info/requires.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pam_python.egg-info/top_level.txt +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/pyproject.toml +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/setup.cfg +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_api.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_cli_templates.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_data_error_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_data_input_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_data_timeout_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_datetime_utils.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_range_transform.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_report_store.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_request_data_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_result_batch_uploader.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_service_dataframe_output.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_sqlite_download_v2.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_task_manager_upload_token.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_temp_file_utils.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_temporal_mode.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_area_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_bar_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_box_plot_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_bubble_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_candlestick_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_donut_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_heatmap_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_line_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_metric_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_pie_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_polar_area_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_radar_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_radial_bar_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_range_area_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_range_bar_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_scatter_report.py +0 -0
- {pam_python-0.2.18 → pam_python-0.2.19}/tests/test_typed_treemap_report.py +0 -0
|
@@ -6,7 +6,7 @@ import sqlite3
|
|
|
6
6
|
import threading
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Iterable, List, Optional, Set
|
|
9
|
+
from typing import Any, Iterable, List, Mapping, Optional, Set
|
|
10
10
|
|
|
11
11
|
from pam.datetime_utils import format_pam_datetime
|
|
12
12
|
from pam.reports.errors import (
|
|
@@ -157,9 +157,12 @@ class ReportStore:
|
|
|
157
157
|
granularity: str,
|
|
158
158
|
columns: Iterable[Field],
|
|
159
159
|
temporal_mode: Optional[TemporalMode] = None,
|
|
160
|
+
row_order: Optional[Mapping[str, Iterable[Any]]] = None,
|
|
160
161
|
) -> TableReport:
|
|
161
162
|
"""Declare a typed Table report."""
|
|
162
|
-
return TableReport.declare(
|
|
163
|
+
return TableReport.declare(
|
|
164
|
+
self, key, granularity, columns, temporal_mode, row_order
|
|
165
|
+
)
|
|
163
166
|
|
|
164
167
|
def metric(
|
|
165
168
|
self,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Typed Table report public API."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence
|
|
3
|
+
from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple
|
|
4
4
|
|
|
5
|
+
from pam.reports.axis import normalize_value_order
|
|
5
6
|
from pam.reports.definition import ReportDefinition
|
|
6
7
|
from pam.reports.errors import ReportStoreValidationError
|
|
7
8
|
from pam.reports.fields import Dimension, Field, Measure
|
|
@@ -22,6 +23,7 @@ class TableReport:
|
|
|
22
23
|
report,
|
|
23
24
|
granularity: str,
|
|
24
25
|
columns: Sequence[Field],
|
|
26
|
+
row_order: Mapping[str, Tuple[Any, ...]],
|
|
25
27
|
):
|
|
26
28
|
self._report = report
|
|
27
29
|
self.key = report.key
|
|
@@ -33,6 +35,7 @@ class TableReport:
|
|
|
33
35
|
self._measures = tuple(
|
|
34
36
|
column for column in self.columns if isinstance(column, Measure)
|
|
35
37
|
)
|
|
38
|
+
self.row_order = dict(row_order)
|
|
36
39
|
|
|
37
40
|
@classmethod
|
|
38
41
|
def declare(
|
|
@@ -42,6 +45,7 @@ class TableReport:
|
|
|
42
45
|
granularity: str,
|
|
43
46
|
columns: Iterable[Field],
|
|
44
47
|
temporal_mode: Optional[TemporalMode] = None,
|
|
48
|
+
row_order: Optional[Mapping[str, Iterable[Any]]] = None,
|
|
45
49
|
):
|
|
46
50
|
if isinstance(columns, (str, bytes)):
|
|
47
51
|
raise ReportStoreValidationError("columns must contain field declarations")
|
|
@@ -71,12 +75,21 @@ class TableReport:
|
|
|
71
75
|
normalized_temporal_mode = normalize_temporal_mode(
|
|
72
76
|
temporal_mode, granularity, measures, "Table"
|
|
73
77
|
)
|
|
78
|
+
normalized_row_order = cls._normalize_row_order(
|
|
79
|
+
normalized_columns, row_order
|
|
80
|
+
)
|
|
74
81
|
|
|
75
82
|
definition = ReportDefinition.create(
|
|
76
83
|
report_type="table",
|
|
77
84
|
contract_version=TABLE_CONTRACT_VERSION,
|
|
78
85
|
fields=normalized_columns,
|
|
79
86
|
binding={"columns": [column.name for column in normalized_columns]},
|
|
87
|
+
options={
|
|
88
|
+
"row_order": {
|
|
89
|
+
name: list(values)
|
|
90
|
+
for name, values in normalized_row_order.items()
|
|
91
|
+
}
|
|
92
|
+
} if normalized_row_order else None,
|
|
80
93
|
temporal_mode=normalized_temporal_mode,
|
|
81
94
|
)
|
|
82
95
|
report = store._declare_typed_report(
|
|
@@ -86,7 +99,40 @@ class TableReport:
|
|
|
86
99
|
granularity=granularity,
|
|
87
100
|
definition_json=definition.to_json(),
|
|
88
101
|
)
|
|
89
|
-
return cls(report, granularity, normalized_columns)
|
|
102
|
+
return cls(report, granularity, normalized_columns, normalized_row_order)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _normalize_row_order(
|
|
106
|
+
columns: Sequence[Field],
|
|
107
|
+
row_order: Optional[Mapping[str, Iterable[Any]]],
|
|
108
|
+
) -> Dict[str, Tuple[Any, ...]]:
|
|
109
|
+
if row_order is None:
|
|
110
|
+
return {}
|
|
111
|
+
if not isinstance(row_order, Mapping) or not row_order:
|
|
112
|
+
raise ReportStoreValidationError(
|
|
113
|
+
"Table row_order must be a non-empty mapping of dimension names to values"
|
|
114
|
+
)
|
|
115
|
+
dimensions = {
|
|
116
|
+
column.name: column
|
|
117
|
+
for column in columns
|
|
118
|
+
if isinstance(column, Dimension)
|
|
119
|
+
}
|
|
120
|
+
unknown = set(row_order) - set(dimensions)
|
|
121
|
+
if unknown:
|
|
122
|
+
raise ReportStoreValidationError(
|
|
123
|
+
f"Table row_order contains unknown dimension fields: {sorted(unknown)}"
|
|
124
|
+
)
|
|
125
|
+
normalized = {}
|
|
126
|
+
for name, values in row_order.items():
|
|
127
|
+
order = normalize_value_order(
|
|
128
|
+
"Table", f"row_order['{name}']", dimensions[name], values
|
|
129
|
+
)
|
|
130
|
+
if order is None:
|
|
131
|
+
raise ReportStoreValidationError(
|
|
132
|
+
f"Table row_order['{name}'] must be non-empty"
|
|
133
|
+
)
|
|
134
|
+
normalized[name] = order
|
|
135
|
+
return normalized
|
|
90
136
|
|
|
91
137
|
def append_rows(
|
|
92
138
|
self,
|
|
@@ -241,4 +287,10 @@ class TableReport:
|
|
|
241
287
|
raise ReportStoreValidationError(
|
|
242
288
|
f"Report '{self.key}' row {index} must contain a dimension value"
|
|
243
289
|
)
|
|
290
|
+
for name, values in self.row_order.items():
|
|
291
|
+
if dimensions.get(name) not in values:
|
|
292
|
+
raise ReportStoreValidationError(
|
|
293
|
+
f"Table row {index} value '{dimensions.get(name)}' is not declared "
|
|
294
|
+
f"in row_order['{name}']"
|
|
295
|
+
)
|
|
244
296
|
return normalized
|
|
@@ -399,6 +399,7 @@ table = store.table(
|
|
|
399
399
|
Dimension("branch", label="สาขา", value_type="string"),
|
|
400
400
|
Measure("members", label="สมาชิก", value_type="integer", unit="คน"),
|
|
401
401
|
],
|
|
402
|
+
row_order={"branch": ["กรุงเทพฯ", "เชียงใหม่", "รวม"]},
|
|
402
403
|
)
|
|
403
404
|
|
|
404
405
|
table.append_rows(
|
|
@@ -413,6 +414,8 @@ table.append_rows(
|
|
|
413
414
|
- Required column ต้องมีครบและค่าต้องตรงชนิด
|
|
414
415
|
- Row ใน Report เดียวกันห้ามมี generated identity ซ้ำ
|
|
415
416
|
- CMS ใช้ลำดับ column จาก definition ไม่ใช่ row แรก
|
|
417
|
+
- `row_order` กำหนดลำดับค่าของ Dimension ได้ โดยค่าที่เขียนต้องอยู่ในรายการทั้งหมด
|
|
418
|
+
เช่น วาง `รวม` เป็นตัวสุดท้ายเพื่อให้อยู่ล่างสุดเสมอ
|
|
416
419
|
- Partition เป็น metadata สำหรับ range/order ไม่ถูกเพิ่มเป็น Day/Month column พิเศษ
|
|
417
420
|
- `remove_rows` รับเฉพาะ Dimension identity; Measure ไม่ใช่ identity
|
|
418
421
|
|
|
@@ -558,11 +558,17 @@ table = store.table(
|
|
|
558
558
|
unit="THB",
|
|
559
559
|
),
|
|
560
560
|
],
|
|
561
|
+
row_order={
|
|
562
|
+
"branch": ["Bangkok", "Chiang Mai", "Total"],
|
|
563
|
+
},
|
|
561
564
|
)
|
|
562
565
|
```
|
|
563
566
|
|
|
564
567
|
Column order is the declaration order. At least one dimension is required. By
|
|
565
|
-
default, all dimension values form the stable row identity.
|
|
568
|
+
default, all dimension values form the stable row identity. Optional `row_order`
|
|
569
|
+
maps Dimension names to their complete display order. Every written value for an
|
|
570
|
+
ordered Dimension must appear in that list; place values such as `Total` last when
|
|
571
|
+
they must remain at the bottom of the table.
|
|
566
572
|
|
|
567
573
|
## Writing rows
|
|
568
574
|
|
|
@@ -105,6 +105,45 @@ class TypedTableReportTest(unittest.TestCase):
|
|
|
105
105
|
with self.assertRaises(ReportStoreValidationError):
|
|
106
106
|
table.delete_partition("2026-01")
|
|
107
107
|
|
|
108
|
+
def test_explicit_row_order(self):
|
|
109
|
+
table = self.store.table(
|
|
110
|
+
key="ordered-table",
|
|
111
|
+
granularity="NONE",
|
|
112
|
+
columns=[
|
|
113
|
+
Dimension("segment", label="Segment", value_type="string"),
|
|
114
|
+
Measure("customers", label="Customers", value_type="integer"),
|
|
115
|
+
],
|
|
116
|
+
row_order={"segment": ["New", "VIP", "Total"]},
|
|
117
|
+
)
|
|
118
|
+
definition = json.loads(
|
|
119
|
+
self.store._connection.execute(
|
|
120
|
+
"SELECT definition_json FROM report_definitions WHERE report_key = ?",
|
|
121
|
+
("ordered-table",),
|
|
122
|
+
).fetchone()[0]
|
|
123
|
+
)
|
|
124
|
+
self.assertEqual(
|
|
125
|
+
{"segment": ["New", "VIP", "Total"]},
|
|
126
|
+
definition["options"]["row_order"],
|
|
127
|
+
)
|
|
128
|
+
table.append_rows([
|
|
129
|
+
{"segment": "VIP", "customers": 2},
|
|
130
|
+
{"segment": "Total", "customers": 3},
|
|
131
|
+
])
|
|
132
|
+
with self.assertRaisesRegex(ReportStoreValidationError, "row_order"):
|
|
133
|
+
table.append_rows([{"segment": "Unknown", "customers": 1}])
|
|
134
|
+
|
|
135
|
+
def test_row_order_rejects_unknown_dimension(self):
|
|
136
|
+
with self.assertRaisesRegex(ReportStoreValidationError, "unknown dimension"):
|
|
137
|
+
self.store.table(
|
|
138
|
+
key="invalid-order",
|
|
139
|
+
granularity="NONE",
|
|
140
|
+
columns=[
|
|
141
|
+
Dimension("segment", label="Segment", value_type="string"),
|
|
142
|
+
Measure("customers", label="Customers", value_type="integer"),
|
|
143
|
+
],
|
|
144
|
+
row_order={"customers": [1, 2]},
|
|
145
|
+
)
|
|
146
|
+
|
|
108
147
|
def test_public_fixture_generation(self):
|
|
109
148
|
fixture = Path(self.temp_dir.name) / "table.sqlite"
|
|
110
149
|
pointer = generate_table_contract_fixture(fixture)
|
|
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
|
|
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
|
|
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
|