satif-ai 0.2.8__py3-none-any.whl → 0.2.10__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.
- satif_ai/__init__.py +19 -0
- satif_ai/adapters/tidy.py +21 -40
- satif_ai/standardize.py +112 -0
- satif_ai/standardizers/ai.py +481 -0
- satif_ai/standardizers/ai_csv.py +5 -2
- satif_ai/standardizers/ai_xlsx.py +372 -0
- satif_ai/transform.py +155 -0
- satif_ai/{code_builders/transformation.py → transformation_builders/syncpulse.py} +22 -29
- satif_ai/utils/__init__.py +5 -0
- satif_ai/utils/merge_sdif.py +535 -0
- satif_ai/utils/openai_mcp.py +97 -0
- satif_ai/utils/zip.py +120 -0
- {satif_ai-0.2.8.dist-info → satif_ai-0.2.10.dist-info}/METADATA +6 -4
- satif_ai-0.2.10.dist-info/RECORD +20 -0
- satif_ai/code_builders/adaptation.py +0 -9
- satif_ai-0.2.8.dist-info/RECORD +0 -13
- /satif_ai/{code_builders → transformation_builders}/__init__.py +0 -0
- {satif_ai-0.2.8.dist-info → satif_ai-0.2.10.dist-info}/LICENSE +0 -0
- {satif_ai-0.2.8.dist-info → satif_ai-0.2.10.dist-info}/WHEEL +0 -0
- {satif_ai-0.2.8.dist-info → satif_ai-0.2.10.dist-info}/entry_points.txt +0 -0
satif_ai/utils/zip.py
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import zipfile
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import List, Tuple
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
# Constants for ZIP file processing, kept local to this utility or passed as args if needed
|
10
|
+
_IGNORED_ZIP_MEMBER_PREFIXES = ("__MACOSX/",)
|
11
|
+
_IGNORED_ZIP_FILENAME_PREFIXES = ("._",)
|
12
|
+
_IGNORED_ZIP_FILENAMES = (".DS_Store",)
|
13
|
+
|
14
|
+
|
15
|
+
async def extract_zip_archive_async(
|
16
|
+
zip_path: Path,
|
17
|
+
extract_to: Path,
|
18
|
+
ignored_member_prefixes: Tuple[str, ...] = _IGNORED_ZIP_MEMBER_PREFIXES,
|
19
|
+
ignored_filename_prefixes: Tuple[str, ...] = _IGNORED_ZIP_FILENAME_PREFIXES,
|
20
|
+
ignored_filenames: Tuple[str, ...] = _IGNORED_ZIP_FILENAMES,
|
21
|
+
) -> List[Path]:
|
22
|
+
"""
|
23
|
+
Asynchronously extracts a ZIP archive to a specified directory, filtering out ignored files.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
zip_path: Path to the ZIP archive.
|
27
|
+
extract_to: Directory where the contents will be extracted.
|
28
|
+
ignored_member_prefixes: Tuple of member path prefixes to ignore.
|
29
|
+
ignored_filename_prefixes: Tuple of filename prefixes to ignore.
|
30
|
+
ignored_filenames: Tuple of exact filenames to ignore.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
A list of paths to the successfully extracted files.
|
34
|
+
|
35
|
+
Raises:
|
36
|
+
ValueError: If the zip_path is invalid or corrupted.
|
37
|
+
RuntimeError: If any other error occurs during extraction.
|
38
|
+
"""
|
39
|
+
|
40
|
+
def blocking_extract() -> List[Path]:
|
41
|
+
extracted_file_paths = []
|
42
|
+
logger.info(f"Extracting ZIP archive '{zip_path.name}' to '{extract_to}'...")
|
43
|
+
try:
|
44
|
+
extract_to.mkdir(
|
45
|
+
parents=True, exist_ok=True
|
46
|
+
) # Ensure extract_to directory exists
|
47
|
+
|
48
|
+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
49
|
+
# Security: Preliminary check for unsafe paths before extraction
|
50
|
+
for member_name in zip_ref.namelist():
|
51
|
+
if member_name.startswith(("/", "..")):
|
52
|
+
logger.error(
|
53
|
+
f"Skipping potentially unsafe path in ZIP: {member_name}"
|
54
|
+
)
|
55
|
+
# Depending on security policy, might raise an error here
|
56
|
+
continue
|
57
|
+
|
58
|
+
# Extract all members
|
59
|
+
zip_ref.extractall(extract_to)
|
60
|
+
|
61
|
+
# After extractall, collect all *file* paths, applying filters
|
62
|
+
# This second pass of filtering ensures that even if extractall creates them,
|
63
|
+
# we don't return paths to ignored files.
|
64
|
+
for root, _, files in extract_to.walk():
|
65
|
+
for filename in files:
|
66
|
+
full_path = root / filename
|
67
|
+
# Create a path relative to 'extract_to' to check against member prefixes
|
68
|
+
# This ensures that '__MACOSX/file.txt' is correctly ignored,
|
69
|
+
# not just a top-level '__MACOSX' directory.
|
70
|
+
try:
|
71
|
+
relative_path_to_check = full_path.relative_to(extract_to)
|
72
|
+
except ValueError:
|
73
|
+
# This can happen if full_path is not under extract_to,
|
74
|
+
# which ideally shouldn't occur if zip_ref.extractall worked as expected
|
75
|
+
# and target_path checks were effective.
|
76
|
+
logger.warning(
|
77
|
+
f"File {full_path} seems to be outside extraction root {extract_to}. Skipping."
|
78
|
+
)
|
79
|
+
continue
|
80
|
+
|
81
|
+
path_str_to_check_prefixes = str(relative_path_to_check)
|
82
|
+
|
83
|
+
if not (
|
84
|
+
any(
|
85
|
+
path_str_to_check_prefixes.startswith(p)
|
86
|
+
for p in ignored_member_prefixes
|
87
|
+
)
|
88
|
+
or any(
|
89
|
+
full_path.name.startswith(p)
|
90
|
+
for p in ignored_filename_prefixes
|
91
|
+
)
|
92
|
+
or full_path.name in ignored_filenames
|
93
|
+
):
|
94
|
+
extracted_file_paths.append(full_path)
|
95
|
+
else:
|
96
|
+
logger.debug(f"Ignoring file post-extraction: {full_path}")
|
97
|
+
|
98
|
+
if not extracted_file_paths:
|
99
|
+
logger.warning(
|
100
|
+
f"ZIP archive '{zip_path.name}' is empty or contains no processable files after filtering."
|
101
|
+
)
|
102
|
+
else:
|
103
|
+
logger.info(
|
104
|
+
f"Successfully extracted {len(extracted_file_paths)} file(s) from '{zip_path.name}'."
|
105
|
+
)
|
106
|
+
return extracted_file_paths
|
107
|
+
except zipfile.BadZipFile as e:
|
108
|
+
logger.error(
|
109
|
+
f"Invalid or corrupted ZIP file: {zip_path.name}", exc_info=True
|
110
|
+
)
|
111
|
+
raise ValueError(f"Invalid or corrupted ZIP file: {zip_path.name}") from e
|
112
|
+
except Exception as e:
|
113
|
+
logger.error(
|
114
|
+
f"Failed to extract ZIP archive '{zip_path.name}': {e}", exc_info=True
|
115
|
+
)
|
116
|
+
raise RuntimeError(
|
117
|
+
f"Unexpected error during ZIP extraction for '{zip_path.name}'"
|
118
|
+
) from e
|
119
|
+
|
120
|
+
return await asyncio.to_thread(blocking_extract)
|
@@ -1,17 +1,19 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: satif-ai
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.10
|
4
4
|
Summary: AI Agents for Satif
|
5
5
|
License: MIT
|
6
|
-
Author:
|
7
|
-
|
8
|
-
|
6
|
+
Author: Syncpulse
|
7
|
+
Maintainer: Bryan Djafer
|
8
|
+
Maintainer-email: bryan.djafer@syncpulse.fr
|
9
|
+
Requires-Python: >=3.10,<3.14
|
9
10
|
Classifier: License :: OSI Approved :: MIT License
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
11
12
|
Classifier: Programming Language :: Python :: 3.10
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
14
15
|
Classifier: Programming Language :: Python :: 3.13
|
16
|
+
Provides-Extra: xlsx
|
15
17
|
Requires-Dist: openai-agents (>=0.0.9,<0.0.10)
|
16
18
|
Requires-Dist: satif-sdk (>=0.1.0,<1.0.0)
|
17
19
|
Requires-Dist: sdif-mcp (>=0.1.0,<1.0.0)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
satif_ai/__init__.py,sha256=cqJ6Kd9IolVodPi9yOBPnfhYQXH5a1JgRB3HfLOtP_4,611
|
2
|
+
satif_ai/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
satif_ai/adapters/tidy.py,sha256=1g7Wcq8agAZhaAqQDhhD8yh3iO5gZ4mwdKHsiNN3hHY,18540
|
4
|
+
satif_ai/standardize.py,sha256=TgAB_nhcHY8zqlfT1PpgfgSswqdE-ly-dheQz-7NC7Q,5674
|
5
|
+
satif_ai/standardizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
satif_ai/standardizers/ai.py,sha256=jtYM-ChjLtkpFaubz980CTCNAoC33iYxB3pq0_hn2lU,21045
|
7
|
+
satif_ai/standardizers/ai_csv.py,sha256=LbCRaLleujQRgSRRyt9ujbED-PIGRq1J8zRnejGM5nc,25437
|
8
|
+
satif_ai/standardizers/ai_xlsx.py,sha256=558Bzfy8WGuk5mdnjMvvtakQXcU3rmwK3ykPjpXKwmQ,15863
|
9
|
+
satif_ai/transform.py,sha256=g5XNeVCIKUgDW3UIhf02MN9xkXnWF3EJXS0Eig_hfD8,7677
|
10
|
+
satif_ai/transformation_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
satif_ai/transformation_builders/syncpulse.py,sha256=c59BZicNnqs3NDKpflBAPqw42pGb6nYB2Zps0ChGyaM,11368
|
12
|
+
satif_ai/utils/__init__.py,sha256=F-usaCt_vX872mXvtukuZdNMPnkVqDb8RaDgox2uow4,212
|
13
|
+
satif_ai/utils/merge_sdif.py,sha256=y4C6pgkdyer0QugroFKUck4Eud4Ap-tJzM-eclMo3Rw,25629
|
14
|
+
satif_ai/utils/openai_mcp.py,sha256=duCQZXG0mBs9DOOFIUvzraJhxD2IDzegWO9iOiLfFwY,3938
|
15
|
+
satif_ai/utils/zip.py,sha256=G_GK8629Iw0TLFCQJfnqOscv7MoKF5zdzxvEAbL7Gss,5186
|
16
|
+
satif_ai-0.2.10.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
|
17
|
+
satif_ai-0.2.10.dist-info/METADATA,sha256=O5QWv8YJFtB5AIniv0LRgmSgpEaRLVdlz8WHZAru1X8,719
|
18
|
+
satif_ai-0.2.10.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
19
|
+
satif_ai-0.2.10.dist-info/entry_points.txt,sha256=Mz2SwYALjktap1bF-Q3EWBgiZVNT6QJCVsCs_fCV33Y,43
|
20
|
+
satif_ai-0.2.10.dist-info/RECORD,,
|
satif_ai-0.2.8.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
satif_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
satif_ai/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
satif_ai/adapters/tidy.py,sha256=2oYj7Gz3vOQtzcpoJI4JbftWlMKvOWL8rdwthjg-zUE,19884
|
4
|
-
satif_ai/code_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
satif_ai/code_builders/adaptation.py,sha256=E29YM0S6pMtAfB0uzSUexoeWKwXfF8iJVyYUCKWQz5k,188
|
6
|
-
satif_ai/code_builders/transformation.py,sha256=5B7a6lDv-gqQo83F8fQeSw2gHpDgznoDfjXsASkLc60,11870
|
7
|
-
satif_ai/standardizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
satif_ai/standardizers/ai_csv.py,sha256=c0CKnIib610GgwGqaF8NaqT_P4pZ2BupO-BTSNuIhoc,25385
|
9
|
-
satif_ai-0.2.8.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
|
10
|
-
satif_ai-0.2.8.dist-info/METADATA,sha256=CZBbNd1A-KL8eoOzmmz7bW3ue4HGOC2Qic60wQ-v6z8,670
|
11
|
-
satif_ai-0.2.8.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
12
|
-
satif_ai-0.2.8.dist-info/entry_points.txt,sha256=Mz2SwYALjktap1bF-Q3EWBgiZVNT6QJCVsCs_fCV33Y,43
|
13
|
-
satif_ai-0.2.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|