sdmxlib 0.16.1__tar.gz → 0.16.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.
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/PKG-INFO +1 -1
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/pyproject.toml +1 -1
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/code_query.py +64 -17
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/polars.py +88 -9
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/storage/lazy.py +1 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/README.md +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/client.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/federated.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/filters.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/policy.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/providers.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/query.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/registry.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/api/session.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/catalog.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/data_store.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_csv/reader.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml21/reader.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/local/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/local/_data_store.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/local/_registry.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/annotations.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/base.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/binding.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/category.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/codelist.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/collections.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/concept.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/constraint.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/convert.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/dataflow.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/dataset.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/datastructure.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/expr.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/hierarchy.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/in_memory_registry.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/istring.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/mapping.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/message.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/metadataflow.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/metadataset.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/metadatastructure.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/organisation.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/provision.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/ref.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/registry.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/representation.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/urn.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/model/validation.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/py.typed +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/storage/__init__.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/storage/readers.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/storage/schema.py +0 -0
- {sdmxlib-0.16.1 → sdmxlib-0.16.2}/src/sdmxlib/storage/writers.py +0 -0
|
@@ -355,6 +355,7 @@ class CodeQuery:
|
|
|
355
355
|
predicates: tuple[Predicate, ...] = ()
|
|
356
356
|
sort_keys: tuple[_SortKey, ...] = ()
|
|
357
357
|
limit_n: int | None = None
|
|
358
|
+
annotation_types: tuple[str, ...] = () # () = no annotations loaded
|
|
358
359
|
|
|
359
360
|
# ── Composition ─────────────────────────────────────────────────────
|
|
360
361
|
|
|
@@ -377,6 +378,21 @@ class CodeQuery:
|
|
|
377
378
|
"""Return a new query capped at ``n`` rows."""
|
|
378
379
|
return attrs.evolve(self, limit_n=n)
|
|
379
380
|
|
|
381
|
+
def with_annotations(self, *types: str) -> CodeQuery:
|
|
382
|
+
"""Return a new query that hydrates the listed annotation types on collect.
|
|
383
|
+
|
|
384
|
+
By default ``.collect()`` returns ``Code`` objects with empty
|
|
385
|
+
annotations — loading them eagerly is expensive on large
|
|
386
|
+
codelists (one round-trip + ``Annotation`` per row). Pass the
|
|
387
|
+
annotation types you actually need:
|
|
388
|
+
|
|
389
|
+
cl.codes.with_annotations("geo_level").collect()
|
|
390
|
+
|
|
391
|
+
Pass no types to clear; pass ``"*"`` (a single asterisk) to load
|
|
392
|
+
every annotation type for matched codes.
|
|
393
|
+
"""
|
|
394
|
+
return attrs.evolve(self, annotation_types=types)
|
|
395
|
+
|
|
380
396
|
# ── Materialisation ─────────────────────────────────────────────────
|
|
381
397
|
|
|
382
398
|
def collect(self) -> list[Code]:
|
|
@@ -384,14 +400,23 @@ class CodeQuery:
|
|
|
384
400
|
|
|
385
401
|
``Code.name`` and ``Code.description`` carry the full
|
|
386
402
|
``InternationalString``; no language is selected here.
|
|
403
|
+
|
|
404
|
+
Annotations are **not** loaded by default. Call
|
|
405
|
+
``.with_annotations(*types)`` first to hydrate the types you
|
|
406
|
+
need — loading every annotation for every code adds a second
|
|
407
|
+
SQL round-trip and per-row Python object construction that
|
|
408
|
+
dominates on large codelists.
|
|
387
409
|
"""
|
|
388
410
|
sql, params = _compile(self)
|
|
389
411
|
rows = self._conn.execute(sql, params).fetchall()
|
|
390
412
|
if not rows:
|
|
391
413
|
return []
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
414
|
+
if self.annotation_types:
|
|
415
|
+
urns = [r[4] for r in rows]
|
|
416
|
+
anns_by_urn = _fetch_annotations_for(self._conn, urns, types=self.annotation_types)
|
|
417
|
+
return [_row_to_code(row, anns_by_urn.get(row[4], Annotations())) for row in rows]
|
|
418
|
+
empty = Annotations()
|
|
419
|
+
return [_row_to_code(row, empty) for row in rows]
|
|
395
420
|
|
|
396
421
|
def ids(self) -> list[str]:
|
|
397
422
|
"""Compile to SQL, execute, and return just the code ids."""
|
|
@@ -427,8 +452,7 @@ class CodeQuery:
|
|
|
427
452
|
).fetchone()
|
|
428
453
|
if row is None:
|
|
429
454
|
raise KeyError(key)
|
|
430
|
-
|
|
431
|
-
return _row_to_code(row, anns)
|
|
455
|
+
return _row_to_code(row, self._anns_for_one(row[4]))
|
|
432
456
|
|
|
433
457
|
offset = key if key >= 0 else len(self) + key
|
|
434
458
|
row = self._conn.execute(
|
|
@@ -443,8 +467,21 @@ class CodeQuery:
|
|
|
443
467
|
).fetchone()
|
|
444
468
|
if row is None:
|
|
445
469
|
raise IndexError(key)
|
|
446
|
-
|
|
447
|
-
|
|
470
|
+
return _row_to_code(row, self._anns_for_one(row[4]))
|
|
471
|
+
|
|
472
|
+
def _anns_for_one(self, code_urn: str) -> Annotations:
|
|
473
|
+
"""Return annotations for a single code, honouring ``annotation_types``.
|
|
474
|
+
|
|
475
|
+
Single-code reads (``cl.codes["A"]``) are cheap enough that we
|
|
476
|
+
load whichever types the query is configured for. Default
|
|
477
|
+
``annotation_types=()`` skips the round-trip entirely.
|
|
478
|
+
"""
|
|
479
|
+
if not self.annotation_types:
|
|
480
|
+
return Annotations()
|
|
481
|
+
return _fetch_annotations_for(self._conn, [code_urn], types=self.annotation_types).get(
|
|
482
|
+
code_urn,
|
|
483
|
+
Annotations(),
|
|
484
|
+
)
|
|
448
485
|
|
|
449
486
|
def __contains__(self, key: object) -> bool:
|
|
450
487
|
if isinstance(key, str):
|
|
@@ -807,20 +844,30 @@ def _row_to_code(row: Sequence[Any], annotations: Annotations) -> Code:
|
|
|
807
844
|
def _fetch_annotations_for(
|
|
808
845
|
conn: duckdb.DuckDBPyConnection,
|
|
809
846
|
code_urns: list[str],
|
|
847
|
+
*,
|
|
848
|
+
types: tuple[str, ...] = ("*",),
|
|
810
849
|
) -> dict[str, Annotations]:
|
|
811
|
-
"""Bulk-load annotations + their text for many code URNs in two queries.
|
|
850
|
+
"""Bulk-load annotations + their text for many code URNs in two queries.
|
|
851
|
+
|
|
852
|
+
``types`` filters which annotation types to load. ``("*",)`` (default)
|
|
853
|
+
loads every type. Pass an explicit tuple of types to project just
|
|
854
|
+
those, e.g. ``types=("geo_level",)``.
|
|
855
|
+
"""
|
|
812
856
|
if not code_urns:
|
|
813
857
|
return {}
|
|
814
858
|
placeholders = ", ".join("?" for _ in code_urns)
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
859
|
+
sql_parts = [
|
|
860
|
+
"SELECT id, owner_urn, annotation_id, title, annotation_type, url, ordinal",
|
|
861
|
+
"FROM sdmx.annotation",
|
|
862
|
+
f"WHERE owner_urn IN ({placeholders})",
|
|
863
|
+
]
|
|
864
|
+
sql_params: list[object] = list(code_urns)
|
|
865
|
+
if types and "*" not in types:
|
|
866
|
+
type_placeholders = ", ".join("?" for _ in types)
|
|
867
|
+
sql_parts.append(f"AND annotation_type IN ({type_placeholders})")
|
|
868
|
+
sql_params.extend(types)
|
|
869
|
+
sql_parts.append("ORDER BY owner_urn, ordinal")
|
|
870
|
+
ann_rows = conn.execute(" ".join(sql_parts), sql_params).fetchall()
|
|
824
871
|
if not ann_rows:
|
|
825
872
|
return {}
|
|
826
873
|
|
|
@@ -19,7 +19,8 @@ directly for standalone use::
|
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
import weakref
|
|
23
|
+
from typing import TYPE_CHECKING, Any
|
|
23
24
|
|
|
24
25
|
import polars as pl
|
|
25
26
|
|
|
@@ -168,6 +169,79 @@ def validation_report(
|
|
|
168
169
|
)
|
|
169
170
|
|
|
170
171
|
|
|
172
|
+
# ── Label-map cache ─────────────────────────────────────────────────────
|
|
173
|
+
#
|
|
174
|
+
# slpl.label() and _label_view() are hot paths. Building the id→label
|
|
175
|
+
# dict by iterating ``codelist.codes`` materialises a Code object per
|
|
176
|
+
# row, which dominates on large codelists (CL_GEOGRAPHY: 78k codes →
|
|
177
|
+
# ~16s per call). We cache the dict per (codelist instance, lang) pair.
|
|
178
|
+
#
|
|
179
|
+
# Identity is the codelist *object* (weakref so the cache doesn't pin
|
|
180
|
+
# instances). When the codelist is garbage-collected the cache entry
|
|
181
|
+
# goes with it. A fresh ingest produces a new instance and a fresh
|
|
182
|
+
# cache slot, so re-ingestion is automatically correct without a
|
|
183
|
+
# manual invalidation call.
|
|
184
|
+
|
|
185
|
+
_LABEL_MAP_CACHE: weakref.WeakKeyDictionary[object, dict[str, dict[str, str]]] = weakref.WeakKeyDictionary()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def clear_label_cache() -> None:
|
|
189
|
+
"""Drop the cached id→label maps used by ``label`` / ``_label_view``.
|
|
190
|
+
|
|
191
|
+
The cache is normally invalidated automatically when the underlying
|
|
192
|
+
codelist instance is garbage-collected. Call this explicitly in
|
|
193
|
+
tests or other contexts that want to force a rebuild.
|
|
194
|
+
"""
|
|
195
|
+
_LABEL_MAP_CACHE.clear()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _component_label_map(component: object, lang: str) -> dict[str, str] | None:
|
|
199
|
+
"""Return ``{code_id: label}`` for a resolved enumerated component.
|
|
200
|
+
|
|
201
|
+
Fast path: when ``component.representation()`` is a
|
|
202
|
+
:class:`~sdmxlib.local.LazyCodelist` (or anything with a
|
|
203
|
+
``code_map(*, lang)`` method), use direct-SQL projection — single
|
|
204
|
+
DuckDB query, no Code/InternationalString construction. Falls back
|
|
205
|
+
to in-memory iteration for plain :class:`Codelist` instances.
|
|
206
|
+
|
|
207
|
+
Returns ``None`` when the component is not enumerated or its
|
|
208
|
+
representation Ref is unresolved.
|
|
209
|
+
"""
|
|
210
|
+
from typing import cast # noqa: PLC0415
|
|
211
|
+
|
|
212
|
+
rep = getattr(component, "representation", None)
|
|
213
|
+
if not (isinstance(rep, Ref) and rep.resolved):
|
|
214
|
+
return None
|
|
215
|
+
cl: object = rep()
|
|
216
|
+
try:
|
|
217
|
+
per_lang = _LABEL_MAP_CACHE.get(cl)
|
|
218
|
+
except TypeError:
|
|
219
|
+
# Some codelist instances (e.g. attrs frozen) aren't weakly
|
|
220
|
+
# referenceable. Skip caching for those.
|
|
221
|
+
per_lang = None
|
|
222
|
+
if per_lang is not None and lang in per_lang:
|
|
223
|
+
return per_lang[lang]
|
|
224
|
+
code_map = getattr(cl, "code_map", None)
|
|
225
|
+
mapping: dict[str, str]
|
|
226
|
+
if callable(code_map):
|
|
227
|
+
# Fast path: direct SQL projection (LazyCodelist).
|
|
228
|
+
result = cast("dict[str, str]", code_map(lang=lang))
|
|
229
|
+
mapping = dict(result)
|
|
230
|
+
else:
|
|
231
|
+
# Fallback: in-memory codelist iteration.
|
|
232
|
+
codes = cast("Any", cl).codes
|
|
233
|
+
mapping = {c.id: c.name.get(lang) or c.id for c in codes}
|
|
234
|
+
try:
|
|
235
|
+
if per_lang is None:
|
|
236
|
+
_LABEL_MAP_CACHE[cl] = {lang: mapping}
|
|
237
|
+
else:
|
|
238
|
+
per_lang[lang] = mapping
|
|
239
|
+
except TypeError:
|
|
240
|
+
# Codelist isn't weakly referenceable — return uncached.
|
|
241
|
+
pass
|
|
242
|
+
return mapping
|
|
243
|
+
|
|
244
|
+
|
|
171
245
|
def _label_view(
|
|
172
246
|
lf: pl.LazyFrame,
|
|
173
247
|
dsd: DataStructure,
|
|
@@ -182,19 +256,20 @@ def _label_view(
|
|
|
182
256
|
``pl.Enum`` (DSD-defined order preserved). Non-enumerated columns are
|
|
183
257
|
unchanged.
|
|
184
258
|
"""
|
|
185
|
-
enum_map: dict[str,
|
|
259
|
+
enum_map: dict[str, dict[str, str]] = {}
|
|
186
260
|
md = [dsd.measure_dimension] if dsd.measure_dimension is not None else []
|
|
187
261
|
for comp in [*dsd.dimensions, *md, *dsd.attributes, *dsd.measures]:
|
|
188
|
-
|
|
262
|
+
mapping = _component_label_map(comp, lang)
|
|
263
|
+
if mapping is None:
|
|
189
264
|
continue
|
|
190
|
-
|
|
191
|
-
labels = [c.name.get(lang) or c.id for c in comp.representation().codes] # type: ignore[union-attr]
|
|
192
|
-
enum_map[comp.id] = (codes, labels)
|
|
265
|
+
enum_map[comp.id] = mapping
|
|
193
266
|
|
|
194
267
|
select_exprs = []
|
|
195
268
|
for col_name in lf.collect_schema().names():
|
|
196
269
|
if col_name in enum_map:
|
|
197
|
-
|
|
270
|
+
mapping = enum_map[col_name]
|
|
271
|
+
codes = list(mapping.keys())
|
|
272
|
+
labels = list(mapping.values())
|
|
198
273
|
unique_labels = list(dict.fromkeys(labels))
|
|
199
274
|
select_exprs.append(
|
|
200
275
|
pl.col(col_name)
|
|
@@ -222,6 +297,10 @@ def label(
|
|
|
222
297
|
appends a ``{col}{suffix}`` column with the code label in ``lang``.
|
|
223
298
|
Codes with no label fall back to the code id.
|
|
224
299
|
|
|
300
|
+
Label maps are cached per ``(codelist_urn, lang)`` after the first
|
|
301
|
+
call — call :func:`clear_label_cache` after re-ingesting a codelist
|
|
302
|
+
in long-running processes.
|
|
303
|
+
|
|
225
304
|
Example:
|
|
226
305
|
labelled = slpl.label(pl.scan_csv("data.csv"), dsd, lang="en").collect()
|
|
227
306
|
"""
|
|
@@ -230,9 +309,9 @@ def label(
|
|
|
230
309
|
for comp in [*dsd.dimensions, *mds, *dsd.attributes, *dsd.measures]:
|
|
231
310
|
if comp.id not in schema_cols:
|
|
232
311
|
continue
|
|
233
|
-
|
|
312
|
+
mapping = _component_label_map(comp, lang)
|
|
313
|
+
if mapping is None:
|
|
234
314
|
continue
|
|
235
|
-
mapping = {c.id: c.name.get(lang) or c.id for c in comp.representation().codes} # type: ignore[union-attr]
|
|
236
315
|
old = list(mapping.keys())
|
|
237
316
|
new = list(mapping.values())
|
|
238
317
|
label_col = f"{comp.id}{suffix}"
|
|
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
|