etld 0.1.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.
- etld-0.1.2/PKG-INFO +44 -0
- etld-0.1.2/README.md +31 -0
- etld-0.1.2/etld/__init__.py +22 -0
- etld-0.1.2/etld/client.py +112 -0
- etld-0.1.2/etld/exceptions.py +19 -0
- etld-0.1.2/etld/models.py +53 -0
- etld-0.1.2/etld.egg-info/PKG-INFO +44 -0
- etld-0.1.2/etld.egg-info/SOURCES.txt +11 -0
- etld-0.1.2/etld.egg-info/dependency_links.txt +1 -0
- etld-0.1.2/etld.egg-info/requires.txt +1 -0
- etld-0.1.2/etld.egg-info/top_level.txt +1 -0
- etld-0.1.2/setup.cfg +4 -0
- etld-0.1.2/setup.py +22 -0
etld-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: etld
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Official Python SDK for the ETL-D API - Stateless Data Middleware for AI Agents.
|
|
5
|
+
Home-page: https://github.com/pablixnieto2/enrichermagic
|
|
6
|
+
Author: ETL-D Team
|
|
7
|
+
Author-email: hello@etl-d.net
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# ETL-D Python SDK
|
|
15
|
+
|
|
16
|
+
Limpia, normaliza y estructura datos globales (direcciones, nombres, importes) con nuestro motor de IA asíncrono.
|
|
17
|
+
|
|
18
|
+
## ¿Cómo funciona nuestra arquitectura asíncrona?
|
|
19
|
+
|
|
20
|
+
A diferencia de otras APIs que te bloquean el hilo esperando una respuesta, ETL-D utiliza un sistema de colas (Celery/Redis).
|
|
21
|
+
1. **Envío:** Tú envías un lote de miles de registros.
|
|
22
|
+
2. **Procesamiento:** El sistema te devuelve un `task_id` instantáneo.
|
|
23
|
+
3. **Polling automático:** Nuestro SDK se queda consultando el estado por ti (en segundo plano) hasta que el trabajo termina, devolviéndote los datos limpios directamente.
|
|
24
|
+
|
|
25
|
+
## Ejemplo de uso (El cliente se encarga del robot por ti)
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from etld import ETLDClient
|
|
29
|
+
|
|
30
|
+
client = ETLDClient(api_key="TU_API_KEY")
|
|
31
|
+
|
|
32
|
+
# Esta función envía los datos y se queda esperando (polling)
|
|
33
|
+
# hasta que el servidor dice "SUCCESS".
|
|
34
|
+
datos_limpios = client.batch_process(
|
|
35
|
+
items=[
|
|
36
|
+
"Calle 70A No. 11 - 12, Bogotá",
|
|
37
|
+
"152 Teheran-ro, Seoul"
|
|
38
|
+
],
|
|
39
|
+
entity_type="address"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
for resultado in datos_limpios:
|
|
43
|
+
print(resultado['address_latin'])
|
|
44
|
+
```
|
etld-0.1.2/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ETL-D Python SDK
|
|
2
|
+
|
|
3
|
+
Limpia, normaliza y estructura datos globales (direcciones, nombres, importes) con nuestro motor de IA asíncrono.
|
|
4
|
+
|
|
5
|
+
## ¿Cómo funciona nuestra arquitectura asíncrona?
|
|
6
|
+
|
|
7
|
+
A diferencia de otras APIs que te bloquean el hilo esperando una respuesta, ETL-D utiliza un sistema de colas (Celery/Redis).
|
|
8
|
+
1. **Envío:** Tú envías un lote de miles de registros.
|
|
9
|
+
2. **Procesamiento:** El sistema te devuelve un `task_id` instantáneo.
|
|
10
|
+
3. **Polling automático:** Nuestro SDK se queda consultando el estado por ti (en segundo plano) hasta que el trabajo termina, devolviéndote los datos limpios directamente.
|
|
11
|
+
|
|
12
|
+
## Ejemplo de uso (El cliente se encarga del robot por ti)
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from etld import ETLDClient
|
|
16
|
+
|
|
17
|
+
client = ETLDClient(api_key="TU_API_KEY")
|
|
18
|
+
|
|
19
|
+
# Esta función envía los datos y se queda esperando (polling)
|
|
20
|
+
# hasta que el servidor dice "SUCCESS".
|
|
21
|
+
datos_limpios = client.batch_process(
|
|
22
|
+
items=[
|
|
23
|
+
"Calle 70A No. 11 - 12, Bogotá",
|
|
24
|
+
"152 Teheran-ro, Seoul"
|
|
25
|
+
],
|
|
26
|
+
entity_type="address"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for resultado in datos_limpios:
|
|
30
|
+
print(resultado['address_latin'])
|
|
31
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .client import ETLDClient
|
|
2
|
+
from .models import ContextParams, AddressOutput, NameOutput, MoneyOutput
|
|
3
|
+
from .exceptions import (
|
|
4
|
+
ETLDBaseException,
|
|
5
|
+
ETLDAuthenticationError,
|
|
6
|
+
ETLDRateLimitError,
|
|
7
|
+
ETLDProcessingError,
|
|
8
|
+
ETLDValidationError
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"ETLDClient",
|
|
13
|
+
"ContextParams",
|
|
14
|
+
"AddressOutput",
|
|
15
|
+
"NameOutput",
|
|
16
|
+
"MoneyOutput",
|
|
17
|
+
"ETLDBaseException",
|
|
18
|
+
"ETLDAuthenticationError",
|
|
19
|
+
"ETLDRateLimitError",
|
|
20
|
+
"ETLDProcessingError",
|
|
21
|
+
"ETLDValidationError",
|
|
22
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
from typing import List, Optional, Dict, Any, Union
|
|
4
|
+
from .models import ContextParams, AddressOutput, NameOutput, MoneyOutput
|
|
5
|
+
from .exceptions import (
|
|
6
|
+
ETLDAuthenticationError,
|
|
7
|
+
ETLDRateLimitError,
|
|
8
|
+
ETLDProcessingError,
|
|
9
|
+
ETLDValidationError
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
class ETLDClient:
|
|
13
|
+
def __init__(self, api_key: str, base_url: str = "https://api.etl-d.net"):
|
|
14
|
+
self.api_key = api_key
|
|
15
|
+
self.base_url = base_url.rstrip("/")
|
|
16
|
+
self.session = requests.Session()
|
|
17
|
+
self.session.headers.update({
|
|
18
|
+
"X-API-KEY": self.api_key,
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
"User-Agent": "etld-python-sdk/0.1.0"
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
|
|
24
|
+
if response.status_code == 401 or response.status_code == 403:
|
|
25
|
+
raise ETLDAuthenticationError(f"Invalid API Key: {response.text}")
|
|
26
|
+
elif response.status_code == 429:
|
|
27
|
+
raise ETLDRateLimitError(f"Rate limit exceeded: {response.text}")
|
|
28
|
+
elif response.status_code == 422:
|
|
29
|
+
raise ETLDValidationError(f"Validation Error: {response.json().get('detail')}")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
response.raise_for_status()
|
|
33
|
+
except requests.exceptions.HTTPError as e:
|
|
34
|
+
raise ETLDProcessingError(f"API Request failed: {str(e)}")
|
|
35
|
+
|
|
36
|
+
return response.json()
|
|
37
|
+
|
|
38
|
+
def enrich_address(self, address: str, context: Optional[Union[Dict[str, Any], ContextParams]] = None) -> AddressOutput:
|
|
39
|
+
if isinstance(context, ContextParams):
|
|
40
|
+
context = context.to_dict()
|
|
41
|
+
|
|
42
|
+
payload = {
|
|
43
|
+
"address_line": address,
|
|
44
|
+
"context": context or {}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
data = self._handle_response(self.session.post(f"{self.base_url}/v1/enrich/address", json=payload))
|
|
48
|
+
return AddressOutput(**data)
|
|
49
|
+
|
|
50
|
+
def enrich_name(self, name: str, context: Optional[Union[Dict[str, Any], ContextParams]] = None) -> NameOutput:
|
|
51
|
+
if isinstance(context, ContextParams):
|
|
52
|
+
context = context.to_dict()
|
|
53
|
+
|
|
54
|
+
payload = {
|
|
55
|
+
"full_name": name,
|
|
56
|
+
"context": context or {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
data = self._handle_response(self.session.post(f"{self.base_url}/v1/enrich/name", json=payload))
|
|
60
|
+
return NameOutput(**data)
|
|
61
|
+
|
|
62
|
+
def enrich_amount(self, amount: str, context: Optional[Union[Dict[str, Any], ContextParams]] = None) -> MoneyOutput:
|
|
63
|
+
if isinstance(context, ContextParams):
|
|
64
|
+
context = context.to_dict()
|
|
65
|
+
|
|
66
|
+
payload = {
|
|
67
|
+
"amount_string": amount,
|
|
68
|
+
"context": context or {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
data = self._handle_response(self.session.post(f"{self.base_url}/v1/enrich/amount", json=payload))
|
|
72
|
+
return MoneyOutput(**data)
|
|
73
|
+
|
|
74
|
+
def batch_process(
|
|
75
|
+
self,
|
|
76
|
+
items: List[str],
|
|
77
|
+
entity_type: str,
|
|
78
|
+
context: Optional[Union[Dict[str, Any], ContextParams]] = None,
|
|
79
|
+
poll_interval: int = 2,
|
|
80
|
+
max_retries: int = 30
|
|
81
|
+
) -> List[Any]:
|
|
82
|
+
if isinstance(context, ContextParams):
|
|
83
|
+
context = context.to_dict()
|
|
84
|
+
|
|
85
|
+
payload = {
|
|
86
|
+
"items": items,
|
|
87
|
+
"context": context or {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# 1. Start Batch
|
|
91
|
+
start_data = self._handle_response(
|
|
92
|
+
self.session.post(f"{self.base_url}/v1/enrich/batch/{entity_type}", json=payload)
|
|
93
|
+
)
|
|
94
|
+
task_id = start_data.get("task_id")
|
|
95
|
+
|
|
96
|
+
# 2. Poll
|
|
97
|
+
retries = 0
|
|
98
|
+
while retries < max_retries:
|
|
99
|
+
status_data = self._handle_response(
|
|
100
|
+
self.session.get(f"{self.base_url}/v1/enrich/batch/{task_id}")
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
status = status_data.get("status")
|
|
104
|
+
if status == "SUCCESS":
|
|
105
|
+
return status_data.get("results", [])
|
|
106
|
+
elif status == "FAILURE":
|
|
107
|
+
raise ETLDProcessingError(f"Batch processing failed: {status_data.get('error')}")
|
|
108
|
+
|
|
109
|
+
time.sleep(poll_interval)
|
|
110
|
+
retries += 1
|
|
111
|
+
|
|
112
|
+
raise ETLDProcessingError(f"Batch processing timed out for task {task_id}")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class ETLDBaseException(Exception):
|
|
2
|
+
"""Base exception for ETL-D SDK"""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class ETLDAuthenticationError(ETLDBaseException):
|
|
6
|
+
"""Raised when API key is invalid or missing"""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class ETLDRateLimitError(ETLDBaseException):
|
|
10
|
+
"""Raised when API rate limit is exceeded"""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class ETLDProcessingError(ETLDBaseException):
|
|
14
|
+
"""Raised when there is an error processing the request"""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class ETLDValidationError(ETLDBaseException):
|
|
18
|
+
"""Raised when request validation fails"""
|
|
19
|
+
pass
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional, List, Any, Dict
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class ContextParams:
|
|
6
|
+
timezone: str = "UTC"
|
|
7
|
+
locale: str = "en_US"
|
|
8
|
+
target_currency: Optional[str] = None
|
|
9
|
+
transliterate: bool = False
|
|
10
|
+
|
|
11
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
12
|
+
return {k: v for k, v in self.__dict__.items() if v is not None}
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class AddressOutput:
|
|
16
|
+
street: Optional[str] = None
|
|
17
|
+
house_number: Optional[str] = None
|
|
18
|
+
floor_unit: Optional[str] = None
|
|
19
|
+
postal_code: Optional[str] = None
|
|
20
|
+
city: Optional[str] = None
|
|
21
|
+
state: Optional[str] = None
|
|
22
|
+
country: Optional[str] = None
|
|
23
|
+
type: Optional[str] = None
|
|
24
|
+
address_latin: Optional[str] = None
|
|
25
|
+
error: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class NameOutput:
|
|
29
|
+
title: Optional[str] = None
|
|
30
|
+
first: Optional[str] = None
|
|
31
|
+
middle: Optional[str] = None
|
|
32
|
+
last: Optional[str] = None
|
|
33
|
+
suffix: Optional[str] = None
|
|
34
|
+
nickname: Optional[str] = None
|
|
35
|
+
gender: Optional[str] = None
|
|
36
|
+
confidence: Optional[str] = None
|
|
37
|
+
name_latin: Optional[str] = None
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class MoneyOutput:
|
|
41
|
+
float_value: Optional[float] = None
|
|
42
|
+
currency: Optional[str] = None
|
|
43
|
+
currency_symbol: Optional[str] = None
|
|
44
|
+
clean_string: Optional[str] = None
|
|
45
|
+
conversion_required: Optional[bool] = None
|
|
46
|
+
error: Optional[str] = None
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class BatchResponse:
|
|
50
|
+
task_id: str
|
|
51
|
+
status: str
|
|
52
|
+
results: Optional[List[Any]] = None
|
|
53
|
+
error: Optional[str] = None
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: etld
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Official Python SDK for the ETL-D API - Stateless Data Middleware for AI Agents.
|
|
5
|
+
Home-page: https://github.com/pablixnieto2/enrichermagic
|
|
6
|
+
Author: ETL-D Team
|
|
7
|
+
Author-email: hello@etl-d.net
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# ETL-D Python SDK
|
|
15
|
+
|
|
16
|
+
Limpia, normaliza y estructura datos globales (direcciones, nombres, importes) con nuestro motor de IA asíncrono.
|
|
17
|
+
|
|
18
|
+
## ¿Cómo funciona nuestra arquitectura asíncrona?
|
|
19
|
+
|
|
20
|
+
A diferencia de otras APIs que te bloquean el hilo esperando una respuesta, ETL-D utiliza un sistema de colas (Celery/Redis).
|
|
21
|
+
1. **Envío:** Tú envías un lote de miles de registros.
|
|
22
|
+
2. **Procesamiento:** El sistema te devuelve un `task_id` instantáneo.
|
|
23
|
+
3. **Polling automático:** Nuestro SDK se queda consultando el estado por ti (en segundo plano) hasta que el trabajo termina, devolviéndote los datos limpios directamente.
|
|
24
|
+
|
|
25
|
+
## Ejemplo de uso (El cliente se encarga del robot por ti)
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from etld import ETLDClient
|
|
29
|
+
|
|
30
|
+
client = ETLDClient(api_key="TU_API_KEY")
|
|
31
|
+
|
|
32
|
+
# Esta función envía los datos y se queda esperando (polling)
|
|
33
|
+
# hasta que el servidor dice "SUCCESS".
|
|
34
|
+
datos_limpios = client.batch_process(
|
|
35
|
+
items=[
|
|
36
|
+
"Calle 70A No. 11 - 12, Bogotá",
|
|
37
|
+
"152 Teheran-ro, Seoul"
|
|
38
|
+
],
|
|
39
|
+
entity_type="address"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
for resultado in datos_limpios:
|
|
43
|
+
print(resultado['address_latin'])
|
|
44
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.25.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
etld
|
etld-0.1.2/setup.cfg
ADDED
etld-0.1.2/setup.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="etld",
|
|
5
|
+
version="0.1.2",
|
|
6
|
+
author="ETL-D Team",
|
|
7
|
+
author_email="hello@etl-d.net",
|
|
8
|
+
description="Official Python SDK for the ETL-D API - Stateless Data Middleware for AI Agents.",
|
|
9
|
+
long_description=open("README.md").read(),
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
url="https://github.com/pablixnieto2/enrichermagic",
|
|
12
|
+
packages=find_packages(),
|
|
13
|
+
install_requires=[
|
|
14
|
+
"requests>=2.25.0",
|
|
15
|
+
],
|
|
16
|
+
classifiers=[
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
],
|
|
21
|
+
python_requires='>=3.7',
|
|
22
|
+
)
|