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

Files changed (93) hide show
  1. iatoolkit/__init__.py +2 -0
  2. iatoolkit/base_company.py +2 -19
  3. iatoolkit/common/routes.py +28 -25
  4. iatoolkit/common/session_manager.py +2 -0
  5. iatoolkit/common/util.py +17 -6
  6. iatoolkit/company_registry.py +1 -2
  7. iatoolkit/iatoolkit.py +54 -20
  8. iatoolkit/locales/en.yaml +167 -0
  9. iatoolkit/locales/es.yaml +163 -0
  10. iatoolkit/repositories/database_manager.py +3 -3
  11. iatoolkit/repositories/document_repo.py +1 -1
  12. iatoolkit/repositories/models.py +3 -4
  13. iatoolkit/repositories/profile_repo.py +0 -4
  14. iatoolkit/services/auth_service.py +44 -32
  15. iatoolkit/services/branding_service.py +35 -27
  16. iatoolkit/services/configuration_service.py +140 -0
  17. iatoolkit/services/dispatcher_service.py +20 -18
  18. iatoolkit/services/document_service.py +5 -2
  19. iatoolkit/services/excel_service.py +15 -11
  20. iatoolkit/services/file_processor_service.py +4 -12
  21. iatoolkit/services/history_service.py +8 -7
  22. iatoolkit/services/i18n_service.py +104 -0
  23. iatoolkit/services/jwt_service.py +7 -9
  24. iatoolkit/services/language_service.py +79 -0
  25. iatoolkit/services/load_documents_service.py +4 -4
  26. iatoolkit/services/mail_service.py +9 -4
  27. iatoolkit/services/onboarding_service.py +10 -4
  28. iatoolkit/services/profile_service.py +59 -38
  29. iatoolkit/services/prompt_manager_service.py +20 -16
  30. iatoolkit/services/query_service.py +15 -14
  31. iatoolkit/services/sql_service.py +6 -2
  32. iatoolkit/services/user_feedback_service.py +70 -29
  33. iatoolkit/static/js/chat_feedback_button.js +80 -0
  34. iatoolkit/static/js/chat_help_content.js +124 -0
  35. iatoolkit/static/js/chat_history_button.js +110 -0
  36. iatoolkit/static/js/chat_logout_button.js +36 -0
  37. iatoolkit/static/js/chat_main.js +32 -184
  38. iatoolkit/static/js/{chat_onboarding.js → chat_onboarding_button.js} +0 -1
  39. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  40. iatoolkit/static/js/chat_reload_button.js +35 -0
  41. iatoolkit/static/styles/chat_iatoolkit.css +251 -205
  42. iatoolkit/static/styles/chat_modal.css +63 -95
  43. iatoolkit/static/styles/chat_public.css +107 -0
  44. iatoolkit/static/styles/landing_page.css +121 -167
  45. iatoolkit/templates/_company_header.html +20 -0
  46. iatoolkit/templates/_login_widget.html +10 -10
  47. iatoolkit/templates/base.html +36 -19
  48. iatoolkit/templates/change_password.html +24 -22
  49. iatoolkit/templates/chat.html +121 -93
  50. iatoolkit/templates/chat_modals.html +113 -74
  51. iatoolkit/templates/error.html +44 -8
  52. iatoolkit/templates/forgot_password.html +17 -15
  53. iatoolkit/templates/index.html +66 -81
  54. iatoolkit/templates/login_simulation.html +16 -5
  55. iatoolkit/templates/onboarding_shell.html +1 -2
  56. iatoolkit/templates/signup.html +22 -20
  57. iatoolkit/views/base_login_view.py +12 -1
  58. iatoolkit/views/change_password_view.py +50 -33
  59. iatoolkit/views/external_login_view.py +5 -11
  60. iatoolkit/views/file_store_api_view.py +7 -9
  61. iatoolkit/views/forgot_password_view.py +21 -19
  62. iatoolkit/views/help_content_api_view.py +54 -0
  63. iatoolkit/views/history_api_view.py +16 -12
  64. iatoolkit/views/home_view.py +61 -0
  65. iatoolkit/views/index_view.py +5 -34
  66. iatoolkit/views/init_context_api_view.py +16 -13
  67. iatoolkit/views/llmquery_api_view.py +38 -28
  68. iatoolkit/views/login_simulation_view.py +14 -2
  69. iatoolkit/views/login_view.py +48 -33
  70. iatoolkit/views/logout_api_view.py +49 -0
  71. iatoolkit/views/profile_api_view.py +46 -0
  72. iatoolkit/views/prompt_api_view.py +8 -8
  73. iatoolkit/views/signup_view.py +27 -25
  74. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  75. iatoolkit/views/tasks_review_api_view.py +55 -0
  76. iatoolkit/views/user_feedback_api_view.py +21 -32
  77. iatoolkit/views/verify_user_view.py +33 -26
  78. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/METADATA +40 -22
  79. iatoolkit-0.67.0.dist-info/RECORD +120 -0
  80. iatoolkit-0.67.0.dist-info/licenses/LICENSE +21 -0
  81. iatoolkit/static/js/chat_context_reload.js +0 -54
  82. iatoolkit/static/js/chat_feedback.js +0 -115
  83. iatoolkit/static/js/chat_history.js +0 -127
  84. iatoolkit/static/styles/chat_info.css +0 -53
  85. iatoolkit/templates/_branding_styles.html +0 -53
  86. iatoolkit/templates/_navbar.html +0 -9
  87. iatoolkit/templates/header.html +0 -31
  88. iatoolkit/templates/test.html +0 -9
  89. iatoolkit/views/chat_token_request_view.py +0 -98
  90. iatoolkit/views/tasks_review_view.py +0 -83
  91. iatoolkit-0.59.1.dist-info/RECORD +0 -111
  92. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/WHEEL +0 -0
  93. {iatoolkit-0.59.1.dist-info → iatoolkit-0.67.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
1
+ # iatoolkit/views/home_view.py
2
+ from flask import render_template, render_template_string
3
+ from flask.views import MethodView
4
+ from injector import inject
5
+ from iatoolkit.services.profile_service import ProfileService
6
+ from iatoolkit.services.branding_service import BrandingService
7
+ from iatoolkit.services.i18n_service import I18nService
8
+ from iatoolkit.common.util import Utility
9
+
10
+ class HomeView(MethodView):
11
+ """
12
+ Handles the rendering of the company-specific home page with a login widget.
13
+ If the custom template is not found or fails, it renders an error page.
14
+ """
15
+
16
+ @inject
17
+ def __init__(self,
18
+ profile_service: ProfileService,
19
+ branding_service: BrandingService,
20
+ i18n_service: I18nService,
21
+ utility: Utility):
22
+ self.profile_service = profile_service
23
+ self.branding_service = branding_service
24
+ self.i18n_service = i18n_service
25
+ self.util = utility
26
+
27
+ def get(self, company_short_name: str):
28
+ try:
29
+ company = self.profile_service.get_company_by_short_name(company_short_name)
30
+ if not company:
31
+ return render_template('error.html',
32
+ message=self.i18n_service.t('errors.templates.company_not_found')), 404
33
+
34
+ branding_data = self.branding_service.get_company_branding(company)
35
+ home_template = self.util.get_company_template(company_short_name, "home.html")
36
+
37
+ # 2. Verificamos si el archivo de plantilla personalizado no existe.
38
+ if not home_template:
39
+ message = self.i18n_service.t('errors.templates.home_template_not_found', company_name=company_short_name)
40
+ return render_template(
41
+ "error.html",
42
+ company_short_name=company_short_name,
43
+ branding=branding_data,
44
+ message=message
45
+ ), 500
46
+
47
+ # 3. Si el archivo existe, intentamos leerlo y renderizarlo.
48
+ return render_template_string(
49
+ home_template,
50
+ company=company,
51
+ company_short_name=company_short_name,
52
+ branding=branding_data,
53
+ )
54
+ except Exception as e:
55
+ message = self.i18n_service.t('errors.templates.processing_error', error=str(e))
56
+ return render_template(
57
+ "error.html",
58
+ company_short_name=company_short_name,
59
+ branding=branding_data,
60
+ message=message
61
+ ), 500
@@ -1,43 +1,14 @@
1
1
  # iatoolkit/views/index_view.py
2
2
 
3
- from flask import render_template, abort, session
3
+ from flask import render_template, session
4
4
  from flask.views import MethodView
5
- from injector import inject
6
- from iatoolkit.services.profile_service import ProfileService
7
- from iatoolkit.services.branding_service import BrandingService
8
5
 
9
6
 
10
7
  class IndexView(MethodView):
11
8
  """
12
- Handles the rendering of the company-specific landing page.
9
+ Handles the rendering of the generic landing page, which no longer depends
10
+ on a specific company.
13
11
  """
14
12
 
15
- @inject
16
- def __init__(self,
17
- profile_service: ProfileService,
18
- branding_service: BrandingService):
19
- self.profile_service = profile_service
20
- self.branding_service = branding_service
21
-
22
- def get(self, company_short_name: str):
23
- # La vista ahora recibe el company_short_name desde la URL
24
- company = self.profile_service.get_company_by_short_name(company_short_name)
25
-
26
- if not company:
27
- abort(404, description=f"La empresa '{company_short_name}' no fue encontrada.")
28
-
29
- # Obtenemos los datos de branding para la plantilla
30
- branding_data = self.branding_service.get_company_branding(company)
31
-
32
- alert_message = session.pop('alert_message', None)
33
- alert_icon = session.pop('alert_icon', 'error')
34
-
35
- # 2. Pasamos las variables a la plantilla. Si no hay mensaje, serán None.
36
- return render_template(
37
- 'index.html',
38
- company=company,
39
- company_short_name=company_short_name,
40
- branding=branding_data,
41
- alert_message=alert_message,
42
- alert_icon=alert_icon
43
- )
13
+ def get(self):
14
+ return render_template('index.html')
@@ -3,7 +3,8 @@ from injector import inject
3
3
  from iatoolkit.services.query_service import QueryService
4
4
  from iatoolkit.services.profile_service import ProfileService
5
5
  from iatoolkit.services.auth_service import AuthService
6
- from flask import jsonify, request
6
+ from iatoolkit.services.i18n_service import I18nService
7
+ from flask import jsonify
7
8
  import logging
8
9
 
9
10
 
@@ -17,10 +18,12 @@ class InitContextApiView(MethodView):
17
18
  def __init__(self,
18
19
  auth_service: AuthService,
19
20
  query_service: QueryService,
20
- profile_service: ProfileService):
21
+ profile_service: ProfileService,
22
+ i18n_service: I18nService):
21
23
  self.auth_service = auth_service
