sibi-dst 2025.8.1__py3-none-any.whl → 2025.8.3__py3-none-any.whl

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,301 @@
1
+ import datetime as _dt
2
+ from typing import Tuple, List, Optional, Dict, Any
3
+
4
+ import fsspec
5
+
6
+ from .log_utils import Logger
7
+
8
+
9
+ class FileAgeChecker:
10
+ """
11
+ Check file/directory "age" (minutes since last modification) using fsspec.
12
+
13
+ Backward compatible methods:
14
+ - is_file_older_than(file_path, max_age_minutes, fs=None, ignore_missing=False, verbose=False) -> bool
15
+ - get_file_or_dir_age_minutes(file_path, fs=None) -> float
16
+
17
+ Enhancements:
18
+ - dir_policy: 'oldest' | 'newest' | 'mean' when evaluating directories
19
+ - recursive: recurse into subdirectories using fs.find()
20
+ - robust mtime extraction for local/S3/FTP-like backends
21
+ - grace_minutes: optional slack for threshold comparisons
22
+ """
23
+
24
+ _UTC = _dt.timezone.utc
25
+
26
+ def __init__(
27
+ self,
28
+ debug: bool = False,
29
+ logger: Optional[Logger] = None,
30
+ *,
31
+ dir_policy: str = "oldest", # 'oldest' (legacy), 'newest', or 'mean'
32
+ recursive_default: bool = False,
33
+ ):
34
+ self.logger = logger or Logger.default_logger(logger_name=self.__class__.__name__)
35
+ self.logger.set_level(Logger.DEBUG if debug else Logger.INFO)
36
+
37
+ if dir_policy not in {"oldest", "newest", "mean"}:
38
+ raise ValueError("dir_policy must be one of: 'oldest', 'newest', 'mean'")
39
+ self.dir_policy = dir_policy
40
+ self.recursive_default = recursive_default
41
+
42
+ # ---------------- Public API ----------------
43
+
44
+ def is_file_older_than(
45
+ self,
46
+ file_path: str,
47
+ max_age_minutes: float,
48
+ fs: Optional[fsspec.AbstractFileSystem] = None,
49
+ ignore_missing: bool = False,
50
+ verbose: bool = False,
51
+ *,
52
+ recursive: Optional[bool] = None,
53
+ dir_policy: Optional[str] = None,
54
+ grace_minutes: float = 0.0,
55
+ ) -> bool:
56
+ """
57
+ Return True if file/dir age (in minutes) is greater than the threshold.
58
+
59
+ :param file_path: Path to the file or directory.
60
+ :param max_age_minutes: Maximum allowed "age" in minutes.
61
+ :param fs: Filesystem object (defaults to local 'file' fs).
62
+ :param ignore_missing: If True, missing paths are treated as NOT old.
63
+ :param verbose: Log extra details.
64
+ :param recursive: Recurse into subdirectories (defaults to instance setting).
65
+ :param dir_policy: 'oldest' | 'newest' | 'mean' (defaults to instance policy).
66
+ :param grace_minutes: Threshold slack; if provided, we compare against
67
+ (max_age_minutes - grace_minutes), floored at 0.
68
+ """
69
+ fs = fs or fsspec.filesystem("file")
70
+ use_recursive = self.recursive_default if recursive is None else bool(recursive)
71
+ policy = dir_policy or self.dir_policy
72
+
73
+ try:
74
+ if not fs.exists(file_path):
75
+ if verbose:
76
+ self.logger.debug(f"Path not found: {file_path}")
77
+ return False if ignore_missing else True
78
+
79
+ age = self.get_file_or_dir_age_minutes(
80
+ file_path, fs=fs, recursive=use_recursive, dir_policy=policy, verbose=verbose
81
+ )
82
+ threshold = max(0.0, float(max_age_minutes) - float(grace_minutes))
83
+ if verbose:
84
+ self.logger.debug(
85
+ f"Age check for {file_path}: age={age:.2f} min, "
86
+ f"threshold={threshold:.2f} min (policy={policy}, recursive={use_recursive})"
87
+ )
88
+ return age > threshold
89
+
90
+ except Exception as e:
91
+ # On errors, be conservative and consider it old (legacy behavior)
92
+ self.logger.warning(f"Error checking {file_path}: {e}")
93
+ return True
94
+
95
+ def get_file_or_dir_age_minutes(
96
+ self,
97
+ file_path: str,
98
+ fs: Optional[fsspec.AbstractFileSystem] = None,
99
+ *,
100
+ recursive: Optional[bool] = None,
101
+ dir_policy: Optional[str] = None,
102
+ verbose: bool = False,
103
+ ) -> float:
104
+ """
105
+ Compute the age (minutes since last modification timestamp) for a file or directory.
106
+
107
+ For directories, applies `dir_policy`:
108
+ - 'oldest' : age of the OLDEST file (max age) [legacy default]
109
+ - 'newest' : age since the MOST RECENT file update (min age)
110
+ - 'mean' : average age across files
111
+
112
+ Returns float('inf') for missing paths, invalid path types, or on errors.
113
+ """
114
+ fs = fs or fsspec.filesystem("file")
115
+ use_recursive = self.recursive_default if recursive is None else bool(recursive)
116
+ policy = dir_policy or self.dir_policy
117
+
118
+ try:
119
+ if not fs.exists(file_path):
120
+ if verbose:
121
+ self.logger.debug(f"Path not found: {file_path}")
122
+ return float("inf")
123
+
124
+ if fs.isdir(file_path):
125
+ return self._get_directory_age_minutes(
126
+ file_path, fs, verbose=verbose, recursive=use_recursive, policy=policy
127
+ )
128
+ if fs.isfile(file_path):
129
+ return self._get_file_age_minutes(file_path, fs, verbose=verbose)
130
+
131
+ self.logger.warning(f"Invalid path type (neither file nor dir): {file_path}")
132
+ return float("inf")
133
+
134
+ except Exception as e:
135
+ self.logger.warning(f"Error getting age for {file_path}: {e}")
136
+ return float("inf")
137
+
138
+ # ---------------- Internals ----------------
139
+
140
+ def _now_utc(self) -> _dt.datetime:
141
+ return _dt.datetime.now(self._UTC)
142
+
143
+ def _get_directory_age_minutes(
144
+ self,
145
+ dir_path: str,
146
+ fs: fsspec.AbstractFileSystem,
147
+ *,
148
+ verbose: bool,
149
+ recursive: bool,
150
+ policy: str,
151
+ ) -> float:
152
+ """Compute directory age using the chosen policy."""
153
+ try:
154
+ paths = self._list_files(dir_path, fs, recursive=recursive)
155
+ except Exception as e:
156
+ self.logger.warning(f"Error listing {dir_path}: {e}")
157
+ return float("inf")
158
+
159
+ if not paths:
160
+ if verbose:
161
+ self.logger.debug(f"Empty directory: {dir_path}")
162
+ return float("inf")
163
+
164
+ ages: List[float] = []
165
+ for p in paths:
166
+ try:
167
+ info = fs.info(p)
168
+ mt = self._extract_mtime_utc(info, p)
169
+ if mt is None:
170
+ continue
171
+ age_min = (self._now_utc() - mt).total_seconds() / 60.0
172
+ ages.append(age_min)
173
+ except Exception as e:
174
+ # Skip problem files but continue
175
+ self.logger.debug(f"Skipping {p}: {e}")
176
+
177
+ if not ages:
178
+ self.logger.warning(f"No valid files with mtime in {dir_path}")
179
+ return float("inf")
180
+
181
+ if policy == "oldest":
182
+ chosen = max(ages) # age of oldest file
183
+ elif policy == "newest":
184
+ chosen = min(ages) # since most recent update
185
+ elif policy == "mean":
186
+ chosen = sum(ages) / len(ages)
187
+ else:
188
+ raise ValueError(f"Unknown dir_policy: {policy}")
189
+
190
+ if verbose:
191
+ self.logger.debug(
192
+ f"Directory age ({policy}) for {dir_path}: {chosen:.2f} minutes "
193
+ f"from {len(ages)} files (recursive={recursive})"
194
+ )
195
+ return chosen
196
+
197
+ def _get_file_age_minutes(
198
+ self,
199
+ file_path: str,
200
+ fs: fsspec.AbstractFileSystem,
201
+ *,
202
+ verbose: bool,
203
+ ) -> float:
204
+ """Age for a single file in minutes."""
205
+ info = fs.info(file_path)
206
+ mt = self._extract_mtime_utc(info, file_path)
207
+ if mt is None:
208
+ if verbose:
209
+ self.logger.debug(f"Missing/invalid mtime for {file_path} (info: {info})")
210
+ return float("inf")
211
+ age = (self._now_utc() - mt).total_seconds() / 60.0
212
+ if verbose:
213
+ self.logger.debug(f"File age for {file_path}: {age:.2f} minutes")
214
+ return age
215
+
216
+ def _list_files(
217
+ self,
218
+ dir_path: str,
219
+ fs: fsspec.AbstractFileSystem,
220
+ *,
221
+ recursive: bool,
222
+ ) -> List[str]:
223
+ """
224
+ Return a list of file paths inside dir_path.
225
+ Uses fs.find() if recursive else fs.ls(); filters out directories.
226
+ """
227
+ if recursive and hasattr(fs, "find"):
228
+ found = fs.find(dir_path)
229
+ # Some fs.find implementations return only files; still filter defensively
230
+ return [p for p in (found or []) if self._is_file(fs, p)]
231
+ else:
232
+ items = fs.ls(dir_path)
233
+ return [p for p in (items or []) if self._is_file(fs, p)]
234
+
235
+ def _is_file(self, fs: fsspec.AbstractFileSystem, path: str) -> bool:
236
+ try:
237
+ return fs.isfile(path)
238
+ except Exception:
239
+ # Some backends: rely on info['type']
240
+ try:
241
+ info = fs.info(path)
242
+ return info.get("type") == "file"
243
+ except Exception:
244
+ return False
245
+
246
+ def _extract_mtime_utc(self, info: Dict[str, Any], path: str) -> Optional[_dt.datetime]:
247
+ """
248
+ Normalize an mtime from fsspec info to a timezone-aware UTC datetime.
249
+ Supports common keys across local/S3/FTP-ish backends.
250
+ """
251
+ # 1) S3-like
252
+ if "LastModified" in info:
253
+ lm = info["LastModified"]
254
+ if isinstance(lm, _dt.datetime):
255
+ return lm if lm.tzinfo else lm.replace(tzinfo=self._UTC)
256
+ if isinstance(lm, str):
257
+ # Try ISO; honor trailing 'Z'
258
+ s = lm.replace("Z", "+00:00") if lm.endswith("Z") else lm
259
+ try:
260
+ dt = _dt.datetime.fromisoformat(s)
261
+ return dt if dt.tzinfo else dt.replace(tzinfo=self._UTC)
262
+ except ValueError:
263
+ pass
264
+
265
+ # 2) Local/posix fsspec
266
+ if "mtime" in info:
267
+ mt = info["mtime"]
268
+ try:
269
+ # fsspec local often returns float seconds
270
+ ts = float(mt)
271
+ return _dt.datetime.fromtimestamp(ts, tz=self._UTC)
272
+ except (TypeError, ValueError):
273
+ # Sometimes mtime is an ISO string
274
+ if isinstance(mt, str):
275
+ s = mt.replace("Z", "+00:00") if mt.endswith("Z") else mt
276
+ try:
277
+ dt = _dt.datetime.fromisoformat(s)
278
+ return dt if dt.tzinfo else dt.replace(tzinfo=self._UTC)
279
+ except ValueError:
280
+ pass
281
+
282
+ # 3) FTP/SSH style
283
+ for k in ("modified", "last_modified", "updated"):
284
+ if k in info and isinstance(info[k], str):
285
+ val = info[k]
286
+ # Try common "%Y-%m-%d %H:%M:%S" first, then ISO
287
+ try:
288
+ dt = _dt.datetime.strptime(val, "%Y-%m-%d %H:%M:%S").replace(tzinfo=self._UTC)
289
+ return dt
290
+ except ValueError:
291
+ s = val.replace("Z", "+00:00") if val.endswith("Z") else val
292
+ try:
293
+ dt = _dt.datetime.fromisoformat(s)
294
+ return dt if dt.tzinfo else dt.replace(tzinfo=self._UTC)
295
+ except ValueError:
296
+ continue
297
+
298
+ # If nothing matched, log once at debug level
299
+ self.logger.debug(f"No usable mtime in info for {path}: {info}")
300
+ return None
301
+
@@ -0,0 +1,42 @@
1
+ # sibi_dst/periods.py
2
+ from __future__ import annotations
3
+ import datetime as dt
4
+ from typing import Dict, Tuple
5
+
6
+ # Map all user-facing labels to canonical keys your orchestrators expect.
7
+ CANON: Dict[str, str] = {
8
+ "ytd": "ytd",
9
+ "itd": "itd",
10
+ "current_month": "current_month",
11
+ "today": "today",
12
+ "custom": "custom", # generic custom range
13
+ # labels that imply a date RANGE
14
+ "last_3_days": "custom",
15
+ "last_7_days": "custom",
16
+ "last_14_days": "custom",
17
+ }
18
+
19
+ def normalize_period(user_period: str) -> str:
20
+ """
21
+ Normalize a user-facing period label to your canonical key.
22
+ Raises ValueError with allowed labels if unsupported.
23
+ """
24
+ try:
25
+ return CANON[user_period]
26
+ except KeyError:
27
+ allowed = ", ".join(sorted(CANON))
28
+ raise ValueError(f"Unsupported period '{user_period}'. Allowed: {allowed}")
29
+
30
+ def compute_range_days(label: str, *, today: dt.date | None = None) -> Tuple[dt.date, dt.date]:
31
+ """
32
+ Convert 'last_N_days' label to an inclusive (start_date, end_date).
33
+ Example: last_3_days with today=2025-08-11 -> (2025-08-08, 2025-08-11)
34
+ """
35
+ today = today or dt.date.today()
36
+ try:
37
+ # label format: 'last_<N>_days'
38
+ days = int(label.split("_")[1])
39
+ except Exception as e:
40
+ raise ValueError(f"Invalid range label '{label}'. Expected 'last_<N>_days'.") from e
41
+ start = today - dt.timedelta(days=days)
42
+ return (start, today)
@@ -5,7 +5,7 @@ from typing import List, Optional, Dict, Union, Tuple, Set, Iterator, ClassVar
5
5
  import pandas as pd
