pam-python 0.2.16__tar.gz → 0.2.17__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.16/pam_python.egg-info → pam_python-0.2.17}/PKG-INFO +1 -1
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/__init__.py +1 -1
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/models/data_input_v2.py +19 -6
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/AGENT.md +5 -0
- {pam_python-0.2.16 → pam_python-0.2.17/pam_python.egg-info}/PKG-INFO +1 -1
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_data_input_v2.py +24 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/LICENSE.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/README.md +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/api.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/cli.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/datetime_utils.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/interface_task_manager.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/logger.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/models/__init__.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/models/request_command.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/models/sqlite_download.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/__init__.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/area.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/axis.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/bar.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/box_plot.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/bubble.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/candlestick.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/collection.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/definition.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/donut.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/errors.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/fields.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/heatmap.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/line.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/manager.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/metric.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/pie.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/polar_area.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/radar.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/radial_bar.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/range_area.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/range_bar.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/range_transform.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/record.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/scatter.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/schema.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/store.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/table.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/temporal.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/transport.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/reports/treemap.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/request_file_format.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/result_batch_uploader.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/server.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/service.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/sqlite.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/task_manager.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/temp_file_utils.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/buildcmd/pamb +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/buildcmd/pamb-base.sh +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/REPORTS-th.md +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/REPORTS.md +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/dockerignore.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/gitignore.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/main.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/pylintrc.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/run_unit_test.bat +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/run_unit_test.ps1 +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/init/run_unit_test.sh +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/project/pip/Dockerfile +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/project/pip/requirements.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/project/uv/Dockerfile +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/project/uv/pyproject.toml +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/project/uv/python-version +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/common/service.yaml +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/pandas/functions.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/pandas/service.test.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/pandas/service_class.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/polars/functions.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/polars/service.test.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/templates/service/polars/service_class.tmpl +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/tester_task.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam/utils.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam_python.egg-info/SOURCES.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam_python.egg-info/dependency_links.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam_python.egg-info/entry_points.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam_python.egg-info/requires.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pam_python.egg-info/top_level.txt +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/pyproject.toml +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/setup.cfg +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_api.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_cli_templates.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_data_error_v2.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_data_timeout_v2.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_datetime_utils.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_range_transform.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_report_store.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_request_data_v2.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_result_batch_uploader.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_service_dataframe_output.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_sqlite_download_v2.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_task_manager_upload_token.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_temp_file_utils.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_temporal_mode.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_area_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_bar_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_box_plot_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_bubble_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_candlestick_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_donut_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_heatmap_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_line_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_metric_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_pie_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_polar_area_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_radar_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_radial_bar_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_range_area_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_range_bar_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_scatter_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_table_report.py +0 -0
- {pam_python-0.2.16 → pam_python-0.2.17}/tests/test_typed_treemap_report.py +0 -0
|
@@ -71,21 +71,34 @@ class DataInputV2Dataset:
|
|
|
71
71
|
for part in self.parts:
|
|
72
72
|
yield from part.iter_csv_rows()
|
|
73
73
|
|
|
74
|
-
def scan_polars(self) -> Any:
|
|
75
|
-
"""Return one lazy frame across all ordered CSV or Parquet parts.
|
|
74
|
+
def scan_polars(self, empty_schema: dict[str, Any] | None = None) -> Any:
|
|
75
|
+
"""Return one lazy frame across all ordered CSV or Parquet parts.
|
|
76
|
+
|
|
77
|
+
PAM represents a valid dataset with no rows as ``row_count=0`` and no
|
|
78
|
+
physical parts. In that case the plugin may provide its domain schema
|
|
79
|
+
so downstream computation can handle the empty input normally.
|
|
80
|
+
"""
|
|
76
81
|
pl = _import_polars()
|
|
77
82
|
if not self.parts:
|
|
78
|
-
|
|
83
|
+
schema = {"id": pl.String} if empty_schema is None else empty_schema
|
|
84
|
+
return pl.LazyFrame(schema=schema)
|
|
79
85
|
scans = [part.scan_polars() for part in self.parts]
|
|
80
86
|
if len(scans) == 1:
|
|
81
87
|
return scans[0]
|
|
82
88
|
return pl.concat(scans, how="diagonal_relaxed")
|
|
83
89
|
|
|
84
|
-
def read_pandas(self) -> Any:
|
|
85
|
-
"""Materialize and concatenate all verified parts using their metadata.
|
|
90
|
+
def read_pandas(self, empty_dtypes: dict[str, Any] | None = None) -> Any:
|
|
91
|
+
"""Materialize and concatenate all verified parts using their metadata.
|
|
92
|
+
|
|
93
|
+
``empty_dtypes`` is used only for a declared empty dataset, allowing
|
|
94
|
+
the plugin to retain its domain columns without fabricating data rows.
|
|
95
|
+
"""
|
|
86
96
|
pd = _import_pandas()
|
|
87
97
|
if not self.parts:
|
|
88
|
-
|
|
98
|
+
dtypes = {"id": "string"} if empty_dtypes is None else empty_dtypes
|
|
99
|
+
return pd.DataFrame(
|
|
100
|
+
{name: pd.Series(dtype=dtype) for name, dtype in dtypes.items()}
|
|
101
|
+
)
|
|
89
102
|
frames = [part.read_pandas() for part in self.parts]
|
|
90
103
|
if len(frames) == 1:
|
|
91
104
|
return frames[0]
|
|
@@ -234,6 +234,11 @@ result = calculate(population, profile_frame, purchase_frame).collect()
|
|
|
234
234
|
|
|
235
235
|
`scan_polars()` returns one lazy frame across ordered CSV or Parquet parts and
|
|
236
236
|
uses verified manifest format metadata. CSV-only code may use `iter_rows()`.
|
|
237
|
+
`row_count=0` with `parts=[]` is a valid empty dataset. The plugin decides the
|
|
238
|
+
business result for the page population; it must not treat the absence of event
|
|
239
|
+
rows as a transport error. If computation requires domain columns on an empty
|
|
240
|
+
frame, call `scan_polars(empty_schema=...)` or
|
|
241
|
+
`read_pandas(empty_dtypes=...)` with the plugin-owned schema.
|
|
237
242
|
The external customer key is `id`. The population file is authoritative: in
|
|
238
243
|
`contacts` mode, contacts with no event rows remain visible there. Use the
|
|
239
244
|
opaque `data.next` cursor for the next `_request_data_v2(...)` call and stop when
|
|
@@ -78,6 +78,30 @@ class DataInputV2Test(unittest.TestCase):
|
|
|
78
78
|
self.assertEqual(0, data_input.datasets[0].row_count)
|
|
79
79
|
self.assertEqual((), data_input.datasets[0].parts)
|
|
80
80
|
|
|
81
|
+
def test_empty_dataset_readers_use_plugin_owned_schema(self):
|
|
82
|
+
import polars as pl
|
|
83
|
+
|
|
84
|
+
manifest = self._valid_manifest()
|
|
85
|
+
manifest["datasets"][0]["row_count"] = 0
|
|
86
|
+
manifest["datasets"][0]["parts"] = []
|
|
87
|
+
self.dataset_path.unlink()
|
|
88
|
+
manifest_path = self._write_manifest(manifest)
|
|
89
|
+
dataset = parse_data_input_v2(
|
|
90
|
+
[str(manifest_path), str(self.population_path)]
|
|
91
|
+
).datasets[0]
|
|
92
|
+
|
|
93
|
+
polars_frame = dataset.scan_polars(
|
|
94
|
+
empty_schema={"id": pl.String, "netamount": pl.Float64}
|
|
95
|
+
).collect()
|
|
96
|
+
self.assertEqual(0, polars_frame.height)
|
|
97
|
+
self.assertEqual(["id", "netamount"], polars_frame.columns)
|
|
98
|
+
|
|
99
|
+
pandas_frame = dataset.read_pandas(
|
|
100
|
+
empty_dtypes={"id": "string", "netamount": "float64"}
|
|
101
|
+
)
|
|
102
|
+
self.assertTrue(pandas_frame.empty)
|
|
103
|
+
self.assertEqual(["id", "netamount"], pandas_frame.columns.tolist())
|
|
104
|
+
|
|
81
105
|
def test_streams_population_and_ordered_dataset_parts(self):
|
|
82
106
|
second_part_path = self._write_file(
|
|
83
107
|
"dataset_001_demographic_part_0002.csv",
|
|
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
|
|
File without changes
|
|
File without changes
|