22
24
  self.query_service = query_service
23
25
  self.profile_service = profile_service
26
+ self.i18n_service = i18n_service
24
27
 
25
28
  def post(self, company_short_name: str):
26
29
  """
@@ -28,16 +31,14 @@ class InitContextApiView(MethodView):
28
31
  an active web session or by the external_user_id in the JSON payload
29
32
  for API calls.
30
33
  """
31
- # 1. Authenticate the request. This handles both session and API Key.
32
- auth_result = self.auth_service.verify()
33
- if not auth_result.get("success"):
34
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
34
+ try:
35
+ # 1. Authenticate the request. This handles both session and API Key.
36
+ auth_result = self.auth_service.verify()
37
+ if not auth_result.get("success"):
38
+ return jsonify(auth_result), auth_result.get("status_code")
35
39
 
36
- user_identifier = auth_result.get('user_identifier')
37
- if not user_identifier:
38
- return jsonify({"error": "Could not identify user from session or payload"}), 400
40
+ user_identifier = auth_result.get('user_identifier')
39
41
 
40
- try:
41
42
  # 2. Execute the forced rebuild sequence using the unified identifier.
42
43
  self.query_service.session_context.clear_all_context(company_short_name, user_identifier)
43
44
  logging.info(f"Context for {company_short_name}/{user_identifier} has been cleared.")
