iatoolkit 0.59.0__py3-none-any.whl → 0.59.2__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.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

iatoolkit/base_company.py CHANGED
@@ -29,11 +29,13 @@ class BaseCompany(ABC):
29
29
  def _create_company(self,
30
30
  short_name: str,
31
31
  name: str,
32
+ parameters: dict | None = None,
32
33
  branding: dict | None = None,
33
- onboarding_cards: dict | None = None
34
+ onboarding_cards: dict | None = None,
34
35
  ) -> Company:
35
36
  company_obj = Company(short_name=short_name,
36
37
  name=name,
38
+ parameters=parameters,
37
39
  branding=branding,
38
40
  onboarding_cards=onboarding_cards)
39
41
  self.company = self.profile_repo.create_company(company_obj)
iatoolkit/iatoolkit.py CHANGED
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
19
19
  from injector import Binder, singleton, Injector
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.59.0"
22
+ IATOOLKIT_VERSION = "0.59.2"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -117,7 +117,7 @@ class IAToolkit:
117
117
  log_level = getattr(logging, log_level_name, logging.INFO)
118
118
 
119
119
  logging.basicConfig(
120
- level=logging.INFO, # log_level,
120
+ level=log_level,
121
121
  format="%(asctime)s - IATOOLKIT - %(name)s - %(levelname)s - %(message)s",
122
122
  handlers=[logging.StreamHandler()],
123
123
  force=True
@@ -230,7 +230,6 @@ class IAToolkit:
230
230
  default_origins = [
231
231
  "http://localhost:5001",
232
232
  "http://127.0.0.1:5001",
233
- "https://portal-interno.maxxa.cl",
234
233
  os.getenv('IATOOLKIT_BASE_URL')
235
234
  ]
236
235
 
@@ -60,7 +60,7 @@ class Company(Base):
60
60
 
61
61
  branding = Column(JSON, nullable=True)
62
62
  onboarding_cards = Column(JSON, nullable=True)
63
- parameters = Column(JSON, nullable=True, default={})
63
+ parameters = Column(JSON, nullable=True)
64
64
  created_at = Column(DateTime, default=datetime.now)
65
65
  allow_jwt = Column(Boolean, default=True, nullable=True)
66
66
 
@@ -140,7 +140,7 @@ class AuthService:
140
140
  }
141
141
 
142
142
  # --- Failure: No valid credentials found ---
143
- logging.info(f"Authentication required. No session cookie or API Key provided.")
143
+ logging.info(f"Authentication required. No session cookie or API Key provided. session: {str(session_info)}")
144
144
  return {"success": False, "error_message": "Authentication required. No session cookie or API Key provided.",
145
145
  "status_code": 402}
146
146
 
@@ -3,62 +3,101 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- from iatoolkit.repositories.models import UserFeedback
6
+ from iatoolkit.repositories.models import UserFeedback, Company
7
7
  from injector import inject
8
8
  from iatoolkit.repositories.profile_repo import ProfileRepo
9
9
  from iatoolkit.infra.google_chat_app import GoogleChatApp
10
+ from iatoolkit.infra.mail_app import MailApp # <-- 1. Importar MailApp
10
11
  import logging
11
12
 
12
13
 
13
14
  class UserFeedbackService:
14
15
  @inject
15
- def __init__(self, profile_repo: ProfileRepo, google_chat_app: GoogleChatApp):
16
+ def __init__(self,
17
+ profile_repo: ProfileRepo,
18
+ google_chat_app: GoogleChatApp,
19
+ mail_app: MailApp):
16
20
  self.profile_repo = profile_repo
17
21
  self.google_chat_app = google_chat_app