6
6
 
7
7
  from sibi_dst.utils import ManagedResource
8
- from .date_utils import FileAgeChecker
8
+ from . import FileAgeChecker
9
9
 
10
10
 
11
11
  class UpdatePlanner(ManagedResource):
@@ -144,7 +144,7 @@ class UpdatePlanner(ManagedResource):
144
144
  console = Console()
145
145
  with console.capture() as capture:
146
146
  console.print(table)
147
- self.logger.info(f"Full Update Plan:\n{capture.get().strip()}")
147
+ self.logger.info(f"Full Update Plan:\n{capture.get().strip()}", extra={"date_of_update": self.reference_date.strftime('%Y-%m-%d'), "dataclass": self.description,"action_module_name": "update_plan"})
148
148
  self._printed_this_run = True
149
149
 
150
150
  def get_tasks_by_priority(self) -> Iterator[Tuple[int, List[dt.date]]]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sibi-dst
3
- Version: 2025.8.1
3
+ Version: 2025.8.3
4
4
  Summary: Data Science Toolkit
5
5
  Author: Luis Valverde
6
6
  Author-email: lvalverdeb@gmail.com
@@ -1,9 +1,10 @@
1
1
  sibi_dst/__init__.py,sha256=D01Z2Ds4zES8uz5Zp7qOWD0EcfCllWgew7AWt2X1SQg,445