@@ -54,11 +55,13 @@ class InitContextApiView(MethodView):
54
55
  )
55
56
 
56
57
  # 3. Respond with JSON, as this is an API endpoint.
57
- return jsonify({'status': 'OK', 'message': 'El context se ha recargado con éxito.'}), 200
58
+ success_message = self.i18n_service.t('api_responses.context_reloaded_success')
59
+ return jsonify({'status': 'OK', 'message': success_message}), 200
58
60
 
59
61
  except Exception as e:
60
- logging.exception(f"Error durante la recarga de contexto {user_identifier}: {e}")
61
- return jsonify({"error_message": str(e)}), 500
62
+ logging.exception(f"errors while reloading context: {e}")
63
+ error_message = self.i18n_service.t('errors.general.unexpected_error', error=str(e))
64
+ return jsonify({"error_message": error_message}), 500
62
65
 
63
66
  def options(self, company_short_name):
64
67
  """
@@ -4,6 +4,7 @@ from injector import inject
4
4
  from iatoolkit.services.query_service import QueryService
5
5
  from iatoolkit.services.auth_service import AuthService
6
6
  from iatoolkit.services.profile_service import ProfileService
7
+ from iatoolkit.services.i18n_service import I18nService
7
8
  import logging
8
9
 
9
10
  class LLMQueryApiView(MethodView):
@@ -12,36 +13,45 @@ class LLMQueryApiView(MethodView):
12
13
  """
