iatoolkit 0.8.1__py3-none-any.whl → 0.63.4__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/__init__.py +8 -34
- iatoolkit/base_company.py +14 -3
- iatoolkit/common/routes.py +83 -52
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +0 -27
- iatoolkit/iatoolkit.py +61 -46
- iatoolkit/infra/llm_client.py +7 -8
- iatoolkit/infra/openai_adapter.py +1 -1
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/repositories/database_manager.py +17 -2
- iatoolkit/repositories/models.py +31 -6
- iatoolkit/repositories/profile_repo.py +7 -2
- iatoolkit/services/auth_service.py +188 -0
- iatoolkit/services/branding_service.py +147 -0
- iatoolkit/services/dispatcher_service.py +10 -40
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/history_service.py +3 -12
- iatoolkit/services/jwt_service.py +15 -24
- iatoolkit/services/onboarding_service.py +43 -0
- iatoolkit/services/profile_service.py +97 -44
- iatoolkit/services/query_service.py +124 -81
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +67 -31
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
- iatoolkit/static/js/chat_history_button.js +126 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +130 -220
- iatoolkit/static/js/chat_onboarding_button.js +97 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +52 -0
- iatoolkit/static/styles/chat_iatoolkit.css +329 -507
- iatoolkit/static/styles/chat_modal.css +95 -56
- iatoolkit/static/styles/landing_page.css +182 -0
- iatoolkit/static/styles/onboarding.css +169 -0
- iatoolkit/system_prompts/query_main.prompt +3 -12
- iatoolkit/templates/_company_header.html +20 -0
- iatoolkit/templates/_login_widget.html +40 -0
- iatoolkit/templates/base.html +8 -3
- iatoolkit/templates/change_password.html +54 -37
- iatoolkit/templates/chat.html +149 -66
- iatoolkit/templates/chat_modals.html +47 -18
- iatoolkit/templates/error.html +41 -8
- iatoolkit/templates/forgot_password.html +37 -24
- iatoolkit/templates/index.html +140 -0
- iatoolkit/templates/login_simulation.html +34 -0
- iatoolkit/templates/onboarding_shell.html +105 -0
- iatoolkit/templates/signup.html +64 -66
- iatoolkit/views/base_login_view.py +81 -0
- iatoolkit/views/change_password_view.py +23 -12
- iatoolkit/views/external_login_view.py +61 -28
- iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
- iatoolkit/views/forgot_password_view.py +23 -13
- iatoolkit/views/history_api_view.py +52 -0
- iatoolkit/views/home_view.py +58 -25
- iatoolkit/views/index_view.py +14 -0
- iatoolkit/views/init_context_api_view.py +68 -0
- iatoolkit/views/llmquery_api_view.py +45 -0
- iatoolkit/views/login_simulation_view.py +81 -0
- iatoolkit/views/login_view.py +118 -34
- iatoolkit/views/logout_api_view.py +45 -0
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
- iatoolkit/views/signup_view.py +38 -29
- iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
- iatoolkit/views/tasks_review_api_view.py +55 -0
- iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
- iatoolkit/views/verify_user_view.py +13 -8
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
- iatoolkit-0.63.4.dist-info/RECORD +113 -0
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
- iatoolkit/common/auth.py +0 -200
- iatoolkit/static/images/arrow_up.png +0 -0
- iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
- iatoolkit/static/images/logo_clinica.png +0 -0
- iatoolkit/static/images/logo_iatoolkit.png +0 -0
- iatoolkit/static/images/logo_maxxa.png +0 -0
- iatoolkit/static/images/logo_notaria.png +0 -0
- iatoolkit/static/images/logo_tarjeta.png +0 -0
- iatoolkit/static/images/logo_umayor.png +0 -0
- iatoolkit/static/images/upload.png +0 -0
- iatoolkit/static/js/chat_history.js +0 -117
- iatoolkit/templates/home.html +0 -201
- iatoolkit/templates/login.html +0 -43
- iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit/views/chat_view.py +0 -51
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/external_chat_login_view.py +0 -88
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/llmquery_view.py +0 -65
- iatoolkit/views/tasks_review_view.py +0 -83
- iatoolkit-0.8.1.dist-info/RECORD +0 -175
- tests/__init__.py +0 -5
- tests/common/__init__.py +0 -0
- tests/common/test_auth.py +0 -279
- tests/common/test_routes.py +0 -42
- tests/common/test_session_manager.py +0 -59
- tests/common/test_util.py +0 -444
- tests/companies/__init__.py +0 -5
- tests/conftest.py +0 -36
- tests/infra/__init__.py +0 -5
- tests/infra/connectors/__init__.py +0 -5
- tests/infra/connectors/test_google_drive_connector.py +0 -107
- tests/infra/connectors/test_local_file_connector.py +0 -85
- tests/infra/connectors/test_s3_connector.py +0 -95
- tests/infra/test_call_service.py +0 -92
- tests/infra/test_database_manager.py +0 -59
- tests/infra/test_gemini_adapter.py +0 -137
- tests/infra/test_google_chat_app.py +0 -68
- tests/infra/test_llm_client.py +0 -165
- tests/infra/test_llm_proxy.py +0 -122
- tests/infra/test_mail_app.py +0 -94
- tests/infra/test_openai_adapter.py +0 -105
- tests/infra/test_redis_session_manager_service.py +0 -117
- tests/repositories/__init__.py +0 -5
- tests/repositories/test_database_manager.py +0 -87
- tests/repositories/test_document_repo.py +0 -76
- tests/repositories/test_llm_query_repo.py +0 -340
- tests/repositories/test_models.py +0 -38
- tests/repositories/test_profile_repo.py +0 -142
- tests/repositories/test_tasks_repo.py +0 -76
- tests/repositories/test_vs_repo.py +0 -107
- tests/services/__init__.py +0 -5
- tests/services/test_dispatcher_service.py +0 -274
- tests/services/test_document_service.py +0 -181
- tests/services/test_excel_service.py +0 -208
- tests/services/test_file_processor_service.py +0 -121
- tests/services/test_history_service.py +0 -164
- tests/services/test_jwt_service.py +0 -255
- tests/services/test_load_documents_service.py +0 -112
- tests/services/test_mail_service.py +0 -70
- tests/services/test_profile_service.py +0 -379
- tests/services/test_prompt_manager_service.py +0 -190
- tests/services/test_query_service.py +0 -243
- tests/services/test_search_service.py +0 -39
- tests/services/test_sql_service.py +0 -160
- tests/services/test_tasks_service.py +0 -252
- tests/services/test_user_feedback_service.py +0 -389
- tests/services/test_user_session_context_service.py +0 -132
- tests/views/__init__.py +0 -5
- tests/views/test_change_password_view.py +0 -191
- tests/views/test_chat_token_request_view.py +0 -188
- tests/views/test_chat_view.py +0 -98
- tests/views/test_download_file_view.py +0 -149
- tests/views/test_external_chat_login_view.py +0 -120
- tests/views/test_external_login_view.py +0 -102
- tests/views/test_file_store_view.py +0 -128
- tests/views/test_forgot_password_view.py +0 -142
- tests/views/test_history_view.py +0 -336
- tests/views/test_home_view.py +0 -61
- tests/views/test_llm_query_view.py +0 -154
- tests/views/test_login_view.py +0 -114
- tests/views/test_prompt_view.py +0 -111
- tests/views/test_signup_view.py +0 -140
- tests/views/test_tasks_review_view.py +0 -104
- tests/views/test_tasks_view.py +0 -130
- tests/views/test_user_feedback_view.py +0 -214
- tests/views/test_verify_user_view.py +0 -110
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
iatoolkit/views/login_view.py
CHANGED
|
@@ -7,54 +7,138 @@ from flask.views import MethodView
|
|
|
7
7
|
from flask import request, redirect, render_template, url_for
|
|
8
8
|
from injector import inject
|
|
9
9
|
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
+
from iatoolkit.services.auth_service import AuthService
|
|
11
|
+
from iatoolkit.services.jwt_service import JWTService
|
|
12
|
+
from iatoolkit.services.query_service import QueryService
|
|
13
|
+
from iatoolkit.services.prompt_manager_service import PromptService
|
|
14
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
15
|
+
from iatoolkit.services.onboarding_service import OnboardingService
|
|
16
|
+
from iatoolkit.views.base_login_view import BaseLoginView
|
|
17
|
+
import logging
|
|
10
18
|
|
|
11
|
-
class LoginView(MethodView):
|
|
12
|
-
@inject
|
|
13
|
-
def __init__(self, profile_service: ProfileService):
|
|
14
|
-
self.profile_service = profile_service
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
class LoginView(BaseLoginView):
|
|
21
|
+
"""
|
|
22
|
+
Handles login for local users.
|
|
23
|
+
Authenticates and then delegates the path decision (fast/slow) to the base class.
|
|
24
|
+
"""
|
|
25
|
+
def post(self, company_short_name: str):
|
|
18
26
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
19
27
|
if not company:
|
|
20
|
-
return render_template('error.html',
|
|
28
|
+
return render_template('error.html',
|
|
29
|
+
company_short_name=company_short_name,
|
|
30
|
+
message="Empresa no encontrada"), 404
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
33
|
+
email = request.form.get('email')
|
|
34
|
+
password = request.form.get('password')
|
|
35
|
+
|
|
36
|
+
# 1. Authenticate internal user
|
|
37
|
+
auth_response = self.auth_service.login_local_user(
|
|
38
|
+
company_short_name=company_short_name,
|
|
39
|
+
email=email,
|
|
40
|
+
password=password
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if not auth_response['success']:
|
|
44
|
+
|
|
45
|
+
return render_template(
|
|
46
|
+
'home.html',
|
|
47
|
+
company_short_name=company_short_name,
|
|
48
|
+
company=company,
|
|
49
|
+
branding=branding_data,
|
|
50
|
+
form_data={"email": email},
|
|
51
|
+
alert_message=auth_response["message"]
|
|
52
|
+
), 400
|
|
53
|
+
|
|
54
|
+
user_identifier = auth_response['user_identifier']
|
|
55
|
+
|
|
56
|
+
# 3. define URL to call when slow path is finished
|
|
57
|
+
target_url = url_for('finalize_no_token',
|
|
58
|
+
company_short_name=company_short_name,
|
|
59
|
+
_external=True)
|
|
60
|
+
|
|
61
|
+
# 2. Delegate the path decision to the centralized logic.
|
|
62
|
+
try:
|
|
63
|
+
return self._handle_login_path(company, user_identifier, target_url)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
return render_template("error.html",
|
|
66
|
+
company_short_name=company_short_name,
|
|
67
|
+
branding=branding_data,
|
|
68
|
+
message=f"Error processing login path: {str(e)}"), 500
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class FinalizeContextView(MethodView):
|
|
72
|
+
"""
|
|
73
|
+
Finalizes context loading in the slow path.
|
|
74
|
+
This view is invoked by the iframe inside onboarding_shell.html.
|
|
75
|
+
"""
|
|
76
|
+
@inject
|
|
77
|
+
def __init__(self,
|
|
78
|
+
profile_service: ProfileService,
|
|
79
|
+
auth_service: AuthService,
|
|
80
|
+
query_service: QueryService,
|
|
81
|
+
prompt_service: PromptService,
|
|
82
|
+
branding_service: BrandingService,
|
|
83
|
+
onboarding_service: OnboardingService,
|
|
84
|
+
jwt_service: JWTService,
|
|
85
|
+
):
|
|
86
|
+
self.profile_service = profile_service
|
|
87
|
+
self.jwt_service = jwt_service
|
|
88
|
+
self.query_service = query_service
|
|
89
|
+
self.prompt_service = prompt_service
|
|
90
|
+
self.branding_service = branding_service
|
|
91
|
+
self.onboarding_service = onboarding_service
|
|
92
|
+
|
|
93
|
+
def get(self, company_short_name: str, token: str = None):
|
|
94
|
+
session_info = self.profile_service.get_current_session_info()
|
|
95
|
+
if session_info:
|
|
96
|
+
# session exists, internal user
|
|
97
|
+
user_identifier = session_info.get('user_identifier')
|
|
98
|
+
token = ''
|
|
99
|
+
elif token:
|
|
100
|
+
# user identified by api-key
|
|
101
|
+
payload = self.jwt_service.validate_chat_jwt(token)
|
|
102
|
+
if not payload:
|
|
103
|
+
logging.warning("Fallo crítico: No se pudo leer el auth token.")
|
|
104
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
105
|
+
|
|
106
|
+
user_identifier = payload.get('user_identifier')
|
|
107
|
+
else:
|
|
108
|
+
logging.warning("Fallo crítico: missing session information or auth token")
|
|
109
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
25
110
|
|
|
26
|
-
def post(self, company_short_name: str):
|
|
27
|
-
# get company info
|
|
28
111
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
29
112
|
if not company:
|
|
30
|
-
return render_template('error.html',
|
|
113
|
+
return render_template('error.html',
|
|
114
|
+
company_short_name=company_short_name,
|
|
115
|
+
message="Empresa no encontrada"), 404
|
|
116
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
31
117
|
|
|
32
|
-
email = request.form.get('email')
|
|
33
118
|
try:
|
|
34
|
-
|
|
119
|
+
# 2. Finalize the context rebuild (the heavy task).
|
|
120
|
+
self.query_service.finalize_context_rebuild(
|
|
121
|
+
company_short_name=company_short_name,
|
|
122
|
+
user_identifier=user_identifier
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# 3. render the chat page.
|
|
126
|
+
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
127
|
+
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
35
128
|
|
|
36
|
-
|
|
129
|
+
return render_template(
|
|
130
|
+
"chat.html",
|
|
37
131
|
company_short_name=company_short_name,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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))
|
|
132
|
+
user_identifier=user_identifier,
|
|
133
|
+
branding=branding_data,
|
|
134
|
+
prompts=prompts,
|
|
135
|
+
onboarding_cards=onboarding_cards,
|
|
136
|
+
redeem_token=token
|
|
137
|
+
)
|
|
54
138
|
|
|
55
139
|
except Exception as e:
|
|
56
140
|
return render_template("error.html",
|
|
57
|
-
company=company,
|
|
58
141
|
company_short_name=company_short_name,
|
|
59
|
-
|
|
142
|
+
branding=branding_data,
|
|
143
|
+
message=f"An unexpected error occurred during context loading: {str(e)}"), 500
|
|
60
144
|
|
|
@@ -0,0 +1,45 @@
|
|
|
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 redirect, url_for, jsonify
|
|
8
|
+
from injector import inject
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
11
|
+
from iatoolkit.common.session_manager import SessionManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LogoutApiView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
profile_service: ProfileService,
|
|
18
|
+
auth_service: AuthService):
|
|
19
|
+
self.profile_service = profile_service
|
|
20
|
+
self.auth_service = auth_service
|
|
21
|
+
|
|
22
|
+
def get(self, company_short_name: str = None):
|
|
23
|
+
# 1. Get the authenticated user's
|
|
24
|
+
auth_result = self.auth_service.verify(anonymous=True)
|
|
25
|
+
if not auth_result.get("success"):
|
|
26
|
+
return jsonify(auth_result), auth_result.get("status_code", 401)
|
|
27
|
+
|
|
28
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
29
|
+
if not company:
|
|
30
|
+
return jsonify({"error": "Empresa no encontrada"}), 404
|
|
31
|
+
|
|
32
|
+
# get URL for redirection
|
|
33
|
+
url_for_redirect = company.parameters.get('external_urls', {}).get('logout_url')
|
|
34
|
+
if not url_for_redirect:
|
|
35
|
+
url_for_redirect = url_for('home', company_short_name=company_short_name)
|
|
36
|
+
|
|
37
|
+
# clear de session cookie
|
|
38
|
+
SessionManager.clear()
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
'status': 'success',
|
|
42
|
+
'url': url_for_redirect,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
@@ -6,24 +6,24 @@
|
|
|
6
6
|
from flask import jsonify
|
|
7
7
|
from flask.views import MethodView
|
|
8
8
|
from iatoolkit.services.prompt_manager_service import PromptService
|
|
9
|
-
from iatoolkit.
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from injector import inject
|
|
11
11
|
import logging
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class PromptApiView(MethodView):
|
|
15
15
|
@inject
|
|
16
16
|
def __init__(self,
|
|
17
|
-
|
|
17
|
+
auth_service: AuthService,
|
|
18
18
|
prompt_service: PromptService ):
|
|
19
|
-
self.
|
|
19
|
+
self.auth_service = auth_service
|
|
20
20
|
self.prompt_service = prompt_service
|
|
21
21
|
|
|
22
22
|
def get(self, company_short_name):
|
|
23
23
|
# get access credentials
|
|
24
|
-
|
|
25
|
-
if not
|
|
26
|
-
return jsonify(
|
|
24
|
+
auth_result = self.auth_service.verify(anonymous=True)
|
|
25
|
+
if not auth_result.get("success"):
|
|
26
|
+
return jsonify(auth_result), auth_result.get('status_code')
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
29
|
response = self.prompt_service.get_user_prompts(company_short_name)
|
iatoolkit/views/signup_view.py
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template
|
|
7
|
+
from flask import render_template, request, url_for, session, redirect
|
|
8
8
|
from iatoolkit.services.profile_service import ProfileService
|
|
9
|
+
from iatoolkit.services.branding_service import BrandingService # 1. Importar BrandingService
|
|
9
10
|
from injector import inject
|
|
10
11
|
from itsdangerous import URLSafeTimedSerializer
|
|
11
|
-
from flask import url_for, request
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class SignupView(MethodView):
|
|
16
16
|
@inject
|
|
17
|
-
def __init__(self, profile_service: ProfileService
|
|
17
|
+
def __init__(self, profile_service: ProfileService,
|
|
18
|
+
branding_service: BrandingService):
|
|
18
19
|
self.profile_service = profile_service
|
|
20
|
+
self.branding_service = branding_service # 3. Guardar la instancia
|
|
19
21
|
self.serializer = URLSafeTimedSerializer(os.getenv("USER_VERIF_KEY"))
|
|
20
22
|
|
|
21
23
|
|
|
@@ -23,21 +25,27 @@ class SignupView(MethodView):
|
|
|
23
25
|
# get company info
|
|
24
26
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
25
27
|
if not company:
|
|
26
|
-
return render_template('error.html',
|
|
28
|
+
return render_template('error.html',
|
|
29
|
+
company_short_name=company_short_name,
|
|
30
|
+
message="Empresa no encontrada"), 404
|
|
31
|
+
|
|
32
|
+
# Obtener los datos de branding
|
|
33
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
27
34
|
|
|
28
|
-
user_agent = request.user_agent
|
|
29
|
-
is_mobile = user_agent.platform in ["android", "iphone", "ipad"] or "mobile" in user_agent.string.lower()
|
|
30
35
|
return render_template('signup.html',
|
|
31
36
|
company=company,
|
|
32
37
|
company_short_name=company_short_name,
|
|
33
|
-
|
|
38
|
+
branding=branding_data)
|
|
34
39
|
|
|
35
40
|
def post(self, company_short_name: str):
|
|
36
41
|
# get company info
|
|
37
42
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
38
43
|
if not company:
|
|
39
|
-
return render_template('error.html',
|
|
44
|
+
return render_template('error.html',
|
|
45
|
+
company_short_name=company_short_name,
|
|
46
|
+
message="Empresa no encontrada"), 404
|
|
40
47
|
|
|
48
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
41
49
|
try:
|
|
42
50
|
first_name = request.form.get('first_name')
|
|
43
51
|
last_name = request.form.get('last_name')
|
|
@@ -60,28 +68,29 @@ class SignupView(MethodView):
|
|
|
60
68
|
|
|
61
69
|
if "error" in response:
|
|
62
70
|
return render_template(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
'signup.html',
|
|
72
|
+
company=company,
|
|
73
|
+
company_short_name=company_short_name,
|
|
74
|
+
branding=branding_data,
|
|
75
|
+
form_data={
|
|
76
|
+
"first_name": first_name,
|
|
77
|
+
"last_name": last_name,
|
|
78
|
+
"email": email,
|
|
79
|
+
"password": password,
|
|
80
|
+
"confirm_password": confirm_password
|
|
81
|
+
},
|
|
82
|
+
alert_message=response["error"]), 400
|
|
83
|
+
|
|
84
|
+
# Guardamos el mensaje de éxito en la sesión
|
|
85
|
+
session['alert_message'] = response["message"]
|
|
86
|
+
session['alert_icon'] = 'success'
|
|
87
|
+
|
|
88
|
+
# Redirigimos al usuario a la página de login
|
|
89
|
+
return redirect(url_for('home', company_short_name=company_short_name))
|
|
90
|
+
|
|
82
91
|
except Exception as e:
|
|
83
92
|
return render_template("error.html",
|
|
84
|
-
company=company,
|
|
85
93
|
company_short_name=company_short_name,
|
|
86
|
-
|
|
94
|
+
branding=branding_data,
|
|
95
|
+
message=f"Ha ocurrido un error inesperado: {str(e)}"), 500
|
|
87
96
|
|
|
@@ -7,54 +7,28 @@ from flask.views import MethodView
|
|
|
7
7
|
from flask import request, jsonify
|
|
8
8
|
from iatoolkit.services.tasks_service import TaskService
|
|
9
9
|
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
10
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
11
|
from injector import inject
|
|
11
12
|
from datetime import datetime
|
|
12
13
|
import logging
|
|
13
14
|
from typing import Optional
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
class
|
|
17
|
+
class TaskApiView(MethodView):
|
|
17
18
|
@inject
|
|
18
|
-
def __init__(self,
|
|
19
|
+
def __init__(self,
|
|
20
|
+
auth_service: AuthService,
|
|
21
|
+
task_service: TaskService,
|
|
22
|
+
profile_repo: ProfileRepo):
|
|
23
|
+
self.auth_service = auth_service
|
|
19
24
|
self.task_service = task_service
|
|
20
25
|
self.profile_repo = profile_repo
|
|
21
26
|
|
|
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
27
|
def post(self):
|
|
53
28
|
try:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return jsonify(error[0]), error[1]
|
|
29
|
+
auth_result = self.auth_service.verify(anonymous=True)
|
|
30
|
+
if not auth_result.get("success"):
|
|
31
|
+
return jsonify(auth_result), auth_result.get("status_code")
|
|
58
32
|
|
|
59
33
|
req_data = request.get_json()
|
|
60
34
|
files = request.files.getlist('files')
|
|
@@ -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 request, jsonify
|
|
8
|
+
from iatoolkit.services.tasks_service import TaskService
|
|
9
|
+
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
10
|
+
from iatoolkit.services.auth_service import AuthService
|
|
11
|
+
from injector import inject
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TaskReviewApiView(MethodView):
|
|
17
|
+
@inject
|
|
18
|
+
def __init__(self,
|
|
19
|
+
auth_service: AuthService,
|
|
20
|
+
task_service: TaskService,
|
|
21
|
+
profile_repo: ProfileRepo):
|
|
22
|
+
self.auth_service = auth_service
|
|
23
|
+
self.task_service = task_service
|
|
24
|
+
self.profile_repo = profile_repo
|
|
25
|
+
|
|
26
|
+
def post(self, task_id: int):
|
|
27
|
+
auth_result = self.auth_service.verify(anonymous=True)
|
|
28
|
+
if not auth_result.get("success"):
|
|
29
|
+
return jsonify(auth_result), auth_result.get("status_code")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
req_data = request.get_json()
|
|
33
|
+
required_fields = ['review_user', 'approved']
|
|
34
|
+
for field in required_fields:
|
|
35
|
+
if field not in req_data:
|
|
36
|
+
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
37
|
+
|
|
38
|
+
review_user = req_data.get('review_user', '')
|
|
39
|
+
approved = req_data.get('approved', False)
|
|
40
|
+
comment = req_data.get('comment', '')
|
|
41
|
+
|
|
42
|
+
new_task = self.task_service.review_task(
|
|
43
|
+
task_id=task_id,
|
|
44
|
+
review_user=review_user,
|
|
45
|
+
approved=approved,
|
|
46
|
+
comment=comment)
|
|
47
|
+
|
|
48
|
+
return jsonify({
|
|
49
|
+
"task_id": new_task.id,
|
|
50
|
+
"status": new_task.status.name
|
|
51
|
+
}), 200
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logging.exception("Error al revisar la tarea: %s", str(e))
|
|
55
|
+
return jsonify({"error": str(e)}), 500
|
|
@@ -3,59 +3,47 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
from flask import request, jsonify
|
|
6
|
+
from flask import request, jsonify
|
|
7
7
|
from flask.views import MethodView
|
|
8
8
|
from iatoolkit.services.user_feedback_service import UserFeedbackService
|
|
9
|
-
from iatoolkit.
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from injector import inject
|
|
11
11
|
import logging
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class UserFeedbackApiView(MethodView):
|
|
15
15
|
@inject
|
|
16
16
|
def __init__(self,
|
|
17
|
-
|
|
17
|
+
auth_service: AuthService,
|
|
18
18
|
user_feedback_service: UserFeedbackService ):
|
|
19
|
-
self.
|
|
19
|
+
self.auth_service = auth_service
|
|
20
20
|
self.user_feedback_service = user_feedback_service
|
|
21
21
|
|
|
22
22
|
def post(self, company_short_name):
|
|
23
|
+
# get access credentials
|
|
24
|
+
auth_result = self.auth_service.verify()
|
|
25
|
+
if not auth_result.get("success"):
|
|
26
|
+
return jsonify(auth_result), auth_result.get("status_code")
|
|
27
|
+
|
|
28
|
+
user_identifier = auth_result.get('user_identifier')
|
|
29
|
+
|
|
23
30
|
data = request.get_json()
|
|
24
31
|
if not data:
|
|
25
|
-
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}),
|
|
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
|
|
32
|
+
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 402
|
|
31
33
|
|
|
32
34
|
message = data.get("message")
|
|
33
35
|
if not message:
|
|
34
36
|
return jsonify({"error_message": "Falta el mensaje de feedback"}), 400
|
|
35
37
|
|
|
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
38
|
rating = data.get("rating")
|
|
45
39
|
if not rating:
|
|
46
40
|
return jsonify({"error_message": "Falta la calificación"}), 400
|
|
47
41
|
|
|
48
|
-
external_user_id = data.get("external_user_id")
|
|
49
|
-
local_user_id = data.get("local_user_id", 0)
|
|
50
|
-
|
|
51
42
|
try:
|
|
52
43
|
response = self.user_feedback_service.new_feedback(
|
|
53
44
|
company_short_name=company_short_name,
|
|
54
45
|
message=message,
|
|
55
|
-
|
|
56
|
-
local_user_id=local_user_id,
|
|
57
|
-
space=space,
|
|
58
|
-
type=type,
|
|
46
|
+
user_identifier=user_identifier,
|
|
59
47
|
rating=rating
|
|
60
48
|
)
|
|
61
49
|
|
|
@@ -66,9 +54,6 @@ class UserFeedbackView(MethodView):
|
|
|
66
54
|
except Exception as e:
|
|
67
55
|
logging.exception(
|
|
68
56
|
f"Error inesperado al procesar feedback para company {company_short_name}: {e}")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
message="Ha ocurrido un error inesperado."), 500
|
|
72
|
-
else:
|
|
73
|
-
return jsonify({"error_message": str(e)}), 500
|
|
57
|
+
|
|
58
|
+
return jsonify({"error_message": str(e)}), 500
|
|
74
59
|
|
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template
|
|
7
|
+
from flask import render_template, url_for, redirect, session
|
|
8
8
|
from iatoolkit.services.profile_service import ProfileService
|
|
9
9
|
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
|
10
|
+
from iatoolkit.services.branding_service import BrandingService # ¡Importante!
|
|
10
11
|
from injector import inject
|
|
11
12
|
import os
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class VerifyAccountView(MethodView):
|
|
15
16
|
@inject
|
|
16
|
-
def __init__(self, profile_service: ProfileService):
|
|
17
|
+
def __init__(self, profile_service: ProfileService, branding_service: BrandingService):
|
|
17
18
|
self.profile_service = profile_service
|
|
19
|
+
self.branding_service = branding_service
|
|
18
20
|
self.serializer = URLSafeTimedSerializer(os.getenv("USER_VERIF_KEY"))
|
|
19
21
|
|
|
20
22
|
def get(self, company_short_name: str, token: str):
|
|
@@ -23,6 +25,7 @@ class VerifyAccountView(MethodView):
|
|
|
23
25
|
if not company:
|
|
24
26
|
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
25
27
|
|
|
28
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
26
29
|
try:
|
|
27
30
|
# decode the token from the URL
|
|
28
31
|
email = self.serializer.loads(token, salt='email-confirm', max_age=3600*5)
|
|
@@ -30,6 +33,7 @@ class VerifyAccountView(MethodView):
|
|
|
30
33
|
return render_template('signup.html',
|
|
31
34
|
company=company,
|
|
32
35
|
company_short_name=company_short_name,
|
|
36
|
+
branding=branding_data,
|
|
33
37
|
token=token,
|
|
34
38
|
alert_message="El enlace de verificación ha expirado. Por favor, solicita uno nuevo."), 400
|
|
35
39
|
|
|
@@ -40,16 +44,17 @@ class VerifyAccountView(MethodView):
|
|
|
40
44
|
'signup.html',
|
|
41
45
|
company=company,
|
|
42
46
|
company_short_name=company_short_name,
|
|
47
|
+
branding=branding_data,
|
|
43
48
|
token=token,
|
|
44
49
|
alert_message=response["error"]), 400
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
# Guardamos el mensaje y el icono en la sesión manualmente
|
|
52
|
+
session['alert_message'] = response['message']
|
|
53
|
+
session['alert_icon'] = "success"
|
|
54
|
+
return redirect(url_for('home', company_short_name=company_short_name))
|
|
55
|
+
|
|
51
56
|
except Exception as e:
|
|
52
57
|
return render_template("error.html",
|
|
53
|
-
company=company,
|
|
54
58
|
company_short_name=company_short_name,
|
|
59
|
+
branding=branding_data,
|
|
55
60
|
message="Ha ocurrido un error inesperado."), 500
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iatoolkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.63.4
|
|
4
4
|
Summary: IAToolkit
|
|
5
5
|
Author: Fernando Libedinsky
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: aiohappyeyeballs==2.4.4
|
|
10
10
|
Requires-Dist: aiohttp==3.11.9
|