2
- sibi_dst/df_helper/__init__.py,sha256=Jur_MO8RGPkVw0CS3XH5YIWv-d922DC_FwRDTvHHV6Y,432
3
- sibi_dst/df_helper/_artifact_updater_multi_wrapper.py,sha256=1e_qKEGTA94wnGp1wZp9ldSU39LXL2N34IETIcRxX6s,11790
4
- sibi_dst/df_helper/_df_helper.py,sha256=oBfC5syyHpTsUgjpJ2yjir6KHIqpJVbt4t54_Pum8mo,26868
5
- sibi_dst/df_helper/_parquet_artifact.py,sha256=DAo6_Eu_vrbGfQ1IyN6X_wxCF63G4O1UpR6p2bDo8cE,11394
6
- sibi_dst/df_helper/_parquet_reader.py,sha256=m98C0TZRroOXvVc2LpEuElrJnquGlR81E1gjI7v1hi4,3102
2
+ sibi_dst/df_helper/__init__.py,sha256=CyDXtFhRnMrycktxNO8jGGkP0938QiScl56kMZS1Sf8,578
3
+ sibi_dst/df_helper/_artifact_updater_async.py,sha256=0lUwel-IkmKewRnmMv9GtuT-P6SivkIKtgOHvKchHlc,8462
4
+ sibi_dst/df_helper/_artifact_updater_threaded.py,sha256=M5GNZismOqMmBrcyfolP1DPv87VILQf_P18is_epn50,7238
5
+ sibi_dst/df_helper/_df_helper.py,sha256=uXG7Ku8ttHuP2kVlMVilek6tkTzpKCJGhw-O0K1JS18,27550
6
+ sibi_dst/df_helper/_parquet_artifact.py,sha256=tqYOjwxHV1MsADmn-RNFuVI_RrEvvmCJHZieRcsVXuc,12334
7
+ sibi_dst/df_helper/_parquet_reader.py,sha256=tFq0OQVczozbKZou93vscokp2R6O2DIJ1zHbZqVjagc,3069
7
8
  sibi_dst/df_helper/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