13
14
 
14
15
  @inject
15
- def __init__(self, auth_service: AuthService, query_service: QueryService, profile_service: ProfileService):
16
+ def __init__(self,
17
+ auth_service: AuthService,
18
+ query_service: QueryService,
19
+ profile_service: ProfileService,
20
+ i18n_service: I18nService):
16
21
  self.auth_service = auth_service
17
22
  self.query_service = query_service
18
23
  self.profile_service = profile_service
24
+ self.i18n_service = i18n_service
19
25
 
20
26
  def post(self, company_short_name: str):
21
- # 1. Authenticate the API request.
22
- auth_result = self.auth_service.verify()
23
- if not auth_result.get("success"):
24
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
25
-
26
- # 2. Get the user identifier from the payload.
27
- user_identifier = auth_result.get('user_identifier')
28
- if not user_identifier:
29
- return jsonify({"error": "Payload must include 'user_identifier'"}), 400
30
-
31
- data = request.get_json()
32
- if not data:
33
- return jsonify({"error": "Invalid JSON body"}), 400
34
-
35
- # 4. Call the unified query service method.
36
- result = self.query_service.llm_query(
37
- company_short_name=company_short_name,
38
- user_identifier=user_identifier,
39
- question=data.get('question', ''),
40
- prompt_name=data.get('prompt_name'),
41
- client_data=data.get('client_data', {}),
42
- files=data.get('files', [])
43
- )
44
- if 'error' in result:
45
- return jsonify(result), 400
46
-
47
- return jsonify(result), 200
27
+ try:
28
+ # 1. Authenticate the API request.
29
+ auth_result = self.auth_service.verify()
30
+ if not auth_result.get("success"):
31
+ return jsonify(auth_result), auth_result.get("status_code")
32
+
33
+ # 2. Get the user identifier from the payload.
34
+ user_identifier = auth_result.get('user_identifier')
35
+
36
+ data = request.get_json()
37
+ if not data:
38
+ return jsonify({"error": "Invalid JSON body"}), 400
39
+
40
+ # 4. Call the unified query service method.
41
+ result = self.query_service.llm_query(
42
+ company_short_name=company_short_name,
43
+ user_identifier=user_identifier,
44
+ question=data.get('question', ''),
45
+ prompt_name=data.get('prompt_name'),
46
+ client_data=data.get('client_data', {}),
47
+ files=data.get('files', [])
48
+ )
49
+ if 'error' in result:
50
+ return jsonify(result), 400
51
+
52
+ return jsonify(result), 200
53
+
54
+ except Exception as e:
55
+ logging.exception(
56
+ f"Unexpected error: {e}")
57
+ return jsonify({"error_message": self.i18n_service.t('errors.general.unexpected_error', error=str(e))}), 500
@@ -10,17 +10,29 @@ from flask.views import MethodView
10
10
  from flask import render_template, request, Response
11
11
  from injector import inject
12
12
  from iatoolkit.services.profile_service import ProfileService