22
+ self.mail_app = mail_app
23
+
24
+ def _send_google_chat_notification(self, space_name: str, message_text: str):
25
+ """Envía una notificación de feedback a un espacio de Google Chat."""
26
+ try:
27
+ chat_data = {
28
+ "type": "MESSAGE_TRIGGER",
29
+ "space": {"name": space_name},
30
+ "message": {"text": message_text}
31
+ }
32
+ chat_result = self.google_chat_app.send_message(message_data=chat_data)
33
+ if not chat_result.get('success'):
34
+ logging.warning(f"Error al enviar notificación a Google Chat: {chat_result.get('message')}")
35
+ except Exception as e:
36
+ logging.exception(f"Fallo inesperado al enviar notificación a Google Chat: {e}")
37
+
38
+ def _send_email_notification(self, destination_email: str, company_name: str, message_text: str):
39
+ """Envía una notificación de feedback por correo electrónico."""
40
+ try:
41
+ subject = f"Nuevo Feedback de {company_name}"
42
+ # Convertir el texto plano a un HTML simple para mantener los saltos de línea
43
+ html_body = message_text.replace('\n', '<br>')
44
+ self.mail_app.send_email(to=destination_email, subject=subject, body=html_body)
45
+ except Exception as e:
46
+ logging.exception(f"Fallo inesperado al enviar email de feedback: {e}")
47
+
48
+ def _handle_notification(self, company: Company, message_text: str):
49
+ """Lee la configuración de la empresa y envía la notificación al canal correspondiente."""
50
+ feedback_params = company.parameters.get('user_feedback')
51
+ if not isinstance(feedback_params, dict):
52
+ logging.warning(f"No se encontró configuración de 'user_feedback' para la empresa {company.short_name}.")
53
+ return
54
+
55
+ # get channel and destination
56
+ channel = feedback_params.get('channel')
57
+ destination = feedback_params.get('destination')
58
+ if not channel or not destination:
59
+ logging.warning(f"Configuración 'user_feedback' incompleta para {company.short_name}. Faltan 'channel' o 'destination'.")
60
+ return
61
+
62
+ if channel == 'google_chat':
63
+ self._send_google_chat_notification(space_name=destination, message_text=message_text)
64
+ elif channel == 'email':
65
+ self._send_email_notification(destination_email=destination, company_name=company.short_name, message_text=message_text)
66
+ else:
67
+ logging.warning(f"Canal de feedback '{channel}' no reconocido para la empresa {company.short_name}.")
18
68
 
19
69
  def new_feedback(self,
20
70
  company_short_name: str,
21
71
  message: str,
22
72
  user_identifier: str,
23
- space: str = None,
24
- type: str = None,
25
73
  rating: int = None) -> dict:
26
74
  try:
27
- # validate company
75
+ # 1. Validar empresa
28
76
  company = self.profile_repo.get_company_by_short_name(company_short_name)
29
77
  if not company:
30
78
  return {'error': f'No existe la empresa: {company_short_name}'}
31
79
 
32
- # send notification to Google Chat
33
- chat_message = f"*Nuevo feedback de {company_short_name}*:\n*Usuario:* {user_identifier}\n*Mensaje:* {message}\n*Calificación:* {rating}"
34
-
35
- # TO DO: get the space and type from the input data
36
- chat_data = {
37
- "type": type,
38
- "space": {
39
- "name": space
40
- },
41
- "message": {
42
- "text": chat_message
43
- }
44
- }
45
-
46
- chat_result = self.google_chat_app.send_message(message_data=chat_data)
47
- if not chat_result.get('success'):
48
- logging.warning(f"Error al enviar notificación a Google Chat: {chat_result.get('message')}")
80
+ # 2. Enviar notificación según la configuración de la empresa
81
+ notification_text = (f"*Nuevo feedback de {company_short_name}*:\n"
82
+ f"*Usuario:* {user_identifier}\n"
83
+ f"*Mensaje:* {message}\n"
84
+ f"*Calificación:* {rating if rating is not None else 'N/A'}")
85
+ self._handle_notification(company, notification_text)
49
86
 
