updated handler exceptions

This commit is contained in:
berkay 2025-01-11 19:40:10 +03:00
parent cecf1e69a2
commit 56b693989d
34 changed files with 571 additions and 1126 deletions

View File

@ -5,9 +5,11 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse
from api_objects.errors.errorMessages import EXCEPTION_DICTS, ERRORS_DICT, ERRORS_LANG
from api_objects.errors.errorHandlers import HTTPExceptionEvyos, HTTPExceptionAnyHandler, HTTPExceptionEvyosHandler
from middlewares.token_middleware import AuthHeaderMiddleware
from application.create_file import create_app
from api_objects.errors.errors_dictionary import ErrorHandlers
from prometheus_fastapi_instrumentator import Instrumentator
app = create_app(routers=routers)
@ -24,17 +26,20 @@ app.add_middleware(
)
app.add_middleware(AuthHeaderMiddleware)
# Initialize error handlers
error_handlers = ErrorHandlers.create(
requests=Request,
exceptions=HTTPException,
response_model=JSONResponse,
status=status,
)
# Initialize Exception and ExceptionInstance handlers
CustomExceptionHandler = HTTPExceptionEvyosHandler(**dict(
statuses=status,
exceptions=HTTPException,
response_model=JSONResponse,
exceptions_dict=EXCEPTION_DICTS,
errors_dict=ERRORS_DICT,
error_language_dict=ERRORS_LANG
))
CustomExceptionAnyHandler = HTTPExceptionAnyHandler(response_model=JSONResponse)
# Register error handlers with bound methods
app.add_exception_handler(HTTPException, error_handlers.exception_handler_http)
app.add_exception_handler(Exception, error_handlers.exception_handler_exception)
app.add_exception_handler(HTTPExceptionEvyos, CustomExceptionHandler.handle_exception)
app.add_exception_handler(Exception, CustomExceptionAnyHandler.any_exception_handler)
if __name__ == "__main__":
uvicorn_config = {

View File

@ -13,7 +13,7 @@ from api_services.templates.password_templates import (
from api_services.token_service import TokenService
from api_services.redis.functions import RedisActions
from api_library.response_handlers import ResponseHandler
from api_library.date_time_actions.date_functions import system_arrow
from api_library.date_time_actions.date_functions import system_arrow, DateTimeLocal
# from api_library.user_logger import UserLogger
@ -74,38 +74,41 @@ class AuthenticationLoginEventMethods(MethodToEvent):
@classmethod
def authentication_login_with_domain_and_creds(cls, data: Login, request: Request):
try:
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
if not found_user:
# UserLogger.log_login_attempt(
# request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# )
return ResponseHandler.unauthorized("Invalid credentials")
from api_objects import HTTPExceptionEvyos
raise HTTPExceptionEvyos(
error_code="UNKNOWN_ERROR",
lang="en",
)
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
Users.client_arrow = DateTimeLocal(
is_client=True, timezone=found_user.local_timezone
)
if not found_user:
# UserLogger.log_login_attempt(
# request, found_user.id, data.domain, data.access_key, success=True
# request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# )
response_data = {
return ResponseHandler.unauthorized("Invalid credentials")
return ResponseHandler.success(
message="User logged in successfully",
data={
"access_token": access_dict.get("access_token"),
"refresh_token": access_dict.get("refresher_token"),
"access_object": access_dict.get("access_object"),
"user": found_user.get_dict(),
}
return ResponseHandler.success(
message="User logged in successfully",
data=response_data,
)
except Exception as e:
# UserLogger.log_login_attempt(
# request, None, data.domain, data.access_key, success=False, error=str(e)
# )
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
},
)
# except Exception as e:
# # UserLogger.log_login_attempt(
# # request, None, data.domain, data.access_key, success=False, error=str(e)
# # )
# raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
class AuthenticationSelectEventMethods(MethodToEvent):
@ -125,6 +128,9 @@ class AuthenticationSelectEventMethods(MethodToEvent):
cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request
):
"""Handle employee company selection"""
Users.client_arrow = DateTimeLocal(
is_client=True, timezone=token_dict.local_timezone
)
if data.company_uu_id not in token_dict.companies_uu_id_list:
return ResponseHandler.unauthorized(
"Company not found in user's company list"

View File

@ -79,7 +79,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
system=True,
).data
if not endpoint:
EndpointRestriction.raise_http_exception(
raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message="Only Occupant can see this data",
@ -94,7 +94,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
== token_dict.selected_occupant.living_space_id,
).data
if not event_occupant:
EndpointRestriction.raise_http_exception(
raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message="This endpoint is not available for this occupant",
@ -110,7 +110,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
Event2Employee.employee_id == token_dict.selected_company.employee_id,
).data
if not event_employee:
EndpointRestriction.raise_http_exception(
raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message="This endpoint is not available for this employee",

View File

@ -10,6 +10,7 @@ from application.create_file import create_app
from api_objects.errors.errors_dictionary import ErrorHandlers
from prometheus_fastapi_instrumentator import Instrumentator
app = create_app(routers=routers)
Instrumentator().instrument(app=app).expose(app=app)

View File

@ -84,6 +84,10 @@ class AuthActions:
person_uu_id=str(found_user.person.uu_id),
request=dict(request.headers),
available_occupants=occupants_selection_dict,
timezone=(
found_user.local_timezone if found_user.local_timezone else "GMT+0"
),
lang=found_user.lang if found_user.lang else "tr",
),
)
return dict(
@ -136,6 +140,7 @@ class AuthActions:
company_address=company_address,
)
)
AccessObjectActions.save_object_to_redis(
access_token=access_token,
model_object=EmployeeTokenObject(
@ -151,6 +156,10 @@ class AuthActions:
companies_id_list=companies_id_list,
duty_uu_id_list=duty_uu_id_list,
duty_id_list=duty_id_list,
timezone=(
found_user.local_timezone if found_user.local_timezone else "GMT+0"
),
lang=found_user.lang if found_user.lang else "tr",
),
)
return dict(
@ -185,11 +194,30 @@ class AuthActions:
found_user.generate_access_token() if not access_token else access_token
)
# Prepare the user's details to save in Redis Session
if found_user.is_occupant: # Check if user is NOT an occupant
return cls.do_occupant_login_token(
if found_user.is_occupant: # Check if user is an occupant
cls.do_occupant_login_token(request, found_user, domain, access_token)
return {
"access_object": cls.do_occupant_login_token(
request, found_user, domain, access_token
),
"access_token": access_token,
"refresher_token": (
found_user.generate_refresh_token()
if found_user.remember_me
else None
),
}
cls.do_employee_login_token(request, found_user, domain, access_token)
return {
"access_object": cls.do_employee_login_token(
request, found_user, domain, access_token
)
return cls.do_employee_login_token(request, found_user, domain, access_token)
),
"access_token": access_token,
"refresher_token": (
found_user.generate_refresh_token() if found_user.remember_me else None
),
}
@classmethod
def update_selected_to_redis(cls, request, add_payload):

View File

