creditpulse 0.0.1__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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 svelezsaman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: creditpulse
3
+ Version: 0.0.1
4
+ Summary: A pyhton library for financial analysis with external databases
5
+ Author-email: Santiago Velez <s.velez@saman-wm.com>
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: click==8.1.8
10
+ Requires-Dist: requests==2.32.4
11
+ Requires-Dist: pydantic==2.8.0
12
+ Requires-Dist: pylint==3.3.7
13
+ Dynamic: license-file
File without changes
@@ -0,0 +1,25 @@
1
+ """A clean Python project template with public API.
2
+
3
+ This package provides a simple, clean interface for users to import and use.
4
+
5
+ Example:
6
+ >>> from CreditPulse import Check
7
+ >>> client = Check()
8
+ >>> result_juridica = client.persona_juridica()
9
+ >>> result_natural = client.persona_natural()
10
+ """
11
+
12
+ # Import version
13
+ try:
14
+ from ._version import version as __version__
15
+ except ImportError:
16
+ __version__ = "unknown"
17
+
18
+ # Package metadata
19
+ __author__ = "Santiago Velez"
20
+ __email__ = "s.velez@saman-wm.com"
21
+ __description__ = "A python library for financial analysis"
22
+
23
+ from .check import Check
24
+
25
+ __all__ = ["Check"]
@@ -0,0 +1,39 @@
1
+ """
2
+ Clase abstracta para definir una base de datos
3
+ """
4
+
5
+ from creditpulse.bases_externas.schema import PersonType, CountryCode, GeneralDatabase
6
+ from creditpulse.requests.request_manager import RequestActionManager
7
+ from abc import ABC, abstractmethod
8
+
9
+
10
+ class Database(RequestActionManager, ABC):
11
+
12
+ @abstractmethod
13
+ def create_check(self, identificacion: str,
14
+ person_type: PersonType,
15
+ autorizacion_datos: bool = False,
16
+ pais: CountryCode = CountryCode.COLOMBIA
17
+ ) -> GeneralDatabase:
18
+ """
19
+ Implementacion de
20
+ :param identificacion:
21
+ :param person_type:
22
+ :param autorizacion_datos:
23
+ :param pais:
24
+ :return:
25
+ """
26
+
27
+ @abstractmethod
28
+ def get_name(self) -> str:
29
+ """
30
+ Da el nombre de la actual base de datos
31
+ :return:
32
+ """
33
+
34
+ @abstractmethod
35
+ def to_general(self) -> GeneralDatabase:
36
+ """
37
+ Traduce base de datos a general
38
+ :return:
39
+ """
@@ -0,0 +1,43 @@
1
+ """
2
+
3
+ External Database Schema standarization
4
+ """
5
+
6
+ from enum import Enum
7
+
8
+
9
+ from creditpulse.bases_externas.database import Database
10
+ from creditpulse.bases_externas.truora import Truora
11
+ from creditpulse.bases_externas.schema import CountryCode, PersonType
12
+
13
+
14
+ class BasesDeDatos(str, Enum):
15
+ """Supported country codes for checks"""
16
+ TRUORA = "tr" # International Lists
17
+
18
+
19
+ class ExternalDatabases:
20
+
21
+ def __init__(self, *bases: BasesDeDatos):
22
+
23
+ if len(bases) == 0:
24
+ raise Exception('Debe seleccionar al menos una base de datos')
25
+
26
+ self.data_bases: [Database] = []
27
+
28
+ for base in bases:
29
+ if base == BasesDeDatos.TRUORA:
30
+ self.data_bases.append(Truora())
31
+
32
+ def obtain_data(self, identificacion: str, natural: bool = True):
33
+ response = {}
34
+
35
+ for base in self.data_bases:
36
+ response[base.get_name()] = base.create_check(
37
+ identificacion=identificacion,
38
+ person_type=PersonType.PERSONA if natural else PersonType.COMPANIA,
39
+ pais=CountryCode.COLOMBIA,
40
+ autorizacion_datos=True
41
+ )
42
+
43
+ return response
@@ -0,0 +1,80 @@
1
+ """
2
+ Definicion de tipos de datos para base de datos externas
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Literal
7
+ from enum import Enum
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class CountryCode(str, Enum):
13
+ """Supported country codes for checks"""
14
+ ALL = "ALL" # International Lists
15
+ BRAZIL = "BR" # Brazil
16
+ COLOMBIA = "CO" # Colombia
17
+ CHILE = "CL" # Chile
18
+ COSTA_RICA = "CR" # Costa Rica
19
+ MEXICO = "MX" # Mexico
20
+ PERU = "PE" # Peru
21
+
22
+
23
+ class PersonType(str, Enum):
24
+ """Supported country codes for checks"""
25
+ PERSONA = "person" # Used to perform a background check on a person.
26
+ VEHICULO = "vehicle" # Used to perform a background check on a driver and their vehicle
27
+ COMPANIA = "company" # Used to perform a background check on a company
28
+
29
+
30
+ class CheckStatus(str, Enum):
31
+ """Status values for checks"""
32
+ NOT_STARTED = "not_started" # The check is enqueued and the data collection has not started yet
33
+ IN_PROGRESS = "in_progress" # Data is being collected but some data sources may have finished already
34
+ DELAYED = "delayed" # One or more data sources are taking a long time to query the data. Most data sources will have already finished
35
+ COMPLETED = "completed" # The check finished and 70% or more of the data sources did not end in error status
36
+ ERROR = "error" # The check finished and more than 30% of the data sources ended in error status
37
+
38
+
39
+ class TruoraCheck(BaseModel):
40
+ """
41
+ Definicion para Check de truora
42
+ """
43
+ check_id: str = Field(..., description="Unique identifier for the check")
44
+ country: CountryCode = Field(
45
+ ...,
46
+ description="""
47
+ Country code -
48
+ ALL for International Lists,
49
+ BR for Brazil,
50
+ CO for Colombia,
51
+ CL for Chile,
52
+ CR for Costa Rica,
53
+ MX for Mexico,
54
+ PE for Peru
55
+ """
56
+ )
57
+ creation_date: datetime = Field(..., description="When the check was created")
58
+ name_score: int = Field(..., description="Score for name matching")
59
+ id_score: int = Field(..., description="Score for ID matching")
60
+ score: int = Field(..., description="Overall check score")
61
+ status: CheckStatus = Field(..., description="Current status of the check")
62
+ update_date: datetime = Field(..., description="When the check was last updated")
63
+ billing_hub: str = Field(..., description="Billing hub identifier")
64
+ national_id: str = Field(..., description="National ID number being checked")
65
+ type: Literal["person"] = Field(..., description="Type of check (person, business, etc.)")
66
+
67
+
68
+ class TruoraCheckData(BaseModel):
69
+ """
70
+ Definicion para la respuesta de truora
71
+ """
72
+ check: TruoraCheck = Field(..., description="Check details")
73
+ details: str = Field(..., description="API endpoint for detailed check information")
74
+ self: str = Field(..., description="API endpoint for this check resource")
75
+
76
+
77
+ class GeneralDatabase(BaseModel):
78
+ score: int = Field(..., description="Overall check score")
79
+ creation_date: datetime = Field(..., description="When the check was created")
80
+ national_id: str = Field(..., description="National ID number being checked")
@@ -0,0 +1,194 @@
1
+ """
2
+ Request manager ara base de datos externa truora
3
+ """
4
+ import json
5
+ import os
6
+ import logging
7
+ from typing import Optional
8
+
9
+ import requests
10
+ from creditpulse.requests.request_manager import RequestManager
11
+ from creditpulse.bases_externas.schema import TruoraCheckData, CountryCode, PersonType, CheckStatus, GeneralDatabase
12
+ from creditpulse.common.error_messages import (
13
+ AutorizacionDatosPersonales,
14
+ TruoraApiKeyRequired,
15
+ TruoraGeneralError
16
+ )
17
+
18
+ from creditpulse.bases_externas.database import Database
19
+
20
+ # https://www.postman.com/truora-api-docs/truora-api-docs/collection/iwmyaus/truora-collection
21
+ TRUORA_API_VERSIONS = '/v1'
22
+ TRUORA_BASE_URL = 'https://api.checks.truora.com' + TRUORA_API_VERSIONS
23
+ CHECK_URL = TRUORA_BASE_URL + '/checks'
24
+ CHECK_URL_DETAILS = TRUORA_BASE_URL + '/checks/{}/details'
25
+ CHECK_SETTINGS_URL = ' https://api.checks.truora.com/v1/settings'
26
+
27
+ settings_data = {
28
+ 'names_matching_type': 'exact',
29
+ 'retries': True,
30
+ 'max_duration': '3m'
31
+ }
32
+
33
+
34
+ class Truora(Database):
35
+ """
36
+
37
+ Clase principal para consultar base de datos externa truora
38
+ """
39
+
40
+ def get_name(self) -> str:
41
+ return 'truora'
42
+
43
+ def _get_check(self, check_id: str):
44
+ return self.session.post(CHECK_URL + "/" + check_id)
45
+
46
+ def on_execute(self) -> requests.Response:
47
+ return self._get_check(self.tcheck.check.check_id)
48
+
49
+ def success_callback(self, response: requests.Response) -> None:
50
+ self.logger.info("Consulta a Base De datos ha sido finalizada")
51
+ response_json = response.json()
52
+ self.tcheck = TruoraCheckData(**response_json)
53
+ self.status = self.tcheck.check.status
54
+
55
+ def error_callback(self, response: requests.Response) -> None:
56
+ response_json = response.json()
57
+ inter_check: TruoraCheckData = TruoraCheckData(**response_json)
58
+
59
+ if inter_check.check.status == CheckStatus.DELAYED:
60
+ self.logger.warning("Consulta base de datos ha sido delayed")
61
+
62
+ self.status = inter_check.check.status
63
+
64
+ def is_request_successful(self, response: requests.Response) -> bool:
65
+ """
66
+
67
+ :param response:
68
+ :return:
69
+ """
70
+ if response.status_code in [200, 203]:
71
+ response_json = response.json()
72
+ try:
73
+ t_checker: TruoraCheckData = TruoraCheckData(**response_json)
74
+ return t_checker.check.status == CheckStatus.COMPLETED
75
+ except (KeyError, TypeError):
76
+ return False
77
+ return False
78
+
79
+ def __init__(self):
80
+ """
81
+
82
+ """
83
+
84
+ self.api_key = os.environ.get('TRUORA_API_TOKEN')
85
+
86
+ if self.api_key is None:
87
+ raise TruoraApiKeyRequired()
88
+
89
+ self.session = requests.session()
90
+
91
+ self.session.headers.update({
92
+ "Truora-API-Key": self.api_key,
93
+ 'Accept': 'application/json'
94
+ })
95
+
96
+ self.request_manager = RequestManager(
97
+ manager=self,
98
+ max_retries=10
99
+ )
100
+
101
+ self.tcheck: Optional[TruoraCheckData] = None
102
+
103
+ self.logger = logging.getLogger(__name__)
104
+
105
+ self.status: CheckStatus = CheckStatus.NOT_STARTED
106
+
107
+ def create_check(self,
108
+ identificacion: str,
109
+ person_type: PersonType,
110
+ autorizacion_datos: bool = False,
111
+ pais: CountryCode = CountryCode.COLOMBIA
112
+ ):
113
+ """
114
+ Funcion principal para consulatr base de datos externa tuora
115
+
116
+ :param identificacion:
117
+ :param person_type:
118
+ :param autorizacion_datos:
119
+ :param pais:
120
+ :return:
121
+ """
122
+ if not autorizacion_datos:
123
+ raise AutorizacionDatosPersonales()
124
+
125
+ settings_response = self.session.post(CHECK_SETTINGS_URL, data=settings_data)
126
+ response_json = settings_response.json()
127
+
128
+ if settings_response.status_code not in [200, 201, 202, 203]:
129
+ self.logger.error(f"Error al crear consulta base de datos externa: {response_json['message']}")
130
+ raise TruoraGeneralError(f"Error al crear consulta base de datos externa: {response_json['message']}")
131
+
132
+ form_data = {
133
+ 'national_id': identificacion,
134
+ 'country': pais,
135
+ 'type': person_type,
136
+ 'user_authorized': autorizacion_datos,
137
+ 'force_creation': True
138
+ }
139
+
140
+ response = self.session.post(CHECK_URL, data=form_data)
141
+ response_json = response.json()
142
+
143
+ if response.status_code not in [200, 201, 202, 203]:
144
+ self.logger.error(f"Error al crear consulta base de datos externa: {response_json['message']}")
145
+ raise TruoraGeneralError(f"Error al crear consulta base de datos externa: {response_json['message']}")
146
+
147
+ self.tcheck = TruoraCheckData(**response_json)
148
+
149
+ if self.tcheck is None:
150
+ raise TruoraGeneralError('Check de truora no fue creado')
151
+
152
+ self.request_manager.start()
153
+
154
+ open('file.txt', "w+").write(json.dumps(self.get_check_details(self.tcheck.check.check_id)))
155
+
156
+ return self.to_general()
157
+
158
+ def get_check(self, check_id: str) -> TruoraCheckData:
159
+ """
160
+
161
+ :param check_id:
162
+ :return:
163
+ """
164
+ response = self._get_check(check_id=check_id)
165
+ if response.status_code not in range(200, 203):
166
+ self.logger.error(response.text)
167
+ raise TruoraGeneralError(response.text)
168
+
169
+ response_json = response.json()
170
+ return TruoraCheckData(**response_json)
171
+
172
+ def get_check_details(self, check_id: str):
173
+ """
174
+
175
+ :param check_id:
176
+ :return:
177
+ """
178
+ response = self.session.post(CHECK_URL_DETAILS.format(check_id))
179
+ if response.status_code not in range(200, 203):
180
+ self.logger.error(response.text)
181
+ raise TruoraGeneralError(response.text)
182
+
183
+ return response.json()
184
+
185
+ def to_general(self) -> GeneralDatabase:
186
+ """
187
+ Traduce truora a general
188
+ :return:
189
+ """
190
+ return GeneralDatabase(
191
+ score=self.tcheck.check.score,
192
+ creation_date=self.tcheck.check.creation_date,
193
+ national_id=self.tcheck.check.national_id
194
+ )
@@ -0,0 +1,27 @@
1
+ """
2
+ Clase pincipal para analysis de creditos financieros
3
+
4
+ """
5
+ import logging
6
+
7
+ from creditpulse.bases_externas.external import ExternalDatabases, BasesDeDatos
8
+ from creditpulse.bases_externas.truora import Truora
9
+
10
+
11
+ class Check:
12
+ """
13
+ Clase que provee interfaz para analysis the personas
14
+ """
15
+
16
+ def __init__(self):
17
+ # Module logger
18
+ self.logger = logging.getLogger(__name__)
19
+
20
+ def get_data(self, identificacion: str, natural: bool = True):
21
+ """
22
+
23
+ :return: Analysis financiero de la persona natural o juridica
24
+ """
25
+ external = ExternalDatabases(BasesDeDatos.TRUORA)
26
+
27
+ return external.obtain_data(identificacion=identificacion, natural=natural)
File without changes
@@ -0,0 +1,11 @@
1
+ """Command line interface."""
2
+
3
+ import click
4
+
5
+
6
+ @click.command()
7
+ @click.option("--name", "-n", help="Name to greet", type=str, default=None, )
8
+ @click.version_option()
9
+ def main(name: str) -> None:
10
+ """My Project CLI."""
11
+ print(name)
File without changes
@@ -0,0 +1,39 @@
1
+ """
2
+ Manejo de error generales
3
+ """
4
+
5
+ AUTORIZACION_BASE_DE_DATOS = """
6
+ Es necesario obtener autorizacion para el el uso y lectura de base de datos
7
+ """
8
+
9
+ TRUORA_API_KEY = """
10
+ Debe obtener un API key de truora para consulta bases de datos externas, mas info en: https://dev.truora.com/checks/account/
11
+ """
12
+
13
+
14
+ class AutorizacionDatosPersonales(Exception):
15
+ """
16
+ Error por falta de autorizacion para uso de datos personales
17
+ """
18
+
19
+ def __init__(self):
20
+ super().__init__(AUTORIZACION_BASE_DE_DATOS)
21
+
22
+
23
+ class TruoraApiKeyRequired(Exception):
24
+ """
25
+ Error por falta de api key de truora
26
+ """
27
+
28
+ def __init__(self):
29
+ super().__init__(TRUORA_API_KEY)
30
+
31
+
32
+ class TruoraGeneralError(Exception):
33
+ """
34
+ Error general de truora que permite dar mensaje de error como paramtero
35
+ """
36
+
37
+ def __init__(self, msg: str):
38
+ super().__init__(msg)
39
+
File without changes
@@ -0,0 +1,114 @@
1
+ """
2
+ Clase para hacer exponential back of de algun callback
3
+ """
4
+ import time
5
+ from abc import ABC, abstractmethod
6
+ import requests
7
+
8
+
9
+
10
+
11
+ class RequestActionManager(ABC):
12
+ """Abstract base class for handling HTTP requests with decision logic"""
13
+
14
+ @abstractmethod
15
+ def on_execute(self) -> requests.Response:
16
+ """Execute the HTTP request and return the response"""
17
+
18
+ @abstractmethod
19
+ def success_callback(self, response: requests.Response) -> None:
20
+ """Handle successful response"""
21
+
22
+ @abstractmethod
23
+ def error_callback(self, response: requests.Response) -> None:
24
+ """Handle error response"""
25
+
26
+ @abstractmethod
27
+ def is_request_successful(self, response: requests.Response) -> bool:
28
+ """Determine if the request was successful"""
29
+
30
+
31
+ class RequestManager:
32
+ """
33
+ Maneja los request para poder hacer un exponential backoff
34
+ """
35
+
36
+ def __init__(self, manager: RequestActionManager, max_retries: int = 10):
37
+ """
38
+
39
+ :param decision: Objeto que ayuda a tomar decisiones para el exponential backoff
40
+ :param max_retries:
41
+ """
42
+
43
+ self.initial_delay = 1.0
44
+ self.max_delay = 60.0
45
+ self.backoff_factor = 1.5
46
+ self.max_retries = max_retries
47
+ self.manager = manager
48
+
49
+ # State variables
50
+ self.current_delay = self.initial_delay
51
+ self.retry_count = 0
52
+ self.running = False
53
+
54
+ def reset_delay(self):
55
+ """
56
+ Re-inicia el delay
57
+ :return:
58
+ """
59
+ self.current_delay = self.initial_delay
60
+ self.retry_count = 0
61
+
62
+ def increase_delay(self):
63
+ """
64
+ Aumenta el delay cada
65
+ :return:
66
+ """
67
+ self.current_delay = min(self.current_delay * self.backoff_factor, self.max_delay)
68
+ self.retry_count += 1
69
+
70
+ def should_continue(self) -> bool:
71
+ """
72
+ Determina si debe seguir ejecutando la accion
73
+ :return:
74
+ """
75
+
76
+ if not self.running:
77
+ return False
78
+ if self.max_retries is not None and self.retry_count >= self.max_retries:
79
+ return False
80
+ return True
81
+
82
+ def stop(self):
83
+ """
84
+ Detiene la ejecusion fozosamente
85
+ :return:
86
+ """
87
+ self.running = False
88
+
89
+ def start(self):
90
+ """
91
+ Inicia el proceso de llamada con backoof
92
+ :return:
93
+ """
94
+ self.running = True
95
+ while self.should_continue():
96
+ try:
97
+ response = self.manager.on_execute()
98
+
99
+ if self.manager.is_request_successful(response=response):
100
+ self.stop()
101
+ self.manager.success_callback(response=response)
102
+ self.reset_delay()
103
+ else:
104
+ self.manager.error_callback(response=response)
105
+ self.increase_delay()
106
+
107
+ except requests.exceptions.RequestException:
108
+ self.increase_delay()
109
+ except KeyboardInterrupt:
110
+ self.stop()
111
+ except Exception:
112
+ self.increase_delay()
113
+ if self.should_continue():
114
+ time.sleep(self.current_delay)
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: creditpulse
3
+ Version: 0.0.1
4
+ Summary: A pyhton library for financial analysis with external databases
5
+ Author-email: Santiago Velez <s.velez@saman-wm.com>
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: click==8.1.8
10
+ Requires-Dist: requests==2.32.4
11
+ Requires-Dist: pydantic==2.8.0
12
+ Requires-Dist: pylint==3.3.7
13
+ Dynamic: license-file
@@ -0,0 +1,24 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ creditpulse/__init__.py
5
+ creditpulse/check.py
6
+ creditpulse.egg-info/PKG-INFO
7
+ creditpulse.egg-info/SOURCES.txt
8
+ creditpulse.egg-info/dependency_links.txt
9
+ creditpulse.egg-info/entry_points.txt
10
+ creditpulse.egg-info/requires.txt
11
+ creditpulse.egg-info/top_level.txt
12
+ creditpulse/bases_externas/__init__.py
13
+ creditpulse/bases_externas/database.py
14
+ creditpulse/bases_externas/external.py
15
+ creditpulse/bases_externas/schema.py
16
+ creditpulse/bases_externas/truora.py
17
+ creditpulse/cli/__init__.py
18
+ creditpulse/cli/commands.py
19
+ creditpulse/common/__init__.py
20
+ creditpulse/common/error_messages.py
21
+ creditpulse/requests/__init__.py
22
+ creditpulse/requests/request_manager.py
23
+ test/test_back_off.py
24
+ test/test_truora_check.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ creditpulse = creditpulse.cli.commands:main
@@ -0,0 +1,4 @@
1
+ click==8.1.8
2
+ requests==2.32.4
3
+ pydantic==2.8.0
4
+ pylint==3.3.7
@@ -0,0 +1 @@
1
+ creditpulse
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "creditpulse"
3
+ version = "0.0.1"
4
+ description = "A pyhton library for financial analysis with external databases"
5
+ authors = [{ name = "Santiago Velez", email = "s.velez@saman-wm.com" }]
6
+ readme = "README.md"
7
+ requires-python = ">=3.7"
8
+
9
+ dependencies = [
10
+ "click==8.1.8",
11
+ "requests==2.32.4",
12
+ "pydantic==2.8.0",
13
+ "pylint==3.3.7"
14
+ ]
15
+
16
+ [build-system]
17
+ requires = ["setuptools>=61.0"]
18
+ build-backend = "setuptools.build_meta"
19
+
20
+ [project.scripts]
21
+ creditpulse = "creditpulse.cli.commands:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,56 @@
1
+ from creditpulse.requests.request_manager import RequestManager, RequestActionManager
2
+
3
+ import unittest
4
+ import requests
5
+
6
+
7
+ class SimpleCheck(RequestActionManager):
8
+
9
+ def __init__(self, endpoint):
10
+ self.endpoint = endpoint
11
+ self.success_called = False
12
+ self.error_called = False
13
+
14
+ def on_execute(self) -> requests.Response:
15
+ return requests.get(self.endpoint)
16
+
17
+ def success_callback(self, response: requests.Response) -> None:
18
+ self.success_called = True
19
+
20
+ def error_callback(self, response: requests.Response) -> None:
21
+ self.error_called = True
22
+
23
+ def is_request_successful(self, response: requests.Response) -> bool:
24
+ return response.status_code in [200, 201]
25
+
26
+
27
+ class TestRequestManager(unittest.TestCase):
28
+
29
+ def test_request_success(self):
30
+ check = SimpleCheck(endpoint='https://httpbin.org/get')
31
+ manager = RequestManager(
32
+ manager=check,
33
+ max_retries=1
34
+ )
35
+
36
+ manager.start()
37
+
38
+ self.assertTrue(check.success_called, "Success callback was not called")
39
+
40
+ def test_request_error(self):
41
+ self.error_callback_code = 200
42
+ self.retries = 3
43
+ check = SimpleCheck(endpoint='https://httpbin.org/status/500')
44
+
45
+ manager = RequestManager(
46
+ manager=check,
47
+ max_retries=self.retries,
48
+ )
49
+
50
+ manager.start()
51
+ self.assertTrue(check.error_called, "Success callback was not called")
52
+ self.assertEqual(manager.retry_count, self.retries)
53
+
54
+
55
+ if __name__ == '__main__':
56
+ unittest.main()
@@ -0,0 +1,16 @@
1
+ import unittest
2
+
3
+ from creditpulse.bases_externas.truora import Truora
4
+ from creditpulse.bases_externas.schema import CountryCode, PersonType
5
+
6
+
7
+ class TestTruoraCheck(unittest.TestCase):
8
+
9
+ def test_simple_request(self):
10
+ client = Truora()
11
+ client.create_check(
12
+ identificacion='1053778047',
13
+ person_type=PersonType.PERSONA,
14
+ pais=CountryCode.COLOMBIA,
15
+ autorizacion_datos=True
16
+ )