9
  sibi_dst/df_helper/backends/http/__init__.py,sha256=d1pfgYxbiYg7E0Iw8RbJ7xfqIfJShqqTBQQGU_S6OOo,105
9
10
  sibi_dst/df_helper/backends/http/_http_config.py,sha256=eGPFdqZ5M3Tscqx2P93B6XoBEEzlmdt7yNg7PXUQnNQ,4726
@@ -19,7 +20,7 @@ sibi_dst/df_helper/backends/sqlalchemy/_model_registry.py,sha256=MHk64f5WDOKHQ_L
19
20
  sibi_dst/df_helper/backends/sqlalchemy/_sql_model_builder.py,sha256=RiCaVPME5wzgZ9xUGY0JOs_c2C0KcDIbTeMGpPupIa0,5242
20
21
  sibi_dst/df_helper/core/__init__.py,sha256=LfmTqFh6GUZup-g95bcXgAxX7J5Hkve7ftLE_CJg_AE,409
21
22
  sibi_dst/df_helper/core/_defaults.py,sha256=9UMEMu2wXznO5UzEhnQ82f_ZazZ20JRyRXIi3HP3gDw,4043
22
- sibi_dst/df_helper/core/_filter_handler.py,sha256=Pmbzygry2mpkNPVS7DBMulHpAb1yYZNFqUU0bJTWJF0,11214
23
+ sibi_dst/df_helper/core/_filter_handler.py,sha256=CYyeSmCyy7qVw_duRfBeGzEKaSQyyM-ZN9U8KsjwxXM,14295
23
24
  sibi_dst/df_helper/core/_params_config.py,sha256=DYx2drDz3uF-lSPzizPkchhy-kxRrQKE5FQRxcEWsac,6736