13
+ from iatoolkit.services.branding_service import BrandingService
13
14
 
14
15
 
15
16
  class LoginSimulationView(MethodView):
16
17
  @inject
17
18
  def __init__(self,
18
- profile_service: ProfileService):
19
+ profile_service: ProfileService,
20
+ branding_service: BrandingService):
19
21
  self.profile_service = profile_service
22
+ self.branding_service = branding_service
23
+
20
24
 
21
25
  def get(self, company_short_name: str = None):
22
- """Muestra el formulario para iniciar la simulación."""
26
+ company = self.profile_service.get_company_by_short_name(company_short_name)
27
+ if not company:
28
+ return render_template('error.html',
29
+ company_short_name=company_short_name,
30
+ message="Empresa no encontrada"), 404
31
+
32
+ branding_data = self.branding_service.get_company_branding(company)
33
+
23
34
  return render_template('login_simulation.html',
35
+ branding=branding_data,
24
36
  company_short_name=company_short_name
25
37
  )
26
38
 
@@ -4,15 +4,16 @@
4
4
  # IAToolkit is open source software.
5
5
 
6
6
  from flask.views import MethodView
7
- from flask import request, redirect, render_template, url_for
7
+ from flask import (request, redirect, render_template, url_for,
8
+ render_template_string, flash)
8
9
  from injector import inject
9
10
  from iatoolkit.services.profile_service import ProfileService
10
- from iatoolkit.services.auth_service import AuthService
11
11
  from iatoolkit.services.jwt_service import JWTService
12
12
  from iatoolkit.services.query_service import QueryService
13
13
  from iatoolkit.services.prompt_manager_service import PromptService
14
14
  from iatoolkit.services.branding_service import BrandingService
15
15
  from iatoolkit.services.onboarding_service import OnboardingService
16
+ from iatoolkit.services.i18n_service import I18nService
16
17
  from iatoolkit.views.base_login_view import BaseLoginView
17
18
  import logging
18
19
 
@@ -25,8 +26,10 @@ class LoginView(BaseLoginView):
25
26
  def post(self, company_short_name: str):
26
27
  company = self.profile_service.get_company_by_short_name(company_short_name)
27
28
  if not company:
28
- return render_template('error.html', message="Empresa no encontrada"), 404
29
+ return render_template('error.html',
30
+ message=self.i18n_service.t('errors.templates.company_not_found')), 404
29
31
 
32
+ branding_data = self.branding_service.get_company_branding(company)
30
33
  email = request.form.get('email')
31
34
  password = request.form.get('password')
32
35
 
@@ -38,15 +41,15 @@ class LoginView(BaseLoginView):
38
41
  )
39
42
 
40
43
  if not auth_response['success']:
41
- branding_data = self.branding_service.get_company_branding(company)
44
+ flash(auth_response["message"], 'error')
45
+ home_template = self.utility.get_company_template(company_short_name, "home.html")
42
46
 
