commons-metrics 0.0.15__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.
- commons_metrics/__init__.py +9 -0
- commons_metrics/azure_devops_client.py +432 -0
- commons_metrics/commons_repos_client.py +114 -0
- commons_metrics/database.py +39 -0
- commons_metrics/github_api_client.py +305 -0
- commons_metrics/repositories.py +269 -0
- commons_metrics/update_design_components.py +203 -0
- commons_metrics/util.py +38 -0
- commons_metrics-0.0.15.dist-info/METADATA +17 -0
- commons_metrics-0.0.15.dist-info/RECORD +13 -0
- commons_metrics-0.0.15.dist-info/WHEEL +5 -0
- commons_metrics-0.0.15.dist-info/licenses/LICENSE +9 -0
- commons_metrics-0.0.15.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from psycopg2.extras import execute_values
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UpdateDesignSystemComponents:
|
|
7
|
+
|
|
8
|
+
# ---------------UTILITY FUNCTIONS---------------#
|
|
9
|
+
def __init__(self, connection):
|
|
10
|
+
self.connection = connection
|
|
11
|
+
|
|
12
|
+
@contextmanager
|
|
13
|
+
def get_db_cursor(self):
|
|
14
|
+
"""
|
|
15
|
+
Context manager para manejo automático de cursor
|
|
16
|
+
"""
|
|
17
|
+
cursor = None
|
|
18
|
+
try:
|
|
19
|
+
cursor = self.connection.connection.cursor()
|
|
20
|
+
yield cursor
|
|
21
|
+
finally:
|
|
22
|
+
if cursor:
|
|
23
|
+
cursor.close()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_new_components(self, components, identifier):
|
|
28
|
+
"""
|
|
29
|
+
Crea nuevos componentes en la base de datos usando un solo query
|
|
30
|
+
"""
|
|
31
|
+
if not components:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
# Preparar los datos para la inserción masiva
|
|
35
|
+
values_to_insert = []
|
|
36
|
+
for comp in components:
|
|
37
|
+
values_to_insert.append((
|
|
38
|
+
comp['technical_name'], # technical_name
|
|
39
|
+
identifier, # type_id
|
|
40
|
+
comp['folder'], # folder
|
|
41
|
+
comp['class'], # class_name (array de clases)
|
|
42
|
+
'ACTIVE', # status (ACTIVE | DEPRECATED)
|
|
43
|
+
))
|
|
44
|
+
|
|
45
|
+
# Query de inserción masiva
|
|
46
|
+
insert_query = """
|
|
47
|
+
INSERT INTO schmetrc.component(technical_name, type_id, folder, class_name, status)
|
|
48
|
+
VALUES %s
|
|
49
|
+
RETURNING id, technical_name;
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
with self.get_db_cursor() as cursor:
|
|
54
|
+
# Ejecutar la inserción masiva
|
|
55
|
+
result = execute_values(
|
|
56
|
+
cursor,
|
|
57
|
+
insert_query,
|
|
58
|
+
values_to_insert,
|
|
59
|
+
template=None,
|
|
60
|
+
page_size=100,
|
|
61
|
+
fetch=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Confirmar la transacción
|
|
65
|
+
self.connection.connection.commit()
|
|
66
|
+
|
|
67
|
+
print(f"✅ {len(values_to_insert)} componentes creados exitosamente en la base de datos:")
|
|
68
|
+
for row in result:
|
|
69
|
+
print(f" - ID: {row[0]}, Nombre: {row[1]}")
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
# Revertir en caso de error
|
|
73
|
+
self.connection.connection.rollback()
|
|
74
|
+
print(f"❌ Error al crear componentes: {e}")
|
|
75
|
+
raise
|
|
76
|
+
|
|
77
|
+
def deprecate_old_components(self, components):
|
|
78
|
+
"""
|
|
79
|
+
Marca componentes antiguos como DEPRECATED en la base de datos
|
|
80
|
+
"""
|
|
81
|
+
if not components:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# Preparar los nombres técnicos para la actualización
|
|
85
|
+
technical_names = [comp['technical_name'] for comp in components]
|
|
86
|
+
|
|
87
|
+
# Query de actualización masiva
|
|
88
|
+
update_query = """
|
|
89
|
+
UPDATE schmetrc.component
|
|
90
|
+
SET status = 'DEPRECATED'
|
|
91
|
+
WHERE technical_name = ANY(%s)
|
|
92
|
+
RETURNING id, technical_name;
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
with self.get_db_cursor() as cursor:
|
|
97
|
+
# Ejecutar la actualización masiva
|
|
98
|
+
cursor.execute(update_query, (technical_names,))
|
|
99
|
+
result = cursor.fetchall()
|
|
100
|
+
|
|
101
|
+
# Confirmar la transacción
|
|
102
|
+
self.connection.connection.commit()
|
|
103
|
+
|
|
104
|
+
print(f"⚠️ {len(result)} componentes marcados como DEPRECATED en la base de datos:")
|
|
105
|
+
for row in result:
|
|
106
|
+
print(f" - ID: {row[0]}, Nombre: {row[1]}")
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
# Revertir en caso de error
|
|
110
|
+
self.connection.connection.rollback()
|
|
111
|
+
print(f"❌ Error al marcar componentes como DEPRECATED: {e}")
|
|
112
|
+
raise
|
|
113
|
+
|
|
114
|
+
def update_db_components_info(self, db_components, components_dict):
|
|
115
|
+
"""
|
|
116
|
+
Actualiza la información de los componentes en la base de datos:
|
|
117
|
+
- Si cambia el folder o las clases asociadas, se actualizan en la base de datos.
|
|
118
|
+
- Si no hay cambios, no se hace nada.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
# Preparar listas para actualizaciones
|
|
122
|
+
components_to_update = []
|
|
123
|
+
|
|
124
|
+
for db_comp in db_components:
|
|
125
|
+
tech_name = db_comp['technical_name']
|
|
126
|
+
if tech_name in components_dict:
|
|
127
|
+
comp = components_dict[tech_name]
|
|
128
|
+
# Verificar si hay cambios en folder o class_name
|
|
129
|
+
if (db_comp['folder'] != comp['folder'] or
|
|
130
|
+
set(db_comp['class_name']) != set(comp['class'])):
|
|
131
|
+
components_to_update.append((
|
|
132
|
+
comp['folder'],
|
|
133
|
+
comp['class'],
|
|
134
|
+
tech_name,
|
|
135
|
+
"ACTIVE"
|
|
136
|
+
))
|
|
137
|
+
|
|
138
|
+
if not components_to_update:
|
|
139
|
+
print("ℹ️ No hay componentes que actualizar en la base de datos.")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Query de actualización masiva usando CTE
|
|
143
|
+
update_query = """
|
|
144
|
+
WITH updates(folder, class_name, technical_name, status) AS (
|
|
145
|
+
VALUES %s
|
|
146
|
+
)
|
|
147
|
+
UPDATE schmetrc.component
|
|
148
|
+
SET folder = updates.folder,
|
|
149
|
+
class_name = updates.class_name,
|
|
150
|
+
status = updates.status
|
|
151
|
+
FROM updates
|
|
152
|
+
WHERE schmetrc.component.technical_name = updates.technical_name
|
|
153
|
+
RETURNING schmetrc.component.id, schmetrc.component.technical_name;
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
with self.get_db_cursor() as cursor:
|
|
158
|
+
# Ejecutar la actualización masiva
|
|
159
|
+
result = execute_values(
|
|
160
|
+
cursor,
|
|
161
|
+
update_query,
|
|
162
|
+
components_to_update,
|
|
163
|
+
template=None,
|
|
164
|
+
page_size=100,
|
|
165
|
+
fetch=True
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Confirmar la transacción
|
|
169
|
+
self.connection.connection.commit()
|
|
170
|
+
|
|
171
|
+
print(f"✅ {len(result)} componentes actualizados en la base de datos:")
|
|
172
|
+
for row in result:
|
|
173
|
+
print(f"🔄 Componente actualizado - ID: {row[0]}, Nombre: {row[1]}")
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
print(f"❌ Error al actualizar componentes en la base de datos: {e}")
|
|
177
|
+
self.connection.connection.rollback()
|
|
178
|
+
raise
|
|
179
|
+
|
|
180
|
+
def update_db(self, db_components, all_components, identifier):
|
|
181
|
+
"""
|
|
182
|
+
Actualiza la base de datos con los componentes actuales del proyecto
|
|
183
|
+
"""
|
|
184
|
+
# Comparar base de datos con componentes encontrados, y listar las diferencias, obtener los componentes completos
|
|
185
|
+
db_components_dict = {comp['technical_name']: comp for comp in db_components}
|
|
186
|
+
components_dict = {comp['technical_name']: comp for comp in all_components}
|
|
187
|
+
|
|
188
|
+
# Obtener conjuntos de nombres para comparación
|
|
189
|
+
db_component_names = set(db_components_dict.keys())
|
|
190
|
+
component_names = set(components_dict.keys())
|
|
191
|
+
|
|
192
|
+
# Componentes que están en el proyecto pero NO en la base de datos (nuevos)
|
|
193
|
+
missing_in_db_names = component_names - db_component_names
|
|
194
|
+
missing_in_db_components = [components_dict[name] for name in missing_in_db_names]
|
|
195
|
+
self.create_new_components(missing_in_db_components, identifier)
|
|
196
|
+
|
|
197
|
+
# Componentes que están en la base de datos pero NO en el proyecto
|
|
198
|
+
missing_in_names = db_component_names - component_names
|
|
199
|
+
missing_in_components = [db_components_dict[name] for name in missing_in_names]
|
|
200
|
+
self.deprecate_old_components(missing_in_components)
|
|
201
|
+
|
|
202
|
+
# Actualizar información de componentes comunes
|
|
203
|
+
self.update_db_components_info(db_components, components_dict)
|
commons_metrics/util.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
class Util:
|
|
5
|
+
"""Utility class for common operations"""
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def get_secret_aws(secret_name: str, logger, region_name: str):
|
|
9
|
+
"""
|
|
10
|
+
Retrieves AWS Secrets Manager secret and returns database credentials
|
|
11
|
+
"""
|
|
12
|
+
try:
|
|
13
|
+
session = boto3.session.Session()
|
|
14
|
+
client = session.client(
|
|
15
|
+
service_name='secretsmanager',
|
|
16
|
+
region_name=region_name
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
|
|
20
|
+
secret_string = get_secret_value_response['SecretString']
|
|
21
|
+
secret_json = json.loads(secret_string)
|
|
22
|
+
|
|
23
|
+
required_keys = ["password", "host", "port", "username", "dbname"]
|
|
24
|
+
missing_keys = [key for key in required_keys if key not in secret_json]
|
|
25
|
+
|
|
26
|
+
if missing_keys:
|
|
27
|
+
msg = f"Missing required keys in AWS secret: {missing_keys}"
|
|
28
|
+
logger.error(msg)
|
|
29
|
+
raise KeyError(msg)
|
|
30
|
+
|
|
31
|
+
return secret_json
|
|
32
|
+
|
|
33
|
+
except Exception as e:
|
|
34
|
+
msg = f"Error getting the secret '{secret_name}': {str(e)}"
|
|
35
|
+
logger.error(msg)
|
|
36
|
+
raise Exception(msg)
|
|
37
|
+
|
|
38
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: commons_metrics
|
|
3
|
+
Version: 0.0.15
|
|
4
|
+
Summary: A simple library for basic statistical calculations
|
|
5
|
+
Author: Bancolombia
|
|
6
|
+
Author-email: omar.david.pino@email.com
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Python: >=3.7
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: boto3
|
|
11
|
+
Dynamic: author
|
|
12
|
+
Dynamic: author-email
|
|
13
|
+
Dynamic: license
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
Dynamic: requires-dist
|
|
16
|
+
Dynamic: requires-python
|
|
17
|
+
Dynamic: summary
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
commons_metrics/__init__.py,sha256=y_liVt7ATvuZ2rUqNKlZ0Xzwo2TKi1sJ1SfYRRtBM6c,434
|
|
2
|
+
commons_metrics/azure_devops_client.py,sha256=sD130ggzZWUsNqkBVNrLH80_AN00CxQ4preP9sBdzHM,16778
|
|
3
|
+
commons_metrics/commons_repos_client.py,sha256=SYIe1fFXM3qlc_G154AmorHScPS3rTsztKqxy3dD28w,4150
|
|
4
|
+
commons_metrics/database.py,sha256=570TtLZ9psNzvIp75UFLYph34cKVEz6eGJgxXyRyjW4,1285
|
|
5
|
+
commons_metrics/github_api_client.py,sha256=yPLsqn_KvQDIXcNp7Ma8qQwWy8svpXNLpDX69vwL9BU,11465
|
|
6
|
+
commons_metrics/repositories.py,sha256=7k3cvIEF_OiMt56C_DAM6V3-4t66SmciiSQ3rGfmEoU,10567
|
|
7
|
+
commons_metrics/update_design_components.py,sha256=QpY0GCCCMjdYOZ7b8oNigU9iTpiGx91CYsyWwN8WVDA,7660
|
|
8
|
+
commons_metrics/util.py,sha256=98zuynalXumQRh-BB0Bcjyoh6vS2BTOUM8tVgr7iS9Q,1225
|
|
9
|
+
commons_metrics-0.0.15.dist-info/licenses/LICENSE,sha256=jsHZ2Sh1wCL74HC25pDDGXCyQ0xgsTAy62FvEnehKIg,1067
|
|
10
|
+
commons_metrics-0.0.15.dist-info/METADATA,sha256=YuGozHIdHx_k_9inq2iS6IKSv85gtJJH15d_aLxoT14,402
|
|
11
|
+
commons_metrics-0.0.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
commons_metrics-0.0.15.dist-info/top_level.txt,sha256=lheUN-3OKdU3A8Tg8Y-1IEB_9i_vVRA0g_FOiUsTQz8,16
|
|
13
|
+
commons_metrics-0.0.15.dist-info/RECORD,,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Paras Arora
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
commons_metrics
|