wpipe 0.0.1__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.
wpipe/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .api_client import APIClient
2
+ from .log import new_logger
3
+ from .pipe import Pipeline
4
+ from .sqlite import Wsqlite
5
+ from .ram import memory
@@ -0,0 +1 @@
1
+ from .api_client import APIClient
@@ -0,0 +1,87 @@
1
+ # pip install requests
2
+ import requests
3
+ import json
4
+
5
+
6
+ class APIClient:
7
+ def __init__(self, base_url: str = None, token: str = None):
8
+ """
9
+ Inicializa el cliente API con la URL base y el token de autorización.
10
+ """
11
+ self.base_url = base_url
12
+ self.headers = {
13
+ "Authorization": f"Bearer {token}",
14
+ "Content-Type": "application/json",
15
+ }
16
+
17
+ def send_post(self, endpoint: str, data: dict):
18
+ """
19
+ Envía una solicitud POST a un endpoint específico.
20
+ """
21
+
22
+ if not self.base_url:
23
+ raise Exception("No 'base_url' defined.")
24
+
25
+ url = f"{self.base_url}{endpoint}"
26
+ try:
27
+ response = requests.post(url, headers=self.headers, data=json.dumps(data))
28
+ response.raise_for_status() # Lanza una excepción si el código de estado indica un error
29
+ return response.json()
30
+ except requests.exceptions.RequestException as e:
31
+ print(f"Error en la solicitud POST a {url}: {e}")
32
+ return None
33
+
34
+ def send_get(self, endpoint: str):
35
+ """
36
+ Envía una solicitud GET a un endpoint específico.
37
+ """
38
+
39
+ if not self.base_url:
40
+ raise Exception("No 'base_url' defined.")
41
+
42
+ url = f"{self.base_url}{endpoint}"
43
+ try:
44
+ response = requests.get(url, headers=self.headers)
45
+ response.raise_for_status()
46
+ return response.json()
47
+ except requests.exceptions.RequestException as e:
48
+ print(f"Error en la solicitud GET a {url}: {e}")
49
+ return None
50
+
51
+ # Métodos específicos para cada endpoint
52
+
53
+ def register_worker(self, data: dict):
54
+ return self.send_post("/matricula", data)
55
+
56
+ def healthcheck_worker(self, data: dict):
57
+ return self.send_post("/healthchecker", data)
58
+
59
+ def register_process(self, data: dict):
60
+ return self.send_post("/newprocess", data)
61
+
62
+ def end_process(self, data: dict):
63
+ return self.send_post("/endprocess", data)
64
+
65
+ def update_task(self, data: dict):
66
+ return self.send_post("/actualizar_task", data)
67
+
68
+ def get_dashboard_workers(self):
69
+ return self.send_get("/dashboard_workers")
70
+
71
+
72
+ # Ejemplo de uso
73
+ if __name__ == "__main__":
74
+ # Configura la URL base y el token de autorización
75
+ client = APIClient(base_url="http://192.168.1.60:8418", token="mysecrettoken")
76
+
77
+ # Ejemplo de cómo registrar un trabajador
78
+ data = {
79
+ # Llena con los datos necesarios para la solicitud
80
+ }
81
+
82
+ # Llama al método para obtener el dashboard de trabajadores
83
+ workers_info = client.get_dashboard_workers()
84
+ print(workers_info)
85
+
86
+
87
+ # pip install requests
wpipe/log/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .log import new_logger
wpipe/log/log.py ADDED
@@ -0,0 +1,31 @@
1
+ """
2
+ This program has a functions that returns
3
+ a logger object.
4
+ """
5
+
6
+ import sys
7
+ from loguru import logger
8
+
9
+
10
+ def new_logger(process_name: str, path_file: str = "/logs/file_{time}.log"):
11
+
12
+ # for print in console
13
+ logger.add(
14
+ sys.stderr,
15
+ format="{time} {level} {message}",
16
+ filter=f"{process_name}",
17
+ level="INFO",
18
+ )
19
+
20
+ # for save in file
21
+ logger.add(
22
+ path_file,
23
+ colorize=True,
24
+ format="<green>{time:YYYY-MM-DD at HH:mm:ss}</green>"
25
+ + "| <level>{level}</level> | <blue>{message}</blue>",
26
+ rotation="50 MB",
27
+ compression="zip",
28
+ retention="10 days",
29
+ )
30
+
31
+ return logger
wpipe/pipe/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .pipe import Pipeline
wpipe/pipe/pipe.py ADDED
@@ -0,0 +1,224 @@
1
+ from wpipe.api_client.api_client import APIClient
2
+
3
+
4
+ # Definición de la clase
5
+ class Pipeline(APIClient):
6
+ worker_id: str = None
7
+ process_id: str = None
8
+ send_to_api: bool = False
9
+ api_config: dict = None
10
+ tasks_list: list = [] # Inicializa un atributo lista vacío
11
+
12
+ def __init__(
13
+ self, worker_id: str = None, api_config: dict = None, verbose: bool = False
14
+ ):
15
+ if api_config:
16
+ # invoca el constructor de APIClient
17
+ super().__init__(
18
+ base_url=api_config.get("base_url"), token=api_config.get("token")
19
+ )
20
+ self.api_config = api_config
21
+
22
+ if worker_id:
23
+ self.set_worker_id(worker_id)
24
+
25
+ self.verbose = verbose
26
+
27
+ def set_worker_id(self, id: str):
28
+ if id and isinstance(id, str):
29
+ if len(id) > 5:
30
+ if self.api_config and not self.worker_id:
31
+ self.send_to_api = True
32
+ print("[INFO] worker_id defined correct")
33
+
34
+ self.worker_id = id
35
+ else:
36
+ self.worker_id = None
37
+ else:
38
+ raise Exception(f"[ERROR] {id} is not correct, have to be a string")
39
+
40
+ def worker_register(self, name: str, version: str):
41
+ data = {
42
+ "name": name,
43
+ "version": version,
44
+ "tasks": [
45
+ {
46
+ "name": name,
47
+ "version": version,
48
+ }
49
+ for func, name, version, _id in self.tasks_list
50
+ ],
51
+ }
52
+
53
+ if self.api_config:
54
+ worker_id = self.register_worker(data)
55
+
56
+ if worker_id and "id" in worker_id:
57
+ self.set_worker_id(worker_id.get("id"))
58
+
59
+ return worker_id
60
+
61
+ def _decorator_task_report(func):
62
+ """Decorador para reportar la ejecución de cada tarea."""
63
+
64
+ def wrapper(self, *args, **kwargs):
65
+
66
+ if self.send_to_api:
67
+ task_updated = self.update_task(
68
+ {"task_id": self.task_id, "status": "start"}
69
+ )
70
+ if self.verbose:
71
+ print(
72
+ "\t" * 2,
73
+ f"[INFO] [START] task '{self.task_name}': {task_updated}",
74
+ )
75
+
76
+ resultado = {}
77
+ try:
78
+ resultado = func(self, *args, **kwargs)
79
+
80
+ if self.send_to_api:
81
+ task_updated = self.update_task(
82
+ {"task_id": self.task_id, "status": "success"}
83
+ )
84
+
85
+ if not task_updated:
86
+ raise Exception("Problem task")
87
+
88
+ if self.verbose:
89
+ print(
90
+ "\t" * 2,
91
+ f"[INFO] [END] task '{self.task_name}': {task_updated}",
92
+ )
93
+ except Exception as e:
94
+ details = str(e)
95
+ resultado["error"] = details
96
+
97
+ if self.send_to_api:
98
+ task_updated = self.update_task(
99
+ {"task_id": self.task_id, "status": "error"}
100
+ )
101
+ print(
102
+ "\t" * 2,
103
+ f"[ERROR] [END] task '{self.task_name}': {task_updated}",
104
+ )
105
+
106
+ return resultado
107
+
108
+ return wrapper
109
+
110
+ def _decorator_pipeline_report(func):
111
+ """Decorador para reportar la ejecución del pipeline completo."""
112
+
113
+ def wrapper(self, *args, **kwargs):
114
+
115
+ worker_id = self.worker_id
116
+
117
+ if self.verbose:
118
+ print("\n", "\t", "*" * 50)
119
+ print("\n", f"\t [WORKER] {self.worker_id}")
120
+ print("\n\t", "*" * 50)
121
+
122
+ if self.send_to_api:
123
+ process_registed = self.register_process({"id": worker_id})
124
+
125
+ if process_registed:
126
+ self.tasks_list = [
127
+ (rta[0], rta[1], rta[2], son["id"])
128
+ for son, rta in zip(process_registed["sons"], self.tasks_list)
129
+ ]
130
+
131
+ self.process_id = process_registed["father"]
132
+
133
+ if self.verbose:
134
+ print(
135
+ "\t",
136
+ f"[INFO] [START] pipeline: new process ({self.process_id})",
137
+ )
138
+
139
+ resultado = {}
140
+ try:
141
+ resultado = func(self, *args, **kwargs)
142
+
143
+ if self.send_to_api:
144
+ status = self.end_process({"id": self.process_id, "details": ""})
145
+ if not status:
146
+ raise Exception("API problem")
147
+
148
+ if self.verbose:
149
+ print("\t", f"[INFO] [END] pipeline: {status}")
150
+
151
+ except Exception as e:
152
+ details = str(e)
153
+ resultado["error"] = details
154
+
155
+ if self.send_to_api:
156
+ status = self.end_process(
157
+ {"id": self.process_id, "details": details}
158
+ )
159
+ print("\t", f"[ERROR] [END] pipeline: {status}")
160
+
161
+ return resultado
162
+
163
+ return wrapper
164
+
165
+ def set_steps(self, lista):
166
+ """
167
+ Define los pasos del pipeline, verificando que cada elemento sea una tupla
168
+ compuesta por una función y un nombre en string.
169
+ """
170
+
171
+ new_list = []
172
+
173
+ for item in lista:
174
+ if not (
175
+ isinstance(item, tuple)
176
+ and len(item) == 3
177
+ and callable(item[0])
178
+ and isinstance(item[1], str)
179
+ ):
180
+ raise ValueError(
181
+ "Cada elemento de la lista debe ser una tupla (función, nombre)."
182
+ )
183
+ else:
184
+ new_list.append((item[0], item[1], item[2], ""))
185
+
186
+ self.tasks_list = new_list
187
+
188
+ @_decorator_task_report
189
+ def _task_invoke(self, func, name, *args, **kwargs):
190
+ return func(*args, **kwargs)
191
+
192
+ @_decorator_pipeline_report
193
+ def run(self, *args, **kwargs):
194
+ """
195
+ Ejecuta el pipeline completo, pasando los resultados de una función como
196
+ entrada a la siguiente.
197
+ """
198
+
199
+ resultado = None
200
+ data = {}
201
+ # TODO: poner un tqdm para ver como van avanzando el progreso de las tareas
202
+ for i, (func, name, version, _id) in enumerate(self.tasks_list):
203
+ self.task_name = name
204
+ self.task_id = _id
205
+
206
+ data.update(args[0])
207
+
208
+ if i == 0:
209
+ resultado = self._task_invoke(func, name, *(data,), **kwargs)
210
+ else:
211
+ resultado = self._task_invoke(func, name, *(data,), **kwargs)
212
+
213
+ data.update(resultado)
214
+
215
+ if self.verbose:
216
+ print()
217
+
218
+ if "error" in data:
219
+ break
220
+
221
+ if "error" in data:
222
+ raise Exception(f"[{self.task_name}] Fail the pipeline:{data['error']}")
223
+
224
+ return data
wpipe/ram/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .ram import memory
wpipe/ram/ram.py ADDED
@@ -0,0 +1,52 @@
1
+ import resource
2
+ import platform
3
+ import sys
4
+
5
+
6
+ def memory_limit(percentage: float):
7
+ """
8
+ 只在linux操作系统起作用
9
+ """
10
+ if platform.system() != "Linux":
11
+ print("Only works on linux!")
12
+ return
13
+ soft, hard = resource.getrlimit(resource.RLIMIT_AS)
14
+ resource.setrlimit(resource.RLIMIT_AS, (get_memory() * 1024 * percentage, hard))
15
+
16
+
17
+ def get_memory():
18
+ with open("/proc/meminfo", "r") as mem:
19
+ free_memory = 0
20
+ for i in mem:
21
+ sline = i.split()
22
+ if str(sline[0]) in ("MemFree:", "Buffers:", "Cached:"):
23
+ free_memory += int(sline[1])
24
+ return free_memory
25
+
26
+
27
+ """
28
+ Este es el decorador
29
+
30
+ example:
31
+
32
+ @memory(percentage=0.8)
33
+ def main():
34
+ print('My memory is limited to 80%.')
35
+ """
36
+
37
+
38
+ def memory(percentage=0.8):
39
+ def decorator(function):
40
+ def wrapper(*args, **kwargs):
41
+ memory_limit(percentage)
42
+ try:
43
+ return function(*args, **kwargs)
44
+ except MemoryError:
45
+ mem = get_memory() / 1024 / 1024
46
+ print("Remain: %.2f GB" % mem)
47
+ sys.stderr.write("\n\nERROR: Memory Exception\n")
48
+ sys.exit(1)
49
+
50
+ return wrapper
51
+
52
+ return decorator
wpipe/sqlite/Sqlite.py ADDED
@@ -0,0 +1,207 @@
1
+ import json
2
+ import os
3
+ import sqlite3
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ from typing import Literal
6
+ from datetime import datetime
7
+ import pandas as pd
8
+
9
+
10
+ class SQLite:
11
+ def __init__(self, db_name: str = "register.db"):
12
+ # nombre absoluto de db_name
13
+ # self.db_name = os.path.basename(db_name)
14
+ self.db_name = db_name
15
+ self._create_table_if_not_exists()
16
+
17
+ self.executor = ThreadPoolExecutor(max_workers=10)
18
+
19
+ def _create_table_if_not_exists(self):
20
+ if not self.db_name:
21
+ return
22
+
23
+ with sqlite3.connect(self.db_name) as conn:
24
+ cursor = conn.cursor()
25
+ cursor.execute(
26
+ """CREATE TABLE IF NOT EXISTS records
27
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ input TEXT,
29
+ output TEXT,
30
+ details TEXT DEFAULT NULL,
31
+ datetime TEXT DEFAULT CURRENT_TIMESTAMP)"""
32
+ )
33
+
34
+ def async_write(
35
+ self, input: str = None, output: str = None, details: str = None, id: int = None
36
+ ):
37
+ self.executor.submit(self.write, input, output, details, id)
38
+
39
+ def write(
40
+ self,
41
+ input: str = None,
42
+ output: Literal["str", "dict"] = None,
43
+ details: str = None,
44
+ id: int = None,
45
+ ):
46
+ if not self.check_table_exists():
47
+ return
48
+
49
+ if isinstance(input, dict):
50
+ input = json.dumps(input)
51
+
52
+ if isinstance(output, dict):
53
+ output["datetime"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
54
+ output = json.dumps(output)
55
+
56
+ elif isinstance(output, str):
57
+ output = json.dumps(
58
+ {
59
+ "output": output,
60
+ "datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
61
+ }
62
+ )
63
+
64
+ if isinstance(details, dict):
65
+ details = json.dumps(details)
66
+
67
+ with sqlite3.connect(self.db_name) as conn:
68
+ cursor = conn.cursor()
69
+
70
+ if not id:
71
+ # insert
72
+ cursor.execute(
73
+ "INSERT INTO records (input, output, details) VALUES (?, ?, ?)",
74
+ (input, output, details),
75
+ )
76
+
77
+ id = cursor.lastrowid
78
+ else:
79
+ # update
80
+ if not self.read_by_id(id):
81
+ return
82
+
83
+ if not input and not details and output:
84
+ cursor.execute(
85
+ "UPDATE records SET output = ? WHERE id = ?",
86
+ (output, id),
87
+ )
88
+ elif not input and output and details:
89
+ cursor.execute(
90
+ "UPDATE records SET output = ?, details = ? WHERE id = ?",
91
+ (output, details, id),
92
+ )
93
+ elif input and output and details:
94
+ cursor.execute(
95
+ "UPDATE records SET input = ?, output = ?, details = ? WHERE id = ?",
96
+ (input, output, details, id),
97
+ )
98
+ elif input and output and not details:
99
+ cursor.execute(
100
+ "UPDATE records SET input = ?, output = ? WHERE id = ?",
101
+ (input, output, id),
102
+ )
103
+ conn.commit()
104
+
105
+ return id
106
+
107
+ def read_by_id(self, id: int) -> list:
108
+ if not self.check_table_exists():
109
+ return []
110
+
111
+ results = []
112
+
113
+ with sqlite3.connect(self.db_name) as conn:
114
+ cursor = conn.cursor()
115
+ cursor.execute("SELECT * FROM records WHERE id = ?", (id,))
116
+
117
+ results = cursor.fetchall()
118
+
119
+ if cursor.rowcount == 0:
120
+ # print("No records found for the specified id")
121
+ pass
122
+
123
+ return results
124
+
125
+ def _view_records(self) -> pd.DataFrame:
126
+ with sqlite3.connect(self.db_name) as conn:
127
+ return pd.read_sql_query("SELECT * FROM records", conn)
128
+
129
+ def export_to_dataframe(
130
+ self, save_csv: bool = False, csv_name: str = "records.csv"
131
+ ) -> pd.DataFrame:
132
+ if not self.check_table_exists():
133
+ return pd.DataFrame()
134
+
135
+ df = self._view_records()
136
+
137
+ if save_csv:
138
+ df.to_csv(csv_name, index=False)
139
+
140
+ return df
141
+
142
+ def get_records_by_date_range(self, start_date: str, end_date: str) -> list:
143
+ if not self.check_table_exists():
144
+ return []
145
+
146
+ with sqlite3.connect(self.db_name) as conn:
147
+ cursor = conn.cursor()
148
+ cursor.execute(
149
+ "SELECT * FROM records WHERE datetime BETWEEN ? AND ?",
150
+ (start_date, end_date),
151
+ )
152
+ return cursor.fetchall()
153
+
154
+ def count_records(self) -> int:
155
+ if not self.check_table_exists():
156
+ return 0
157
+
158
+ with sqlite3.connect(self.db_name) as conn:
159
+ cursor = conn.cursor()
160
+ cursor.execute("SELECT COUNT(*) FROM records")
161
+ return cursor.fetchone()[0]
162
+
163
+ def delete_by_id(self, id_saved: int):
164
+ if not self.check_table_exists():
165
+ return
166
+
167
+ with sqlite3.connect(self.db_name) as conn:
168
+ cursor = conn.cursor()
169
+ cursor.execute("DELETE FROM records WHERE id = ?", (id_saved,))
170
+ conn.commit()
171
+
172
+ def check_table_exists(self) -> bool:
173
+ if not self.db_name:
174
+ return False
175
+
176
+ if len(os.path.dirname(self.db_name)) > 0:
177
+ if not os.path.exists(os.path.dirname(self.db_name)):
178
+ os.makedirs(os.path.dirname(self.db_name))
179
+
180
+ self._create_table_if_not_exists()
181
+
182
+ return True
183
+
184
+ def __enter__(self):
185
+ return self
186
+
187
+ def __exit__(self, exc_type, exc_val, exc_tb):
188
+ if self.executor:
189
+ self.executor.shutdown(wait=True)
190
+
191
+
192
+ if __name__ == "__main__":
193
+ import uuid
194
+
195
+ my_uuid = str(uuid.uuid4().hex)
196
+
197
+ with SQLite() as registro:
198
+ registro.write(topic="topic 1", type_data="image", value=my_uuid)
199
+ registro.write(topic="topic 2", type_data="image", value=my_uuid)
200
+ registro.write(topic="topic 3", type_data="image", value=my_uuid)
201
+ registro.write(topic="topic 4", type_data="image", value=my_uuid)
202
+
203
+ print(registro.read_by_id(str(uuid.uuid4().hex)))
204
+
205
+ print(registro.export_to_dataframe().head())
206
+
207
+ print("total", registro.count_records())
@@ -0,0 +1,54 @@
1
+ from wpipe.sqlite.Sqlite import SQLite
2
+
3
+
4
+ class Wsqlite:
5
+
6
+ id: str = None
7
+
8
+ output_db: dict = {}
9
+ details_db: dict = {}
10
+ input_db: dict = {}
11
+
12
+ def __init__(self, db_name: str = "register.db") -> None:
13
+ self.db_name = db_name
14
+
15
+ @property
16
+ def _input(self):
17
+ pass
18
+
19
+ @_input.setter
20
+ def input(self, input: dict):
21
+ self._create(input=input)
22
+
23
+ @property
24
+ def _output(self):
25
+ pass
26
+
27
+ @_output.setter
28
+ def output(self, output: dict):
29
+ self.output_db = output
30
+
31
+ @property
32
+ def _details(self):
33
+ pass
34
+
35
+ @_details.setter
36
+ def details(self, details: dict):
37
+ self.details_db = details
38
+
39
+ def _create(self, input: dict):
40
+ id = None
41
+ with SQLite(self.db_name) as conection_db:
42
+ id = conection_db.async_write(input=input)
43
+
44
+ self.id = str(id)
45
+
46
+ def _update(self, output: dict, details: dict = {}):
47
+ with SQLite(self.db_name) as conection_db:
48
+ conection_db.async_write(output=output, details=details, id=self.id)
49
+
50
+ def __enter__(self):
51
+ return self
52
+
53
+ def __exit__(self, exc_type, exc_val, exc_tb):
54
+ self._update(output=self.output_db, details=self.details_db)
@@ -0,0 +1 @@
1
+ from .Wsqlite import Wsqlite
wpipe/util/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .utils import leer_yaml, escribir_yaml