24
25
  sibi_dst/df_helper/core/_query_config.py,sha256=1ApqmuSGXTC3CdF-xMsSbCa3V2Z5hOP3Wq5huhzZwqY,439
25
26
  sibi_dst/df_helper/data_cleaner.py,sha256=lkxQoXLvGzXCicFUimnA5nen5qkrO1oxgl_p2Be2o8w,5183
@@ -36,24 +37,27 @@ sibi_dst/osmnx_helper/route_path_builder.py,sha256=XJJyu4YXegAkCRjE-knyQncwXaxDV
36
37
  sibi_dst/osmnx_helper/utils.py,sha256=HfxrmXVPq3akf68SiwncbAp7XI1ER-zp8YN_doh7YaY,20679
37
38
  sibi_dst/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
39
  sibi_dst/tests/test_data_wrapper_class.py,sha256=6uFmZR2DxnxQz49L5jT2ehlKvlLnpUHMLFB_PqqUq7k,3336
39
- sibi_dst/utils/__init__.py,sha256=PQsG188_lnqgSFljkCc15Nyv933HnvmQ7XYs02m77Vc,1217
40
- sibi_dst/utils/base.py,sha256=Ar-2cCC6INdVvtEXUTf6vSFRtWjW--ohnk7Pg3VNfCk,11709
40
+ sibi_dst/utils/__init__.py,sha256=vShNCOMPw8KKwlb4tq5XGrpjqakJ_OE8YDc_xDAWAxI,1302
41
+ sibi_dst/utils/base.py,sha256=IyObjZ7AaE-YjVU0RLIXNCnQKWwzi5NH2I6D1KfcIyk,8716
42
+ sibi_dst/utils/business_days.py,sha256=dP0Xj4FhTBIvZZrZYLOHZl5zOpDAgWkD4p_1a7BOT7I,8461
41
43
  sibi_dst/utils/clickhouse_writer.py,sha256=pE-igxddDdxekJywsaWQqKlGXIvLjPMpoFBUJ24t9Tw,20255
