iatoolkit 0.7.4__py3-none-any.whl → 0.7.6__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.
- common/__init__.py +0 -0
- common/auth.py +200 -0
- common/exceptions.py +46 -0
- common/routes.py +86 -0
- common/session_manager.py +25 -0
- common/util.py +358 -0
- iatoolkit/iatoolkit.py +3 -3
- {iatoolkit-0.7.4.dist-info → iatoolkit-0.7.6.dist-info}/METADATA +1 -1
- iatoolkit-0.7.6.dist-info/RECORD +80 -0
- iatoolkit-0.7.6.dist-info/top_level.txt +6 -0
- infra/__init__.py +5 -0
- infra/call_service.py +140 -0
- infra/connectors/__init__.py +5 -0
- infra/connectors/file_connector.py +17 -0
- infra/connectors/file_connector_factory.py +57 -0
- infra/connectors/google_cloud_storage_connector.py +53 -0
- infra/connectors/google_drive_connector.py +68 -0
- infra/connectors/local_file_connector.py +46 -0
- infra/connectors/s3_connector.py +33 -0
- infra/gemini_adapter.py +356 -0
- infra/google_chat_app.py +57 -0
- infra/llm_client.py +430 -0
- infra/llm_proxy.py +139 -0
- infra/llm_response.py +40 -0
- infra/mail_app.py +145 -0
- infra/openai_adapter.py +90 -0
- infra/redis_session_manager.py +76 -0
- repositories/__init__.py +5 -0
- repositories/database_manager.py +95 -0
- repositories/document_repo.py +33 -0
- repositories/llm_query_repo.py +91 -0
- repositories/models.py +309 -0
- repositories/profile_repo.py +118 -0
- repositories/tasks_repo.py +52 -0
- repositories/vs_repo.py +139 -0
- views/__init__.py +5 -0
- views/change_password_view.py +91 -0
- views/chat_token_request_view.py +98 -0
- views/chat_view.py +51 -0
- views/download_file_view.py +58 -0
- views/external_chat_login_view.py +88 -0
- views/external_login_view.py +40 -0
- views/file_store_view.py +58 -0
- views/forgot_password_view.py +64 -0
- views/history_view.py +57 -0
- views/home_view.py +34 -0
- views/llmquery_view.py +65 -0
- views/login_view.py +60 -0
- views/prompt_view.py +37 -0
- views/signup_view.py +87 -0
- views/tasks_review_view.py +83 -0
- views/tasks_view.py +98 -0
- views/user_feedback_view.py +74 -0
- views/verify_user_view.py +55 -0
- iatoolkit-0.7.4.dist-info/RECORD +0 -30
- iatoolkit-0.7.4.dist-info/top_level.txt +0 -2
- {iatoolkit-0.7.4.dist-info → iatoolkit-0.7.6.dist-info}/WHEEL +0 -0
views/llmquery_view.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import request, jsonify, render_template
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
from services.query_service import QueryService
|
|
9
|
+
from common.auth import IAuthentication
|
|
10
|
+
from injector import inject
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LLMQueryView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
iauthentication: IAuthentication,
|
|
18
|
+
query_service: QueryService,
|
|
19
|
+
):
|
|
20
|
+
self.iauthentication = iauthentication
|
|
21
|
+
self.query_service = query_service
|
|
22
|
+
|
|
23
|
+
def post(self, company_short_name):
|
|
24
|
+
data = request.get_json()
|
|
25
|
+
if not data:
|
|
26
|
+
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 400
|
|
27
|
+
|
|
28
|
+
# get access credentials
|
|
29
|
+
iaut = self.iauthentication.verify(company_short_name, data.get("external_user_id"))
|
|
30
|
+
if not iaut.get("success"):
|
|
31
|
+
return jsonify(iaut), 401
|
|
32
|
+
|
|
33
|
+
company_id = iaut.get("company_id")
|
|
34
|
+
external_user_id = iaut.get("external_user_id")
|
|
35
|
+
local_user_id = iaut.get("local_user_id")
|
|
36
|
+
|
|
37
|
+
# now check the form
|
|
38
|
+
question = data.get("question")
|
|
39
|
+
files = data.get("files", [])
|
|
40
|
+
client_data = data.get("client_data", {})
|
|
41
|
+
prompt_name = data.get("prompt_name")
|
|
42
|
+
if not question and not prompt_name:
|
|
43
|
+
return jsonify({"error_message": "Falta la consulta o el prompt_name"}), 400
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
response = self.query_service.llm_query(
|
|
47
|
+
company_short_name=company_short_name,
|
|
48
|
+
external_user_id=external_user_id,
|
|
49
|
+
local_user_id=local_user_id,
|
|
50
|
+
question=question,
|
|
51
|
+
prompt_name=prompt_name,
|
|
52
|
+
client_data=client_data,
|
|
53
|
+
files=files)
|
|
54
|
+
if "error" in response:
|
|
55
|
+
return {'error_message': response.get("error_message", '')}, 401
|
|
56
|
+
|
|
57
|
+
return response, 200
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logging.exception(
|
|
60
|
+
f"Error inesperado al procesar llm_query para company {company_short_name}: {e}")
|
|
61
|
+
if local_user_id:
|
|
62
|
+
return render_template("error.html",
|
|
63
|
+
message="Ha ocurrido un error inesperado."), 500
|
|
64
|
+
else:
|
|
65
|
+
return jsonify({"error_message": str(e)}), 500
|
views/login_view.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask.views import MethodView
|
|
7
|
+
from flask import request, redirect, render_template, url_for
|
|
8
|
+
from injector import inject
|
|
9
|
+
from services.profile_service import ProfileService
|
|
10
|
+
|
|
11
|
+
class LoginView(MethodView):
|
|
12
|
+
@inject
|
|
13
|
+
def __init__(self, profile_service: ProfileService):
|
|
14
|
+
self.profile_service = profile_service
|
|
15
|
+
|
|
16
|
+
def get(self, company_short_name: str):
|
|
17
|
+
# get company info
|
|
18
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
19
|
+
if not company:
|
|
20
|
+
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
21
|
+
|
|
22
|
+
return render_template('login.html',
|
|
23
|
+
company=company,
|
|
24
|
+
company_short_name=company_short_name)
|
|
25
|
+
|
|
26
|
+
def post(self, company_short_name: str):
|
|
27
|
+
# get company info
|
|
28
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
29
|
+
if not company:
|
|
30
|
+
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
31
|
+
|
|
32
|
+
email = request.form.get('email')
|
|
33
|
+
try:
|
|
34
|
+
password = request.form.get('password')
|
|
35
|
+
|
|
36
|
+
response = self.profile_service.login(
|
|
37
|
+
company_short_name=company_short_name,
|
|
38
|
+
email=email,
|
|
39
|
+
password=password
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if "error" in response:
|
|
43
|
+
return render_template(
|
|
44
|
+
'login.html',
|
|
45
|
+
company_short_name=company_short_name,
|
|
46
|
+
company=company,
|
|
47
|
+
form_data={
|
|
48
|
+
"email": email,
|
|
49
|
+
"password": password,
|
|
50
|
+
},
|
|
51
|
+
alert_message=response["error"]), 400
|
|
52
|
+
|
|
53
|
+
return redirect(url_for('chat', company_short_name=company_short_name))
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
return render_template("error.html",
|
|
57
|
+
company=company,
|
|
58
|
+
company_short_name=company_short_name,
|
|
59
|
+
message="Ha ocurrido un error inesperado."), 500
|
|
60
|
+
|
views/prompt_view.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import jsonify
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
from services.prompt_manager_service import PromptService
|
|
9
|
+
from common.auth import IAuthentication
|
|
10
|
+
from injector import inject
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PromptView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
iauthentication: IAuthentication,
|
|
18
|
+
prompt_service: PromptService ):
|
|
19
|
+
self.iauthentication = iauthentication
|
|
20
|
+
self.prompt_service = prompt_service
|
|
21
|
+
|
|
22
|
+
def get(self, company_short_name):
|
|
23
|
+
# get access credentials
|
|
24
|
+
iaut = self.iauthentication.verify(company_short_name)
|
|
25
|
+
if not iaut.get("success"):
|
|
26
|
+
return jsonify(iaut), 401
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
response = self.prompt_service.get_user_prompts(company_short_name)
|
|
30
|
+
if "error" in response:
|
|
31
|
+
return {'error_message': response["error"]}, 402
|
|
32
|
+
|
|
33
|
+
return response, 200
|
|
34
|
+
except Exception as e:
|
|
35
|
+
logging.exception(
|
|
36
|
+
f"Error inesperado al obtener el historial de consultas para company {company_short_name}: {e}")
|
|
37
|
+
return jsonify({"error_message": str(e)}), 500
|
views/signup_view.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask.views import MethodView
|
|
7
|
+
from flask import render_template
|
|
8
|
+
from services.profile_service import ProfileService
|
|
9
|
+
from injector import inject
|
|
10
|
+
from itsdangerous import URLSafeTimedSerializer
|
|
11
|
+
from flask import url_for, request
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SignupView(MethodView):
|
|
16
|
+
@inject
|
|
17
|
+
def __init__(self, profile_service: ProfileService):
|
|
18
|
+
self.profile_service = profile_service
|
|
19
|
+
self.serializer = URLSafeTimedSerializer(os.getenv("USER_VERIF_KEY"))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get(self, company_short_name: str):
|
|
23
|
+
# get company info
|
|
24
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
25
|
+
if not company:
|
|
26
|
+
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
27
|
+
|
|
28
|
+
user_agent = request.user_agent
|
|
29
|
+
is_mobile = user_agent.platform in ["android", "iphone", "ipad"] or "mobile" in user_agent.string.lower()
|
|
30
|
+
return render_template('signup.html',
|
|
31
|
+
company=company,
|
|
32
|
+
company_short_name=company_short_name,
|
|
33
|
+
is_mobile=is_mobile)
|
|
34
|
+
|
|
35
|
+
def post(self, company_short_name: str):
|
|
36
|
+
# get company info
|
|
37
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
38
|
+
if not company:
|
|
39
|
+
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
first_name = request.form.get('first_name')
|
|
43
|
+
last_name = request.form.get('last_name')
|
|
44
|
+
email = request.form.get('email')
|
|
45
|
+
password = request.form.get('password')
|
|
46
|
+
confirm_password = request.form.get('confirm_password')
|
|
47
|
+
|
|
48
|
+
# create verification token and url for verification
|
|
49
|
+
token = self.serializer.dumps(email, salt='email-confirm')
|
|
50
|
+
verification_url = url_for('verify_account',
|
|
51
|
+
company_short_name=company_short_name,
|
|
52
|
+
token=token, _external=True)
|
|
53
|
+
|
|
54
|
+
response = self.profile_service.signup(
|
|
55
|
+
company_short_name=company_short_name,
|
|
56
|
+
email=email,
|
|
57
|
+
first_name=first_name, last_name=last_name,
|
|
58
|
+
password=password, confirm_password=confirm_password,
|
|
59
|
+
verification_url=verification_url)
|
|
60
|
+
|
|
61
|
+
if "error" in response:
|
|
62
|
+
return render_template(
|
|
63
|
+
'signup.html',
|
|
64
|
+
company=company,
|
|
65
|
+
company_short_name=company_short_name,
|
|
66
|
+
form_data={
|
|
67
|
+
"first_name": first_name,
|
|
68
|
+
"last_name": last_name,
|
|
69
|
+
"email": email,
|
|
70
|
+
"password": password,
|
|
71
|
+
"confirm_password": confirm_password
|
|
72
|
+
},
|
|
73
|
+
alert_message=response["error"]), 400
|
|
74
|
+
|
|
75
|
+
# all is OK
|
|
76
|
+
return render_template(
|
|
77
|
+
'login.html',
|
|
78
|
+
company=company,
|
|
79
|
+
company_short_name=company_short_name,
|
|
80
|
+
alert_icon='success',
|
|
81
|
+
alert_message=response["message"]), 200
|
|
82
|
+
except Exception as e:
|
|
83
|
+
return render_template("error.html",
|
|
84
|
+
company=company,
|
|
85
|
+
company_short_name=company_short_name,
|
|
86
|
+
message="Ha ocurrido un error inesperado."), 500
|
|
87
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask.views import MethodView
|
|
7
|
+
from flask import request, jsonify
|
|
8
|
+
from services.tasks_service import TaskService
|
|
9
|
+
from repositories.profile_repo import ProfileRepo
|
|
10
|
+
from injector import inject
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TaskReviewView(MethodView):
|
|
16
|
+
@inject
|
|
17
|
+
def __init__(self, task_service: TaskService, profile_repo: ProfileRepo):
|
|
18
|
+
self.task_service = task_service
|
|
19
|
+
self.profile_repo = profile_repo
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _authenticate_requesting_company_via_api_key(self) -> tuple[
|
|
23
|
+
Optional[int], Optional[str], Optional[tuple[dict, int]]]:
|
|
24
|
+
"""
|
|
25
|
+
Autentica a la compañía usando su API Key.
|
|
26
|
+
Retorna (company_id, company_short_name, None) en éxito.
|
|
27
|
+
Retorna (None, None, (error_json, status_code)) en fallo.
|
|
28
|
+
"""
|
|
29
|
+
api_key_header = request.headers.get('Authorization')
|
|
30
|
+
if not api_key_header or not api_key_header.startswith('Bearer '):
|
|
31
|
+
return None, None, ({"error": "API Key faltante o mal formada en el header Authorization"}, 401)
|
|
32
|
+
|
|
33
|
+
api_key_value = api_key_header.split('Bearer ')[1]
|
|
34
|
+
try:
|
|
35
|
+
api_key_entry = self.profile_repo.get_active_api_key_entry(api_key_value)
|
|
36
|
+
if not api_key_entry:
|
|
37
|
+
return None, None, ({"error": "API Key inválida o inactiva"}, 401)
|
|
38
|
+
|
|
39
|
+
# api_key_entry.company ya está cargado por joinedload en get_active_api_key_entry
|
|
40
|
+
if not api_key_entry.company: # Sanity check
|
|
41
|
+
logging.error(
|
|
42
|
+
f"ChatTokenRequest: API Key {api_key_value[:5]}... no tiene compañía asociada a pesar de ser válida.")
|
|
43
|
+
return None, None, ({"error": "Error interno del servidor al verificar API Key"}, 500)
|
|
44
|
+
|
|
45
|
+
return api_key_entry.company_id, api_key_entry.company.short_name, None
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logging.exception(f"ChatTokenRequest: Error interno durante validación de API Key: {e}")
|
|
49
|
+
return None, None, ({"error": "Error interno del servidor al validar API Key"}, 500)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def post(self, task_id: int):
|
|
53
|
+
try:
|
|
54
|
+
# only requests with valid api-key are allowed
|
|
55
|
+
auth_company_id, auth_company_short_name, error = self._authenticate_requesting_company_via_api_key()
|
|
56
|
+
if error:
|
|
57
|
+
return jsonify(error[0]), error[1]
|
|
58
|
+
|
|
59
|
+
req_data = request.get_json()
|
|
60
|
+
|
|
61
|
+
required_fields = ['review_user', 'approved']
|
|
62
|
+
for field in required_fields:
|
|
63
|
+
if field not in req_data:
|
|
64
|
+
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
65
|
+
|
|
66
|
+
review_user = req_data.get('review_user', '')
|
|
67
|
+
approved = req_data.get('approved', False)
|
|
68
|
+
comment = req_data.get('comment', '')
|
|
69
|
+
|
|
70
|
+
new_task = self.task_service.review_task(
|
|
71
|
+
task_id=task_id,
|
|
72
|
+
review_user=review_user,
|
|
73
|
+
approved=approved,
|
|
74
|
+
comment=comment)
|
|
75
|
+
|
|
76
|
+
return jsonify({
|
|
77
|
+
"task_id": new_task.id,
|
|
78
|
+
"status": new_task.status.name
|
|
79
|
+
}), 200
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logging.exception("Error al revisar la tarea: %s", str(e))
|
|
83
|
+
return jsonify({"error": str(e)}), 500
|
views/tasks_view.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask.views import MethodView
|
|
7
|
+
from flask import request, jsonify
|
|
8
|
+
from services.tasks_service import TaskService
|
|
9
|
+
from repositories.profile_repo import ProfileRepo
|
|
10
|
+
from injector import inject
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TaskView(MethodView):
|
|
17
|
+
@inject
|
|
18
|
+
def __init__(self, task_service: TaskService, profile_repo: ProfileRepo):
|
|
19
|
+
self.task_service = task_service
|
|
20
|
+
self.profile_repo = profile_repo
|
|
21
|
+
|
|
22
|
+
def _authenticate_requesting_company_via_api_key(self) -> tuple[
|
|
23
|
+
Optional[int], Optional[str], Optional[tuple[dict, int]]]:
|
|
24
|
+
"""
|
|
25
|
+
Autentica a la compañía usando su API Key.
|
|
26
|
+
Retorna (company_id, company_short_name, None) en éxito.
|
|
27
|
+
Retorna (None, None, (error_json, status_code)) en fallo.
|
|
28
|
+
"""
|
|
29
|
+
api_key_header = request.headers.get('Authorization')
|
|
30
|
+
if not api_key_header or not api_key_header.startswith('Bearer '):
|
|
31
|
+
return None, None, ({"error": "API Key faltante o mal formada en el header Authorization"}, 401)
|
|
32
|
+
|
|
33
|
+
api_key_value = api_key_header.split('Bearer ')[1]
|
|
34
|
+
try:
|
|
35
|
+
api_key_entry = self.profile_repo.get_active_api_key_entry(api_key_value)
|
|
36
|
+
if not api_key_entry:
|
|
37
|
+
return None, None, ({"error": "API Key inválida o inactiva"}, 401)
|
|
38
|
+
|
|
39
|
+
# api_key_entry.company ya está cargado por joinedload en get_active_api_key_entry
|
|
40
|
+
if not api_key_entry.company: # Sanity check
|
|
41
|
+
logging.error(
|
|
42
|
+
f"ChatTokenRequest: API Key {api_key_value[:5]}... no tiene compañía asociada a pesar de ser válida.")
|
|
43
|
+
return None, None, ({"error": "Error interno del servidor al verificar API Key"}, 500)
|
|
44
|
+
|
|
45
|
+
return api_key_entry.company_id, api_key_entry.company.short_name, None
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logging.exception(f"ChatTokenRequest: Error interno durante validación de API Key: {e}")
|
|
49
|
+
return None, None, ({"error": "Error interno del servidor al validar API Key"}, 500)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def post(self):
|
|
53
|
+
try:
|
|
54
|
+
# only requests with valid api-key are allowed
|
|
55
|
+
auth_company_id, auth_company_short_name, error = self._authenticate_requesting_company_via_api_key()
|
|
56
|
+
if error:
|
|
57
|
+
return jsonify(error[0]), error[1]
|
|
58
|
+
|
|
59
|
+
req_data = request.get_json()
|
|
60
|
+
files = request.files.getlist('files')
|
|
61
|
+
|
|
62
|
+
required_fields = ['company', 'task_type', 'client_data']
|
|
63
|
+
for field in required_fields:
|
|
64
|
+
if field not in req_data:
|
|
65
|
+
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
66
|
+
|
|
67
|
+
company_short_name = req_data.get('company', '')
|
|
68
|
+
task_type = req_data.get('task_type', '')
|
|
69
|
+
client_data = req_data.get('client_data', {})
|
|
70
|
+
company_task_id = req_data.get('company_task_id', 0)
|
|
71
|
+
execute_at = req_data.get('execute_at', None)
|
|
72
|
+
|
|
73
|
+
# validate date format is parameter is present
|
|
74
|
+
if execute_at:
|
|
75
|
+
try:
|
|
76
|
+
# date in iso format
|
|
77
|
+
execute_at = datetime.fromisoformat(execute_at)
|
|
78
|
+
except ValueError:
|
|
79
|
+
return jsonify({
|
|
80
|
+
"error": "El formato de execute_at debe ser YYYY-MM-DD HH:MM:SS"
|
|
81
|
+
}), 400
|
|
82
|
+
|
|
83
|
+
new_task = self.task_service.create_task(
|
|
84
|
+
company_short_name=company_short_name,
|
|
85
|
+
task_type_name=task_type,
|
|
86
|
+
client_data=client_data,
|
|
87
|
+
company_task_id=company_task_id,
|
|
88
|
+
execute_at=execute_at,
|
|
89
|
+
files=files)
|
|
90
|
+
|
|
91
|
+
return jsonify({
|
|
92
|
+
"task_id": new_task.id,
|
|
93
|
+
"status": new_task.status.name
|
|
94
|
+
}), 201
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logging.exception("Error al crear la tarea: %s", str(e))
|
|
98
|
+
return jsonify({"error": str(e)}), 500
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import request, jsonify, render_template
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
from services.user_feedback_service import UserFeedbackService
|
|
9
|
+
from common.auth import IAuthentication
|
|
10
|
+
from injector import inject
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UserFeedbackView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
iauthentication: IAuthentication,
|
|
18
|
+
user_feedback_service: UserFeedbackService ):
|
|
19
|
+
self.iauthentication = iauthentication
|
|
20
|
+
self.user_feedback_service = user_feedback_service
|
|
21
|
+
|
|
22
|
+
def post(self, company_short_name):
|
|
23
|
+
data = request.get_json()
|
|
24
|
+
if not data:
|
|
25
|
+
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 400
|
|
26
|
+
|
|
27
|
+
# get access credentials
|
|
28
|
+
iaut = self.iauthentication.verify(company_short_name, data.get("external_user_id"))
|
|
29
|
+
if not iaut.get("success"):
|
|
30
|
+
return jsonify(iaut), 401
|
|
31
|
+
|
|
32
|
+
message = data.get("message")
|
|
33
|
+
if not message:
|
|
34
|
+
return jsonify({"error_message": "Falta el mensaje de feedback"}), 400
|
|
35
|
+
|
|
36
|
+
space = data.get("space")
|
|
37
|
+
if not space:
|
|
38
|
+
return jsonify({"error_message": "Falta el espacio de Google Chat"}), 400
|
|
39
|
+
|
|
40
|
+
type = data.get("type")
|
|
41
|
+
if not type:
|
|
42
|
+
return jsonify({"error_message": "Falta el tipo de feedback"}), 400
|
|
43
|
+
|
|
44
|
+
rating = data.get("rating")
|
|
45
|
+
if not rating:
|
|
46
|
+
return jsonify({"error_message": "Falta la calificación"}), 400
|
|
47
|
+
|
|
48
|
+
external_user_id = data.get("external_user_id")
|
|
49
|
+
local_user_id = data.get("local_user_id", 0)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
response = self.user_feedback_service.new_feedback(
|
|
53
|
+
company_short_name=company_short_name,
|
|
54
|
+
message=message,
|
|
55
|
+
external_user_id=external_user_id,
|
|
56
|
+
local_user_id=local_user_id,
|
|
57
|
+
space=space,
|
|
58
|
+
type=type,
|
|
59
|
+
rating=rating
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if "error" in response:
|
|
63
|
+
return {'error_message': response["error"]}, 402
|
|
64
|
+
|
|
65
|
+
return response, 200
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logging.exception(
|
|
68
|
+
f"Error inesperado al procesar feedback para company {company_short_name}: {e}")
|
|
69
|
+
if local_user_id:
|
|
70
|
+
return render_template("error.html",
|
|
71
|
+
message="Ha ocurrido un error inesperado."), 500
|
|
72
|
+
else:
|
|
73
|
+
return jsonify({"error_message": str(e)}), 500
|
|
74
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask.views import MethodView
|
|
7
|
+
from flask import render_template
|
|
8
|
+
from services.profile_service import ProfileService
|
|
9
|
+
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
|
10
|
+
from injector import inject
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class VerifyAccountView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self, profile_service: ProfileService):
|
|
17
|
+
self.profile_service = profile_service
|
|
18
|
+
self.serializer = URLSafeTimedSerializer(os.getenv("USER_VERIF_KEY"))
|
|
19
|
+
|
|
20
|
+
def get(self, company_short_name: str, token: str):
|
|
21
|
+
# get company info
|
|
22
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
23
|
+
if not company:
|
|
24
|
+
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
# decode the token from the URL
|
|
28
|
+
email = self.serializer.loads(token, salt='email-confirm', max_age=3600*5)
|
|
29
|
+
except SignatureExpired:
|
|
30
|
+
return render_template('signup.html',
|
|
31
|
+
company=company,
|
|
32
|
+
company_short_name=company_short_name,
|
|
33
|
+
token=token,
|
|
34
|
+
alert_message="El enlace de verificación ha expirado. Por favor, solicita uno nuevo."), 400
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = self.profile_service.verify_account(email)
|
|
38
|
+
if "error" in response:
|
|
39
|
+
return render_template(
|
|
40
|
+
'signup.html',
|
|
41
|
+
company=company,
|
|
42
|
+
company_short_name=company_short_name,
|
|
43
|
+
token=token,
|
|
44
|
+
alert_message=response["error"]), 400
|
|
45
|
+
|
|
46
|
+
return render_template('login.html',
|
|
47
|
+
company=company,
|
|
48
|
+
company_short_name=company_short_name,
|
|
49
|
+
alert_icon='success',
|
|
50
|
+
alert_message=response['message'])
|
|
51
|
+
except Exception as e:
|
|
52
|
+
return render_template("error.html",
|
|
53
|
+
company=company,
|
|
54
|
+
company_short_name=company_short_name,
|
|
55
|
+
message="Ha ocurrido un error inesperado."), 500
|
iatoolkit-0.7.4.dist-info/RECORD
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
iatoolkit/__init__.py,sha256=GkFxAQHKPifz4Kd8M73Rc8TWRVIxjxkl1N0nsPvb_sU,1743
|
|
2
|
-
iatoolkit/base_company.py,sha256=WmD4o0qFC1n5DW5eRsRsuNfaGot9nxGFcJe3LmibSuE,4259
|
|
3
|
-
iatoolkit/cli_commands.py,sha256=BGuThg19eoSssrBJIqzBNaWpMmyy7u4yRUz0JA7d-vc,2270
|
|
4
|
-
iatoolkit/company_registry.py,sha256=tduqt3oV8iDX_IB1eA7KIgvIxE4edTcy-3qZIXh3Lzw,2549
|
|
5
|
-
iatoolkit/iatoolkit.py,sha256=0q6P0QHDHD_d_KEqvcKpbDXRGIoWLZgBhHTD0zdFRJY,15748
|
|
6
|
-
iatoolkit/system_prompts/format_styles.prompt,sha256=MSMe1qvR3cF_0IbFshn8R0z6Wx6VCHQq1p37rpu5wwk,3576
|
|
7
|
-
iatoolkit/system_prompts/query_main.prompt,sha256=w_9ybgWgiQH4V_RbAXqsvz0M7oOuiyhxcwf-D0CgfA4,3017
|
|
8
|
-
iatoolkit/system_prompts/sql_rules.prompt,sha256=y4nURVnb9AyFwt-lrbMNBHHtZlhk6kC9grYoOhRnrJo,59174
|
|
9
|
-
services/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
|
|
10
|
-
services/benchmark_service.py,sha256=_ruKh9YzrTLtR0ZKrRNxqJQW0HdbwWuFz1gfLzJ9owA,5850
|
|
11
|
-
services/dispatcher_service.py,sha256=y2J-TfrkoCcsOESr3E6E3q5bleJfRibT4ONxs9AxUSg,13959
|
|
12
|
-
services/document_service.py,sha256=8MsJz0pihM9E9Z92PrPqDgQnpMAmpFrbogXr5HscMWM,5926
|
|
13
|
-
services/excel_service.py,sha256=ATPaeAvkLwQAkPZ3AKIUpO73RVyRg0D8c6i37_mcql0,3559
|
|
14
|
-
services/file_processor_service.py,sha256=98yWYF7nIq1nm7nh6IzMmTKaOMTIeqCFWYwXVtV-ZJI,4102
|
|
15
|
-
services/history_service.py,sha256=j0QCqcIIyw7DBy3GrZrEZNk0I4m-uuRoG5g0Z2RCcOE,1586
|
|
16
|
-
services/jwt_service.py,sha256=YoZ9h7_o9xBko-arNQv4MbcwnxoSWVNj4VbZmMo_QGY,3908
|
|
17
|
-
services/load_documents_service.py,sha256=UGfomYz7seWFXawbDuk2t6CyoEr1vggR8vmrCUAeLBg,7190
|
|
18
|
-
services/mail_service.py,sha256=_67pctxZO46DHnWBip51ayuYtWd4bEoS1kg29ACO7_I,2162
|
|
19
|
-
services/profile_service.py,sha256=vTK9TvH_2AFdqgL3sGjhck9LyLGIbvdC2USoaRx82G8,17561
|
|
20
|
-
services/prompt_manager_service.py,sha256=7SMC6N_T4BP4m5-ZNYAL3Y2YWHwl1bytXSgnEqu5bWI,8301
|
|
21
|
-
services/query_service.py,sha256=gvUnq0Vpn4gv5ycQk8-fklh7NDFIXpO1Vt1lT2ugO6Q,15283
|
|
22
|
-
services/search_service.py,sha256=bB3FWFxJi1iYsOdBxyu9tzIO406nQxcyeQzEowpmpjY,1803
|
|
23
|
-
services/sql_service.py,sha256=s84K1ADlvMtum949wgMh8jsmqlOUeL-m_SWfAM4Wsv4,2141
|
|
24
|
-
services/tasks_service.py,sha256=1DdbERlAxIkCpGEylnHDKC-KAsXRJugbaRSzRbPfL58,6790
|
|
25
|
-
services/user_feedback_service.py,sha256=_LeNBYz4hHFapXfYTQVfkkD34gE8j2UeKnyOZ8H0nWo,2442
|
|
26
|
-
services/user_session_context_service.py,sha256=GluNSgqP6W_hFke4oslSnfGnU_b-ph28BHH6jf3EIm0,3797
|
|
27
|
-
iatoolkit-0.7.4.dist-info/METADATA,sha256=TCwvfWjnFKTqpAjPbIXF0Q4haWHAORKU0ecnVjMxG54,9300
|
|
28
|
-
iatoolkit-0.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
29
|
-
iatoolkit-0.7.4.dist-info/top_level.txt,sha256=dqlBbmgo9okD9d_WMR9uYzdup7Rxgj26yFF85jRGeu4,19
|
|
30
|
-
iatoolkit-0.7.4.dist-info/RECORD,,
|
|
File without changes
|