dlt-utils 0.1.0__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.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: dlt_utils
3
+ Version: 0.1.0
4
+ Summary: Shared utilities for dlt data pipelines with multi-company support
5
+ Author: Jay van den Bos
6
+ License: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/dlt_utils/
8
+ Project-URL: Repository, https://dev.azure.com/your-org/your-project/_git/dlt_utils
9
+ Keywords: dlt,data,pipeline,etl,incremental
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Database
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.13
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: dlt>=1.19.1
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Requires-Dist: pytest-cov; extra == "dev"
24
+ Requires-Dist: ruff; extra == "dev"
25
+
26
+ # dlt_utils
27
+
28
+ [![PyPI version](https://badge.fury.io/py/dlt_utils.svg)](https://badge.fury.io/py/dlt_utils)
29
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+
32
+ Shared utilities for [dlt](https://dlthub.com/) data pipelines with multi-company support.
33
+
34
+ ## Features
35
+
36
+ - **PartitionedIncremental**: Incremental state tracking per partition key (e.g., company_id)
37
+ - **Date utilities**: Generate (year, week) and (year, month) tuples for time-based partitioning
38
+ - **Schema utilities**: Ensure tables exist in destination database
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ # From PyPI
44
+ pip install dlt_utils
45
+
46
+ # For development
47
+ pip install -e ".[dev]"
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### PartitionedIncremental
53
+
54
+ Track incremental state per company (or any partition key):
55
+
56
+ ```python
57
+ import dlt
58
+ from dlt_utils import PartitionedIncremental
59
+
60
+ @dlt.resource
61
+ def sync_resource():
62
+ state = dlt.current.resource_state()
63
+ inc = PartitionedIncremental(
64
+ state=state,
65
+ state_key="sequences",
66
+ cursor_path="sequenceNumber",
67
+ initial_value=0,
68
+ )
69
+
70
+ for company_id in ["company_a", "company_b"]:
71
+ start_seq = inc.get_last_value(company_id)
72
+ for record in fetch_data(company_id, since=start_seq):
73
+ inc.track(company_id, record["sequenceNumber"])
74
+ yield record
75
+ ```
76
+
77
+ ### Date utilities
78
+
79
+ Generate time periods for partitioned data extraction:
80
+
81
+ ```python
82
+ from dlt_utils import generate_year_weeks, generate_year_months
83
+
84
+ # Generate weeks from 2024 to now + 52 weeks
85
+ weeks = generate_year_weeks(start_year=2024)
86
+ # [(2024, 1), (2024, 2), ..., (2025, 52)]
87
+
88
+ # Generate months from October 2024 to February 2025
89
+ months = generate_year_months(2024, 10, 2025, 2)
90
+ # [(2024, 10), (2024, 11), (2024, 12), (2025, 1), (2025, 2)]
91
+ ```
92
+
93
+ ### Schema utilities
94
+
95
+ Ensure tables exist before running pipeline:
96
+
97
+ ```python
98
+ from dlt_utils import ensure_all_tables_exist, ensure_tables_for_resources
99
+
100
+ # Create all tables from schema
101
+ ensure_all_tables_exist(pipeline)
102
+
103
+ # Create only specific resource tables (including child tables)
104
+ ensure_tables_for_resources(pipeline, ["trade_items", "organizations"])
105
+ ```
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ # Install dev dependencies
111
+ pip install -e ".[dev]"
112
+
113
+ # Run tests
114
+ pytest
115
+
116
+ # Run linter
117
+ ruff check dlt_utils/
118
+ ```
119
+
120
+ ## CI/CD Pipeline
121
+
122
+ De pipeline draait automatisch bij:
123
+ - **Push naar `main`**: Voert tests uit
124
+ - **Tag met `v*` prefix**: Voert tests uit én publiceert naar PyPI
125
+
126
+ ### Pipeline Workflow
127
+
128
+ ```
129
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
130
+ │ Push/Tag │────▶│ Test Stage │────▶│ Publish Stage │
131
+ │ naar repo │ │ (altijd) │ │ (alleen tags) │
132
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
133
+ │ │
134
+ ▼ ▼
135
+ - Install deps - Build package
136
+ - Run pytest - Upload to PyPI
137
+ - Publish results
138
+ ```
139
+
140
+ ### Nieuwe Versie Releasen
141
+
142
+ #### Optie 1: Via Git CLI
143
+
144
+ ```bash
145
+ # 1. Zorg dat alle changes gecommit zijn
146
+ git add .
147
+ git commit -m "Release v0.2.0"
148
+
149
+ # 2. Maak een tag aan
150
+ git tag v0.2.0
151
+
152
+ # 3. Push commit én tag naar remote
153
+ git push origin main
154
+ git push origin v0.2.0
155
+ ```
156
+
157
+ #### Optie 2: Via Azure DevOps
158
+
159
+ 1. Ga naar **Repos** → **Tags**
160
+ 2. Klik op **New tag**
161
+ 3. Vul in:
162
+ - **Name**: `v0.2.0` (moet beginnen met `v`)
163
+ - **Based on**: selecteer de commit of branch (bijv. `main`)
164
+ - **Description**: optioneel, bijv. "Added new feature X"
165
+ 4. Klik op **Create**
166
+
167
+ De pipeline wordt automatisch getriggered en publiceert naar PyPI.
168
+
169
+ ### Versienummering
170
+
171
+ Gebruik [Semantic Versioning](https://semver.org/):
172
+ - `vMAJOR.MINOR.PATCH` (bijv. `v1.2.3`)
173
+ - **MAJOR**: Breaking changes
174
+ - **MINOR**: Nieuwe features (backwards compatible)
175
+ - **PATCH**: Bugfixes
176
+
177
+ > ⚠️ **Belangrijk**: Vergeet niet de versie in `pyproject.toml` bij te werken vóór het taggen!
178
+
179
+ ## License
180
+
181
+ MIT
@@ -0,0 +1,156 @@
1
+ # dlt_utils
2
+
3
+ [![PyPI version](https://badge.fury.io/py/dlt_utils.svg)](https://badge.fury.io/py/dlt_utils)
4
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Shared utilities for [dlt](https://dlthub.com/) data pipelines with multi-company support.
8
+
9
+ ## Features
10
+
11
+ - **PartitionedIncremental**: Incremental state tracking per partition key (e.g., company_id)
12
+ - **Date utilities**: Generate (year, week) and (year, month) tuples for time-based partitioning
13
+ - **Schema utilities**: Ensure tables exist in destination database
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ # From PyPI
19
+ pip install dlt_utils
20
+
21
+ # For development
22
+ pip install -e ".[dev]"
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### PartitionedIncremental
28
+
29
+ Track incremental state per company (or any partition key):
30
+
31
+ ```python
32
+ import dlt
33
+ from dlt_utils import PartitionedIncremental
34
+
35
+ @dlt.resource
36
+ def sync_resource():
37
+ state = dlt.current.resource_state()
38
+ inc = PartitionedIncremental(
39
+ state=state,
40
+ state_key="sequences",
41
+ cursor_path="sequenceNumber",
42
+ initial_value=0,
43
+ )
44
+
45
+ for company_id in ["company_a", "company_b"]:
46
+ start_seq = inc.get_last_value(company_id)
47
+ for record in fetch_data(company_id, since=start_seq):
48
+ inc.track(company_id, record["sequenceNumber"])
49
+ yield record
50
+ ```
51
+
52
+ ### Date utilities
53
+
54
+ Generate time periods for partitioned data extraction:
55
+
56
+ ```python
57
+ from dlt_utils import generate_year_weeks, generate_year_months
58
+
59
+ # Generate weeks from 2024 to now + 52 weeks
60
+ weeks = generate_year_weeks(start_year=2024)
61
+ # [(2024, 1), (2024, 2), ..., (2025, 52)]
62
+
63
+ # Generate months from October 2024 to February 2025
64
+ months = generate_year_months(2024, 10, 2025, 2)
65
+ # [(2024, 10), (2024, 11), (2024, 12), (2025, 1), (2025, 2)]
66
+ ```
67
+
68
+ ### Schema utilities
69
+
70
+ Ensure tables exist before running pipeline:
71
+
72
+ ```python
73
+ from dlt_utils import ensure_all_tables_exist, ensure_tables_for_resources
74
+
75
+ # Create all tables from schema
76
+ ensure_all_tables_exist(pipeline)
77
+
78
+ # Create only specific resource tables (including child tables)
79
+ ensure_tables_for_resources(pipeline, ["trade_items", "organizations"])
80
+ ```
81
+
82
+ ## Development
83
+
84
+ ```bash
85
+ # Install dev dependencies
86
+ pip install -e ".[dev]"
87
+
88
+ # Run tests
89
+ pytest
90
+
91
+ # Run linter
92
+ ruff check dlt_utils/
93
+ ```
94
+
95
+ ## CI/CD Pipeline
96
+
97
+ De pipeline draait automatisch bij:
98
+ - **Push naar `main`**: Voert tests uit
99
+ - **Tag met `v*` prefix**: Voert tests uit én publiceert naar PyPI
100
+
101
+ ### Pipeline Workflow
102
+
103
+ ```
104
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
105
+ │ Push/Tag │────▶│ Test Stage │────▶│ Publish Stage │
106
+ │ naar repo │ │ (altijd) │ │ (alleen tags) │
107
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
108
+ │ │
109
+ ▼ ▼
110
+ - Install deps - Build package
111
+ - Run pytest - Upload to PyPI
112
+ - Publish results
113
+ ```
114
+
115
+ ### Nieuwe Versie Releasen
116
+
117
+ #### Optie 1: Via Git CLI
118
+
119
+ ```bash
120
+ # 1. Zorg dat alle changes gecommit zijn
121
+ git add .
122
+ git commit -m "Release v0.2.0"
123
+
124
+ # 2. Maak een tag aan
125
+ git tag v0.2.0
126
+
127
+ # 3. Push commit én tag naar remote
128
+ git push origin main
129
+ git push origin v0.2.0
130
+ ```
131
+
132
+ #### Optie 2: Via Azure DevOps
133
+
134
+ 1. Ga naar **Repos** → **Tags**
135
+ 2. Klik op **New tag**
136
+ 3. Vul in:
137
+ - **Name**: `v0.2.0` (moet beginnen met `v`)
138
+ - **Based on**: selecteer de commit of branch (bijv. `main`)
139
+ - **Description**: optioneel, bijv. "Added new feature X"
140
+ 4. Klik op **Create**
141
+
142
+ De pipeline wordt automatisch getriggered en publiceert naar PyPI.
143
+
144
+ ### Versienummering
145
+
146
+ Gebruik [Semantic Versioning](https://semver.org/):
147
+ - `vMAJOR.MINOR.PATCH` (bijv. `v1.2.3`)
148
+ - **MAJOR**: Breaking changes
149
+ - **MINOR**: Nieuwe features (backwards compatible)
150
+ - **PATCH**: Bugfixes
151
+
152
+ > ⚠️ **Belangrijk**: Vergeet niet de versie in `pyproject.toml` bij te werken vóór het taggen!
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,32 @@
1
+ """
2
+ dlt_utils: Shared utilities for dlt data pipelines with multi-company support.
3
+
4
+ This package provides common utilities for building dlt pipelines that work with
5
+ multiple companies/tenants, including:
6
+
7
+ - PartitionedIncremental: State tracking per partition key
8
+ - Date utilities: Generate (year, week) and (year, month) sequences
9
+ - Schema utilities: Ensure database tables exist
10
+ """
11
+
12
+ from .incremental import PartitionedIncremental
13
+ from .dates import generate_year_weeks, generate_year_months
14
+ from .schema import (
15
+ ensure_all_tables_exist,
16
+ ensure_tables_for_resources,
17
+ get_tables_for_resources,
18
+ )
19
+
20
+ __version__ = "0.1.0"
21
+
22
+ __all__ = [
23
+ # Incremental
24
+ "PartitionedIncremental",
25
+ # Dates
26
+ "generate_year_weeks",
27
+ "generate_year_months",
28
+ # Schema
29
+ "ensure_all_tables_exist",
30
+ "ensure_tables_for_resources",
31
+ "get_tables_for_resources",
32
+ ]
@@ -0,0 +1,160 @@
1
+ """
2
+ Utility functions for date-based partitioning in dlt data extraction.
3
+
4
+ This module provides helper functions to generate sequences of (year, week) and
5
+ (year, month) tuples for partitioning data extraction across time periods.
6
+ These are used by resources that require parametric date-based queries.
7
+ """
8
+
9
+ from datetime import date, datetime, timedelta
10
+ from typing import List, Optional, Tuple
11
+
12
+
13
+ def generate_year_weeks(
14
+ start_year: Optional[int] = None,
15
+ start_week: Optional[int] = 1,
16
+ end_year: Optional[int] = None,
17
+ end_week: Optional[int] = None,
18
+ years_back: int = 3,
19
+ weeks_forward: int = 52,
20
+ ) -> List[Tuple[int, int]]:
21
+ """
22
+ Generate a list of (year, week) tuples using ISO week numbers.
23
+
24
+ Uses ISO 8601 week numbers where weeks start on Monday and the first
25
+ week of the year contains at least 4 days in that year.
26
+
27
+ Args:
28
+ start_year: Start year. Defaults to `years_back` years ago.
29
+ start_week: Start ISO week number 1-53. Defaults to 1.
30
+ end_year: End year. Defaults to the year of calculated end date.
31
+ end_week: End ISO week number 1-53. Defaults to current + `weeks_forward`.
32
+ years_back: Years to go back for default start_year. Defaults to 3.
33
+ weeks_forward: Weeks to add for default end calculation. Defaults to 52.
34
+
35
+ Returns:
36
+ List of (year, week) tuples in chronological order.
37
+
38
+ Examples:
39
+ >>> # Specific range
40
+ >>> generate_year_weeks(2024, 1, 2024, 4)
41
+ [(2024, 1), (2024, 2), (2024, 3), (2024, 4)]
42
+
43
+ >>> # Default: 3 years back to 52 weeks forward
44
+ >>> weeks = generate_year_weeks()
45
+ >>> weeks[0] # First week
46
+ (2022, 1)
47
+
48
+ >>> # Custom defaults
49
+ >>> generate_year_weeks(years_back=1, weeks_forward=4)
50
+ # From 1 year ago to 4 weeks from now
51
+ """
52
+ today = date.today()
53
+
54
+ # Default start_year: years_back years ago
55
+ if start_year is None:
56
+ start_year = today.year - years_back
57
+
58
+ # Default end: current date + weeks_forward
59
+ if end_year is None or end_week is None:
60
+ future_date = today + timedelta(weeks=weeks_forward)
61
+ iso_cal = future_date.isocalendar()
62
+ end_year = end_year or iso_cal.year
63
+ end_week = end_week or iso_cal.week
64
+
65
+ # Start from start_week of start_year
66
+ start_date = datetime.strptime(f"{start_year}-W{start_week:02d}-1", "%G-W%V-%u").date()
67
+ end_date = datetime.strptime(f"{end_year}-W{end_week:02d}-1", "%G-W%V-%u").date()
68
+
69
+ year_weeks = []
70
+ current = start_date
71
+ while current <= end_date:
72
+ iso_cal = current.isocalendar()
73
+ year_weeks.append((iso_cal.year, iso_cal.week))
74
+ current += timedelta(weeks=1)
75
+
76
+ return year_weeks
77
+
78
+
79
+ def generate_year_months(
80
+ start_year: Optional[int] = None,
81
+ start_month: Optional[int] = 1,
82
+ end_year: Optional[int] = None,
83
+ end_month: Optional[int] = None,
84
+ years_back: int = 3,
85
+ months_forward: int = 1,
86
+ ) -> List[Tuple[int, int]]:
87
+ """
88
+ Generate a list of (year, month) tuples from start to end.
89
+
90
+ Args:
91
+ start_year: Start year. Defaults to `years_back` years ago.
92
+ start_month: Start month 1-12. Defaults to 1 (January).
93
+ end_year: End year. Defaults to the year of calculated end date.
94
+ end_month: End month 1-12. Defaults to current + `months_forward`.
95
+ years_back: Years to go back for default start_year. Defaults to 3.
96
+ months_forward: Months to add for default end calculation. Defaults to 1.
97
+
98
+ Returns:
99
+ List of (year, month) tuples in chronological order.
100
+
101
+ Examples:
102
+ >>> generate_year_months(2024, 10, 2025, 2)
103
+ [(2024, 10), (2024, 11), (2024, 12), (2025, 1), (2025, 2)]
104
+
105
+ >>> # Default: 3 years back to 1 month forward
106
+ >>> months = generate_year_months()
107
+ >>> months[0]
108
+ (2022, 1)
109
+ """
110
+ today = date.today()
111
+
112
+ # Default start_year: years_back years ago
113
+ if start_year is None:
114
+ start_year = today.year - years_back
115
+
116
+ # Default end: current date + months_forward
117
+ if end_year is None or end_month is None:
118
+ # Calculate future date by adding months
119
+ future_month = today.month + months_forward
120
+ future_year = today.year
121
+ while future_month > 12:
122
+ future_month -= 12
123
+ future_year += 1
124
+ end_year = end_year or future_year
125
+ end_month = end_month or future_month
126
+
127
+ year_months = []
128
+ current_year = start_year
129
+ current_month = start_month
130
+
131
+ while (current_year, current_month) <= (end_year, end_month):
132
+ year_months.append((current_year, current_month))
133
+ current_month += 1
134
+ if current_month > 12:
135
+ current_month = 1
136
+ current_year += 1
137
+
138
+ return year_months
139
+
140
+
141
+ def get_current_iso_week() -> Tuple[int, int]:
142
+ """
143
+ Get the current ISO year and week number.
144
+
145
+ Returns:
146
+ Tuple of (year, week) for today.
147
+ """
148
+ iso_cal = date.today().isocalendar()
149
+ return (iso_cal.year, iso_cal.week)
150
+
151
+
152
+ def get_current_year_month() -> Tuple[int, int]:
153
+ """
154
+ Get the current year and month.
155
+
156
+ Returns:
157
+ Tuple of (year, month) for today.
158
+ """
159
+ today = date.today()
160
+ return (today.year, today.month)
@@ -0,0 +1,161 @@
1
+ """
2
+ Incremental state tracking voor dlt resources met partitionering.
3
+
4
+ Dit module biedt een PartitionedIncremental class die werkt als dlt.sources.incremental,
5
+ maar state per partition key (bijv. company_id) bijhoudt in plaats van per resource.
6
+ """
7
+
8
+ from typing import Any, Callable, Dict, List, TypeVar
9
+
10
+ TCursorValue = TypeVar("TCursorValue")
11
+
12
+
13
+ class PartitionedIncremental:
14
+ """
15
+ Incremental state tracking per partition key (bijv. company_id).
16
+
17
+ Werkt als dlt.sources.incremental, maar partitioneert state per key zodat
18
+ meerdere companies onafhankelijke cursors kunnen hebben binnen dezelfde resource.
19
+
20
+ Voorbeeld:
21
+ ```python
22
+ @dlt.resource
23
+ def sync_resource():
24
+ state = dlt.current.resource_state()
25
+ inc = PartitionedIncremental(
26
+ state=state,
27
+ state_key="sequences",
28
+ cursor_path="sequenceNumber",
29
+ initial_value=0,
30
+ )
31
+
32
+ for company_id in ["company_a", "company_b"]:
33
+ start_seq = inc.get_last_value(company_id)
34
+ for record in fetch_data(company_id, since=start_seq):
35
+ inc.track(company_id, record["sequenceNumber"])
36
+ yield record
37
+ ```
38
+
39
+ State structuur in dlt:
40
+ ```json
41
+ {
42
+ "sequences": {
43
+ "company_a": 12345,
44
+ "company_b": 67890
45
+ }
46
+ }
47
+ ```
48
+
49
+ Use cases:
50
+ - sequenceNumber tracking per company (Floriday sync endpoints)
51
+ - last_modified_at per company + period (Easyflex journaalposten)
52
+ - Any cursor that needs to be tracked per partition
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ state: Dict[str, Any],
58
+ state_key: str,
59
+ cursor_path: str = None,
60
+ initial_value: TCursorValue = None,
61
+ last_value_func: Callable[[List[TCursorValue]], TCursorValue] = max,
62
+ ):
63
+ """
64
+ Initialiseer PartitionedIncremental.
65
+
66
+ Args:
67
+ state: Resource state dict van dlt.current.resource_state().
68
+ state_key: Sleutel in state dict waar partition values worden opgeslagen.
69
+ cursor_path: Optioneel pad naar cursor veld in records (voor track_record).
70
+ initial_value: Waarde voor partitions zonder bestaande state.
71
+ last_value_func: Functie om "laatste" waarde te bepalen (default: max).
72
+ Gebruik min() voor aflopende cursors.
73
+ """
74
+ self._state = state
75
+ self._state_key = state_key
76
+ self._cursor_path = cursor_path
77
+ self._initial_value = initial_value
78
+ self._last_value_func = last_value_func
79
+
80
+ # Zorg dat state structuur bestaat
81
+ if state_key not in state:
82
+ state[state_key] = {}
83
+ self._partition_state = state[state_key]
84
+
85
+ # Track huidige waarden tijdens streaming (voor running max/min)
86
+ self._current_values: Dict[str, TCursorValue] = {}
87
+
88
+ def get_last_value(self, partition_key: str) -> TCursorValue:
89
+ """
90
+ Haal de laatst opgeslagen waarde op voor een partition.
91
+
92
+ Args:
93
+ partition_key: Identifier voor de partition (bijv. company_id).
94
+
95
+ Returns:
96
+ De opgeslagen waarde, of initial_value als er geen state is.
97
+ """
98
+ return self._partition_state.get(partition_key, self._initial_value)
99
+
100
+ def track(self, partition_key: str, cursor_value: TCursorValue) -> None:
101
+ """
102
+ Track een cursor waarde voor een partition.
103
+
104
+ Werkt running max/min bij en persisted naar state. Roep dit aan voor
105
+ elk record tijdens het streamen zodat de hoogste/laagste waarde
106
+ wordt onthouden.
107
+
108
+ Args:
109
+ partition_key: Identifier voor de partition (bijv. company_id).
110
+ cursor_value: De te tracken waarde (bijv. sequenceNumber).
111
+ """
112
+ current = self._current_values.get(partition_key)
113
+ if current is None:
114
+ current = self.get_last_value(partition_key)
115
+
116
+ if cursor_value is not None:
117
+ new_value = self._last_value_func(
118
+ [cursor_value, current] if current is not None else [cursor_value]
119
+ )
120
+ self._current_values[partition_key] = new_value
121
+ # Direct persisten naar state (dlt commit na resource completion)
122
+ self._partition_state[partition_key] = new_value
123
+
124
+ def track_record(self, partition_key: str, record: Dict[str, Any]) -> Dict[str, Any]:
125
+ """
126
+ Track cursor waarde uit een record via cursor_path.
127
+
128
+ Convenience methode voor gebruik in map functies.
129
+
130
+ Args:
131
+ partition_key: Identifier voor de partition.
132
+ record: Record dict met cursor veld.
133
+
134
+ Returns:
135
+ Het record ongewijzigd (voor chaining in pipelines).
136
+ """
137
+ if self._cursor_path:
138
+ cursor_value = record.get(self._cursor_path)
139
+ self.track(partition_key, cursor_value)
140
+ return record
141
+
142
+ def get_all_partitions(self) -> Dict[str, TCursorValue]:
143
+ """
144
+ Haal alle opgeslagen partition states op.
145
+
146
+ Returns:
147
+ Dict van partition_key -> cursor_value.
148
+ """
149
+ return dict(self._partition_state)
150
+
151
+ def reset_partition(self, partition_key: str) -> None:
152
+ """
153
+ Reset state voor een specifieke partition.
154
+
155
+ Args:
156
+ partition_key: Identifier voor de partition om te resetten.
157
+ """
158
+ if partition_key in self._partition_state:
159
+ del self._partition_state[partition_key]
160
+ if partition_key in self._current_values:
161
+ del self._current_values[partition_key]