42
44
  sibi_dst/utils/credentials.py,sha256=cHJPPsmVyijqbUQIq7WWPe-lIallA-mI5RAy3YUuRME,1724
43
45
  sibi_dst/utils/data_from_http_source.py,sha256=AcpKNsqTgN2ClNwuhgUpuNCx62r5_DdsAiKY8vcHEBA,1867
44
46
  sibi_dst/utils/data_utils.py,sha256=7bLidEjppieNoozDFb6OuRY0W995cxg4tiGAlkGfePI,7768
45
- sibi_dst/utils/data_wrapper.py,sha256=mLQJmoXY5ywT0rtEw2Gf1pd0EomZYFiXuZhBY0LRvWg,19561
46
- sibi_dst/utils/date_utils.py,sha256=txRVVf_7MVVc7DVaECwABe0gueRzbL47quOvKKARhi0,18071
47
+ sibi_dst/utils/data_wrapper.py,sha256=EQyk0KniZYRTLncoYsAt5-KgdtU6McQxdaTzeNg03t8,21247
48
+ sibi_dst/utils/date_utils.py,sha256=hBVWu9_cqiZ-XsLR7QY9Iek09DQKLwrY1ZlYxWlXj7g,31101
47
49
  sibi_dst/utils/df_utils.py,sha256=bQGromLOEdRTvbVVcuHq0vQ0fIgqhwOoD_eIp5v7VEY,10899