43
- return render_template(
44
- 'index.html',
47
+ return render_template_string(
48
+ home_template,
45
49
  company_short_name=company_short_name,
46
50
  company=company,
47
51
  branding=branding_data,
48
52
  form_data={"email": email},
49
- alert_message=auth_response["message"]
50
53
  ), 400
51
54
 
52
55
  user_identifier = auth_response['user_identifier']
@@ -60,8 +63,13 @@ class LoginView(BaseLoginView):
60
63
  try:
61
64
  return self._handle_login_path(company, user_identifier, target_url)
62
65
  except Exception as e:
63
- return render_template("error.html", company=company, company_short_name=company_short_name,
64
- message=f"Error processing login path: {str(e)}"), 500
66
+ message = self.i18n_service.t('errors.templates.processing_error', error=str(e))
67
+ return render_template(
68
+ "error.html",
69
+ company_short_name=company_short_name,
70
+ branding=branding_data,
71
+ message=message
72
+ ), 500
65
73
 
66
74
 
67
75
  class FinalizeContextView(MethodView):
@@ -72,12 +80,12 @@ class FinalizeContextView(MethodView):
72
80
  @inject
73
81
  def __init__(self,
74
82
  profile_service: ProfileService,
75
- auth_service: AuthService,
76
83
  query_service: QueryService,
77
84
  prompt_service: PromptService,
78
85
  branding_service: BrandingService,
79
86
  onboarding_service: OnboardingService,
80
87
  jwt_service: JWTService,
88
+ i18n_service: I18nService
81
89
  ):
82
90
  self.profile_service = profile_service
83
91
  self.jwt_service = jwt_service
@@ -85,30 +93,34 @@ class FinalizeContextView(MethodView):
85
93
  self.prompt_service = prompt_service
86
94
  self.branding_service = branding_service
87
95
  self.onboarding_service = onboarding_service
96
+ self.i18n_service = i18n_service
88
97
 
89
98
  def get(self, company_short_name: str, token: str = None):
90
- session_info = self.profile_service.get_current_session_info()
91
- if session_info:
92
- # session exists, internal user
93
- user_identifier = session_info.get('user_identifier')
94
- token = ''
95
- elif token:
96
- # user identified by api-key
97
- payload = self.jwt_service.validate_chat_jwt(token)
98
- if not payload:
99
- logging.warning("Fallo crítico: No se pudo leer el auth token.")
100
- return redirect(url_for('index', company_short_name=company_short_name))
101
-
102
- user_identifier = payload.get('user_identifier')
103
- else:
104
- logging.warning("Fallo crítico: missing session information or auth token")
105
- return redirect(url_for('index', company_short_name=company_short_name))
106
-
107
- company = self.profile_service.get_company_by_short_name(company_short_name)
108
- if not company:
109
- return render_template('error.html', message="Empresa no encontrada"), 404
110
-
111
99
  try:
100
+ session_info = self.profile_service.get_current_session_info()
101
+ if session_info:
102
+ # session exists, internal user
103
+ user_identifier = session_info.get('user_identifier')
104
+ token = ''
105
+ elif token:
106
+ # user identified by api-key
107
+ payload = self.jwt_service.validate_chat_jwt(token)
108
+ if not payload:
109
+ logging.warning("Fallo crítico: No se pudo leer el auth token.")
110
+ return redirect(url_for('home', company_short_name=company_short_name))
111
+
112
+ user_identifier = payload.get('user_identifier')
113
+ else:
114
+ logging.warning("Fallo crítico: missing session information or auth token")
115
+ return redirect(url_for('home', company_short_name=company_short_name))
116
+
117
+ company = self.profile_service.get_company_by_short_name(company_short_name)
118
+ if not company:
119
+ return render_template('error.html',
120
+ company_short_name=company_short_name,
121
+ message="Empresa no encontrada"), 404
122
+ branding_data = self.branding_service.get_company_branding(company)
123
+
112
124
  # 2. Finalize the context rebuild (the heavy task).
113
125
  self.query_service.finalize_context_rebuild(
114
126
  company_short_name=company_short_name,
@@ -117,9 +129,11 @@ class FinalizeContextView(MethodView):
117
129
 
118
130
  # 3. render the chat page.
119
131
  prompts = self.prompt_service.get_user_prompts(company_short_name)
120
- branding_data = self.branding_service.get_company_branding(company)
121
132
  onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
122
133
 
134
+ # Get the entire 'js_messages' block in the correct language.
135
+ js_translations = self.i18n_service.get_translation_block('js_messages')
136
+
123
137
  return render_template(
124
138
  "chat.html",
125
139
  company_short_name=company_short_name,
@@ -127,12 +141,13 @@ class FinalizeContextView(MethodView):
127
141
  branding=branding_data,
128
142
  prompts=prompts,
129
143
  onboarding_cards=onboarding_cards,
144
+ js_translations=js_translations,
130
145
  redeem_token=token
131
146
  )
132
147
 
133
148
  except Exception as e:
134
149
  return render_template("error.html",
135
- company=company,
136
150
  company_short_name=company_short_name,
151
+ branding=branding_data,
137
152
  message=f"An unexpected error occurred during context loading: {str(e)}"), 500
138
153
 
@@ -0,0 +1,49 @@
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
+ import logging
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
+ try:
24
+ # 1. Get the authenticated user's
25
+ auth_result = self.auth_service.verify(anonymous=True)
26
+ if not auth_result.get("success"):
27
+ return jsonify(auth_result), auth_result.get("status_code", 401)
28
+
29
+ company = self.profile_service.get_company_by_short_name(company_short_name)
30
+ if not company:
31
+ return jsonify({"error": "company not found."}), 404
32
+
33
+ # get URL for redirection
34
+ url_for_redirect = company.parameters.get('external_urls', {}).get('logout_url')
35
+ if not url_for_redirect:
36
+ url_for_redirect = url_for('home', company_short_name=company_short_name)
37
+
38
+ # clear de session cookie
39
+ SessionManager.clear()
40
+
41
+ return {
42
+ 'status': 'success',
43
+ 'url': url_for_redirect,
44
+ }, 200
45
+ except Exception as e:
46
+ logging.exception(f"Unexpected error: {e}")
47
+ return {'status': 'error'}, 500
48
+
49
+
@@ -0,0 +1,46 @@
1
+ # iatoolkit/views/profile_api_view.py
2
+ from flask import request, jsonify
3
+ from flask.views import MethodView
4
+ from injector import inject
5
+ from iatoolkit.services.auth_service import AuthService
6
+ from iatoolkit.services.profile_service import ProfileService
7
+
8
+
9
+ class UserLanguageApiView(MethodView):
10
+ """
11
+ API endpoint for managing user language preferences.
12
+ """
13
+
14
+ @inject
15
+ def __init__(self,
16
+ auth_service: AuthService,
17
+ profile_service: ProfileService):
18
+ self.auth_service = auth_service
19
+ self.profile_service = profile_service
20
+
21
+ def post(self):
22
+ """
23
+ Handles POST requests to update the user's preferred language.
24
+ Expects a JSON body with a 'language' key, e.g., {"language": "en"}.
25
+ """
26
+ # 1. Authenticate the user from the current session.
27
+ auth_result = self.auth_service.verify()
28
+ if not auth_result.get("success"):
29
+ return jsonify(auth_result), auth_result.get("status_code")
30
+
31
+ user_identifier = auth_result.get('user_identifier')
32
+
33
+ # 2. Validate request body
34
+ data = request.get_json()
35
+ if not data or 'language' not in data:
36
+ return jsonify({"error_message": "Missing 'language' field in request body"}), 400
37
+
38
+ new_lang = data.get('language')
39
+
40
+ # 3. Call the service to perform the update
41
+ update_result = self.profile_service.update_user_language(user_identifier, new_lang)
42
+
43
+ if not update_result.get('success'):
44
+ return jsonify(update_result), 400
45
+
46
+ return jsonify({"message": "Language preference updated successfully"}), 200
@@ -14,18 +14,18 @@ import logging
14
14
  class PromptApiView(MethodView):
15
15
  @inject
16
16
  def __init__(self,
17
- iauthentication: AuthService,
17
+ auth_service: AuthService,
18
18
  prompt_service: PromptService ):
19
- self.iauthentication = iauthentication
19
+ self.auth_service = auth_service
20
20
  self.prompt_service = prompt_service
21
21
 
22
22
  def get(self, company_short_name):
23
- # get access credentials
24
- iaut = self.iauthentication.verify()
25
- if not iaut.get("success"):
26
- return jsonify(iaut), 401
27
-
28
23
  try:
24
+ # get access credentials
25
+ auth_result = self.auth_service.verify(anonymous=True)
26
+ if not auth_result.get("success"):
27
+ return jsonify(auth_result), auth_result.get('status_code')
28
+
29
29
  response = self.prompt_service.get_user_prompts(company_short_name)
30
30
  if "error" in response:
31
31
  return {'error_message': response["error"]}, 402
@@ -33,5 +33,5 @@ class PromptApiView(MethodView):
33
33
  return response, 200
34
34
  except Exception as e:
35
35
  logging.exception(
36
- f"Error inesperado al obtener el historial de consultas para company {company_short_name}: {e}")
36
+ f"unexpected error getting company prompts: {e}")
37
37
  return jsonify({"error_message": str(e)}), 500