@ -24,6 +24,7 @@ class AccessObjectActions:
Raises:
HTTPException: If save fails
"""
try:
RedisActions.save_object_to_redis(
access_token=access_token,

View File

@ -1,8 +1,100 @@
from typing import Any, Union
from typing import Any, Union, Callable, Any
from fastapi import status
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from api_validations.validations_request import PydanticBaseModel, BaseModelRegular
from databases.sql_models.response_model import AlchemyResponse
from sqlalchemy.orm import Query
MODEL_TYPE = Callable[[Any], Any]
class Pagination:
size: int = 10
page: int = 1
orderField: str = "id"
orderType: str = "asc"
pageCount: int = 1
totalCount: int = 1
totalPage: int = 1
def change(self, page=None, size=None, order_field=None, order_type=None):
self.page = page or self.page
self.size = size or self.size
self.orderField = order_field or self.orderField
self.orderType = order_type or self.orderType
self.setter_page()
def feed(self, data):
if isinstance(data, list):
self.totalCount = len(data)
elif isinstance(data, AlchemyResponse):
self.totalCount = data.count
elif isinstance(data, Query):
self.totalCount = data.count()
def setter_page(self):
self.pageCount = self.size
self.totalPage = int(round(self.totalCount / self.size, 0))
if self.totalCount % self.size > 0:
if self.page == self.totalPage:
self.pageCount = self.totalCount % self.size
self.totalPage = int(round(self.totalCount / self.size, 0)) + 1
def as_dict(self):
return {
"size": self.size,
"page": self.page,
"totalCount": self.totalCount,
"totalPage": self.totalPage,
"pageCount": self.pageCount,
"orderField": self.orderField,
"orderType": self.orderType,
}
class SingleAlchemyResponse:
status_code = "HTTP_200_OK"
result: AlchemyResponse
response_model: MODEL_TYPE
message: str
completed: bool
def __new__(
cls,
message: str,
response_model: MODEL_TYPE,
status_code: str = "HTTP_200_OK",
result: AlchemyResponse = None,
completed: bool = True,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.response_model = response_model
if not isinstance(cls.result, AlchemyResponse):
raise Exception("Invalid response type 4 single alchemy response")
if not cls.result.first:
raise Exception("Invalid data type 4 single alchemy response")
pagination = Pagination()
pagination.change(page=1)
BaseModelRegular(**cls.result.data.get_dict())
data = cls.result.data.get_dict()
if cls.response_model:
data = cls.response_model(**cls.result.data.get_dict()).dump()
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=data,
),
)
class AlchemyJsonResponse:
@ -11,27 +103,16 @@ class AlchemyJsonResponse:
result: AlchemyResponse
completed: bool
filter_attributes: Any = None
response_model: Any = None
response_model: MODEL_TYPE = None
cls_object: Any = None
@staticmethod
def get_total_count(cls_object, filter_attributes):
total_page_number = 1
count_to_use = cls_object.total_count / int(filter_attributes.size)
if cls_object.total_count > int(filter_attributes.size):
if isinstance(count_to_use, int):
total_page_number = round(count_to_use, 0)
elif isinstance(count_to_use, float):
total_page_number = round(count_to_use, 0) + 1
return total_page_number
def __new__(
cls,
message: str,
status_code: str = "HTTP_200_OK",
result: Union[Any, list] = None,
result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True,
response_model: Any = None,
response_model: MODEL_TYPE = None,
cls_object: Any = None,
filter_attributes: Any = None,
):
@ -40,108 +121,121 @@ class AlchemyJsonResponse:
cls.result = result
cls.completed = completed
cls.response_model = response_model
pagination_dict = {
"size/total_count": [10, 10],
"page/total_page": [1, 1],
"order_field": "id",
"order_type": "asc",
}
if filter_attributes:
total_page_number = cls.get_total_count(cls_object, filter_attributes)
pagination_dict = {
"size/total_count": [filter_attributes.size, cls_object.total_count],
"page/total_page": [filter_attributes.page, total_page_number],
"order_field": filter_attributes.order_field,
"order_type": filter_attributes.order_type,
}
if isinstance(cls.result, dict) or isinstance(cls.result, list):
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=len(cls.result),
count=len(cls.result),
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=cls.result,
),
)
first_item = getattr(cls.result, "data", None)
if not first_item:
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=0,
count=0,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=[],
),
)
cls.filter_attributes = filter_attributes
cls.cls_object = cls_object
pagination = Pagination()
if cls.result.first:
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=1,
count=1,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=cls.result.data.get_dict(),
),
)
raise Exception("Invalid data type 4 alchemy response")
if not cls.result.get(1).filter_attr and isinstance(cls.result.data, list):
counts = cls.result.count
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=counts,
count=counts,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=[result_data.get_dict() for result_data in cls.result.data],
),
if filter_attributes:
pagination.change(
page=filter_attributes.page, size=filter_attributes.size,
order_field=filter_attributes.order_field,
order_type=filter_attributes.order_type,
)
# filter_model = cls.result.get(1).filter_attr
total_count = cls.result.get(1).query.limit(None).offset(None).count()
total_page_number = cls.get_total_count(cls_object, filter_attributes)
pagination_dict = {
"size/total_count": [filter_attributes.size, cls_object.total_count],
"page/total_page": [filter_attributes.page, total_page_number],
"order_field": filter_attributes.order_field,
"order_type": filter_attributes.order_type,
}
include_joins = dict(
include_joins=(
filter_attributes.include_joins
if filter_attributes.include_joins
else []
)
)
data = []
for data_object in cls.result.data:
data_dict = data_object.get_dict(include_joins=include_joins)
data_dict = data_object.get_dict()
if cls.response_model:
data_dict = cls.response_model(
**data_object.get_dict(include_joins=include_joins)
).dump()
data_dict = cls.response_model(**data_object.get_dict()).dump()
data.append(data_dict)
pagination.feed(data)
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=total_count or 1,
count=cls.result.count,
pagination=pagination_dict,
pagination=pagination.as_dict(),
message=cls.message,
completed=cls.completed,
data=data,
),
)
class ListJsonResponse:
status_code = "HTTP_200_OK"
result: list
message: str
completed: bool
filter_attributes: Any
response_model: MODEL_TYPE = None,
cls_object: Any = None,
def __new__(
cls,
message: str,
status_code: str = "HTTP_200_OK",
result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True,
response_model: MODEL_TYPE = None,
cls_object: Any = None,
filter_attributes: Any = None,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.filter_attributes = filter_attributes
cls.response_model: MODEL_TYPE = response_model
if not isinstance(cls.result, list):
raise Exception("Invalid data type 4 list json response")
pagination = Pagination()
pagination.change(page=1)
data = list(cls.result)
if cls.response_model:
data = [cls.response_model(**data_object).dump() for data_object in cls.result]
pagination.feed(data)
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=cls.result,
),
)
class DictJsonResponse:
status_code = "HTTP_200_OK"
result: dict
message: str
completed: bool
filter_attributes: Any
response_model: MODEL_TYPE = None,
cls_object: Any = None,
def __new__(
cls,
message: str,
status_code: str = "HTTP_200_OK",
result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True,
response_model: MODEL_TYPE = None,
cls_object: Any = None,
filter_attributes: Any = None,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.filter_attributes = filter_attributes
cls.response_model: MODEL_TYPE = response_model
if not isinstance(cls.result, dict):
raise Exception("Invalid data type 4 dict json response")
pagination = Pagination()
pagination.change(page=1)
data = cls.result
if cls.response_model:
data = cls.response_model(**cls.result).dump()
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=data,
),
)

View File

@ -13,6 +13,12 @@ services:
- auth_venv:/service_app/.venv
- auth_logs:/service_app/logs
# wag_management_init_service:
# container_name: wag_management_init_service
# build:
# context: .
# dockerfile: service_app_init/Dockerfile
# wag_management_event_service:
# container_name: wag_management_event_service
# # restart: on-failure
@ -41,11 +47,7 @@ services:
# - validation_venv:/service_app/.venv
# - validation_logs:/service_app/logs
# wag_management_init_service:
# container_name: wag_management_init_service
# build:
# context: .
# dockerfile: service_app_init/Dockerfile
volumes:
auth_venv:

View File

@ -14,20 +14,6 @@ from api_events.events.address.address import (
AddressPostCodeUpdateEventMethod,
AddressPostCodeListEventMethod,
)
from api_events.events.application.authentication import (
AuthenticationLoginEventMethod,
AuthenticationSelectEventMethod,
AuthenticationCheckTokenEventMethod,
AuthenticationRefreshEventMethod,
AuthenticationChangePasswordEventMethod,
AuthenticationCreatePasswordEventMethod,
AuthenticationResetPasswordEventMethod,
AuthenticationDisconnectUserEventMethod,
AuthenticationLogoutEventMethod,
AuthenticationRefreshTokenEventMethod,
AuthenticationForgotPasswordEventMethod,
AuthenticationDownloadAvatarEventMethod,
)
from api_events.events.account.account_records import (
AccountRecordsListEventMethod,
AccountRecordsCreateEventMethod,
@ -184,18 +170,6 @@ __all__ = [
"PeopleUpdateEventMethod",
"PeoplePatchEventMethod",
"PeopleCreateEventMethod",
"AuthenticationLoginEventMethod",
"AuthenticationSelectEventMethod",
"AuthenticationCheckTokenEventMethod",
"AuthenticationRefreshEventMethod",
"AuthenticationChangePasswordEventMethod",
"AuthenticationCreatePasswordEventMethod",
"AuthenticationResetPasswordEventMethod",
"AuthenticationDisconnectUserEventMethod",
"AuthenticationLogoutEventMethod",
"AuthenticationRefreshTokenEventMethod",
"AuthenticationForgotPasswordEventMethod",
"AuthenticationDownloadAvatarEventMethod",
"AccountRecordsListEventMethod",
"AccountRecordsCreateEventMethod",
"AccountRecordsUpdateEventMethod",

View File

@ -64,7 +64,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if not isinstance(token_dict, OccupantTokenObject):
raise AccountRecords().raise_http_exception(
raise AccountRecords.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message="Only Occupant can see this data",
@ -76,7 +76,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
id=token_dict.selected_occupant.living_space_id
).data
if not living_space:
raise AccountRecords().raise_http_exception(
raise AccountRecords.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message="Living space not found",
@ -214,7 +214,7 @@ class AccountRecordsCreateEventMethods(MethodToEvent):
BuildIbans.build_id == token_dict.selected_occupant.build_id,
).data
if not build_iban:
BuildIbans.raise_http_exception(
raise BuildIbans.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message=f"{data.iban} is not found in company related to your organization",

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class DecisionBookEvents(MethodToEvent): ...

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class ApplicationEvents(MethodToEvent): ...

View File

@ -1,750 +0,0 @@
import json
from typing import Union
from fastapi import status
from fastapi.requests import Request
from fastapi.exceptions import HTTPException
from api_objects import OccupantTokenObject, EmployeeTokenObject
from api_objects.auth.token_objects import CompanyToken, OccupantToken
from api_services.templates.password_templates import (
password_is_changed_template,
change_your_password_template,
)
from api_services.token_service import TokenService
from api_services.redis.functions import RedisActions
from api_library.response_handlers import ResponseHandler
from api_library.date_time_actions.date_functions import system_arrow
# from api_library.user_logger import UserLogger
from api_validations.validations_request import (
Login,
Logout,
ChangePassword,
EmployeeSelection,
OccupantSelection,
CreatePassword,
Forgot,
# ResetPassword,
# RefreshToken,
)
from api_validations.validations_response import (
AuthenticationLoginResponse,
AuthenticationRefreshResponse,
AuthenticationUserInfoResponse,
)
from ApiServices.api_handlers.auth_actions.auth import AuthActions
from api_configs import Auth, ApiStatic
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from databases import (
Companies,
Staff,
Duties,
Departments,
Employees,
BuildLivingSpace,
BuildParts,
Build,
Duty,
Event2Occupant,
Event2Employee,
Users,
UsersTokens,
OccupantTypes,
RelationshipEmployee2Build,
)
from api_services import (
send_email,
)
class AuthenticationLoginEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Login via domain and access key : [email] | [phone]"
event_category = "AUTHENTICATION"
__event_keys__ = {
"e672846d-cc45-4d97-85d5-6f96747fac67": "authentication_login_with_domain_and_creds",
}
__event_validation__ = {
"e672846d-cc45-4d97-85d5-6f96747fac67": AuthenticationLoginResponse,
}
@classmethod
def authentication_login_with_domain_and_creds(cls, data: Login, request: Request):
try:
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
if not found_user:
# UserLogger.log_login_attempt(
# request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# )
return ResponseHandler.unauthorized("Invalid credentials")
# UserLogger.log_login_attempt(
# request, found_user.id, data.domain, data.access_key, success=True
# )
response_data = {
"access_token": access_dict.get("access_token"),
"refresh_token": access_dict.get("refresher_token"),
"access_object": access_dict.get("access_object"),
"user": found_user.get_dict(),
}
return ResponseHandler.success(
message="User logged in successfully",
data=response_data,
)
except Exception as e:
# UserLogger.log_login_attempt(
# request, None, data.domain, data.access_key, success=False, error=str(e)
# )
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
class AuthenticationSelectEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Select Employee Duty or Occupant Type"
event_category = "AUTHENTICATION"
__event_keys__ = {
"cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type",
}
__event_validation__ = {
"cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type",
}
@classmethod
def _handle_employee_selection(
cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request
):
"""Handle employee company selection"""
if data.company_uu_id not in token_dict.companies_uu_id_list:
return ResponseHandler.unauthorized(
"Company not found in user's company list"
)
selected_company = Companies.filter_one(
Companies.uu_id == data.company_uu_id
).data
if not selected_company:
return ResponseHandler.not_found("Company not found")
# Get department IDs for the company
department_ids = [
dept.id
for dept in Departments.filter_all(
Departments.company_id == selected_company.id
).data
]
# Get duties IDs for the company
duties_ids = [
duty.id
for duty in Duties.filter_all(Duties.company_id == selected_company.id).data
]
# Get staff IDs
staff_ids = [
staff.id for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids)).data
]
# Get employee
employee = Employees.filter_one(
Employees.people_id == token_dict.person_id,
Employees.staff_id.in_(staff_ids),
).data
if not employee:
return ResponseHandler.not_found("Employee not found")
# Get reachable events
reachable_event_list_id = Event2Employee.get_event_id_by_employee_id(
employee_id=employee.id
)
# Get staff and duties
staff = Staff.filter_one(Staff.id == employee.staff_id).data
duties = Duties.filter_one(Duties.id == staff.duties_id).data
department = Departments.filter_one(Departments.id == duties.department_id).data
# Get bulk duty
bulk_id = Duty.filter_by_one(system=True, duty_code="BULK").data
bulk_duty_id = Duties.filter_by_one(
company_id=selected_company.id,
duties_id=bulk_id.id,
**Duties.valid_record_dict,
).data
# Create company token
company_token = CompanyToken(
company_uu_id=selected_company.uu_id.__str__(),
company_id=selected_company.id,
department_id=department.id,
department_uu_id=department.uu_id.__str__(),
duty_id=duties.id,
duty_uu_id=duties.uu_id.__str__(),
bulk_duties_id=bulk_duty_id.id,
staff_id=staff.id,
staff_uu_id=staff.uu_id.__str__(),
employee_id=employee.id,
employee_uu_id=employee.uu_id.__str__(),
reachable_event_list_id=reachable_event_list_id,
)
# Update Redis
AuthActions.update_selected_to_redis(request=request, add_payload=company_token)
return ResponseHandler.success("Company selected successfully")
@classmethod
def _handle_occupant_selection(
cls, data: OccupantSelection, token_dict: OccupantTokenObject, request: Request
):
"""Handle occupant type selection"""
# Get occupant type
occupant_type = OccupantTypes.filter_by_one(
system=True, uu_id=data.occupant_uu_id
).data
if not occupant_type:
return ResponseHandler.not_found("Occupant Type not found")
# Get build part
build_part = BuildParts.filter_by_one(
system=True, uu_id=data.build_part_uu_id
).data
if not build_part:
return ResponseHandler.not_found("Build Part not found")
# Get build and company info
build = Build.filter_one(Build.id == build_part.build_id).data
related_company = RelationshipEmployee2Build.filter_one(
RelationshipEmployee2Build.member_id == build.id
).data
company_related = Companies.filter_one(
Companies.id == related_company.company_id
).data
responsible_employee = Employees.filter_one(
Employees.id == related_company.employee_id
).data
# Get selected occupant type
selected_occupant_type = BuildLivingSpace.filter_one(
BuildLivingSpace.occupant_type == occupant_type.id,
BuildLivingSpace.person_id == token_dict.person_id,
BuildLivingSpace.build_parts_id == build_part.id,
).data
if not selected_occupant_type:
return ResponseHandler.not_found("Selected occupant type not found")
# Get reachable events
reachable_event_list_id = Event2Occupant.get_event_id_by_build_living_space_id(
build_living_space_id=selected_occupant_type.id
)
# Create occupant token
occupant_token = OccupantToken(
living_space_id=selected_occupant_type.id,
living_space_uu_id=selected_occupant_type.uu_id.__str__(),
occupant_type_id=occupant_type.id,
occupant_type_uu_id=occupant_type.uu_id.__str__(),
occupant_type=occupant_type.occupant_type,
build_id=build.id,
build_uuid=build.uu_id.__str__(),
build_part_id=build_part.id,
build_part_uuid=build_part.uu_id.__str__(),
responsible_employee_id=responsible_employee.id,
responsible_employee_uuid=responsible_employee.uu_id.__str__(),
responsible_company_id=company_related.id,
responsible_company_uuid=company_related.uu_id.__str__(),
reachable_event_list_id=reachable_event_list_id,
)
# Update Redis
AuthActions.update_selected_to_redis(
request=request, add_payload=occupant_token
)
return ResponseHandler.success("Occupant selected successfully")
@classmethod
def authentication_select_company_or_occupant_type(
cls,
request: Request,
data: Union[EmployeeSelection, OccupantSelection],
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
"""Handle selection of company or occupant type"""
try:
if isinstance(token_dict, EmployeeTokenObject):
return cls._handle_employee_selection(data, token_dict, request)
elif isinstance(token_dict, OccupantTokenObject):
return cls._handle_occupant_selection(data, token_dict, request)
return ResponseHandler.error(
"Invalid token type", status_code=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return ResponseHandler.error(
str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
class AuthenticationCheckTokenEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Check Token is valid for user"
event_category = "AUTHENTICATION"
__event_keys__ = {
"73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid",
}
__event_validation__ = {
"73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid",
}
@classmethod
def authentication_check_token_is_valid(cls, request: Request):
try:
if RedisActions.get_object_via_access_key(request=request):
return ResponseHandler.success("Access Token is valid")
except HTTPException:
return ResponseHandler.unauthorized("Access Token is NOT valid")
class AuthenticationRefreshEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Refresh user info using access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"48379bb2-ba81-4d8e-a9dd-58837cfcbf67": "authentication_refresh_user_info",
}
__event_validation__ = {
"48379bb2-ba81-4d8e-a9dd-58837cfcbf67": AuthenticationRefreshResponse,
}
@classmethod
def authentication_refresh_user_info(
cls,
request: Request,
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
try:
access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG)
if not access_token:
return ResponseHandler.unauthorized()
# Get user and token info
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
user_token = UsersTokens.filter_one(
UsersTokens.domain == found_user.domain_name,
UsersTokens.user_id == found_user.id,
UsersTokens.token_type == "RememberMe",
).data
response_data = {
"access_token": access_token,
"refresh_token": getattr(user_token, "token", None),
"user": found_user.get_dict(),
}
return ResponseHandler.success(
"User info refreshed successfully",
data=response_data,
)
except Exception as e:
return ResponseHandler.error(str(e))
class AuthenticationChangePasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Change password with access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password",
}
__event_validation__ = {
"f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password",
}
@classmethod
def authentication_change_password(
cls,
request: Request,
data: ChangePassword,
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
try:
if not isinstance(token_dict, EmployeeTokenObject):
return ResponseHandler.unauthorized(
"Only employees can change password"
)
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
if not found_user.check_password(data.old_password):
# UserLogger.log_password_change(
# request,
# found_user.id,
# "change",
# success=False,
# error="Invalid old password",
# )
return ResponseHandler.unauthorized("Old password is incorrect")
found_user.set_password(data.new_password)
# UserLogger.log_password_change(
# request, found_user.id, "change", success=True
# )
return ResponseHandler.success("Password changed successfully")
except Exception as e:
# UserLogger.log_password_change(
# request,
# found_user.id if found_user else None,
# "change",
# success=False,
# error=str(e),
# )
return ResponseHandler.error(str(e))
class AuthenticationCreatePasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Create password with password reset token requested via email"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password",
}
__event_validation__ = {
"c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password",
}
@classmethod
def authentication_create_password(cls, data: CreatePassword):
if not data.re_password == data.password:
raise HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE, detail="Password must match"
)
if found_user := Users.filter_one(
Users.password_token == data.password_token
).data:
found_user.create_password(found_user=found_user, password=data.password)
found_user.password_token = ""
found_user.save()
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, your password has been changed.",
receivers=[str(found_user.email)],
html=password_is_changed_template(user_name=found_user.user_tag),
)
if not send_email_completed:
raise HTTPException(
status_code=400, detail="Email can not be sent. Try again later"
)
return ResponseHandler.success(
"Password is created successfully",
data=found_user.get_dict(),
)
return ResponseHandler.not_found("Record not found")
class AuthenticationDisconnectUserEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Disconnect all sessions of user in access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user",
}
__event_validation__ = {
"8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user",
}
@classmethod
def authentication_disconnect_user(
cls, data: Logout, token_dict: Union[EmployeeTokenObject, OccupantTokenObject]
):
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
if already_tokens := RedisActions.get_object_via_user_uu_id(
user_id=str(found_user.uu_id)
):
for key, token_user in already_tokens.items():
RedisActions.delete(key)
selected_user = Users.filter_one(
Users.uu_id == token_user.get("uu_id"),
).data
selected_user.remove_refresher_token(
domain=data.domain, disconnect=True
)
return ResponseHandler.success(
"All sessions are disconnected",
data=selected_user.get_dict(),
)
return ResponseHandler.not_found("Invalid data")
class AuthenticationLogoutEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Logout only single session of user which domain is provided"
event_category = "AUTHENTICATION"
__event_keys__ = {
"5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user",
}
__event_validation__ = {
"5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user",
}
@classmethod
def authentication_logout_user(
cls, request: Request, data: Logout, token_dict: dict = None
):
token_user = None
if already_tokens := RedisActions.get_object_via_access_key(request=request):
for key in already_tokens:
token_user = RedisActions.get_json(key)
if token_user.get("domain") == data.domain:
RedisActions.delete(key)
selected_user = Users.filter_one(
Users.uu_id == token_user.get("uu_id"),
).data
selected_user.remove_refresher_token(domain=data.domain)
return ResponseHandler.success(
"Session is logged out",
data=token_user,
)
return ResponseHandler.not_found("Logout is not successfully completed")
class AuthenticationRefreshTokenEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Refresh access token with refresher token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c90f3334-10c9-4181-b5ff-90d98a0287b2": "authentication_refresher_token",
}
__event_validation__ = {
"c90f3334-10c9-4181-b5ff-90d98a0287b2": AuthenticationRefreshResponse,
}
@classmethod
def authentication_refresher_token(
# cls, request: Request, data: RefreshToken, token_dict: dict = None
cls,
request: Request,
data,
token_dict: dict = None,
):
token_refresher = UsersTokens.filter_by_one(
token=data.refresh_token,
domain=data.domain,
**UsersTokens.valid_record_dict,
).data
if not token_refresher:
return ResponseHandler.not_found("Invalid data")
if found_user := Users.filter_one(
Users.id == token_refresher.user_id,
).data:
found_user: Users = found_user
access_key = AuthActions.save_access_token_to_redis(
request=request, found_user=found_user, domain=data.domain
)
found_user.last_agent = request.headers.get("User-Agent", None)
found_user.last_platform = request.headers.get("Origin", None)
found_user.last_remote_addr = getattr(
request, "remote_addr", None
) or request.headers.get("X-Forwarded-For", None)
found_user.last_seen = str(system_arrow.now())
response_data = {
"access_token": access_key,
"refresh_token": data.refresh_token,
}
return ResponseHandler.success(
"User is logged in successfully via refresher token",
data=response_data,
)
return ResponseHandler.not_found("Invalid data")
class AuthenticationForgotPasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Send an email to user for a valid password reset token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password",
}
__event_validation__ = {
"e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password",
}
@classmethod
def authentication_forgot_password(
cls,
request: Request,
data: Forgot,
):
found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain
)
forgot_key = AuthActions.save_access_token_to_redis(
request=request, found_user=found_user, domain=data.domain
)
forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key)
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, your forgot password link has been sent.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag, forgot_link=forgot_link
),
)
if not send_email_completed:
raise HTTPException(
status_code=400, detail="Email can not be sent. Try again later"
)
found_user.password_token = forgot_key
found_user.password_token_is_valid = str(system_arrow.shift(days=1))
found_user.save()
return ResponseHandler.success(
"Password is change link is sent to your email or phone",
data={},
)
class AuthenticationResetPasswordEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"af9e121e-24bb-44ac-a616-471d5754360e": "authentication_reset_password",
}
@classmethod
def authentication_reset_password(cls, data: Forgot):
from sqlalchemy import or_
found_user = Users.query.filter(
or_(
Users.email == str(data.access_key).lower(),
Users.phone_number == str(data.access_key).replace(" ", ""),
),
).first()
if not found_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Given access key or domain is not matching with the any user record.",
)
reset_password_token = found_user.reset_password_token(found_user=found_user)
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, a password reset request has been received.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag,
forgot_link=ApiStatic.forgot_link(forgot_key=reset_password_token),
),
)
if not send_email_completed:
raise found_user.raise_http_exception(
status_code=400, message="Email can not be sent. Try again later"
)
return ResponseHandler.success(
"Password change link is sent to your email or phone",
data=found_user.get_dict(),
)
class AuthenticationDownloadAvatarEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Download avatar icon and profile info of user"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c140cd5f-307f-4046-a93e-3ade032a57a7": "authentication_download_avatar",
}
__event_validation__ = {
"c140cd5f-307f-4046-a93e-3ade032a57a7": AuthenticationUserInfoResponse,
}
@classmethod
def authentication_download_avatar(
cls, token_dict: Union[EmployeeTokenObject, OccupantTokenObject]
):
if found_user := Users.filter_one(Users.id == token_dict.user_id).data:
expired_starts = str(
system_arrow.now() - system_arrow.get(str(found_user.expiry_ends))
)
expired_int = (
system_arrow.now() - system_arrow.get(str(found_user.expiry_ends))
).days
user_info = {
"lang": token_dict.lang,
"full_name": found_user.person.full_name,
"avatar": found_user.avatar,
"remember_me": found_user.remember_me,
"expiry_ends": str(found_user.expiry_ends),
"expired_str": expired_starts,
"expired_int": int(expired_int),
}
return ResponseHandler.success(
"Avatar and profile is shared via user credentials",
data=user_info,
)
return ResponseHandler.not_found("Invalid data")
AuthenticationLoginEventMethod = AuthenticationLoginEventMethods(
action=ActionsSchema(endpoint="/authentication/login")
)
AuthenticationSelectEventMethod = AuthenticationSelectEventMethods(
action=ActionsSchema(endpoint="/authentication/select")
)
AuthenticationCheckTokenEventMethod = AuthenticationCheckTokenEventMethods(
action=ActionsSchema(endpoint="/authentication/valid")
)
AuthenticationRefreshEventMethod = AuthenticationRefreshEventMethods(
action=ActionsSchema(endpoint="/authentication/refresh")
)
AuthenticationChangePasswordEventMethod = AuthenticationChangePasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/change_password")
)
AuthenticationCreatePasswordEventMethod = AuthenticationCreatePasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/create_password")
)
AuthenticationDisconnectUserEventMethod = AuthenticationDisconnectUserEventMethods(
action=ActionsSchema(endpoint="/authentication/disconnect")
)
AuthenticationLogoutEventMethod = AuthenticationLogoutEventMethods(
action=ActionsSchema(endpoint="/authentication/logout")
)
AuthenticationRefreshTokenEventMethod = AuthenticationRefreshTokenEventMethods(
action=ActionsSchema(endpoint="/authentication/refresher")
)
AuthenticationForgotPasswordEventMethod = AuthenticationForgotPasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/forgot")
)
AuthenticationDownloadAvatarEventMethod = AuthenticationDownloadAvatarEventMethods(
action=ActionsSchema(endpoint="/authentication/avatar")
)
AuthenticationResetPasswordEventMethod = AuthenticationResetPasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/reset_password")
)

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class RulesEvents(MethodToEvent): ...

View File

@ -80,7 +80,7 @@ class BuildAreaCreateEventMethods(MethodToEvent):
selected_build = None
if isinstance(token_dict, OccupantTokenObject):
if not token_dict.selected_occupant.build_uuid == data.build_uu_id:
BuildArea.raise_http_exception(
raise BuildArea.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Occupant can not create build area for {data.build_uu_id}",
@ -96,7 +96,7 @@ class BuildAreaCreateEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id
).all()
if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]:
BuildArea.raise_http_exception(
raise BuildArea.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Employee can not create build area for {data.build_uu_id}",

View File

@ -47,7 +47,7 @@ class BuildSitesListEventMethods(MethodToEvent):
)
employees_build_list = [build.address_id for build in employees_build.all()]
if not employees_build_list:
BuildSites.raise_http_exception(
raise BuildSites.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="NOT_FOUND",
message="Employee has no build sites registered",
@ -86,7 +86,7 @@ class BuildSitesCreateEventMethods(MethodToEvent):
):
if isinstance(token_dict, OccupantTokenObject):
if not token_dict.selected_occupant.build_uuid == data.build_uu_id:
BuildSites.raise_http_exception(
raise BuildSites.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Occupant can not create build sites for {data.build_uu_id}",
@ -99,7 +99,7 @@ class BuildSitesCreateEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id
).all()
if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]:
BuildSites.raise_http_exception(
raise BuildSites.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Employee can not create build sites for {data.build_uu_id}",

View File

@ -47,7 +47,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
Build.id == token_dict.selected_occupant.build_id,
).data
if not occupants_build_id:
Build.raise_http_exception(
raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Occupant has no build registered in the system. Contact with your company.",
@ -57,7 +57,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
BuildParts.build_id.in_(occupants_build_id.id),
).data
if not occupants_build_parts:
Build.raise_http_exception(
raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Occupant has no build parts registered in the system. Contact with your company.",
@ -80,7 +80,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id
)
if not build_id_list_query:
Build.raise_http_exception(
raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Employee has no build registered in the system. Contact with your supervisor.",
@ -92,7 +92,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
),
).data
if not build_part_id_list_query:
Build.raise_http_exception(
raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"Employee has no build parts registered in the system. Contact with your supervisor.",
@ -142,7 +142,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
BuildParts.build_id.in_([build.id for build in build_id_list_query.all()]),
).data
if not build_part:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message=f"{data.build_parts_uu_id} - Build Part is not found in database. Check build part uu_id",
@ -154,7 +154,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
People.uu_id == data.person_uu_id,
).data
if not life_person:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message=f"{data.person_uu_id} - Living Person is not found in database.",
@ -164,7 +164,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
)
occupant_type = OccupantTypes.filter_by_one(uu_id=data.occupant_type_uu_id).data
if not occupant_type:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED",
message=f"{data.occupant_type_uu_id} - Occupant Type is not found in database. Check occupant type uu_id",
@ -252,7 +252,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
),
).data
if not build_part:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"{data.life_person_uu_id} - Living Person is not found in database.",
@ -264,7 +264,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
People.uu_id == data.life_person_uu_id or ""
).data
if not life_person:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message=f"{data.life_person_uu_id} - Living Person is not found in database.",
@ -290,7 +290,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
if data_dict["is_tenant_live"]:
owner_person = getattr(last_living_space, "owner_person_id", None)
if not owner_person:
BuildLivingSpace.raise_http_exception(
raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED",
message="Owner person of build part is not defined. Please register owner of part first.",

View File

@ -6,6 +6,7 @@ from api_objects.auth.token_objects import (
OccupantToken,
ApplicationToken,
)
from api_objects.errors.errorHandlers import HTTPExceptionEvyos
__all__ = [
"OccupantTokenObject",
@ -14,4 +15,5 @@ __all__ = [
"CompanyToken",
"OccupantToken",
"ApplicationToken",
"HTTPExceptionEvyos",
]

View File

@ -23,7 +23,7 @@ class ApplicationToken(BaseModel):
domain: Optional[str] = "app.evyos.com.tr"
lang: Optional[str] = "TR"
timezone: Optional[str] = "Europe/Istanbul"
timezone: Optional[str] = "GMT+3"
user_type: int = UserType.occupant.value
credentials: dict = None

View File

@ -1,74 +1,6 @@
from .errors_dictionary import ErrorMessages
class AlchemyError:
ERRORS_DICT = {
"100": "HTTP_100_CONTINUE",
"101": "HTTP_101_SWITCHING_PROTOCOLS",
"102": "HTTP_102_PROCESSING",
"103": "HTTP_103_EARLY_HINTS",
"200": "HTTP_200_OK",
"201": "HTTP_201_CREATED",
"202": "HTTP_202_ACCEPTED",
"203": "HTTP_203_NON_AUTHORITATIVE_INFORMATION",
"204": "HTTP_204_NO_CONTENT",
"205": "HTTP_205_RESET_CONTENT",
"206": "HTTP_206_PARTIAL_CONTENT",
"207": "HTTP_207_MULTI_STATUS",
"208": "HTTP_208_ALREADY_REPORTED",
"226": "HTTP_226_IM_USED",
"300": "HTTP_300_MULTIPLE_CHOICES",
"301": "HTTP_301_MOVED_PERMANENTLY",
"302": "HTTP_302_FOUND",
"303": "HTTP_303_SEE_OTHER",
"304": "HTTP_304_NOT_MODIFIED",
"305": "HTTP_305_USE_PROXY",
"306": "HTTP_306_RESERVED",
"307": "HTTP_307_TEMPORARY_REDIRECT",
"308": "HTTP_308_PERMANENT_REDIRECT",
"400": "HTTP_400_BAD_REQUEST",
"401": "HTTP_401_UNAUTHORIZED",
"402": "HTTP_402_PAYMENT_REQUIRED",
"403": "HTTP_403_FORBIDDEN",
"404": "HTTP_404_NOT_FOUND",
"405": "HTTP_405_METHOD_NOT_ALLOWED",
"406": "HTTP_406_NOT_ACCEPTABLE",
"407": "HTTP_407_PROXY_AUTHENTICATION_REQUIRED",
"408": "HTTP_408_REQUEST_TIMEOUT",
"409": "HTTP_409_CONFLICT",
"410": "HTTP_410_GONE",
"411": "HTTP_411_LENGTH_REQUIRED",
"412": "HTTP_412_PRECONDITION_FAILED",
"413": "HTTP_413_REQUEST_ENTITY_TOO_LARGE",
"414": "HTTP_414_REQUEST_URI_TOO_LONG",
"415": "HTTP_415_UNSUPPORTED_MEDIA_TYPE",
"416": "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE",
"417": "HTTP_417_EXPECTATION_FAILED",
"418": "HTTP_418_IM_A_TEAPOT",
"421": "HTTP_421_MISDIRECTED_REQUEST",
"422": "HTTP_422_UNPROCESSABLE_ENTITY",
"423": "HTTP_423_LOCKED",
"424": "HTTP_424_FAILED_DEPENDENCY",
"426": "HTTP_426_UPGRADE_REQUIRED",
"428": "HTTP_428_PRECONDITION_REQUIRED",
"429": "HTTP_429_TOO_MANY_REQUESTS",
"431": "HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE",
"451": "HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS",
"500": "HTTP_500_INTERNAL_SERVER_ERROR",
}
ERRORS_KEYS = {
"delete": "DeletedRecord",
"update": "UpdatedRecord",
"create": "CreatedRecord",
"list": "ListedRecords",
"not_found": "RecordNotFound",
"already_exist": "AlreadyExists",
"not_deleted": "RecordNotDeleted",
"not_updated": "RecordNotUpdated",
"not_created": "RecordNotCreated",
"not_listed": "RecordsNotListed",
"not_confirm": "IsNotConfirmed",
}
class HTTPExceptionError:
def __init__(self, lang):
self.lang = lang

View File

@ -0,0 +1,60 @@
from typing import Any
# class HTTPExceptionInstance:
# def __init__(self, statuses, exceptions, exceptions_dict, errors_dict, response_model, error_language_dict):
# self.EXCEPTIONS = exceptions # from fastapi.exceptions import HTTPException
# self.STATUSES = statuses # from fastapi import status
# self.EXCEPTION_DICTS: dict = exceptions_dict
# self.ERRORS_DICT: dict = errors_dict
# self.ERRORS_LANG: dict = error_language_dict
# self.RESPONSE_MODEL: Any = response_model
class HTTPExceptionEvyos(Exception):
def __init__(self, error_code: str, lang: str):
self.error_code = error_code
self.lang = lang
class HTTPExceptionEvyosHandler:
def __init__(self, statuses, exceptions, exceptions_dict, errors_dict, response_model, error_language_dict):
self.EXCEPTIONS = exceptions # from fastapi.exceptions import HTTPException
self.STATUSES = statuses # from fastapi import status
self.EXCEPTION_DICTS: dict = exceptions_dict
self.ERRORS_DICT: dict = errors_dict
self.ERRORS_LANG: dict = error_language_dict
self.RESPONSE_MODEL: Any = response_model
def retrieve_error_status_code(self, exc: HTTPExceptionEvyos):
grab_status = self.ERRORS_DICT.get(str(exc.error_code).upper(), "")
grab_status_code = self.EXCEPTION_DICTS.get(str(grab_status).upper(), "500")
return getattr(self.STATUSES, str(grab_status_code), getattr(self.STATUSES, "HTTP_500_INTERNAL_SERVER_ERROR"))
def retrieve_error_message(self, exc: HTTPExceptionEvyos):
message_by_lang = self.ERRORS_LANG.get(str(exc.lang).lower(), {})
message_by_code = message_by_lang.get(str(exc.error_code).upper(), "Unknown error")
return message_by_code
def handle_exception(self, request, exc: HTTPExceptionEvyos):
headers = getattr(request, "headers", {})
status_code = self.retrieve_error_status_code(exc)
error_message = self.retrieve_error_message(exc)
return self.RESPONSE_MODEL(
status_code=int(status_code),
content={"message": error_message, "lang": exc.lang},
)
class HTTPExceptionAnyHandler:
def __init__(self, response_model):
self.RESPONSE_MODEL: Any = response_model
def any_exception_handler(self, request, exc: Exception):
return self.RESPONSE_MODEL(
status_code=200,
content={"message": str(exc), "lang": None, "status_code": 417},
)

View File

@ -0,0 +1,17 @@
from .baseErrorCluster import (
BASE_ERRORS,
BASE_ERROR_LANGUAGE,
EXCEPTION_DICTS,
)
ERRORS_LANG = {
"tr": {
**BASE_ERROR_LANGUAGE["tr"],
},
"en": {
**BASE_ERROR_LANGUAGE["en"],
}
}
ERRORS_DICT = {
**BASE_ERRORS,
}

View File

@ -0,0 +1,104 @@
BASE_ERRORS = {
"NOT_CREATED": 405,
"NOT_DELETED": 405,
"NOT_UPDATED": 405,
"NOT_LISTED": 404,
"NOT_FOUND": 404,
"ALREADY_EXISTS": 400,
"IS_NOT_CONFIRMED": 405,
"NOT_AUTHORIZED": 401,
"NOT_VALID": 406,
"NOT_ACCEPTABLE": 406,
"INVALID_DATA": 422,
"UNKNOWN_ERROR": 502,
}
BASE_ERROR_LANGUAGE = {
"tr": {
"NOT_CREATED": "Kayıt oluşturulamadı.",
"NOT_DELETED": "Kayıt silinemedi.",
"NOT_UPDATED": "Kayıt güncellenemedi.",
"NOT_LISTED": "Kayıt listelenemedi.",
"NOT_FOUND": "Kayıt bulunamadı.",
"ALREADY_EXISTS": "Kayıt zaten mevcut.",
"IS_NOT_CONFIRMED": "Kayıt onaylanmadı.",
"NOT_AUTHORIZED": "Yetkisiz kullanıcı.",
"NOT_VALID": "Gecersiz veri.",
"NOT_ACCEPTABLE": "Gecersiz veri.",
"INVALID_DATA": "Gecersiz veri.",
"UNKNOWN_ERROR": "Bilinmeyen bir hata oluştu.",
},
"en": {
"NOT_CREATED": "Not Created.",
"NOT_DELETED": "Not Deleted.",
"NOT_UPDATED": "Not Updated.",
"NOT_LISTED": "Not Listed.",
"NOT_FOUND": "Not Found.",
"ALREADY_EXISTS": "Already Exists.",
"IS_NOT_CONFIRMED": "Not Confirmed.",
"NOT_AUTHORIZED": "Not Authorized.",
"NOT_VALID": "Not Valid.",
"NOT_ACCEPTABLE": "Not Acceptable.",
"INVALID_DATA": "Invalid Data.",
"UNKNOWN_ERROR": "Unknown Error occured.",
},
}
from fastapi import status
EXCEPTION_DICTS = {
"100": "HTTP_100_CONTINUE",
"101": "HTTP_101_SWITCHING_PROTOCOLS",
"102": "HTTP_102_PROCESSING",
"103": "HTTP_103_EARLY_HINTS",
"200": "HTTP_200_OK",
"201": "HTTP_201_CREATED",
"202": "HTTP_202_ACCEPTED",
"203": "HTTP_203_NON_AUTHORITATIVE_INFORMATION",
"204": "HTTP_204_NO_CONTENT",
"205": "HTTP_205_RESET_CONTENT",
"206": "HTTP_206_PARTIAL_CONTENT",
"207": "HTTP_207_MULTI_STATUS",
"208": "HTTP_208_ALREADY_REPORTED",
"226": "HTTP_226_IM_USED",
"300": "HTTP_300_MULTIPLE_CHOICES",
"301": "HTTP_301_MOVED_PERMANENTLY",
"302": "HTTP_302_FOUND",
"303": "HTTP_303_SEE_OTHER",
"304": "HTTP_304_NOT_MODIFIED",
"305": "HTTP_305_USE_PROXY",
"306": "HTTP_306_RESERVED",
"307": "HTTP_307_TEMPORARY_REDIRECT",
"308": "HTTP_308_PERMANENT_REDIRECT",
"400": "HTTP_400_BAD_REQUEST",
"401": "HTTP_401_UNAUTHORIZED",
"402": "HTTP_402_PAYMENT_REQUIRED",
"403": "HTTP_403_FORBIDDEN",
"404": "HTTP_404_NOT_FOUND",
"405": "HTTP_405_METHOD_NOT_ALLOWED",
"406": "HTTP_406_NOT_ACCEPTABLE",
"407": "HTTP_407_PROXY_AUTHENTICATION_REQUIRED",
"408": "HTTP_408_REQUEST_TIMEOUT",
"409": "HTTP_409_CONFLICT",
"410": "HTTP_410_GONE",
"411": "HTTP_411_LENGTH_REQUIRED",
"412": "HTTP_412_PRECONDITION_FAILED",
"413": "HTTP_413_REQUEST_ENTITY_TOO_LARGE",
"414": "HTTP_414_REQUEST_URI_TOO_LONG",
"415": "HTTP_415_UNSUPPORTED_MEDIA_TYPE",
"416": "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE",
"417": "HTTP_417_EXPECTATION_FAILED",
"418": "HTTP_418_IM_A_TEAPOT",
"421": "HTTP_421_MISDIRECTED_REQUEST",
"422": "HTTP_422_UNPROCESSABLE_ENTITY",
"423": "HTTP_423_LOCKED",
"424": "HTTP_424_FAILED_DEPENDENCY",
"426": "HTTP_426_UPGRADE_REQUIRED",
"428": "HTTP_428_PRECONDITION_REQUIRED",
"429": "HTTP_429_TOO_MANY_REQUESTS",
"431": "HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE",
"451": "HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS",
"500": "HTTP_500_INTERNAL_SERVER_ERROR",
"502": "HTTP_502_BAD_GATEWAY",
}

View File

@ -1,49 +0,0 @@
from json import loads
class ErrorMessages:
__messages__ = {}
@classmethod
def get_message(cls, message_key, lang):
return cls.__messages__[lang][message_key]
class ErrorHandlers:
def __init__(self, requests, exceptions, response_model, status):
self.requests = requests # from fastapi.requests import Request
self.exceptions = exceptions # from fastapi.exceptions import HTTPException
self.response_model = (
response_model # from fastapi.responses import JSONResponse
)
self.status = status # from fastapi import status
@classmethod
def create(cls, requests, exceptions, response_model, status):
return cls(requests, exceptions, response_model, status)
def exception_handler_http(self, request, exc):
exc_detail = getattr(exc, "detail", None)
try:
detail = loads(str(exc_detail))
return self.response_model(
status_code=exc.status_code,
content={
"Data": detail.get("data", {}),
"Error": detail.get("error_case", "UNKNOWN"),
"Message": detail.get(
"message", "An error occurred while processing the request"
),
},
)
except Exception as e:
return self.response_model(
status_code=exc.status_code,
content={"Error": str(exc_detail), "Message": f"{str(e)}", "Data": {}},
)
def exception_handler_exception(self, request, exc):
return self.response_model(
status_code=self.status.HTTP_417_EXPECTATION_FAILED,
content={"message": exc.__str__()},
)

View File

@ -1,21 +1,22 @@
import hashlib
import uuid
import requests
import secrets
from datetime import timedelta
from fastapi.exceptions import HTTPException
from api_services.redis.functions import RedisActions
from databases.no_sql_models.validations import (
PasswordHistoryViaUser,
AccessHistoryViaUser,
)
from api_library.date_time_actions.date_functions import system_arrow, client_arrow
from api_library.date_time_actions.date_functions import system_arrow
from api_configs import ApiStatic, Auth
class PasswordModule:
@staticmethod
def generate_token(length=32):
return uuid.uuid4().__str__()[:length]
return secrets.token_urlsafe(length)
@staticmethod
def create_hashed_password(domain: str, id_: str, password: str):
@ -25,6 +26,10 @@ class PasswordModule:
def check_password(cls, domain, id_, password, password_hashed):
return cls.create_hashed_password(domain, id_, password) == password_hashed
@classmethod
def generate_access_token(cls):
return secrets.token_urlsafe(Auth.ACCESS_TOKEN_LENGTH)
class AuthModule(PasswordModule):
@ -33,6 +38,7 @@ class AuthModule(PasswordModule):
from databases import Users
from sqlalchemy import or_
from fastapi import status
from fastapi.exceptions import HTTPException
found_user: Users = Users.query.filter(
or_(
@ -56,9 +62,6 @@ class AuthModule(PasswordModule):
)
return found_user
def generate_access_token(self):
return self.generate_token(Auth.ACCESS_TOKEN_LENGTH)
def remove_refresher_token(self, domain, disconnect: bool = False):
from databases import UsersTokens
@ -76,6 +79,8 @@ class AuthModule(PasswordModule):
UsersTokens.save()
def check_password_is_different(self, password):
from fastapi.exceptions import HTTPException
main_domain = self.get_main_domain_and_other_domains(get_main_domain=True)
if self.hash_password == self.create_hashed_password(
domain=main_domain, id_=self.uu_id, password=password
@ -88,9 +93,9 @@ class AuthModule(PasswordModule):
@staticmethod
def create_password(found_user, password, password_token=None):
from databases import MongoQueryIdentity, Users
from fastapi.exceptions import HTTPException
found_user: Users = found_user
if found_user.password_token:
replace_day = 0
try:
@ -145,9 +150,7 @@ class AuthModule(PasswordModule):
return found_user.password_token
def generate_refresher_token(self, domain: str, remember_me=False):
from databases import (
UsersTokens,
)
from databases import UsersTokens
if remember_me:
refresh_token = self.generate_token(Auth.REFRESHER_TOKEN_LENGTH)
@ -183,6 +186,7 @@ class AuthModule(PasswordModule):
class UserLoginModule(AuthModule):
@classmethod
def set_login_details_to_mongo_database(
cls, found_user, headers_request, access_token, record_id
@ -212,8 +216,8 @@ class UserLoginModule(AuthModule):
"address": address_package,
"user_id": found_user.id,
}
already_exits = mongo_db.mongo_engine.filter_by(filter_query) or None
no_address_validates = mongo_db.mongo_engine.get_all()[0] == 0
already_exits = mongo_db.mongo_engine.filter_by(filter_query).data
no_address_validates = mongo_db.mongo_engine.get_all().data
access_via_user = query_engine.update_access_history_via_user(
AccessHistoryViaUser(
**{
@ -241,7 +245,6 @@ class UserLoginModule(AuthModule):
}
},
)
print("update_mongo", update_mongo)
else:
insert_mongo = mongo_db.mongo_engine.insert(
payload={
@ -257,14 +260,14 @@ class UserLoginModule(AuthModule):
"is_first": True if no_address_validates else False,
}
)
print("insert_mongo", insert_mongo)
@classmethod
def login_user_with_credentials(cls, data, request):
from databases.sql_models.identity.identity import Users
from ApiServices.api_handlers.auth_actions.auth import AuthActions
from fastapi.exceptions import HTTPException
found_user = Users.check_user_exits(
found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain
)
if not found_user:
@ -280,19 +283,20 @@ class UserLoginModule(AuthModule):
)
if found_user.check_password(
domain=data.domain,
id_=found_user.uu_id,
password_hashed=found_user.hash_password,
password=data.password,
):
domain=data.domain,
id_=found_user.uu_id,
password_hashed=found_user.hash_password,
password=data.password,
):
access_object_to_redis = AuthActions.save_access_token_to_redis(
request=request,
found_user=found_user,
domain=data.domain,
# remember_me=data.remember_me,
)
print("access_object_to_redis", access_object_to_redis)
access_token = access_object_to_redis.get("access_token")
access_object_to_redis["user"] = found_user
headers_request = dict(request.headers)
headers_request["evyos-user-agent"] = headers_request.get("user-agent")
headers_request["evyos-platform"] = headers_request.get("user-agent")

View File

@ -17,7 +17,7 @@ from sqlalchemy import (
Numeric,
Integer,
)
from sqlalchemy.orm import mapped_column, Mapped, relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from api_validations.validations_request import (
InsertDecisionBook,
@ -26,6 +26,19 @@ from api_validations.validations_request import (
InsertBuildDecisionBookProjects,
)
from databases.sql_models.core_mixin import CrudCollection
from databases.language_models.building.decision_book import (
BuildDecisionBookLanguageModel,
BuildDecisionBookInvitationsLanguageModel,
BuildDecisionBookPersonLanguageModel,
BuildDecisionBookPersonOccupantsLanguageModel,
BuildDecisionBookItemsLanguageModel,
BuildDecisionBookItemsUnapprovedLanguageModel,
BuildDecisionBookPaymentsLanguageModel,
BuildDecisionBookLegalLanguageModel,
BuildDecisionBookProjectsLanguageModel,
BuildDecisionBookProjectPersonLanguageModel,
BuildDecisionBookProjectItemsLanguageModel,
)
class BuildDecisionBook(CrudCollection):
@ -44,6 +57,7 @@ class BuildDecisionBook(CrudCollection):
__tablename__ = "build_decision_book"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookLanguageModel
decision_book_pdf_path: Mapped[str] = mapped_column(
String, server_default="", nullable=True
@ -242,6 +256,7 @@ class BuildDecisionBookInvitations(CrudCollection):
__tablename__ = "build_decision_book_invitations"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookInvitationsLanguageModel
build_id: Mapped[int] = mapped_column(Integer, nullable=False)
build_uu_id: Mapped[str] = mapped_column(
@ -341,6 +356,7 @@ class BuildDecisionBookPerson(CrudCollection):
__tablename__ = "build_decision_book_person"
__exclude__fields__ = []
__enum_list__ = [("management_typecode", "BuildManagementType", "bm")]
__language_model__ = BuildDecisionBookPersonLanguageModel
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
dues_fix_discount: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
@ -517,6 +533,7 @@ class BuildDecisionBookPersonOccupants(CrudCollection):
__tablename__ = "build_decision_book_person_occupants"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookPersonOccupantsLanguageModel
build_decision_book_person_id: Mapped[int] = mapped_column(
ForeignKey("build_decision_book_person.id"), nullable=False
@ -559,6 +576,7 @@ class BuildDecisionBookItems(CrudCollection):
__tablename__ = "build_decision_book_items"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookItemsLanguageModel
item_order: Mapped[int] = mapped_column(
SmallInteger, nullable=False, comment="Order Number of Item"
@ -799,6 +817,7 @@ class BuildDecisionBookItemsUnapproved(CrudCollection):
__tablename__ = "build_decision_book_items_unapproved"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookItemsUnapprovedLanguageModel
item_objection: Mapped[str] = mapped_column(
Text, nullable=False, comment="Objection Content"
@ -841,6 +860,7 @@ class BuildDecisionBookPayments(CrudCollection):
__tablename__ = "build_decision_book_payments"
__exclude__fields__ = []
__enum_list__ = [("receive_debit", "DebitTypes", "D")]
__language_model__ = BuildDecisionBookPaymentsLanguageModel
payment_plan_time_periods: Mapped[str] = mapped_column(
String(10), nullable=False, comment="Payment Plan Time Periods"
@ -951,6 +971,7 @@ class BuildDecisionBookLegal(CrudCollection):
__tablename__ = "build_decision_book_legal"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookLegalLanguageModel
period_start_date: Mapped[TIMESTAMP] = mapped_column(
TIMESTAMP(timezone=True), nullable=False, comment="Start Date of Legal Period"
@ -1027,6 +1048,7 @@ class BuildDecisionBookProjects(CrudCollection):
__tablename__ = "build_decision_book_projects"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectsLanguageModel
project_no: Mapped[str] = mapped_column(
String(12), nullable=True, comment="Project Number of Decision Book"
@ -1194,6 +1216,7 @@ class BuildDecisionBookProjectPerson(CrudCollection):
__tablename__ = "build_decision_book_project_person"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectPersonLanguageModel
# __enum_list__ = [("management_typecode", "ProjectTeamTypes", "PTT-EMP")]
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
@ -1226,6 +1249,7 @@ class BuildDecisionBookProjectItems(CrudCollection):
__tablename__ = "build_decision_book_project_items"
__exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectItemsLanguageModel
item_header: Mapped[str] = mapped_column(
String, nullable=False, comment="Item Header"

View File

@ -5,6 +5,13 @@ from sqlalchemy import (
Numeric,
)
from sqlalchemy.orm import mapped_column, Mapped
from databases.language_models.company.employee import (
StaffLanguageModel,
EmployeesLanguageModel,
EmployeeHistoryLanguageModel,
EmployeesSalariesLanguageModel,
)
from databases.sql_models.core_mixin import CrudCollection
from api_validations.validations_request import InsertCompanyEmployees
@ -14,6 +21,7 @@ class Staff(CrudCollection):
__tablename__ = "staff"
__exclude__fields__ = []
__language_model__ = StaffLanguageModel
staff_description: Mapped[str] = mapped_column(
String, server_default="", comment="Staff Description"
@ -61,6 +69,7 @@ class Employees(CrudCollection):
__tablename__ = "employees"
__exclude__fields__ = []
__language_model__ = EmployeesLanguageModel
staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id"))
staff_uu_id: Mapped[str] = mapped_column(
@ -81,6 +90,7 @@ class EmployeeHistory(CrudCollection):
__tablename__ = "employee_history"
__exclude__fields__ = []
__language_model__ = EmployeeHistoryLanguageModel
staff_id: Mapped[int] = mapped_column(
ForeignKey("staff.id"), nullable=False, comment="Staff ID"
@ -105,6 +115,7 @@ class EmployeesSalaries(CrudCollection):
__tablename__ = "employee_salaries"
__exclude__fields__ = []
__language_model__ = EmployeesSalariesLanguageModel
gross_salary: Mapped[float] = mapped_column(
Numeric(20, 6), nullable=False, comment="Gross Salary"

View File

@ -21,7 +21,7 @@ from sqlalchemy_mixins.serialize import SerializeMixin
from sqlalchemy_mixins.repr import ReprMixin
from sqlalchemy_mixins.smartquery import SmartQueryMixin
from api_library import DateTimeLocal, client_arrow, system_arrow
from api_library import DateTimeLocal, system_arrow
from databases.sql_models.sql_operations import FilterAttributes
from databases.sql_models.postgres_database import Base
@ -138,14 +138,14 @@ class CrudMixin(Base, SmartQueryMixin, SessionMixin, FilterAttributes):
return True, int(val)
elif key_ == Mapped[TIMESTAMP]:
return True, str(
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss")
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss +0")
)
elif key_ == Mapped[str]:
return True, str(val)
else:
if isinstance(val, datetime.datetime):
return True, str(
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss")
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss +0")
)
elif isinstance(value_type, bool):
return True, bool(val)

View File

@ -122,6 +122,9 @@ class Users(CrudCollection, UserLoginModule, SelectAction):
person_uu_id: Mapped[str] = mapped_column(
String, server_default="", comment="Person UUID", index=True
)
local_timezone = mapped_column(
String, server_default="GMT+3", comment="Local timezone of user"
)
person = relationship("People", back_populates="user", foreign_keys=[person_id])
@property
@ -183,11 +186,6 @@ class Users(CrudCollection, UserLoginModule, SelectAction):
@classmethod
def credentials(cls):
person_object = People.filter_by_one(system=True, id=cls.person_id).data
# if not person_object:
# raise HTTPException(
# status_code=401,
# detail="Person not found. Please contact the admin.",
# )
if person_object:
return {
"person_id": person_object.id,

View File

@ -15,6 +15,7 @@ from databases.sql_models.core_mixin import CrudCollection
class ApiEnumDropdown(CrudCollection):
__tablename__ = "api_enum_dropdown"
__exclude__fields__ = ["enum_class"]
__language_model__ = None
id: Mapped[int] = mapped_column(primary_key=True)
uu_id: Mapped[str] = mapped_column(

View File

@ -1,7 +1,9 @@
from sqlalchemy import String, Boolean
from databases.sql_models.core_mixin import CrudCollection
from sqlalchemy import String
from sqlalchemy.orm import mapped_column, Mapped
from databases.language_models.rules.rules import EndpointRestrictionLanguageModel
from databases.sql_models.core_mixin import CrudCollection
class EndpointRestriction(CrudCollection):
"""
@ -10,6 +12,7 @@ class EndpointRestriction(CrudCollection):
__tablename__ = "endpoint_restriction"
__exclude__fields__ = []
__language_model__ = EndpointRestrictionLanguageModel
endpoint_function: Mapped[str] = mapped_column(
String, server_default="", comment="Function name of the API endpoint"

View File

@ -50,18 +50,9 @@ class FilterAttributes:
"""Save the data via the metadata."""
try:
meta_data = getattr(cls, "meta_data", {})
meta_data_created = meta_data.get("created", False)
if meta_data_created:
print("meta_data_created commit", meta_data_created)
if meta_data.get("created", False):
cls.__session__.commit()
print("meta_data_created rollback", meta_data_created)
cls.__session__.rollback()
# cls.raise_http_exception(
# status_code="HTTP_304_NOT_MODIFIED",
# error_case=meta_data.get("error_case", "Error on save and commit"),
# data={},
# message=meta_data.get("message", "Error on save and commit"),
# )
except SQLAlchemyError as e:
cls.raise_http_exception(
status_code="HTTP_304_NOT_MODIFIED",
@ -233,8 +224,9 @@ class FilterAttributes:
for smart_iter in cls.filter_expr(**filter_list.get("query", {})):
if key := arg_left(smart_iter):
args = cls.add_new_arg_to_args(args, key, smart_iter)
query = cls._query().filter(*args)
query = cls._query()
cls.total_count = query.count()
query = query.filter(*args)
if cls.filter_attr:
data_query = cls.add_query_to_filter(query, filter_list)
cls.filter_attr = None

View File

@ -157,6 +157,6 @@ def create_application_defaults_func(create_address=False):
if __name__ == "__main__":
print("Service App Initial Default Runner is running")
# do_alembic()
create_application_defaults_func(create_address=True)
do_alembic()
# create_application_defaults_func(create_address=True)
print("Service App Initial Default Runner is completed")