50
+ sibi_dst/utils/file_age_checker.py,sha256=44B3lwH_PLwzMfiKkgvJKjKx-qSgITIXxKfNbdf_VeA,11552
48
51
  sibi_dst/utils/file_utils.py,sha256=cm__02IKCfEOzAKAZwdNIjnRL8H4XtPa6hKcj510pto,1310
49
52
  sibi_dst/utils/filepath_generator.py,sha256=Ke_OwBjLJkNMeOP0QjbLIZpSMkzhAIxKyf4hZ5P5re0,12916
50
53
  sibi_dst/utils/log_utils.py,sha256=daKptJtZW10UTezT6XAjcOabi94ukNVzfeeLDQ0O9hQ,27897
51
54
  sibi_dst/utils/manifest_manager.py,sha256=9y4cV-Ig8O-ekhApp_UObTY-cTsl-bGnvKIThItEzg4,7394
52
55
  sibi_dst/utils/parquet_saver.py,sha256=aYBlijqPAn-yuJXhmaRIteAN_IAQZvPh8I8Os2TLGgI,4861
56
+ sibi_dst/utils/periods.py,sha256=8eTGi-bToa6_a8Vwyg4fkBPryyzft9Nzy-3ToxjqC8c,1434
53
57
  sibi_dst/utils/phone_formatter.py,sha256=oeM22nLjhObENrpItCNeVpkYS4pXRm5hSxdk0M4nvwU,4580
54
58
  sibi_dst/utils/storage_config.py,sha256=uaCBF8rgCeYkk-lxVSCjsic8O8HJKAu455MR-OBliCo,4325
55
59
  sibi_dst/utils/storage_manager.py,sha256=yyZqT8XjTf4MKFrfznCmxXxOYz_TiWgtQhzqPoXR9So,6569
56
- sibi_dst/utils/update_planner.py,sha256=aeCpCK_uhkOeddP-wmV56Ib-4uhMgJlEjDUHI6R8y14,11346
60
+ sibi_dst/utils/update_planner.py,sha256=smlMHpr1p8guZnP5SyzCe6RsC-XkPOJWIsdeospUyb0,11471
57
61
  sibi_dst/utils/webdav_client.py,sha256=D9J5d1f1qQwHGm5FE5AMVpOPwcU5oD7K8JZoKGP8NpM,5811
58
62
  sibi_dst/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
63
  sibi_dst/v2/df_helper/__init__.py,sha256=XuH6jKYAPg2DdRbsxxBSxp9X3x-ARyaT0xe27uILrVo,99
@@ -75,6 +79,6 @@ sibi_dst/v2/df_helper/core/_params_config.py,sha256=DYx2drDz3uF-lSPzizPkchhy-kxR
75
79
  sibi_dst/v2/df_helper/core/_query_config.py,sha256=Y8LVSyaKuVkrPluRDkQoOwuXHQxner1pFWG3HPfnDHM,441
76
80
  sibi_dst/v2/utils/__init__.py,sha256=6H4cvhqTiFufnFPETBF0f8beVVMpfJfvUs6Ne0TQZNY,58
77
81
  sibi_dst/v2/utils/log_utils.py,sha256=rfk5VsLAt-FKpv6aPTC1FToIPiyrnHAFFBAkHme24po,4123
78
- sibi_dst-2025.8.1.dist-info/METADATA,sha256=1Pakz10A8gfHDQmnGcolkUqHgjE8W_DvyBDdxoZjyFU,2610
79
- sibi_dst-2025.8.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
80
- sibi_dst-2025.8.1.dist-info/RECORD,,
82
+ sibi_dst-2025.8.3.dist-info/METADATA,sha256=zdQXSnLpJ6bVQPpI-N4fnwB2ajCzyyRrGFzmEfpzjvk,2610
83
+ sibi_dst-2025.8.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
84
+ sibi_dst-2025.8.3.dist-info/RECORD,,