50
- # create the UserFeedback object
51
- new_feedback = UserFeedback(
87
+ # 3. Guardar el feedback en la base de datos (independientemente del éxito de la notificación)
88
+ new_feedback_obj = UserFeedback(
52
89
  company_id=company.id,
53
90
  message=message,
54
91
  user_identifier=user_identifier,
55
92
  rating=rating
56
93
  )
57
- new_feedback = self.profile_repo.save_feedback(new_feedback)
58
- if not new_feedback:
94
+ saved_feedback = self.profile_repo.save_feedback(new_feedback_obj)
95
+ if not saved_feedback:
96
+ logging.error(f"No se pudo guardar el feedback para el usuario {user_identifier} en la empresa {company_short_name}")
59
97
  return {'error': 'No se pudo guardar el feedback'}
60
98
 
61
99
  return {'message': 'Feedback guardado correctamente'}
62
100
 
63
101
  except Exception as e:
102
+ logging.exception(f"Error crítico en el servicio de feedback: {e}")
64
103
  return {'error': str(e)}
@@ -8,7 +8,7 @@ document.addEventListener('DOMContentLoaded', function() {
8
8
  // Configuración de Toastr para que aparezca abajo a la derecha
9
9
  toastr.options = { "positionClass": "toast-bottom-right", "preventDuplicates": true };
10
10
 
11
- reloadButton.addEventListener('click', function(event) {
11
+ reloadButton.addEventListener('click', async function(event) {
12
12
  event.preventDefault();
13
13
 
14
14
  if (reloadButton.disabled) return; // Prevenir doble clic
@@ -19,43 +19,36 @@ document.addEventListener('DOMContentLoaded', function() {
19
19
  icon.className = spinnerIconClass;
20
20
  toastr.info('Iniciando recarga de contexto en segundo plano...');
21
21
 
22
- // 2. Construir la URL dinámicamente
23
- const company = window.companyShortName;
24
- const reloadUrl = `${window.iatoolkit_base_url}/${company}/api/init-context`;
25
- console.log('URL de recarga:', reloadUrl);
26
-
27
- // 3. Hacer la llamada AJAX con POST
28
- fetch(reloadUrl, {
29
- method: 'POST',
30
- headers: {
31
- 'Content-Type': 'application/json'
32
- },
33
- // Envía un cuerpo vacío o los datos necesarios
34
- body: JSON.stringify({'user_identifier': window.user_identifier})
35
- })
36
- .then(response => {
37
- if (!response.ok) {
38
- return response.json().then(err => {
39
- throw new Error(err.error_message || `Error del servidor: ${response.status}`);
40
- });
41
- }
42
- return response.json();
43
- })
44
- .then(data => {
45
- if (data.status === 'OK') {
46
- toastr.success(data.message || 'Contexto recargado exitosamente.');
22
+ try {
23
+ // 2. Definir los parámetros para callToolkit
24
+ const apiPath = '/api/init-context';
25
+ const payload = { 'user_identifier': window.user_identifier };
26
+
27
+ // 3. Hacer la llamada usando callToolkit
28
+ const data = await callToolkit(apiPath, payload, 'POST');
29
+
30
+ // 4. Procesar la respuesta
31
+ // callToolkit devuelve null si hubo un error que ya mostró en el chat.
32
+ if (data) {
33
+ if (data.status === 'OK') {
34
+ toastr.success(data.message || 'Contexto recargado exitosamente.');
35
+ } else {
36
+ // El servidor respondió 200 OK pero con un mensaje de error en el cuerpo
37
+ toastr.error(data.error_message || 'Ocurrió un error desconocido durante la recarga.');
38
+ }
47
39
  } else {
48
- toastr.error(data.error_message || 'Ocurrió un error desconocido.');
40
+ // Si data es null, callToolkit ya manejó el error (mostrando un mensaje en el chat).
41
+ // Añadimos un toast para notificar al usuario que algo falló.
42
+ toastr.error('Falló la recarga del contexto. Revisa el chat para más detalles.');
49
43
  }
50
- })
51
- .catch(error => {
44
+ } catch (error) {
45
+ // Este bloque se ejecutará para errores no controlados por callToolkit (como AbortError)
52
46
  console.error('Error durante la recarga del contexto:', error);
53
47
  toastr.error(error.message || 'Error de red al intentar recargar.');
54
- })
55
- .finally(() => {
56
- // 4. Restaurar el botón
48
+ } finally {
49
+ // 5. Restaurar el botón en cualquier caso
57
50
  reloadButton.disabled = false;
58
51
  icon.className = originalIconClass;
59
- });
52
+ }
60
53
  });
61
54
  });
@@ -101,12 +101,10 @@ const sendFeedback = async function(message) {
101
101
  "user_identifier": window.user_identifier,
102
102
  "message": message,
103
103
  "rating": activeStars,
104
- "space": "spaces/AAQAupQldd4", // Este valor podría necesitar ser dinámico
105
- "type": "MESSAGE_TRIGGER"
106
104
  };
107
105
  try {
108
106
  // Asumiendo que callLLMAPI está definido globalmente en otro archivo (ej. chat_main.js)
109
- const responseData = await callToolkit('/feedback', data, "POST");
107
+ const responseData = await callToolkit('/api/feedback', data, "POST");
110
108
  return responseData;
111
109
  } catch (error) {
112
110
  console.error("Error al enviar feedback:", error);
@@ -53,8 +53,6 @@ class InitContextApiView(MethodView):
53
53
  user_identifier=user_identifier
54
54
  )
55
55
 
56
- # logging.info(f"Context for {company_short_name}/{user_identifier} rebuilt successfully.")
57
-
58
56
  # 3. Respond with JSON, as this is an API endpoint.
59
57
  return jsonify({'status': 'OK', 'message': 'El context se ha recargado con éxito.'}), 200
60
58
 
@@ -37,14 +37,6 @@ class UserFeedbackApiView(MethodView):
37
37
  if not message:
38
38
  return jsonify({"error_message": "Falta el mensaje de feedback"}), 400
39
39
 
40
- space = data.get("space")
41
- if not space:
42
- return jsonify({"error_message": "Falta el espacio de Google Chat"}), 400
43
-
44
- type = data.get("type")
45
- if not type:
46
- return jsonify({"error_message": "Falta el tipo de feedback"}), 400
47
-
48
40
  rating = data.get("rating")
49
41
  if not rating:
50
42
  return jsonify({"error_message": "Falta la calificación"}), 400
@@ -54,8 +46,6 @@ class UserFeedbackApiView(MethodView):
54
46
  company_short_name=company_short_name,
55
47
  message=message,
56
48
  user_identifier=user_identifier,
57
- space=space,
58
- type=type,
59
49
  rating=rating
60
50
  )
61
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.59.0
3
+ Version: 0.59.2
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -1,8 +1,8 @@
1
1
  iatoolkit/__init__.py,sha256=4PWjMJjktixtrxF6BY405qyA50Sv967kEP2x-oil6qk,1120
2
- iatoolkit/base_company.py,sha256=nfF-G0h63jy3Qh9kCnvx8Ozx76IjG2p7a34HpweWhOk,4608
2
+ iatoolkit/base_company.py,sha256=vU4ki-wB3PWIn3_Bvehfh0TfBH_XNC614tRBKNmEd84,4718
3
3
  iatoolkit/cli_commands.py,sha256=G5L9xQXZ0lVFXQWBaE_KEZHyfuiT6PL1nTQRoSdnBzc,2302
4
4
  iatoolkit/company_registry.py,sha256=tduqt3oV8iDX_IB1eA7KIgvIxE4edTcy-3qZIXh3Lzw,2549
5
- iatoolkit/iatoolkit.py,sha256=lcL_tjD4-az55Sh5zDEnJVPoVDOEFVej3QuJ9PaDHeQ,17647
5
+ iatoolkit/iatoolkit.py,sha256=N922fz-tHZYZSpu5_PupW8p4x1yi66wnRq7UUgpPCfs,17583
6
6
  iatoolkit/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  iatoolkit/common/exceptions.py,sha256=EXx40n5htp7UiOM6P1xfJ9U6NMcADqm62dlFaKz7ICU,1154
8
8
  iatoolkit/common/routes.py,sha256=en9LNxQ3oj7wPUA19okmauGVqdA1yIB_YjPo_-CV-UQ,6195
@@ -29,12 +29,12 @@ iatoolkit/repositories/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCL
29
29
  iatoolkit/repositories/database_manager.py,sha256=QgV8hNnVv9RmeOvUdomdj_mfk0bf3Rl8Ti41a-5zIAY,3700
30
30
  iatoolkit/repositories/document_repo.py,sha256=Y7bF1kZB1HWJsAGjWdF7P2aVYeTYNufq9ngQXp7mDkY,1124
31
31
  iatoolkit/repositories/llm_query_repo.py,sha256=YT_t7cYGQk8rwzH_17-28aTzO-e2jUfa2rvXy8tugvA,3612
32
- iatoolkit/repositories/models.py,sha256=qM95kiKm_92JoPNXiwMT4HTjr5BjalnrDiatG3Rte-M,14300
32
+ iatoolkit/repositories/models.py,sha256=6KQpyCtp2l-ExfbeoPmoqc2V5qlTmSmEbzHYISZtu6g,14288
33
33
  iatoolkit/repositories/profile_repo.py,sha256=21am3GP7XCG0nq6i3pArQ7mfGsrRn8rdcWT98fsdwlU,4397
34
34
  iatoolkit/repositories/tasks_repo.py,sha256=icVO_r2oPagGnnBhwVFzznnvEEU2EAx-2dlWuWvoDC4,1745
35
35
  iatoolkit/repositories/vs_repo.py,sha256=UkpmQQiocgM5IwRBmmWhw3HHzHP6zK1nN3J3TcQgjhc,5300
36
36
  iatoolkit/services/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
37
- iatoolkit/services/auth_service.py,sha256=fId2gVjzLzMJQm3S1zep8yo1tGbWDJ7a7pdE9K8_T3c,7142
37
+ iatoolkit/services/auth_service.py,sha256=1_YfoEflzV7UboOO9xIduwXuwb4I8XHC8mZLgYyDEyw,7171
38
38
  iatoolkit/services/benchmark_service.py,sha256=CdbFYyS3FHFhNzWQEa9ZNjUlmON10DT1nKNbZQ1EUi8,5880
39
39
  iatoolkit/services/branding_service.py,sha256=gXj9Lj6EIFNIHT6wAHia5lr4_2a2sD-ExMbewno5YD8,7505
40
40
  iatoolkit/services/dispatcher_service.py,sha256=Qdn2x4cozpgpKg2448sUxkhO6tuplzb8xPWUxdTTFBE,12772
@@ -52,11 +52,11 @@ iatoolkit/services/query_service.py,sha256=gtMEAQ7IRVrFAMq4h_Pc_lHlMlXFI6heLSuBY
52
52
  iatoolkit/services/search_service.py,sha256=i1xGWu7ORKIIDH0aAQBkF86dVVbLQ0Yrooz5TiZ6aGo,1823
53
53
  iatoolkit/services/sql_service.py,sha256=MIslAtpJWnTMgSD74nnqTvQj27p-lHiyRXc6OiA2C_c,2172
54
54
  iatoolkit/services/tasks_service.py,sha256=itREO5rDnUIgsqtyCOBKDtH30QL5v1egs4qPTiBK8xU,6865
55
- iatoolkit/services/user_feedback_service.py,sha256=ooy750qWmYOeJi-IJQofu8pLG4svGjGU_JKpKMURZkw,2353
55
+ iatoolkit/services/user_feedback_service.py,sha256=Bb6PVWcxzoQ3awev_k4MI0BQhkMh6iLPGC8_CK02t5g,4986
56
56
  iatoolkit/services/user_session_context_service.py,sha256=vYF_vWM37tPB_ZyPBJ6f6WTJVjT2j-4L8JfZbqbI93k,6775
57
57
  iatoolkit/static/images/fernando.jpeg,sha256=W68TYMuo5hZVpbP-evwH6Nu4xWFv2bc8pJzSKDoLTeQ,100612
58
- iatoolkit/static/js/chat_context_reload.js,sha256=hiShGMTYxHo6bHzhCaaffmU3sz-F_WIFos2BtXjvLnE,2380
59
- iatoolkit/static/js/chat_feedback.js,sha256=zlLEDQfEocGK7RKG2baqI-9fyQlqe6hVuAHOKTPmWek,4399
58
+ iatoolkit/static/js/chat_context_reload.js,sha256=DCOGEf7-t_YLw9wuqkP-kFpiFHt3UyaesFfePp9Cs_k,2512
59
+ iatoolkit/static/js/chat_feedback.js,sha256=j2LieutME4RrtMmHbyi3ptfRLefcxechule2NLxzCm8,4284
60
60
  iatoolkit/static/js/chat_filepond.js,sha256=mzXafm7a506EpM37KATTK3zvAswO1E0KSUY1vKbwuRc,3163
61
61
  iatoolkit/static/js/chat_history.js,sha256=4h6ldU7cDvgkW84fMKB8JReoxCX0NKSQAir_4CzAF9I,4382
62
62
  iatoolkit/static/js/chat_main.js,sha256=o1ZNkeaBgGG9d07e5BxTL9OI0aJ26z1bvca1lGoQ-Uo,18535
@@ -95,7 +95,7 @@ iatoolkit/views/file_store_api_view.py,sha256=Uz9f6sey3_F5K8zuyQz6SwYRKAalCjD1ek
95
95
  iatoolkit/views/forgot_password_view.py,sha256=-qKJeeOBqJFdvDUk7rCNg1E1cDQnJQkozPpb0T0FgwA,3159
96
96
  iatoolkit/views/history_api_view.py,sha256=x-tZhB8UzqrD2n-WDIfmHK9iVhGZ9f0yncsGs9mxwt0,1953
97
97
  iatoolkit/views/index_view.py,sha256=P5aVdEWxsYOZGbzcXd6WFE733qZ7YXIoeqriUMAM6V8,1527
98
- iatoolkit/views/init_context_api_view.py,sha256=8mZ7kyD388BGlgJp4CkzM6ptQGS53UnqwQnQ-J3v0_0,2983
98
+ iatoolkit/views/init_context_api_view.py,sha256=NbJdGD4BDTkmDhGO5jkrGihdkpdIvuqUsl_Qg7MDZvA,2878
99
99
  iatoolkit/views/llmquery_api_view.py,sha256=ihRtZygDLAamz8DIJ0UEN73MG_SHzjgvQIznXvCPyxg,1816
100
100
  iatoolkit/views/login_simulation_view.py,sha256=0Qt-puRnltI2HZxlfdyJmOf26-hQp3xjknGV_jkwV7E,3484
101
101
  iatoolkit/views/login_view.py,sha256=ESJLKHGUKQw71STHK2AoxugQUJmxnPYlI13n7ZawLLg,5663
@@ -103,9 +103,9 @@ iatoolkit/views/prompt_api_view.py,sha256=MP0r-MiswwKcbNc_5KY7aVbHkrR218I8XCiCX1
103
103
  iatoolkit/views/signup_view.py,sha256=BCjhM2lMiDPwYrlW_eEwPl-ZLupblbFfsonWtq0E4vU,3922
104
104
  iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC_7ChbLg,3388
105
105
  iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
106
- iatoolkit/views/user_feedback_api_view.py,sha256=59XB9uQLHI4Q6QA4_XhK787HzfXb-c6EY7k1Ccyr4hI,2424
106
+ iatoolkit/views/user_feedback_api_view.py,sha256=PNtXs3G26COw3QeZ9WlX14lYizualGMZ4wVfv77IUoU,2075
107
107
  iatoolkit/views/verify_user_view.py,sha256=7XLSaxvs8LjBr3cYOUDa9B8DqW_50IGlq0IvmOQcD0Y,2340
108
- iatoolkit-0.59.0.dist-info/METADATA,sha256=6QqFuFn3nxO2Vxxy-lP26psE2brKz7KR_eMBh9_Yexc,9301
109
- iatoolkit-0.59.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
- iatoolkit-0.59.0.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
- iatoolkit-0.59.0.dist-info/RECORD,,
108
+ iatoolkit-0.59.2.dist-info/METADATA,sha256=XUACxsD5jRzps23Y-avo13tfLAXlF23w7v2jrq-Pr4Q,9301
109
+ iatoolkit-0.59.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
+ iatoolkit-0.59.2.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
+ iatoolkit-0.59.2.dist-info/RECORD,,