iatoolkit 0.59.0__tar.gz → 0.64.0__tar.gz
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-0.59.0 → iatoolkit-0.64.0}/PKG-INFO +1 -1
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/pyproject.toml +1 -1
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/base_company.py +3 -1
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/common/routes.py +22 -25
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/common/util.py +0 -6
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/iatoolkit.py +16 -19
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/models.py +1 -1
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/auth_service.py +33 -26
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/branding_service.py +28 -22
- iatoolkit-0.64.0/src/iatoolkit/services/help_content_service.py +30 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/profile_service.py +1 -0
- iatoolkit-0.64.0/src/iatoolkit/services/user_feedback_service.py +103 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_feedback_button.js +82 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_history_button.js +93 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_logout_button.js +36 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_main.js +8 -162
- iatoolkit-0.59.0/src/iatoolkit/static/js/chat_onboarding.js → iatoolkit-0.64.0/src/iatoolkit/static/js/chat_onboarding_button.js +0 -1
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit-0.64.0/src/iatoolkit/static/js/chat_reload_button.js +35 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +259 -141
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_modal.css +97 -91
- iatoolkit-0.64.0/src/iatoolkit/static/styles/landing_page.css +182 -0
- iatoolkit-0.64.0/src/iatoolkit/templates/_company_header.html +20 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/_login_widget.html +4 -6
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/base.html +2 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/change_password.html +12 -14
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/chat.html +86 -81
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/chat_modals.html +91 -66
- iatoolkit-0.64.0/src/iatoolkit/templates/error.html +48 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/forgot_password.html +10 -12
- iatoolkit-0.64.0/src/iatoolkit/templates/index.html +142 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/onboarding_shell.html +1 -2
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/signup.html +12 -10
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/change_password_view.py +8 -6
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/external_login_view.py +5 -11
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/file_store_api_view.py +7 -9
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/forgot_password_view.py +10 -6
- iatoolkit-0.64.0/src/iatoolkit/views/help_content_api_view.py +50 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/history_api_view.py +8 -9
- iatoolkit-0.64.0/src/iatoolkit/views/home_view.py +67 -0
- iatoolkit-0.64.0/src/iatoolkit/views/index_view.py +14 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/init_context_api_view.py +2 -6
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/llmquery_api_view.py +1 -3
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/login_view.py +14 -8
- iatoolkit-0.64.0/src/iatoolkit/views/logout_api_view.py +45 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/prompt_api_view.py +5 -5
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/signup_view.py +10 -6
- iatoolkit-0.59.0/src/iatoolkit/views/tasks_view.py → iatoolkit-0.64.0/src/iatoolkit/views/tasks_api_view.py +10 -36
- iatoolkit-0.64.0/src/iatoolkit/views/tasks_review_api_view.py +55 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/user_feedback_api_view.py +6 -18
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/verify_user_view.py +8 -3
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/PKG-INFO +1 -1
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/SOURCES.txt +14 -9
- iatoolkit-0.59.0/src/iatoolkit/services/user_feedback_service.py +0 -64
- iatoolkit-0.59.0/src/iatoolkit/static/js/chat_context_reload.js +0 -61
- iatoolkit-0.59.0/src/iatoolkit/static/js/chat_feedback.js +0 -115
- iatoolkit-0.59.0/src/iatoolkit/static/js/chat_history.js +0 -127
- iatoolkit-0.59.0/src/iatoolkit/static/styles/landing_page.css +0 -228
- iatoolkit-0.59.0/src/iatoolkit/templates/_branding_styles.html +0 -53
- iatoolkit-0.59.0/src/iatoolkit/templates/_navbar.html +0 -9
- iatoolkit-0.59.0/src/iatoolkit/templates/error.html +0 -15
- iatoolkit-0.59.0/src/iatoolkit/templates/index.html +0 -160
- iatoolkit-0.59.0/src/iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit-0.59.0/src/iatoolkit/views/index_view.py +0 -43
- iatoolkit-0.59.0/src/iatoolkit/views/tasks_review_view.py +0 -83
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/readme.md +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/requirements.txt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/setup.cfg +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/gemini_adapter.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_client.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/mail_app.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/openai_adapter.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/tasks_repo.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/document_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/history_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/load_documents_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/onboarding_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/prompt_manager_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/query_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/search_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/tasks_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/chat_info.css +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/about.html +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/header.html +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/login_simulation.html +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/templates/test.html +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit/views/login_simulation_view.py +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-0.59.0 → iatoolkit-0.64.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -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)
|
|
@@ -3,17 +3,8 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
from flask import render_template, redirect,
|
|
7
|
-
from iatoolkit.common.session_manager import SessionManager
|
|
6
|
+
from flask import render_template, redirect, url_for,send_from_directory, current_app, abort
|
|
8
7
|
from flask import jsonify
|
|
9
|
-
from iatoolkit.views.history_api_view import HistoryApiView
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def logout(company_short_name: str):
|
|
14
|
-
SessionManager.clear()
|
|
15
|
-
flash("Has cerrado sesión correctamente", "info")
|
|
16
|
-
return redirect(url_for('index', company_short_name=company_short_name))
|
|
17
8
|
|
|
18
9
|
|
|
19
10
|
# this function register all the views
|
|
@@ -22,8 +13,8 @@ def register_views(injector, app):
|
|
|
22
13
|
from iatoolkit.views.index_view import IndexView
|
|
23
14
|
from iatoolkit.views.init_context_api_view import InitContextApiView
|
|
24
15
|
from iatoolkit.views.llmquery_api_view import LLMQueryApiView
|
|
25
|
-
from iatoolkit.views.
|
|
26
|
-
from iatoolkit.views.
|
|
16
|
+
from iatoolkit.views.tasks_api_view import TaskApiView
|
|
17
|
+
from iatoolkit.views.tasks_review_api_view import TaskReviewApiView
|
|
27
18
|
from iatoolkit.views.login_simulation_view import LoginSimulationView
|
|
28
19
|
from iatoolkit.views.signup_view import SignupView
|
|
29
20
|
from iatoolkit.views.verify_user_view import VerifyAccountView
|
|
@@ -32,12 +23,19 @@ def register_views(injector, app):
|
|
|
32
23
|
from iatoolkit.views.file_store_api_view import FileStoreApiView
|
|
33
24
|
from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
|
|
34
25
|
from iatoolkit.views.prompt_api_view import PromptApiView
|
|
35
|
-
from iatoolkit.views.
|
|
26
|
+
from iatoolkit.views.history_api_view import HistoryApiView
|
|
27
|
+
from iatoolkit.views.help_content_api_view import HelpContentApiView
|
|
28
|
+
|
|
36
29
|
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
37
30
|
from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
|
|
31
|
+
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
32
|
+
from iatoolkit.views.home_view import HomeView
|
|
38
33
|
|
|
39
34
|
# iatoolkit home page
|
|
40
|
-
app.add_url_rule('
|
|
35
|
+
app.add_url_rule('/', view_func=IndexView.as_view('index'))
|
|
36
|
+
|
|
37
|
+
# company home view
|
|
38
|
+
app.add_url_rule('/<company_short_name>/home', view_func=HomeView.as_view('home'))
|
|
41
39
|
|
|
42
40
|
# login for the iatoolkit integrated frontend
|
|
43
41
|
app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
|
|
@@ -57,23 +55,21 @@ def register_views(injector, app):
|
|
|
57
55
|
view_func=FinalizeContextView.as_view('finalize_with_token')
|
|
58
56
|
)
|
|
59
57
|
|
|
58
|
+
# logout
|
|
59
|
+
app.add_url_rule('/<company_short_name>/api/logout',
|
|
60
|
+
view_func=LogoutApiView.as_view('logout'))
|
|
61
|
+
|
|
60
62
|
# this endpoint is called by the JS for changing the token for a session
|
|
61
63
|
app.add_url_rule('/<string:company_short_name>/api/redeem_token',
|
|
62
64
|
view_func = RedeemTokenApiView.as_view('redeem_token'))
|
|
63
65
|
|
|
64
|
-
#
|
|
65
|
-
app.add_url_rule('/auth/chat_token',
|
|
66
|
-
view_func=ChatTokenRequestView.as_view('chat-token'))
|
|
67
|
-
|
|
68
|
-
# init (reset) the company context (with api-key)
|
|
66
|
+
# init (reset) the company context
|
|
69
67
|
app.add_url_rule('/<company_short_name>/api/init-context',
|
|
70
68
|
view_func=InitContextApiView.as_view('init-context'),
|
|
71
69
|
methods=['POST', 'OPTIONS'])
|
|
72
70
|
|
|
73
71
|
# register new user, account verification and forgot password
|
|
74
72
|
app.add_url_rule('/<company_short_name>/signup',view_func=SignupView.as_view('signup'))
|
|
75
|
-
app.add_url_rule('/<company_short_name>/logout', 'logout', logout)
|
|
76
|
-
app.add_url_rule('/logout', 'logout', logout)
|
|
77
73
|
app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
|
|
78
74
|
app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
|
|
79
75
|
app.add_url_rule('/<company_short_name>/change-password/<token>', view_func=ChangePasswordView.as_view('change_password'))
|
|
@@ -85,13 +81,14 @@ def register_views(injector, app):
|
|
|
85
81
|
# open the promt directory
|
|
86
82
|
app.add_url_rule('/<company_short_name>/api/prompts', view_func=PromptApiView.as_view('prompt'))
|
|
87
83
|
|
|
88
|
-
#
|
|
84
|
+
# toolbar buttons
|
|
89
85
|
app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
|
|
90
86
|
app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
|
|
87
|
+
app.add_url_rule('/<company_short_name>/api/help-content', view_func=HelpContentApiView.as_view('help-content'))
|
|
91
88
|
|
|
92
89
|
# tasks management endpoints: create task, and review answer
|
|
93
|
-
app.add_url_rule('/tasks', view_func=
|
|
94
|
-
app.add_url_rule('/tasks/review/<int:task_id>', view_func=
|
|
90
|
+
app.add_url_rule('/tasks', view_func=TaskApiView.as_view('tasks'))
|
|
91
|
+
app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewApiView.as_view('tasks-review'))
|
|
95
92
|
|
|
96
93
|
# this endpoint is for upload documents into the vector store (api-key)
|
|
97
94
|
app.add_url_rule('/api/load', view_func=FileStoreApiView.as_view('load_api'))
|
|
@@ -133,6 +130,6 @@ def register_views(injector, app):
|
|
|
133
130
|
# hacer que la raíz '/' vaya al home de iatoolkit
|
|
134
131
|
@app.route('/')
|
|
135
132
|
def root_redirect():
|
|
136
|
-
return redirect(url_for('index'
|
|
133
|
+
return redirect(url_for('index'))
|
|
137
134
|
|
|
138
135
|
|
|
@@ -21,10 +21,8 @@ class Utility:
|
|
|
21
21
|
def __init__(self):
|
|
22
22
|
self.encryption_key = os.getenv('FERNET_KEY')
|
|
23
23
|
|
|
24
|
-
|
|
25
24
|
def render_prompt_from_template(self,
|
|
26
25
|
template_pathname: str,
|
|
27
|
-
query: str = None,
|
|
28
26
|
client_data: dict = {},
|
|
29
27
|
**kwargs) -> str:
|
|
30
28
|
|
|
@@ -37,8 +35,6 @@ class Utility:
|
|
|
37
35
|
env = Environment(loader=FileSystemLoader(template_dir))
|
|
38
36
|
template = env.get_template(template_file)
|
|
39
37
|
|
|
40
|
-
kwargs["query"] = query
|
|
41
|
-
|
|
42
38
|
# add all the keys in client_data to kwargs
|
|
43
39
|
kwargs.update(client_data)
|
|
44
40
|
|
|
@@ -53,7 +49,6 @@ class Utility:
|
|
|
53
49
|
def render_prompt_from_string(self,
|
|
54
50
|
template_string: str,
|
|
55
51
|
searchpath: str | list[str] = None,
|
|
56
|
-
query: str = None,
|
|
57
52
|
client_data: dict = {},
|
|
58
53
|
**kwargs) -> str:
|
|
59
54
|
"""
|
|
@@ -76,7 +71,6 @@ class Utility:
|
|
|
76
71
|
env = Environment(loader=loader)
|
|
77
72
|
template = env.from_string(template_string)
|
|
78
73
|
|
|
79
|
-
kwargs["query"] = query
|
|
80
74
|
kwargs.update(client_data)
|
|
81
75
|
|
|
82
76
|
prompt = template.render(**kwargs)
|
|
@@ -16,10 +16,10 @@ import os
|
|
|
16
16
|
from typing import Optional, Dict, Any
|
|
17
17
|
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
18
18
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
19
|
-
from injector import Binder,
|
|
19
|
+
from injector import Binder, Injector, singleton
|
|
20
20
|
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
21
21
|
|
|
22
|
-
IATOOLKIT_VERSION = "0.
|
|
22
|
+
IATOOLKIT_VERSION = "0.64.0"
|
|
23
23
|
|
|
24
24
|
# global variable for the unique instance of IAToolkit
|
|
25
25
|
_iatoolkit_instance: Optional['IAToolkit'] = None
|
|
@@ -52,7 +52,7 @@ class IAToolkit:
|
|
|
52
52
|
self.app = None
|
|
53
53
|
self.db_manager = None
|
|
54
54
|
self._injector = None
|
|
55
|
-
self.version = IATOOLKIT_VERSION
|
|
55
|
+
self.version = IATOOLKIT_VERSION # default version
|
|
56
56
|
|
|
57
57
|
@classmethod
|
|
58
58
|
def get_instance(cls) -> 'IAToolkit':
|
|
@@ -88,7 +88,7 @@ class IAToolkit:
|
|
|
88
88
|
# and other integrations, as views are handled manually.
|
|
89
89
|
FlaskInjector(app=self.app, injector=self._injector)
|
|
90
90
|
|
|
91
|
-
# Step 6: initialize dispatcher and registered
|
|
91
|
+
# Step 6: initialize dispatcher and registered companies
|
|
92
92
|
self._init_dispatcher_and_company_instances()
|
|
93
93
|
|
|
94
94
|
# Step 7: Finalize setup within the application context
|
|
@@ -101,8 +101,6 @@ class IAToolkit:
|
|
|
101
101
|
# Step 8: define the download_dir for excel's
|
|
102
102
|
self._setup_download_dir()
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
104
|
logging.info(f"🎉 IAToolkit v{self.version} inicializado correctamente")
|
|
107
105
|
self._initialized = True
|
|
108
106
|
return self.app
|
|
@@ -117,7 +115,7 @@ class IAToolkit:
|
|
|
117
115
|
log_level = getattr(logging, log_level_name, logging.INFO)
|
|
118
116
|
|
|
119
117
|
logging.basicConfig(
|
|
120
|
-
level=
|
|
118
|
+
level=log_level,
|
|
121
119
|
format="%(asctime)s - IATOOLKIT - %(name)s - %(levelname)s - %(message)s",
|
|
122
120
|
handlers=[logging.StreamHandler()],
|
|
123
121
|
force=True
|
|
@@ -226,20 +224,19 @@ class IAToolkit:
|
|
|
226
224
|
|
|
227
225
|
def _setup_cors(self):
|
|
228
226
|
"""🌐 Configura CORS"""
|
|
229
|
-
|
|
227
|
+
from iatoolkit.company_registry import get_company_registry
|
|
228
|
+
|
|
229
|
+
# default CORS origin
|
|
230
230
|
default_origins = [
|
|
231
|
-
"http://localhost:5001",
|
|
232
|
-
"http://127.0.0.1:5001",
|
|
233
|
-
"https://portal-interno.maxxa.cl",
|
|
234
231
|
os.getenv('IATOOLKIT_BASE_URL')
|
|
235
232
|
]
|
|
236
233
|
|
|
237
|
-
#
|
|
234
|
+
# Iterate through the registered company names
|
|
238
235
|
extra_origins = []
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
236
|
+
all_company_instances = get_company_registry().get_all_company_instances()
|
|
237
|
+
for company_name, company_instance in all_company_instances.items():
|
|
238
|
+
cors_origin = company_instance.company.parameters.get('cors_origin', [])
|
|
239
|
+
extra_origins += cors_origin
|
|
243
240
|
|
|
244
241
|
all_origins = default_origins + extra_origins
|
|
245
242
|
|
|
@@ -254,7 +251,6 @@ class IAToolkit:
|
|
|
254
251
|
|
|
255
252
|
logging.info(f"✅ CORS configurado para: {all_origins}")
|
|
256
253
|
|
|
257
|
-
|
|
258
254
|
def _configure_core_dependencies(self, binder: Binder):
|
|
259
255
|
"""⚙️ Configures all system dependencies."""
|
|
260
256
|
try:
|
|
@@ -325,8 +321,8 @@ class IAToolkit:
|
|
|
325
321
|
from iatoolkit.services.auth_service import AuthService
|
|
326
322
|
from iatoolkit.common.util import Utility
|
|
327
323
|
|
|
328
|
-
binder.bind(LLMProxy, to=LLMProxy
|
|
329
|
-
binder.bind(llmClient, to=llmClient
|
|
324
|
+
binder.bind(LLMProxy, to=LLMProxy)
|
|
325
|
+
binder.bind(llmClient, to=llmClient)
|
|
330
326
|
binder.bind(GoogleChatApp, to=GoogleChatApp)
|
|
331
327
|
binder.bind(MailApp, to=MailApp)
|
|
332
328
|
binder.bind(AuthService, to=AuthService)
|
|
@@ -385,6 +381,7 @@ class IAToolkit:
|
|
|
385
381
|
'iatoolkit_base_url': os.environ.get('IATOOLKIT_BASE_URL', ''),
|
|
386
382
|
}
|
|
387
383
|
|
|
384
|
+
|
|
388
385
|
def _get_default_static_folder(self) -> str:
|
|
389
386
|
try:
|
|
390
387
|
current_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/iatoolkit
|
|
@@ -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
|
|
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
|
|
|
@@ -91,9 +91,10 @@ class AuthService:
|
|
|
91
91
|
)
|
|
92
92
|
return {'success': False, 'error': 'No se pudo crear la sesión del usuario'}
|
|
93
93
|
|
|
94
|
-
def verify(self) -> dict:
|
|
94
|
+
def verify(self, anonymous: bool = False) -> dict:
|
|
95
95
|
"""
|
|
96
96
|
Verifies the current request and identifies the user.
|
|
97
|
+
If anonymous is True the non-presence of use_identifier is ignored
|
|
97
98
|
|
|
98
99
|
Returns a dictionary with:
|
|
99
100
|
- success: bool
|
|
@@ -109,7 +110,7 @@ class AuthService:
|
|
|
109
110
|
return {
|
|
110
111
|
"success": True,
|
|
111
112
|
"company_short_name": session_info['company_short_name'],
|
|
112
|
-
"user_identifier": session_info['user_identifier']
|
|
113
|
+
"user_identifier": session_info['user_identifier'],
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
# --- Priority 2: Check for a valid API Key in headers ---
|
|
@@ -118,31 +119,37 @@ class AuthService:
|
|
|
118
119
|
if isinstance(auth, str) and auth.lower().startswith('bearer '):
|
|
119
120
|
api_key = auth.split(' ', 1)[1].strip()
|
|
120
121
|
|
|
121
|
-
if api_key:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
if not api_key:
|
|
123
|
+
# --- Failure: No valid credentials found ---
|
|
124
|
+
logging.info(f"Authentication required. No session cookie or API Key provided.")
|
|
125
|
+
return {"success": False,
|
|
126
|
+
"error_message": "Authentication required. No session cookie or API Key provided.",
|
|
127
|
+
"status_code": 401}
|
|
128
|
+
|
|
129
|
+
# check if the api-key is valid and active
|
|
130
|
+
api_key_entry = self.profile_service.get_active_api_key_entry(api_key)
|
|
131
|
+
if not api_key_entry:
|
|
132
|
+
logging.info(f"Invalid or inactive API Key {api_key}")
|
|
133
|
+
return {"success": False, "error_message": "Invalid or inactive API Key",
|
|
134
|
+
"status_code": 402}
|
|
135
|
+
|
|
136
|
+
# get the company from the api_key_entry
|
|
137
|
+
company = api_key_entry.company
|
|
138
|
+
|
|
139
|
+
# For API calls, the external_user_id must be provided in the request.
|
|
140
|
+
data = request.get_json(silent=True) or {}
|
|
141
|
+
user_identifier = data.get('user_identifier', '')
|
|
142
|
+
if not anonymous and not user_identifier:
|
|
143
|
+
logging.info(f"No user_identifier provided for API call.")
|
|
144
|
+
return {"success": False, "error_message": "No user_identifier provided for API call.",
|
|
145
|
+
"status_code": 403}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
"success": True,
|
|
149
|
+
"company_short_name": company.short_name,
|
|
150
|
+
"user_identifier": user_identifier
|
|
151
|
+
}
|
|
126
152
|
|
|
127
|
-
# obtain the company from the api_key_entry
|
|
128
|
-
company = api_key_entry.company
|
|
129
|
-
|
|
130
|
-
# For API calls, the external_user_id must be provided in the request.
|
|
131
|
-
user_identifier = ''
|
|
132
|
-
if request.is_json:
|
|
133
|
-
data = request.get_json() or {}
|
|
134
|
-
user_identifier = data.get('user_identifier', '')
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
"success": True,
|
|
138
|
-
"company_short_name": company.short_name,
|
|
139
|
-
"user_identifier": user_identifier
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
# --- Failure: No valid credentials found ---
|
|
143
|
-
logging.info(f"Authentication required. No session cookie or API Key provided.")
|
|
144
|
-
return {"success": False, "error_message": "Authentication required. No session cookie or API Key provided.",
|
|
145
|
-
"status_code": 402}
|
|
146
153
|
|
|
147
154
|
def log_access(self,
|
|
148
155
|
company_short_name: str,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from iatoolkit.repositories.models import Company
|
|
7
|
+
from injector import inject
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class BrandingService:
|
|
@@ -11,6 +12,7 @@ class BrandingService:
|
|
|
11
12
|
Servicio centralizado que gestiona la configuración de branding.
|
|
12
13
|
"""
|
|
13
14
|
|
|
15
|
+
@inject
|
|
14
16
|
def __init__(self):
|
|
15
17
|
"""
|
|
16
18
|
Define los estilos de branding por defecto para la aplicación.
|
|
@@ -19,13 +21,16 @@ class BrandingService:
|
|
|
19
21
|
# --- Estilos del Encabezado Principal ---
|
|
20
22
|
"header_background_color": "#FFFFFF",
|
|
21
23
|
"header_text_color": "#6C757D",
|
|
22
|
-
"primary_font_weight": "
|
|
23
|
-
"primary_font_size": "
|
|
24
|
-
"secondary_font_weight": "
|
|
25
|
-
"secondary_font_size": "0.
|
|
26
|
-
"tertiary_font_weight": "
|
|
27
|
-
"tertiary_font_size": "0.
|
|
28
|
-
"tertiary_opacity": "0.
|
|
24
|
+
"primary_font_weight": "600",
|
|
25
|
+
"primary_font_size": "1.2rem",
|
|
26
|
+
"secondary_font_weight": "400",
|
|
27
|
+
"secondary_font_size": "0.9rem",
|
|
28
|
+
"tertiary_font_weight": "300",
|
|
29
|
+
"tertiary_font_size": "0.8rem",
|
|
30
|
+
"tertiary_opacity": "0.7",
|
|
31
|
+
|
|
32
|
+
# headings
|
|
33
|
+
"brand_text_heading_color": "#334155", # Gris pizarra por defecto
|
|
29
34
|
|
|
30
35
|
# Estilos Globales de la Marca ---
|
|
31
36
|
"brand_primary_color": "#0d6efd", # Azul de Bootstrap por defecto
|
|
@@ -40,25 +45,27 @@ class BrandingService:
|
|
|
40
45
|
"brand_danger_border": "#f5c2c7", # Borde rojo intermedio
|
|
41
46
|
|
|
42
47
|
# Estilos para Alertas Informativas ---
|
|
43
|
-
"brand_info_bg": "#
|
|
44
|
-
"brand_info_text": "#
|
|
45
|
-
"brand_info_border": "#
|
|
48
|
+
"brand_info_bg": "#F0F4F8", # Un fondo de gris azulado muy pálido
|
|
49
|
+
"brand_info_text": "#0d6efd", # Texto en el color primario
|
|
50
|
+
"brand_info_border": "#D9E2EC", # Borde de gris azulado pálido
|
|
46
51
|
|
|
47
52
|
# Estilos para el Asistente de Prompts ---
|
|
48
53
|
"prompt_assistant_bg": "#f8f9fa",
|
|
49
54
|
"prompt_assistant_border": "#dee2e6",
|
|
50
|
-
"prompt_assistant_icon_color": "#6c757d",
|
|
51
55
|
"prompt_assistant_button_bg": "#FFFFFF",
|
|
52
56
|
"prompt_assistant_button_text": "#495057",
|
|
53
57
|
"prompt_assistant_button_border": "#ced4da",
|
|
54
58
|
"prompt_assistant_dropdown_bg": "#f8f9fa",
|
|
55
59
|
"prompt_assistant_header_bg": "#e9ecef",
|
|
56
60
|
"prompt_assistant_header_text": "#495057",
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
|
|
62
|
+
# this use the primary by default
|
|
63
|
+
"prompt_assistant_icon_color": None,
|
|
64
|
+
"prompt_assistant_item_hover_bg": None,
|
|
65
|
+
"prompt_assistant_item_hover_text": None,
|
|
59
66
|
|
|
60
67
|
# Color para el botón de Enviar ---
|
|
61
|
-
"send_button_color": "#212529"
|
|
68
|
+
"send_button_color": "#212529" # Gris oscuro/casi negro por defecto
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
def get_company_branding(self, company: Company | None) -> dict:
|
|
@@ -80,10 +87,6 @@ class BrandingService:
|
|
|
80
87
|
secondary_rgb = hex_to_rgb(final_branding_values['brand_secondary_color'])
|
|
81
88
|
|
|
82
89
|
# --- CONSTRUCCIÓN DE ESTILOS Y VARIABLES CSS ---
|
|
83
|
-
header_style = (
|
|
84
|
-
f"background-color: {final_branding_values['header_background_color']}; "
|
|
85
|
-
f"color: {final_branding_values['header_text_color']};"
|
|
86
|
-
)
|
|
87
90
|
primary_text_style = (
|
|
88
91
|
f"font-weight: {final_branding_values['primary_font_weight']}; "
|
|
89
92
|
f"font-size: {final_branding_values['primary_font_size']};"
|
|
@@ -103,6 +106,10 @@ class BrandingService:
|
|
|
103
106
|
:root {{
|
|
104
107
|
--brand-primary-color: {final_branding_values['brand_primary_color']};
|
|
105
108
|
--brand-secondary-color: {final_branding_values['brand_secondary_color']};
|
|
109
|
+
--brand-header-bg: {final_branding_values['header_background_color']};
|
|
110
|
+
--brand-header-text: {final_branding_values['header_text_color']};
|
|
111
|
+
--brand-text-heading-color: {final_branding_values['brand_text_heading_color']};
|
|
112
|
+
|
|
106
113
|
--brand-primary-color-rgb: {', '.join(map(str, primary_rgb))};
|
|
107
114
|
--brand-secondary-color-rgb: {', '.join(map(str, secondary_rgb))};
|
|
108
115
|
--brand-text-on-primary: {final_branding_values['brand_text_on_primary']};
|
|
@@ -114,11 +121,11 @@ class BrandingService:
|
|
|
114
121
|
--brand-danger-text: {final_branding_values['brand_danger_text']};
|
|
115
122
|
--brand-danger-border: {final_branding_values['brand_danger_border']};
|
|
116
123
|
--brand-info-bg: {final_branding_values['brand_info_bg']};
|
|
117
|
-
--brand-info-text: {final_branding_values['brand_info_text']};
|
|
124
|
+
--brand-info-text: {final_branding_values['brand_info_text'] or final_branding_values['brand_primary_color']};
|
|
118
125
|
--brand-info-border: {final_branding_values['brand_info_border']};
|
|
119
126
|
--brand-prompt-assistant-bg: {final_branding_values['prompt_assistant_bg']};
|
|
120
127
|
--brand-prompt-assistant-border: {final_branding_values['prompt_assistant_border']};
|
|
121
|
-
--brand-prompt-assistant-icon-color: {final_branding_values['prompt_assistant_icon_color']};
|
|
128
|
+
--brand-prompt-assistant-icon-color: {final_branding_values['prompt_assistant_icon_color'] or final_branding_values['brand_primary_color']};
|
|
122
129
|
--brand-prompt-assistant-button-bg: {final_branding_values['prompt_assistant_button_bg']};
|
|
123
130
|
--brand-prompt-assistant-button-text: {final_branding_values['prompt_assistant_button_text']};
|
|
124
131
|
--brand-prompt-assistant-button-border: {final_branding_values['prompt_assistant_button_border']};
|
|
@@ -133,11 +140,10 @@ class BrandingService:
|
|
|
133
140
|
|
|
134
141
|
return {
|
|
135
142
|
"name": company.name if company else "IAToolkit",
|
|
136
|
-
"header_style": header_style,
|
|
137
143
|
"primary_text_style": primary_text_style,
|
|
138
144
|
"secondary_text_style": secondary_text_style,
|
|
139
145
|
"tertiary_text_style": tertiary_text_style,
|
|
140
146
|
"header_text_color": final_branding_values['header_text_color'],
|
|
141
147
|
"css_variables": css_variables,
|
|
142
|
-
"send_button_color": final_branding_values['
|
|
148
|
+
"send_button_color": final_branding_values['brand_primary_color']
|
|
143
149
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from iatoolkit.common.util import Utility
|
|
7
|
+
from iatoolkit.common.exceptions import IAToolkitException
|
|
8
|
+
import os
|
|
9
|
+
from injector import inject
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HelpContentService:
|
|
14
|
+
@inject
|
|
15
|
+
def __init__(self, util: Utility):
|
|
16
|
+
self.util = util
|
|
17
|
+
|
|
18
|
+
def get_content(self, company_short_name: str | None) -> dict:
|
|
19
|
+
filepath = f'companies/{company_short_name}/help_content.yaml'
|
|
20
|
+
if not os.path.exists(filepath):
|
|
21
|
+
return {}
|
|
22
|
+
|
|
23
|
+
# read the file
|
|
24
|
+
try:
|
|
25
|
+
help_content = self.util.load_schema_from_yaml(filepath)
|
|
26
|
+
return help_content
|
|
27
|
+
except Exception as e:
|
|
28
|
+
logging.exception(e)
|
|
29
|
+
raise IAToolkitException(IAToolkitException.ErrorType.CONFIG_ERROR,
|
|
30
|
+
f"Error obteniendo help de {company_short_name}: {str(e)}") from e
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from iatoolkit.repositories.models import UserFeedback, Company
|
|
7
|
+
from injector import inject
|
|
8
|
+
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
9
|
+
from iatoolkit.infra.google_chat_app import GoogleChatApp
|
|
10
|
+
from iatoolkit.infra.mail_app import MailApp # <-- 1. Importar MailApp
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UserFeedbackService:
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
profile_repo: ProfileRepo,
|
|
18
|
+
google_chat_app: GoogleChatApp,
|
|
19
|
+
mail_app: MailApp):
|
|
20
|
+
self.profile_repo = profile_repo
|
|
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}.")
|
|
68
|
+
|
|
69
|
+
def new_feedback(self,
|
|
70
|
+
company_short_name: str,
|
|
71
|
+
message: str,
|
|
72
|
+
user_identifier: str,
|
|
73
|
+
rating: int = None) -> dict:
|
|
74
|
+
try:
|
|
75
|
+
# 1. Validar empresa
|
|
76
|
+
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
77
|
+
if not company:
|
|
78
|
+
return {'error': f'No existe la empresa: {company_short_name}'}
|
|
79
|
+
|
|
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)
|
|
86
|
+
|
|
87
|
+
# 3. Guardar el feedback en la base de datos (independientemente del éxito de la notificación)
|
|
88
|
+
new_feedback_obj = UserFeedback(
|
|
89
|
+
company_id=company.id,
|
|
90
|
+
message=message,
|
|
91
|
+
user_identifier=user_identifier,
|
|
92
|
+
rating=rating
|
|
93
|
+
)
|
|
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}")
|
|
97
|
+
return {'error': 'No se pudo guardar el feedback'}
|
|
98
|
+
|
|
99
|
+
return {'success': True, 'message': 'Feedback guardado correctamente'}
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logging.exception(f"Error crítico en el servicio de feedback: {e}")
|
|
103
|
+
return {'error': str(e)}
|