iatoolkit 0.11.0__py3-none-any.whl → 0.66.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iatoolkit/base_company.py +11 -3
- iatoolkit/common/routes.py +92 -52
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +17 -27
- iatoolkit/iatoolkit.py +91 -47
- iatoolkit/infra/llm_client.py +7 -8
- iatoolkit/infra/openai_adapter.py +1 -1
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/locales/en.yaml +144 -0
- iatoolkit/locales/es.yaml +140 -0
- iatoolkit/repositories/database_manager.py +17 -2
- iatoolkit/repositories/models.py +31 -4
- iatoolkit/repositories/profile_repo.py +7 -2
- iatoolkit/services/auth_service.py +193 -0
- iatoolkit/services/branding_service.py +59 -18
- iatoolkit/services/dispatcher_service.py +10 -40
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/help_content_service.py +30 -0
- iatoolkit/services/history_service.py +2 -11
- iatoolkit/services/i18n_service.py +104 -0
- iatoolkit/services/jwt_service.py +15 -24
- iatoolkit/services/language_service.py +77 -0
- iatoolkit/services/onboarding_service.py +43 -0
- iatoolkit/services/profile_service.py +148 -75
- iatoolkit/services/query_service.py +124 -81
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +68 -32
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/js/chat_feedback_button.js +80 -0
- iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit/static/js/chat_history_button.js +112 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +148 -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 +35 -0
- iatoolkit/static/styles/chat_iatoolkit.css +367 -199
- iatoolkit/static/styles/chat_modal.css +98 -76
- iatoolkit/static/styles/chat_public.css +107 -0
- 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 +42 -0
- iatoolkit/templates/base.html +40 -20
- iatoolkit/templates/change_password.html +57 -36
- iatoolkit/templates/chat.html +169 -83
- iatoolkit/templates/chat_modals.html +134 -68
- iatoolkit/templates/error.html +44 -8
- iatoolkit/templates/forgot_password.html +40 -23
- iatoolkit/templates/index.html +145 -0
- iatoolkit/templates/login_simulation.html +34 -0
- iatoolkit/templates/onboarding_shell.html +104 -0
- iatoolkit/templates/signup.html +63 -65
- iatoolkit/views/base_login_view.py +92 -0
- iatoolkit/views/change_password_view.py +56 -30
- 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 +27 -19
- iatoolkit/views/help_content_api_view.py +54 -0
- iatoolkit/views/history_api_view.py +56 -0
- iatoolkit/views/home_view.py +50 -23
- iatoolkit/views/index_view.py +14 -0
- iatoolkit/views/init_context_api_view.py +73 -0
- iatoolkit/views/llmquery_api_view.py +57 -0
- iatoolkit/views/login_simulation_view.py +81 -0
- iatoolkit/views/login_view.py +130 -37
- iatoolkit/views/logout_api_view.py +49 -0
- iatoolkit/views/profile_api_view.py +46 -0
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +10 -10
- iatoolkit/views/signup_view.py +42 -35
- iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
- iatoolkit/views/tasks_review_api_view.py +55 -0
- iatoolkit/views/user_feedback_api_view.py +60 -0
- iatoolkit/views/verify_user_view.py +35 -28
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/METADATA +2 -2
- iatoolkit-0.66.2.dist-info/RECORD +119 -0
- 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_feedback.js +0 -115
- iatoolkit/static/js/chat_history.js +0 -117
- iatoolkit/static/styles/chat_info.css +0 -53
- iatoolkit/templates/header.html +0 -31
- iatoolkit/templates/home.html +0 -199
- iatoolkit/templates/login.html +0 -43
- iatoolkit/templates/test.html +0 -9
- iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit/views/chat_view.py +0 -58
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/external_chat_login_view.py +0 -95
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/llmquery_view.py +0 -65
- iatoolkit/views/tasks_review_view.py +0 -83
- iatoolkit/views/user_feedback_view.py +0 -74
- iatoolkit-0.11.0.dist-info/RECORD +0 -110
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/WHEEL +0 -0
- {iatoolkit-0.11.0.dist-info → iatoolkit-0.66.2.dist-info}/top_level.txt +0 -0
|
@@ -5,91 +5,163 @@
|
|
|
5
5
|
|
|
6
6
|
from injector import inject
|
|
7
7
|
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
8
|
+
from iatoolkit.services.i18n_service import I18nService
|
|
8
9
|
from iatoolkit.repositories.models import User, Company, ApiKey
|
|
9
10
|
from flask_bcrypt import check_password_hash
|
|
10
11
|
from iatoolkit.common.session_manager import SessionManager
|
|
12
|
+
from iatoolkit.services.user_session_context_service import UserSessionContextService
|
|
11
13
|
from flask_bcrypt import Bcrypt
|
|
12
14
|
from iatoolkit.infra.mail_app import MailApp
|
|
13
15
|
import random
|
|
14
|
-
import logging
|
|
15
16
|
import re
|
|
16
17
|
import secrets
|
|
17
18
|
import string
|
|
18
|
-
|
|
19
|
-
from iatoolkit.services.
|
|
20
|
-
from iatoolkit.services.query_service import QueryService
|
|
19
|
+
import logging
|
|
20
|
+
from iatoolkit.services.dispatcher_service import Dispatcher
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ProfileService:
|
|
24
24
|
@inject
|
|
25
25
|
def __init__(self,
|
|
26
|
+
i18n_service: I18nService,
|
|
26
27
|
profile_repo: ProfileRepo,
|
|
27
28
|
session_context_service: UserSessionContextService,
|
|
28
|
-
|
|
29
|
+
dispatcher: Dispatcher,
|
|
29
30
|
mail_app: MailApp):
|
|
31
|
+
self.i18n_service = i18n_service
|
|
30
32
|
self.profile_repo = profile_repo
|
|
33
|
+
self.dispatcher = dispatcher
|
|
31
34
|
self.session_context = session_context_service
|
|
32
|
-
self.query_service = query_service
|
|
33
35
|
self.mail_app = mail_app
|
|
34
36
|
self.bcrypt = Bcrypt()
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
def login(self, company_short_name: str, email: str, password: str) -> dict:
|
|
38
40
|
try:
|
|
39
|
-
# check if
|
|
41
|
+
# check if user exists
|
|
40
42
|
user = self.profile_repo.get_user_by_email(email)
|
|
41
43
|
if not user:
|
|
42
|
-
return {
|
|
44
|
+
return {'success': False, 'message': self.i18n_service.t('errors.auth.user_not_found')}
|
|
43
45
|
|
|
44
46
|
# check the encrypted password
|
|
45
47
|
if not check_password_hash(user.password, password):
|
|
46
|
-
return {
|
|
48
|
+
return {'success': False, 'message': self.i18n_service.t('errors.auth.invalid_password')}
|
|
47
49
|
|
|
48
|
-
company = self.get_company_by_short_name(company_short_name)
|
|
50
|
+
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
49
51
|
if not company:
|
|
50
|
-
return {"
|
|
52
|
+
return {'success': False, "message": "Empresa no encontrada"}
|
|
51
53
|
|
|
52
|
-
# check that user belongs to
|
|
54
|
+
# check that user belongs to company
|
|
53
55
|
if company not in user.companies:
|
|
54
|
-
return {"
|
|
56
|
+
return {'success': False, "message": "Usuario no esta autorizado para esta empresa"}
|
|
55
57
|
|
|
56
58
|
if not user.verified:
|
|
57
|
-
return {
|
|
59
|
+
return {'success': False,
|
|
60
|
+
"message": "Tu cuenta no ha sido verificada. Por favor, revisa tu correo."}
|
|
61
|
+
|
|
62
|
+
# 1. Build the local user profile dictionary here.
|
|
63
|
+
# the user_profile variables are used on the LLM templates also (see in query_main.prompt)
|
|
64
|
+
user_identifier = user.email # no longer de ID
|
|
65
|
+
user_profile = {
|
|
66
|
+
"user_email": user.email,
|
|
67
|
+
"user_fullname": f'{user.first_name} {user.last_name}',
|
|
68
|
+
"user_is_local": True,
|
|
69
|
+
"extras": {}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# 2. create user_profile in context
|
|
73
|
+
self.save_user_profile(company, user_identifier, user_profile)
|
|
74
|
+
|
|
75
|
+
# 3. create the web session
|
|
76
|
+
self.set_session_for_user(company.short_name, user_identifier)
|
|
77
|
+
return {'success': True, "user_identifier": user_identifier, "message": "Login exitoso"}
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logging.error(f"Error in login: {e}")
|
|
80
|
+
return {'success': False, "message": str(e)}
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
def create_external_user_profile_context(self, company: Company, user_identifier: str):
|
|
83
|
+
"""
|
|
84
|
+
Public method for views to create a user profile context for an external user.
|
|
85
|
+
"""
|
|
86
|
+
# 1. Fetch the external user profile via Dispatcher.
|
|
87
|
+
external_user_profile = self.dispatcher.get_user_info(
|
|
88
|
+
company_name=company.short_name,
|
|
89
|
+
user_identifier=user_identifier
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# 2. Call the session creation helper with external_user_id as user_identifier
|
|
93
|
+
self.save_user_profile(
|
|
94
|
+
company=company,
|
|
95
|
+
user_identifier=user_identifier,
|
|
96
|
+
user_profile=external_user_profile)
|
|
97
|
+
|
|
98
|
+
def save_user_profile(self, company: Company, user_identifier: str, user_profile: dict):
|
|
99
|
+
"""
|
|
100
|
+
Private helper: Takes a pre-built profile, saves it to Redis, and sets the Flask cookie.
|
|
101
|
+
"""
|
|
102
|
+
user_profile['company_short_name'] = company.short_name
|
|
103
|
+
user_profile['user_identifier'] = user_identifier
|
|
104
|
+
user_profile['id'] = user_identifier
|
|
105
|
+
user_profile['company_id'] = company.id
|
|
106
|
+
user_profile['company'] = company.name
|
|
61
107
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
company_short_name=company_short_name,
|
|
65
|
-
local_user_id=user.id
|
|
66
|
-
)
|
|
108
|
+
# save user_profile in Redis session
|
|
109
|
+
self.session_context.save_profile_data(company.short_name, user_identifier, user_profile)
|
|
67
110
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
111
|
+
def set_session_for_user(self, company_short_name: str, user_identifier:str ):
|
|
112
|
+
# save a min Flask session cookie for this user
|
|
113
|
+
SessionManager.set('company_short_name', company_short_name)
|
|
114
|
+
SessionManager.set('user_identifier', user_identifier)
|
|
115
|
+
|
|
116
|
+
def get_current_session_info(self) -> dict:
|
|
117
|
+
"""
|
|
118
|
+
Gets the current web user's profile from the unified session.
|
|
119
|
+
This is the standard way to access user data for web requests.
|
|
120
|
+
"""
|
|
121
|
+
# 1. Get identifiers from the simple Flask session cookie.
|
|
122
|
+
user_identifier = SessionManager.get('user_identifier')
|
|
123
|
+
company_short_name = SessionManager.get('company_short_name')
|
|
124
|
+
|
|
125
|
+
if not user_identifier or not company_short_name:
|
|
126
|
+
# No authenticated web user.
|
|
127
|
+
return {}
|
|
128
|
+
|
|
129
|
+
# 2. Use the identifiers to fetch the full, authoritative profile from Redis.
|
|
130
|
+
profile = self.session_context.get_profile_data(company_short_name, user_identifier)
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"user_identifier": user_identifier,
|
|
134
|
+
"company_short_name": company_short_name,
|
|
135
|
+
"profile": profile
|
|
88
136
|
}
|
|
89
|
-
SessionManager.set('user', user_data)
|
|
90
137
|
|
|
91
|
-
|
|
92
|
-
|
|
138
|
+
def update_user_language(self, user_identifier: str, new_lang: str) -> dict:
|
|
139
|
+
"""
|
|
140
|
+
Business logic to update a user's preferred language.
|
|
141
|
+
It validates the language and then calls the generic update method.
|
|
142
|
+
"""
|
|
143
|
+
# 1. Validate that the language is supported by checking the loaded translations.
|
|
144
|
+
if new_lang not in self.i18n_service.translations:
|
|
145
|
+
return {'success': False, 'error_message': self.i18n_service.t('errors.general.unsupported_language')}
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
# 2. Call the generic update_user method, passing the specific field to update.
|
|
149
|
+
self.update_user(user_identifier, preferred_language=new_lang)
|
|
150
|
+
return {'success': True, 'message': 'Language updated successfully.'}
|
|
151
|
+
except Exception as e:
|
|
152
|
+
# Log the error and return a generic failure message.
|
|
153
|
+
logging.error(f"Failed to update language for {user_identifier}: {e}")
|
|
154
|
+
return {'success': False, 'error_message': self.i18n_service.t('errors.general.unexpected_error')}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_profile_by_identifier(self, company_short_name: str, user_identifier: str) -> dict:
|
|
158
|
+
"""
|
|
159
|
+
Fetches a user profile directly by their identifier, bypassing the Flask session.
|
|
160
|
+
This is ideal for API-side checks.
|
|
161
|
+
"""
|
|
162
|
+
if not company_short_name or not user_identifier:
|
|
163
|
+
return {}
|
|
164
|
+
return self.session_context.get_profile_data(company_short_name, user_identifier)
|
|
93
165
|
|
|
94
166
|
|
|
95
167
|
def signup(self,
|
|
@@ -103,9 +175,10 @@ class ProfileService:
|
|
|
103
175
|
try:
|
|
104
176
|
|
|
105
177
|
# get company info
|
|
106
|
-
company = self.get_company_by_short_name(company_short_name)
|
|
178
|
+
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
107
179
|
if not company:
|
|
108
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
"error": self.i18n_service.t('errors.signup.company_not_found', company_name=company_short_name)}
|
|
109
182
|
|
|
110
183
|
# normalize format's
|
|
111
184
|
email = email.lower()
|
|
@@ -115,24 +188,25 @@ class ProfileService:
|
|
|
115
188
|
if existing_user:
|
|
116
189
|
# validate password
|
|
117
190
|
if not self.bcrypt.check_password_hash(existing_user.password, password):
|
|
118
|
-
return {"error":
|
|
191
|
+
return {"error": self.i18n_service.t('errors.signup.incorrect_password_for_existing_user', email=email)}
|
|
119
192
|
|
|
120
193
|
# check if register
|
|
121
194
|
if company in existing_user.companies:
|
|
122
|
-
return {"error":
|
|
195
|
+
return {"error": self.i18n_service.t('errors.signup.user_already_registered', email=email)}
|
|
123
196
|
else:
|
|
124
197
|
# add new company to existing user
|
|
125
198
|
existing_user.companies.append(company)
|
|
126
199
|
self.profile_repo.save_user(existing_user)
|
|
127
|
-
return {"message":
|
|
200
|
+
return {"message": self.i18n_service.t('flash_messages.user_associated_success')}
|
|
128
201
|
|
|
129
202
|
# add the new user
|
|
130
203
|
if password != confirm_password:
|
|
131
|
-
return {"error":
|
|
204
|
+
return {"error": self.i18n_service.t('errors.signup.password_mismatch')}
|
|
132
205
|
|
|
133
206
|
is_valid, message = self.validate_password(password)
|
|
134
207
|
if not is_valid:
|
|
135
|
-
|
|
208
|
+
# Translate the key returned by validate_password
|
|
209
|
+
return {"error": self.i18n_service.t(message)}
|
|
136
210
|
|
|
137
211
|
# encrypt the password
|
|
138
212
|
hashed_password = self.bcrypt.generate_password_hash(password).decode('utf-8')
|
|
@@ -154,9 +228,9 @@ class ProfileService:
|
|
|
154
228
|
# send email with verification
|
|
155
229
|
self.send_verification_email(new_user, company_short_name)
|
|
156
230
|
|
|
157
|
-
return {"message":
|
|
231
|
+
return {"message": self.i18n_service.t('flash_messages.signup_success')}
|
|
158
232
|
except Exception as e:
|
|
159
|
-
return {"error":
|
|
233
|
+
return {"error": self.i18n_service.t('errors.general.unexpected_error')}
|
|
160
234
|
|
|
161
235
|
def update_user(self, email: str, **kwargs) -> User:
|
|
162
236
|
return self.profile_repo.update_user(email, **kwargs)
|
|
@@ -166,14 +240,14 @@ class ProfileService:
|
|
|
166
240
|
# check if user exist
|
|
167
241
|
user = self.profile_repo.get_user_by_email(email)
|
|
168
242
|
if not user:
|
|
169
|
-
return {"error":
|
|
243
|
+
return {"error": self.i18n_service.t('errors.verification.user_not_found')}
|
|
170
244
|
|
|
171
245
|
# activate the user account
|
|
172
246
|
self.profile_repo.verify_user(email)
|
|
173
|
-
return {"message":
|
|
247
|
+
return {"message": self.i18n_service.t('flash_messages.account_verified_success')}
|
|
174
248
|
|
|
175
249
|
except Exception as e:
|
|
176
|
-
return {"error":
|
|
250
|
+
return {"error": self.i18n_service.t('errors.general.unexpected_error')}
|
|
177
251
|
|
|
178
252
|
def change_password(self,
|
|
179
253
|
email: str,
|
|
@@ -182,28 +256,28 @@ class ProfileService:
|
|
|
182
256
|
confirm_password: str):
|
|
183
257
|
try:
|
|
184
258
|
if new_password != confirm_password:
|
|
185
|
-
return {"error":
|
|
259
|
+
return {"error": self.i18n_service.t('errors.change_password.password_mismatch')}
|
|
186
260
|
|
|
187
261
|
# check the temporary code
|
|
188
262
|
user = self.profile_repo.get_user_by_email(email)
|
|
189
263
|
if not user or user.temp_code != temp_code:
|
|
190
|
-
return {"error":
|
|
264
|
+
return {"error": self.i18n_service.t('errors.change_password.invalid_temp_code')}
|
|
191
265
|
|
|
192
266
|
# encrypt and save the password, make the temporary code invalid
|
|
193
267
|
hashed_password = self.bcrypt.generate_password_hash(new_password).decode('utf-8')
|
|
194
268
|
self.profile_repo.update_password(email, hashed_password)
|
|
195
269
|
self.profile_repo.reset_temp_code(email)
|
|
196
270
|
|
|
197
|
-
return {"message":
|
|
271
|
+
return {"message": self.i18n_service.t('flash_messages.password_changed_success')}
|
|
198
272
|
except Exception as e:
|
|
199
|
-
return {"error":
|
|
273
|
+
return {"error": self.i18n_service.t('errors.general.unexpected_error')}
|
|
200
274
|
|
|
201
275
|
def forgot_password(self, email: str, reset_url: str):
|
|
202
276
|
try:
|
|
203
277
|
# Verificar si el usuario existe
|
|
204
278
|
user = self.profile_repo.get_user_by_email(email)
|
|
205
279
|
if not user:
|
|
206
|
-
return {"error":
|
|
280
|
+
return {"error": self.i18n_service.t('errors.forgot_password.user_not_registered', email=email)}
|
|
207
281
|
|
|
208
282
|
# Gen a temporary code and store in the repositories
|
|
209
283
|
temp_code = ''.join(random.choices(string.ascii_letters + string.digits, k=6)).upper()
|
|
@@ -212,35 +286,31 @@ class ProfileService:
|
|
|
212
286
|
# send email to the user
|
|
213
287
|
self.send_forgot_password_email(user, reset_url)
|
|
214
288
|
|
|
215
|
-
return {"message":
|
|
289
|
+
return {"message": self.i18n_service.t('flash_messages.forgot_password_success')}
|
|
216
290
|
except Exception as e:
|
|
217
|
-
return {"error":
|
|
291
|
+
return {"error": self.i18n_service.t('errors.general.unexpected_error')}
|
|
218
292
|
|
|
219
293
|
def validate_password(self, password):
|
|
220
294
|
"""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
- Contiene al menos una letra mayúscula
|
|
224
|
-
- Contiene al menos una letra minúscula
|
|
225
|
-
- Contiene al menos un número
|
|
226
|
-
- Contiene al menos un carácter especial
|
|
295
|
+
Validates that a password meets all requirements.
|
|
296
|
+
Returns (True, "...") on success, or (False, "translation.key") on failure.
|
|
227
297
|
"""
|
|
228
298
|
if len(password) < 8:
|
|
229
|
-
return False, "
|
|
299
|
+
return False, "errors.validation.password_too_short"
|
|
230
300
|
|
|
231
301
|
if not any(char.isupper() for char in password):
|
|
232
|
-
return False, "
|
|
302
|
+
return False, "errors.validation.password_no_uppercase"
|
|
233
303
|
|
|
234
304
|
if not any(char.islower() for char in password):
|
|
235
|
-
return False, "
|
|
305
|
+
return False, "errors.validation.password_no_lowercase"
|
|
236
306
|
|
|
237
307
|
if not any(char.isdigit() for char in password):
|
|
238
|
-
return False, "
|
|
308
|
+
return False, "errors.validation.password_no_digit"
|
|
239
309
|
|
|
240
310
|
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
|
241
|
-
return False, "
|
|
311
|
+
return False, "errors.validation.password_no_special_char"
|
|
242
312
|
|
|
243
|
-
return True, "
|
|
313
|
+
return True, "Password is valid."
|
|
244
314
|
|
|
245
315
|
def get_companies(self):
|
|
246
316
|
return self.profile_repo.get_companies()
|
|
@@ -248,6 +318,9 @@ class ProfileService:
|
|
|
248
318
|
def get_company_by_short_name(self, short_name: str) -> Company:
|
|
249
319
|
return self.profile_repo.get_company_by_short_name(short_name)
|
|
250
320
|
|
|
321
|
+
def get_active_api_key_entry(self, api_key_value: str) -> ApiKey | None:
|
|
322
|
+
return self.profile_repo.get_active_api_key_entry(api_key_value)
|
|
323
|
+
|
|
251
324
|
def new_api_key(self, company_short_name: str):
|
|
252
325
|
company = self.get_company_by_short_name(company_short_name)
|
|
253
326
|
if not company:
|