supertable 2.2.0__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {supertable-2.2.0/supertable.egg-info → supertable-2.2.2}/PKG-INFO +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/pyproject.toml +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/setup.py +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/__init__.py +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/data_reader.py +26 -11
- {supertable-2.2.0 → supertable-2.2.2}/supertable/data_writer.py +616 -21
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/defaults.py +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/gc/daemon.py +5 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/monitoring/partitions.py +65 -32
- {supertable-2.2.0 → supertable-2.2.2}/supertable/processing.py +424 -168
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/role_manager.py +15 -16
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/tests/test_rbac.py +136 -8
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/user_manager.py +9 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/redis_catalog.py +100 -6
- {supertable-2.2.0 → supertable-2.2.2}/supertable/redis_keys.py +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/simple_table.py +44 -18
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_data_reader_preflight.py +37 -0
- supertable-2.2.2/supertable/tests/test_data_writer_compact.py +1177 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_data_writer_tombstones.py +115 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_monitoring_partitions.py +53 -21
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_newer_than.py +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_process_delete_only.py +2 -2
- supertable-2.2.2/supertable/tests/test_processing_compact_resources.py +575 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_redis_key_prefix.py +2 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_simple_table.py +98 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_small_file_compaction.py +2 -2
- supertable-2.2.2/supertable/utils/profiler.py +116 -0
- {supertable-2.2.0 → supertable-2.2.2/supertable.egg-info}/PKG-INFO +1 -1
- {supertable-2.2.0 → supertable-2.2.2}/supertable.egg-info/SOURCES.txt +3 -0
- {supertable-2.2.0 → supertable-2.2.2}/LICENSE +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/README.md +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/requirements.txt +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/setup.cfg +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/admin.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/chain.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/consumers.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/crypto.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/events.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/export.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/logger.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/middleware.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/reader.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/retention.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/test_chain.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/test_crypto.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/test_emit.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/test_events.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/tests/test_retention.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/writer_parquet.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/audit/writer_redis.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/defaults.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/homedir.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/settings.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/tests/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/tests/test_defaults.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/tests/test_homedir.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/config/tests/test_settings.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/data_classes.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/__main__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/check_filter_builder.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/controller.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/data_writer_helpers.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/defaults.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/dummy_data.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/read_parquet_header.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s01_01_01_create_super_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s01_01_02_enable_mirroring_formats.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s01_02_create_roles.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s01_03_create_users.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_01_write_dummy_data.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_02_write_single_data.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_03_01_write_staging.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_03_02_create_pipe.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_04_01_write_monitoring_simple.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_04_02_write_monitoring_parallel.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s02_05_write_tombstone.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_01_read_data_error.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_02_01_read_super_data_ok.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_02_02_read_table_data_ok.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_03_read_meta.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_04_read_staging.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_06_01_read_roles.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_06_02_read_user.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_07_01_estimate_read.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_07_02_estimate_files.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s03_08_read_snapshot_history.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s04_01_03_delete_pipe.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s05_01_delete_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/quickstart/s05_02_delete_super_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/core.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/generate.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/load.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/demo/webshop/topup.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/data_estimator.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/duckdb_lite.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/duckdb_pro.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/engine_common.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/engine_enum.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/executor.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/plan_stats.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/spark_thrift.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/tests/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/tests/conftest.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/tests/test_dedup_read.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/engine/tests/test_engine.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/errors.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/gc/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/gc/cleaner.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/gc/queue.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/benchmarks/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/benchmarks/benchmark_locking.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/benchmarks/measure_lock_speed.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/benchmarks/measure_lock_time.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/file_lock.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/redis_lock.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/tests/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/tests/test_file_lock.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/locking/tests/test_redis_lock.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/logging.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/meta_reader.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/mirroring/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/mirroring/mirror_delta.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/mirroring/mirror_formats.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/mirroring/mirror_iceberg.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/mirroring/mirror_parquet.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/monitoring/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/monitoring_writer.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/plan_extender.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/query_plan_manager.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/access_control.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/filter_builder.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/permissions.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/row_column_security.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/tests/test_filter_builder.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/rbac/tests/test_rbac_per_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/redis_connector.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/redis_infra.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/staging_area.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/azure_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/gcp_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/local_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/minio_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/s3_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/storage_factory.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/storage_interface.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/storage/tests/test_storage.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/super_pipe.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/super_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_align_to_schema_fix.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_create_if_missing.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_data_reader.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_data_writer.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_data_writer_comprehensive.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_dedup_on_read_write.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_errors.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_gc_cleaner.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_gc_daemon.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_gc_queue.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_meta_reader.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_monitoring_sink_guard.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_processing.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_query_sql.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_super_table.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/tests/test_supertable_all.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/utils/__init__.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/utils/helper.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/utils/sql_parser.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/utils/tests/test_sql_parser_columns.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable/utils/timer.py +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable.egg-info/dependency_links.txt +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable.egg-info/entry_points.txt +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable.egg-info/requires.txt +0 -0
- {supertable-2.2.0 → supertable-2.2.2}/supertable.egg-info/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ long_description = readme.read_text(encoding="utf-8") if readme.exists() else ""
|
|
|
19
19
|
|
|
20
20
|
setup(
|
|
21
21
|
name="supertable",
|
|
22
|
-
version="2.2.
|
|
22
|
+
version="2.2.2",
|
|
23
23
|
description="SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.",
|
|
24
24
|
long_description=long_description,
|
|
25
25
|
long_description_content_type="text/markdown",
|
|
@@ -25,7 +25,7 @@ See the ``supertable.demo`` package for runnable end-to-end demos and the
|
|
|
25
25
|
project documentation for the full API surface.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
__version__ = "2.2.
|
|
28
|
+
__version__ = "2.2.2"
|
|
29
29
|
|
|
30
30
|
# Re-export the core public surface so users can do ``from supertable import …``
|
|
31
31
|
# instead of remembering submodule paths.
|
|
@@ -119,7 +119,32 @@ class DataReader:
|
|
|
119
119
|
tables = parser.get_table_tuples()
|
|
120
120
|
physical_tables = parser.get_physical_tables()
|
|
121
121
|
|
|
122
|
-
#
|
|
122
|
+
# Read-path policy: reads never create. Verify every referenced
|
|
123
|
+
# (super, simple) exists in the Redis catalog **before** anything
|
|
124
|
+
# downstream — RBAC, estimator, or the executor — gets a chance
|
|
125
|
+
# to side-effect-bootstrap them.
|
|
126
|
+
#
|
|
127
|
+
# ORDERING MATTERS: ``restrict_read_access`` (called next) builds
|
|
128
|
+
# ``RoleManager(super_name=..., organization=...)`` which boots
|
|
129
|
+
# RBAC role storage in Redis for the supertable if it doesn't
|
|
130
|
+
# exist. Running the RBAC check first against a missing
|
|
131
|
+
# supertable would silently mint the RBAC scaffold before our
|
|
132
|
+
# existence check fires. Pre-flight FIRST.
|
|
133
|
+
#
|
|
134
|
+
# The check runs in its own try block so SuperTable/TableNotFound
|
|
135
|
+
# convert to the standard (empty_df, Status.ERROR, message)
|
|
136
|
+
# return — we don't want to raise these into the caller, but we
|
|
137
|
+
# DO want to keep ``restrict_read_access``'s PermissionError
|
|
138
|
+
# raising naturally (legacy behaviour API layers depend on for
|
|
139
|
+
# 403 translation).
|
|
140
|
+
try:
|
|
141
|
+
self._assert_targets_exist(physical_tables)
|
|
142
|
+
except (SuperTableNotFoundError, TableNotFoundError) as e:
|
|
143
|
+
logger.warning(self._lp(f"target missing: {e}"))
|
|
144
|
+
return pd.DataFrame(), Status.ERROR, str(e)
|
|
145
|
+
|
|
146
|
+
# RBAC check — also returns per-alias column/row filter definitions.
|
|
147
|
+
# PermissionError propagates to the caller (legacy behaviour).
|
|
123
148
|
rbac_views = restrict_read_access(
|
|
124
149
|
super_name=self.super_name,
|
|
125
150
|
organization=self.organization,
|
|
@@ -129,16 +154,6 @@ class DataReader:
|
|
|
129
154
|
)
|
|
130
155
|
|
|
131
156
|
try:
|
|
132
|
-
# Read-path policy: reads never create. Verify every
|
|
133
|
-
# referenced (super, simple) exists in the Redis catalog
|
|
134
|
-
# *before* the estimator / executor get a chance to
|
|
135
|
-
# side-effect-bootstrap them via SuperTable constructor
|
|
136
|
-
# calls. On a miss this raises SuperTableNotFoundError /
|
|
137
|
-
# TableNotFoundError, which the surrounding ``except`` turns
|
|
138
|
-
# into the same (empty_df, Status.ERROR, message) tuple as
|
|
139
|
-
# every other read failure — no state is touched.
|
|
140
|
-
self._assert_targets_exist(physical_tables)
|
|
141
|
-
|
|
142
157
|
# Make executor aware of storage for presign retry
|
|
143
158
|
executor = Executor(storage=self.storage, organization=self.organization)
|
|
144
159
|
|