validations updated
This commit is contained in:
parent
d6785ed36f
commit
8e34497c80
|
|
@ -11,7 +11,7 @@ from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
from ApiLibrary.date_time_actions.date_functions import DateTimeLocal
|
from ApiLibrary.date_time_actions.date_functions import DateTimeLocal
|
||||||
from ApiServices.Login.user_login_handler import UserLoginModule
|
from ApiServices.Login.user_login_handler import UserLoginModule
|
||||||
from ApiServices.Token.token_handler import TokenService
|
from ApiServices.Token.token_handler import TokenService
|
||||||
from ApiValidations.Custom.token_objects import CompanyToken
|
from ApiValidations.Custom.token_objects import CompanyToken, OccupantToken
|
||||||
from ApiValidations.Request.authentication import (
|
from ApiValidations.Request.authentication import (
|
||||||
Login,
|
Login,
|
||||||
EmployeeSelectionValidation,
|
EmployeeSelectionValidation,
|
||||||
|
|
@ -20,11 +20,16 @@ from ApiValidations.Request.authentication import (
|
||||||
EmployeeSelection,
|
EmployeeSelection,
|
||||||
)
|
)
|
||||||
from ErrorHandlers import HTTPExceptionApi
|
from ErrorHandlers import HTTPExceptionApi
|
||||||
|
from Schemas.building.build import (
|
||||||
|
BuildLivingSpace,
|
||||||
|
BuildParts,
|
||||||
|
RelationshipEmployee2Build,
|
||||||
|
)
|
||||||
from Schemas.company.company import Companies
|
from Schemas.company.company import Companies
|
||||||
from Schemas.company.department import Departments, Duties, Duty
|
from Schemas.company.department import Departments, Duties, Duty
|
||||||
from Schemas.company.employee import Staff, Employees
|
from Schemas.company.employee import Staff, Employees
|
||||||
from Schemas.event.event import Event2Employee
|
from Schemas.event.event import Event2Employee, Event2Occupant
|
||||||
from Schemas.identity.identity import Users
|
from Schemas.identity.identity import OccupantTypes, Users
|
||||||
from Services.Redis.Actions.actions import RedisActions
|
from Services.Redis.Actions.actions import RedisActions
|
||||||
from .models import (
|
from .models import (
|
||||||
LoginData,
|
LoginData,
|
||||||
|
|
@ -170,9 +175,7 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get reachable events
|
# Get reachable events
|
||||||
reachable_event_list_id = Event2Employee.get_event_id_by_employee_id(
|
reachable_event_codes = Event2Employee.get_event_codes(employee_id=employee.id)
|
||||||
employee_id=employee.id
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get staff and duties
|
# Get staff and duties
|
||||||
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db_session).data
|
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db_session).data
|
||||||
|
|
@ -189,7 +192,6 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||||
**Duties.valid_record_dict,
|
**Duties.valid_record_dict,
|
||||||
db=db_session,
|
db=db_session,
|
||||||
).data
|
).data
|
||||||
|
|
||||||
# Create company token
|
# Create company token
|
||||||
company_token = CompanyToken(
|
company_token = CompanyToken(
|
||||||
company_uu_id=selected_company.uu_id.__str__(),
|
company_uu_id=selected_company.uu_id.__str__(),
|
||||||
|
|
@ -203,7 +205,7 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||||
staff_uu_id=staff.uu_id.__str__(),
|
staff_uu_id=staff.uu_id.__str__(),
|
||||||
employee_id=employee.id,
|
employee_id=employee.id,
|
||||||
employee_uu_id=employee.uu_id.__str__(),
|
employee_uu_id=employee.uu_id.__str__(),
|
||||||
reachable_event_list_id=reachable_event_list_id,
|
reachable_event_codes=reachable_event_codes,
|
||||||
)
|
)
|
||||||
try: # Update Redis
|
try: # Update Redis
|
||||||
update_token = TokenService.update_token_at_redis(
|
update_token = TokenService.update_token_at_redis(
|
||||||
|
|
@ -226,12 +228,81 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||||
request: "Request",
|
request: "Request",
|
||||||
):
|
):
|
||||||
"""Handle occupant type selection"""
|
"""Handle occupant type selection"""
|
||||||
raise HTTPExceptionApi(
|
db = BuildLivingSpace.new_session()
|
||||||
error_code="HTTP_400_BAD_REQUEST",
|
# Get selected occupant type
|
||||||
lang=token_dict.lang,
|
selected_build_living_space = BuildLivingSpace.filter_one(
|
||||||
loc=get_line_number_for_error(),
|
BuildLivingSpace.uu_id == data.build_living_space_uu_id,
|
||||||
sys_msg="Occupant selection not implemented",
|
db=db,
|
||||||
|
).data
|
||||||
|
if not selected_build_living_space:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="HTTP_400_BAD_REQUEST",
|
||||||
|
lang=token_dict.lang,
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Selected occupant type not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get reachable events
|
||||||
|
reachable_event_codes = Event2Occupant.get_event_codes(
|
||||||
|
build_living_space_id=selected_build_living_space.id
|
||||||
)
|
)
|
||||||
|
occupant_type = OccupantTypes.filter_one(
|
||||||
|
OccupantTypes.id == selected_build_living_space.occupant_type_id,
|
||||||
|
db=db,
|
||||||
|
system=True,
|
||||||
|
).data
|
||||||
|
build_part = BuildParts.filter_one(
|
||||||
|
BuildParts.id == selected_build_living_space.build_parts_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
build = BuildParts.filter_one(
|
||||||
|
BuildParts.id == build_part.build_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
responsible_employee = Employees.filter_one(
|
||||||
|
Employees.id == build_part.responsible_employee_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
related_company = RelationshipEmployee2Build.filter_one(
|
||||||
|
RelationshipEmployee2Build.member_id == build.id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
# Get company
|
||||||
|
company_related = Companies.filter_one(
|
||||||
|
Companies.id == related_company.company_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
|
||||||
|
# Create occupant token
|
||||||
|
occupant_token = OccupantToken(
|
||||||
|
living_space_id=selected_build_living_space.id,
|
||||||
|
living_space_uu_id=selected_build_living_space.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_codes=reachable_event_codes,
|
||||||
|
)
|
||||||
|
|
||||||
|
try: # Update Redis
|
||||||
|
update_token = TokenService.update_token_at_redis(
|
||||||
|
request=request, add_payload=occupant_token
|
||||||
|
)
|
||||||
|
return update_token
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg=f"{e}",
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def authentication_select_company_or_occupant_type(
|
async def authentication_select_company_or_occupant_type(
|
||||||
|
|
@ -246,14 +317,6 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||||
elif token_dict.is_occupant:
|
elif token_dict.is_occupant:
|
||||||
return cls._handle_occupant_selection(data, token_dict, request)
|
return cls._handle_occupant_selection(data, token_dict, request)
|
||||||
|
|
||||||
# except Exception as e:
|
|
||||||
# raise HTTPExceptionApi(
|
|
||||||
# error_code="HTTP_500_INTERNAL_SERVER_ERROR",
|
|
||||||
# lang="en",
|
|
||||||
# loc=get_line_number_for_error(),
|
|
||||||
# sys_msg=str(e),
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationCheckTokenEventMethods(MethodToEvent):
|
class AuthenticationCheckTokenEventMethods(MethodToEvent):
|
||||||
event_type = "LOGIN"
|
event_type = "LOGIN"
|
||||||
|
|
@ -3,8 +3,8 @@ Authentication endpoint configurations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Dict, Any, Union, Annotated
|
from typing import TYPE_CHECKING, Dict, Any, Union, Annotated
|
||||||
from fastapi import HTTPException, status, Body
|
|
||||||
|
|
||||||
|
from ApiServices.Token.token_handler import TokenService
|
||||||
from ApiValidations.Request import (
|
from ApiValidations.Request import (
|
||||||
Logout,
|
Logout,
|
||||||
Login,
|
Login,
|
||||||
|
|
@ -49,7 +49,7 @@ from ApiEvents.abstract_class import (
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import Request
|
from fastapi import Request, HTTPException, status, Body
|
||||||
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,22 +59,18 @@ from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTok
|
||||||
@endpoint_wrapper("/authentication/select")
|
@endpoint_wrapper("/authentication/select")
|
||||||
async def authentication_select_company_or_occupant_type(
|
async def authentication_select_company_or_occupant_type(
|
||||||
request: "Request",
|
request: "Request",
|
||||||
data: EndpointBaseRequestModel,
|
data: Union[EmployeeSelection, OccupantSelection],
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Select company or occupant type.
|
Select company or occupant type.
|
||||||
"""
|
"""
|
||||||
auth_dict = authentication_select_company_or_occupant_type.auth
|
auth_dict = authentication_select_company_or_occupant_type.auth
|
||||||
if data.data.get("company_uu_id"):
|
|
||||||
data = EmployeeSelection(**data.data)
|
|
||||||
elif data.data.get("build_living_space_uu_id"):
|
|
||||||
data = OccupantSelection(**data.data)
|
|
||||||
if await AuthenticationSelectEventMethods.authentication_select_company_or_occupant_type(
|
if await AuthenticationSelectEventMethods.authentication_select_company_or_occupant_type(
|
||||||
request=request, data=data, token_dict=auth_dict
|
request=request, data=data, token_dict=auth_dict
|
||||||
):
|
):
|
||||||
if isinstance(data, EmployeeSelection):
|
if data.is_employee:
|
||||||
return {"selected_company": data.company_uu_id}
|
return {"selected_company": data.company_uu_id}
|
||||||
elif isinstance(data, OccupantSelection):
|
elif data.is_occupant:
|
||||||
return {"selected_occupant": data.build_living_space_uu_id}
|
return {"selected_occupant": data.build_living_space_uu_id}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -99,10 +95,18 @@ async def authentication_check_token_is_valid(
|
||||||
"""
|
"""
|
||||||
Check if a token is valid.
|
Check if a token is valid.
|
||||||
"""
|
"""
|
||||||
return {
|
try:
|
||||||
"headers": dict(request.headers),
|
access_token = TokenService.get_access_token_from_request(request=request)
|
||||||
"data": data.model_dump(),
|
if TokenService.get_object_via_access_key(access_token=access_token):
|
||||||
}
|
return {
|
||||||
|
"status": True,
|
||||||
|
"message": "Access Token is valid",
|
||||||
|
}
|
||||||
|
except HTTPException:
|
||||||
|
return {
|
||||||
|
"status": False,
|
||||||
|
"message": "Access Token is NOT valid",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@endpoint_wrapper("/authentication/refresh")
|
@endpoint_wrapper("/authentication/refresh")
|
||||||
|
|
@ -6,15 +6,13 @@ to be used by the dynamic route creation system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, List, Any
|
||||||
from .auth.endpoints import AUTH_CONFIG
|
from .events.auth.endpoints import AUTH_CONFIG
|
||||||
|
|
||||||
|
|
||||||
# Registry of all route configurations
|
# Registry of all route configurations
|
||||||
ROUTE_CONFIGS = [
|
ROUTE_CONFIGS = [AUTH_CONFIG]
|
||||||
AUTH_CONFIG,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_route_configs() -> List[Dict[str, Any]]:
|
def get_route_configs() -> List[Dict[str, Any]]:
|
||||||
"""Get all registered route configurations."""
|
"""Get all registered route configurations."""
|
||||||
return [AUTH_CONFIG]
|
return ROUTE_CONFIGS
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Account records service implementation.
|
Account records service implementation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, Any, Union
|
from typing import Union
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from ApiEvents.abstract_class import MethodToEvent, endpoint_wrapper
|
from ApiEvents.abstract_class import MethodToEvent, endpoint_wrapper
|
||||||
|
|
@ -26,7 +26,6 @@ from Schemas import (
|
||||||
)
|
)
|
||||||
from Services.PostgresDb.Models.alchemy_response import (
|
from Services.PostgresDb.Models.alchemy_response import (
|
||||||
AlchemyJsonResponse,
|
AlchemyJsonResponse,
|
||||||
DictJsonResponse,
|
|
||||||
)
|
)
|
||||||
from ApiValidations.Response import AccountRecordResponse
|
from ApiValidations.Response import AccountRecordResponse
|
||||||
from .models import (
|
from .models import (
|
||||||
|
|
@ -36,7 +35,7 @@ from .models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccountRecordsListEventMethods(MethodToEvent):
|
class AccountListEventMethod(MethodToEvent):
|
||||||
|
|
||||||
event_type = "SELECT"
|
event_type = "SELECT"
|
||||||
event_description = ""
|
event_description = ""
|
||||||
|
|
@ -47,8 +46,8 @@ class AccountRecordsListEventMethods(MethodToEvent):
|
||||||
"208e6273-17ef-44f0-814a-8098f816b63a": "account_records_list_flt_res",
|
"208e6273-17ef-44f0-814a-8098f816b63a": "account_records_list_flt_res",
|
||||||
}
|
}
|
||||||
__event_validation__ = {
|
__event_validation__ = {
|
||||||
"7192c2aa-5352-4e36-98b3-dafb7d036a3d": AccountRecordResponse,
|
"7192c2aa-5352-4e36-98b3-dafb7d036a3d": (AccountRecordResponse, [AccountRecords.__language_model__]),
|
||||||
"208e6273-17ef-44f0-814a-8098f816b63a": AccountRecordResponse,
|
"208e6273-17ef-44f0-814a-8098f816b63a": (AccountRecordResponse, [AccountRecords.__language_model__]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -217,7 +216,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccountRecordsCreateEventMethods(MethodToEvent):
|
class AccountCreateEventMethod(MethodToEvent):
|
||||||
|
|
||||||
event_type = "CREATE"
|
event_type = "CREATE"
|
||||||
event_description = ""
|
event_description = ""
|
||||||
|
|
@ -227,7 +226,7 @@ class AccountRecordsCreateEventMethods(MethodToEvent):
|
||||||
"31f4f32f-0cd4-4995-8a6a-f9f56335848a": "account_records_create",
|
"31f4f32f-0cd4-4995-8a6a-f9f56335848a": "account_records_create",
|
||||||
}
|
}
|
||||||
__event_validation__ = {
|
__event_validation__ = {
|
||||||
"31f4f32f-0cd4-4995-8a6a-f9f56335848a": InsertAccountRecord,
|
"31f4f32f-0cd4-4995-8a6a-f9f56335848a": (InsertAccountRecord, [AccountRecords.__language_model__]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -305,7 +304,7 @@ class AccountRecordsCreateEventMethods(MethodToEvent):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccountRecordsUpdateEventMethods(MethodToEvent):
|
class AccountUpdateEventMethod(MethodToEvent):
|
||||||
|
|
||||||
event_type = "UPDATE"
|
event_type = "UPDATE"
|
||||||
event_description = ""
|
event_description = ""
|
||||||
|
|
@ -315,11 +314,11 @@ class AccountRecordsUpdateEventMethods(MethodToEvent):
|
||||||
"ec98ef2c-bcd0-432d-a8f4-1822a56c33b2": "account_records_update",
|
"ec98ef2c-bcd0-432d-a8f4-1822a56c33b2": "account_records_update",
|
||||||
}
|
}
|
||||||
__event_validation__ = {
|
__event_validation__ = {
|
||||||
"ec98ef2c-bcd0-432d-a8f4-1822a56c33b2": UpdateAccountRecord,
|
"ec98ef2c-bcd0-432d-a8f4-1822a56c33b2": (UpdateAccountRecord, [AccountRecords.__language_model__]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build_area_update(
|
def account_records_update(
|
||||||
cls,
|
cls,
|
||||||
build_uu_id: str,
|
build_uu_id: str,
|
||||||
data: UpdateAccountRecordRequestModel,
|
data: UpdateAccountRecordRequestModel,
|
||||||
|
|
@ -14,19 +14,15 @@ from Services.PostgresDb.Models.alchemy_response import DictJsonResponse
|
||||||
from fastapi import Request, Path, Body
|
from fastapi import Request, Path, Body
|
||||||
|
|
||||||
|
|
||||||
@endpoint_wrapper("/account/records/address/list")
|
@endpoint_wrapper("/account/records/list")
|
||||||
async def address_list(request: "Request", data: EndpointBaseRequestModel):
|
async def address_list(request: "Request", data: EndpointBaseRequestModel):
|
||||||
"""Handle address list endpoint."""
|
"""Handle address list endpoint."""
|
||||||
auth_dict = address_list.auth
|
auth_dict = address_list.auth
|
||||||
return {
|
code_dict = getattr(address_list, "func_code", {"function_code": None})
|
||||||
"data": data,
|
return {"auth_dict": auth_dict, "code_dict": code_dict, "data": data}
|
||||||
"request": str(request.headers),
|
|
||||||
"request_url": str(request.url),
|
|
||||||
"request_base_url": str(request.base_url),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@endpoint_wrapper("/account/records/address/create")
|
@endpoint_wrapper("/account/records/create")
|
||||||
async def address_create(request: "Request", data: EndpointBaseRequestModel):
|
async def address_create(request: "Request", data: EndpointBaseRequestModel):
|
||||||
"""Handle address creation endpoint."""
|
"""Handle address creation endpoint."""
|
||||||
return {
|
return {
|
||||||
|
|
@ -37,7 +33,7 @@ async def address_create(request: "Request", data: EndpointBaseRequestModel):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@endpoint_wrapper("/account/records/address/search")
|
@endpoint_wrapper("/account/records/search")
|
||||||
async def address_search(request: "Request", data: EndpointBaseRequestModel):
|
async def address_search(request: "Request", data: EndpointBaseRequestModel):
|
||||||
"""Handle address search endpoint."""
|
"""Handle address search endpoint."""
|
||||||
auth_dict = address_search.auth
|
auth_dict = address_search.auth
|
||||||
|
|
@ -45,7 +41,7 @@ async def address_search(request: "Request", data: EndpointBaseRequestModel):
|
||||||
return {"auth_dict": auth_dict, "code_dict": code_dict, "data": data}
|
return {"auth_dict": auth_dict, "code_dict": code_dict, "data": data}
|
||||||
|
|
||||||
|
|
||||||
@endpoint_wrapper("/account/records/address/{address_uu_id}")
|
@endpoint_wrapper("/account/records/{address_uu_id}")
|
||||||
async def address_update(
|
async def address_update(
|
||||||
request: Request,
|
request: Request,
|
||||||
address_uu_id: str = Path(..., description="UUID of the address to update"),
|
address_uu_id: str = Path(..., description="UUID of the address to update"),
|
||||||
|
|
@ -0,0 +1,336 @@
|
||||||
|
"""
|
||||||
|
request models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any, List, Optional, TypedDict, Union
|
||||||
|
from pydantic import BaseModel, Field, model_validator, RootModel, ConfigDict
|
||||||
|
from ApiEvents.abstract_class import MethodToEvent
|
||||||
|
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||||
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
from ApiValidations.Request.address import SearchAddress, UpdateAddress
|
||||||
|
from ApiValidations.Request.base_validations import ListOptions
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from Schemas.identity.identity import (
|
||||||
|
AddressPostcode,
|
||||||
|
AddressStreet,
|
||||||
|
Addresses,
|
||||||
|
RelationshipEmployee2PostCode,
|
||||||
|
)
|
||||||
|
from ApiValidations.Request import (
|
||||||
|
InsertAddress,
|
||||||
|
)
|
||||||
|
from ApiValidations.Response import (
|
||||||
|
ListAddressResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
|
||||||
|
class AddressListEventMethod(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "SELECT"
|
||||||
|
event_description = "List Address records"
|
||||||
|
event_category = "Address"
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"9c251d7d-da70-4d63-a72c-e69c26270442": "address_list_super_user",
|
||||||
|
"52afe375-dd95-4f4b-aaa2-4ec61bc6de52": "address_list_employee",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"9c251d7d-da70-4d63-a72c-e69c26270442": (ListAddressResponse, [Addresses.__language_model__]),
|
||||||
|
"52afe375-dd95-4f4b-aaa2-4ec61bc6de52": (ListAddressResponse, [Addresses.__language_model__]),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def address_list_super_user(
|
||||||
|
cls,
|
||||||
|
list_options: ListOptions,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
db = RelationshipEmployee2PostCode.new_session()
|
||||||
|
post_code_list = RelationshipEmployee2PostCode.filter_all(
|
||||||
|
RelationshipEmployee2PostCode.company_id
|
||||||
|
== token_dict.selected_company.company_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
post_code_id_list = [post_code.member_id for post_code in post_code_list]
|
||||||
|
if not post_code_id_list:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="User has no post code registered. User can not list addresses.",
|
||||||
|
)
|
||||||
|
get_street_ids = [
|
||||||
|
street_id[0]
|
||||||
|
for street_id in AddressPostcode.select_only(
|
||||||
|
AddressPostcode.id.in_(post_code_id_list),
|
||||||
|
select_args=[AddressPostcode.street_id],
|
||||||
|
order_by=AddressPostcode.street_id.desc(),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
]
|
||||||
|
if not get_street_ids:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="User has no street registered. User can not list addresses.",
|
||||||
|
)
|
||||||
|
Addresses.pre_query = Addresses.filter_all(
|
||||||
|
Addresses.street_id.in_(get_street_ids),
|
||||||
|
db=db,
|
||||||
|
).query
|
||||||
|
Addresses.filter_attr = list_options
|
||||||
|
records = Addresses.filter_all(db=db).data
|
||||||
|
return {}
|
||||||
|
# return AlchemyJsonResponse(
|
||||||
|
# completed=True, message="List Address records", result=records
|
||||||
|
# )
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def address_list_employee(
|
||||||
|
cls,
|
||||||
|
list_options: ListOptions,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
# Addresses.filter_attr = list_options
|
||||||
|
Addresses.pre_query = Addresses.filter_all(
|
||||||
|
Addresses.street_id.in_(get_street_ids),
|
||||||
|
)
|
||||||
|
records = Addresses.filter_all().data
|
||||||
|
return
|
||||||
|
# return AlchemyJsonResponse(
|
||||||
|
# completed=True, message="List Address records", result=records
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
class AddressCreateEventMethod(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "CREATE"
|
||||||
|
event_description = ""
|
||||||
|
event_category = ""
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"ffdc445f-da10-4ce4-9531-d2bdb9a198ae": "create_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"ffdc445f-da10-4ce4-9531-d2bdb9a198ae": (InsertAddress, [Addresses.__language_model__]),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_address(
|
||||||
|
cls,
|
||||||
|
data: InsertAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
post_code = AddressPostcode.filter_one(
|
||||||
|
AddressPostcode.uu_id == data.post_code_uu_id,
|
||||||
|
).data
|
||||||
|
if not post_code:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="Post code not found. User can not create address without post code.",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_dict = data.excluded_dump()
|
||||||
|
data_dict["street_id"] = post_code.street_id
|
||||||
|
data_dict["street_uu_id"] = str(post_code.street_uu_id)
|
||||||
|
del data_dict["post_code_uu_id"]
|
||||||
|
address = Addresses.find_or_create(**data_dict)
|
||||||
|
address.save()
|
||||||
|
address.update(is_confirmed=True)
|
||||||
|
address.save()
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True,
|
||||||
|
message="Address created successfully",
|
||||||
|
result=address.get_dict(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddressSearchEventMethod(MethodToEvent):
|
||||||
|
"""Event methods for searching addresses.
|
||||||
|
|
||||||
|
This class handles address search functionality including text search
|
||||||
|
and filtering.
|
||||||
|
"""
|
||||||
|
|
||||||
|
event_type = "SEARCH"
|
||||||
|
event_description = "Search for addresses using text and filters"
|
||||||
|
event_category = "Address"
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"e0ac1269-e9a7-4806-9962-219ac224b0d0": "search_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"e0ac1269-e9a7-4806-9962-219ac224b0d0": (SearchAddress, [Addresses.__language_model__]),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_order_clause(
|
||||||
|
cls, filter_list: Dict[str, Any], schemas: List[str], filter_table: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Build the ORDER BY clause for the query.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filter_list: Dictionary of filter options
|
||||||
|
schemas: List of available schema fields
|
||||||
|
filter_table: SQLAlchemy table to query
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SQLAlchemy order_by clause
|
||||||
|
"""
|
||||||
|
# Default to ordering by UUID if field not in schema
|
||||||
|
if filter_list.get("order_field") not in schemas:
|
||||||
|
filter_list["order_field"] = "uu_id"
|
||||||
|
else:
|
||||||
|
# Extract table and field from order field
|
||||||
|
table_name, field_name = str(filter_list.get("order_field")).split(".")
|
||||||
|
filter_table = getattr(databases.sql_models, table_name)
|
||||||
|
filter_list["order_field"] = field_name
|
||||||
|
|
||||||
|
# Build order clause
|
||||||
|
field = getattr(filter_table, filter_list.get("order_field"))
|
||||||
|
return (
|
||||||
|
field.desc()
|
||||||
|
if str(filter_list.get("order_type"))[0] == "d"
|
||||||
|
else field.asc()
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _format_record(cls, record: Any, schemas: List[str]) -> Dict[str, str]:
|
||||||
|
"""Format a database record into a dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
record: Database record to format
|
||||||
|
schemas: List of schema fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted record dictionary
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for index, schema in enumerate(schemas):
|
||||||
|
value = str(record[index])
|
||||||
|
# Special handling for UUID fields
|
||||||
|
if "uu_id" in value:
|
||||||
|
value = str(value)
|
||||||
|
result[schema] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def search_address(
|
||||||
|
cls,
|
||||||
|
data: SearchAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
) -> Any:
|
||||||
|
"""Search for addresses using text search and filters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Search parameters including text and filters
|
||||||
|
token_dict: Authentication token
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response with search results
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPExceptionApi: If search fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Start performance measurement
|
||||||
|
start_time = perf_counter()
|
||||||
|
|
||||||
|
# Get initial query
|
||||||
|
search_result = AddressStreet.search_address_text(search_text=data.search)
|
||||||
|
if not search_result:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="No addresses found matching search criteria",
|
||||||
|
)
|
||||||
|
|
||||||
|
query = search_result.get("query")
|
||||||
|
schemas = search_result.get("schema")
|
||||||
|
|
||||||
|
# Apply filters
|
||||||
|
filter_list = data.list_options.dump()
|
||||||
|
filter_table = AddressStreet
|
||||||
|
|
||||||
|
# Build and apply order clause
|
||||||
|
order = cls._build_order_clause(filter_list, schemas, filter_table)
|
||||||
|
|
||||||
|
# Apply pagination
|
||||||
|
page_size = int(filter_list.get("size"))
|
||||||
|
offset = (int(filter_list.get("page")) - 1) * page_size
|
||||||
|
|
||||||
|
# Execute query
|
||||||
|
query = (
|
||||||
|
query.order_by(order)
|
||||||
|
.limit(page_size)
|
||||||
|
.offset(offset)
|
||||||
|
.populate_existing()
|
||||||
|
)
|
||||||
|
records = list(query.all())
|
||||||
|
|
||||||
|
# Format results
|
||||||
|
results = [cls._format_record(record, schemas) for record in records]
|
||||||
|
|
||||||
|
# Log performance
|
||||||
|
duration = perf_counter() - start_time
|
||||||
|
print(f"Address search completed in {duration:.3f}s")
|
||||||
|
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True, message="Address search results", result=results
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPExceptionApi as e:
|
||||||
|
# Re-raise HTTP exceptions
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
# Log and wrap other errors
|
||||||
|
print(f"Address search error: {str(e)}")
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Failed to search addresses",
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
class AddressUpdateEventMethod(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "UPDATE"
|
||||||
|
event_description = ""
|
||||||
|
event_category = ""
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"1f9c3a9c-e5bd-4dcd-9b9a-3742d7e03a27": "update_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"1f9c3a9c-e5bd-4dcd-9b9a-3742d7e03a27": (UpdateAddress, [Addresses.__language_model__]),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_address(
|
||||||
|
cls,
|
||||||
|
address_uu_id: str,
|
||||||
|
data: UpdateAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
if isinstance(token_dict, EmployeeTokenObject):
|
||||||
|
address = Addresses.filter_one(
|
||||||
|
Addresses.uu_id == address_uu_id,
|
||||||
|
).data
|
||||||
|
if not address:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Address not found. User can not update with given address uuid : {address_uu_id}",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_dict = data.excluded_dump()
|
||||||
|
updated_address = address.update(**data_dict)
|
||||||
|
updated_address.save()
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True,
|
||||||
|
message="Address updated successfully",
|
||||||
|
result=updated_address.get_dict(),
|
||||||
|
)
|
||||||
|
elif isinstance(token_dict, OccupantTokenObject):
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=403,
|
||||||
|
detail="Occupant can not update address.",
|
||||||
|
)
|
||||||
|
|
@ -6,7 +6,7 @@ to be used by the dynamic route creation system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, List, Any
|
||||||
from .account.endpoints import ACCOUNT_RECORDS_CONFIG
|
from .events.account.endpoints import ACCOUNT_RECORDS_CONFIG
|
||||||
|
|
||||||
|
|
||||||
# Registry of all route configurations
|
# Registry of all route configurations
|
||||||
|
|
@ -17,4 +17,4 @@ ROUTE_CONFIGS = [
|
||||||
|
|
||||||
def get_route_configs() -> List[Dict[str, Any]]:
|
def get_route_configs() -> List[Dict[str, Any]]:
|
||||||
"""Get all registered route configurations."""
|
"""Get all registered route configurations."""
|
||||||
return [ACCOUNT_RECORDS_CONFIG]
|
return ROUTE_CONFIGS
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any, Union
|
||||||
|
|
||||||
|
from ApiEvents.base_request_model import DictRequestModel, EndpointBaseRequestModel
|
||||||
|
from ApiEvents.abstract_class import (
|
||||||
|
RouteFactoryConfig,
|
||||||
|
EndpointFactoryConfig,
|
||||||
|
endpoint_wrapper,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from fastapi import Request, HTTPException, status, Body
|
||||||
|
|
||||||
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
|
||||||
|
|
||||||
|
# Type aliases for common types
|
||||||
|
|
||||||
|
prefix = ""
|
||||||
|
|
||||||
|
|
||||||
|
@endpoint_wrapper(f"{prefix}")
|
||||||
|
async def authentication_select_company_or_occupant_type(
|
||||||
|
request: "Request",
|
||||||
|
data: EndpointBaseRequestModel,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Select company or occupant type.
|
||||||
|
"""
|
||||||
|
auth_dict = authentication_select_company_or_occupant_type.auth
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
_CONFIG = RouteFactoryConfig(
|
||||||
|
name="",
|
||||||
|
prefix=prefix,
|
||||||
|
tags=[""],
|
||||||
|
include_in_schema=True,
|
||||||
|
endpoints=[
|
||||||
|
EndpointFactoryConfig(
|
||||||
|
url_prefix=prefix,
|
||||||
|
url_endpoint="/",
|
||||||
|
url_of_endpoint="/",
|
||||||
|
endpoint="/",
|
||||||
|
method="POST",
|
||||||
|
summary="",
|
||||||
|
description="",
|
||||||
|
is_auth_required=True, # Needs token_dict
|
||||||
|
is_event_required=False,
|
||||||
|
endpoint_function=lambda: "",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).as_dict()
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
request models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any, Literal, Optional, TypedDict, Union
|
||||||
|
from pydantic import BaseModel, Field, model_validator, RootModel, ConfigDict
|
||||||
|
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||||
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
from ApiValidations.Request.base_validations import ListOptions
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from Schemas.identity.identity import (
|
||||||
|
AddressPostcode,
|
||||||
|
Addresses,
|
||||||
|
RelationshipEmployee2PostCode,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from fastapi import Request
|
||||||
|
|
@ -0,0 +1,325 @@
|
||||||
|
"""
|
||||||
|
request models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any, Literal, Optional, TypedDict, Union
|
||||||
|
from pydantic import BaseModel, Field, model_validator, RootModel, ConfigDict
|
||||||
|
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||||
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
from ApiValidations.Request.base_validations import ListOptions
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from Schemas.identity.identity import (
|
||||||
|
AddressPostcode,
|
||||||
|
Addresses,
|
||||||
|
RelationshipEmployee2PostCode,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
|
||||||
|
class AddressListEventMethods(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "SELECT"
|
||||||
|
event_description = "List Address records"
|
||||||
|
event_category = "Address"
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"9c251d7d-da70-4d63-a72c-e69c26270442": "address_list_super_user",
|
||||||
|
"52afe375-dd95-4f4b-aaa2-4ec61bc6de52": "address_list_employee",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"9c251d7d-da70-4d63-a72c-e69c26270442": ListAddressResponse,
|
||||||
|
"52afe375-dd95-4f4b-aaa2-4ec61bc6de52": ListAddressResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def address_list_super_user(
|
||||||
|
cls,
|
||||||
|
list_options: ListOptions,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
db = RelationshipEmployee2PostCode.new_session()
|
||||||
|
post_code_list = RelationshipEmployee2PostCode.filter_all(
|
||||||
|
RelationshipEmployee2PostCode.company_id
|
||||||
|
== token_dict.selected_company.company_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
post_code_id_list = [post_code.member_id for post_code in post_code_list]
|
||||||
|
if not post_code_id_list:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="User has no post code registered. User can not list addresses.",
|
||||||
|
)
|
||||||
|
get_street_ids = [
|
||||||
|
street_id[0]
|
||||||
|
for street_id in AddressPostcode.select_only(
|
||||||
|
AddressPostcode.id.in_(post_code_id_list),
|
||||||
|
select_args=[AddressPostcode.street_id],
|
||||||
|
order_by=AddressPostcode.street_id.desc(),
|
||||||
|
).data
|
||||||
|
]
|
||||||
|
if not get_street_ids:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="User has no street registered. User can not list addresses.",
|
||||||
|
)
|
||||||
|
Addresses.pre_query = Addresses.filter_all(
|
||||||
|
Addresses.street_id.in_(get_street_ids),
|
||||||
|
).query
|
||||||
|
Addresses.filter_attr = list_options
|
||||||
|
records = Addresses.filter_all().data
|
||||||
|
return
|
||||||
|
# return AlchemyJsonResponse(
|
||||||
|
# completed=True, message="List Address records", result=records
|
||||||
|
# )
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def address_list_employee(
|
||||||
|
cls,
|
||||||
|
list_options: ListOptions,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
Addresses.filter_attr = list_options
|
||||||
|
Addresses.pre_query = Addresses.filter_all(
|
||||||
|
Addresses.street_id.in_(get_street_ids),
|
||||||
|
)
|
||||||
|
records = Addresses.filter_all().data
|
||||||
|
return
|
||||||
|
# return AlchemyJsonResponse(
|
||||||
|
# completed=True, message="List Address records", result=records
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
class AddressCreateEventMethods(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "CREATE"
|
||||||
|
event_description = ""
|
||||||
|
event_category = ""
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"ffdc445f-da10-4ce4-9531-d2bdb9a198ae": "create_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"ffdc445f-da10-4ce4-9531-d2bdb9a198ae": InsertAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_address(
|
||||||
|
cls,
|
||||||
|
data: InsertAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
post_code = AddressPostcode.filter_one(
|
||||||
|
AddressPostcode.uu_id == data.post_code_uu_id,
|
||||||
|
).data
|
||||||
|
if not post_code:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail="Post code not found. User can not create address without post code.",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_dict = data.excluded_dump()
|
||||||
|
data_dict["street_id"] = post_code.street_id
|
||||||
|
data_dict["street_uu_id"] = str(post_code.street_uu_id)
|
||||||
|
del data_dict["post_code_uu_id"]
|
||||||
|
address = Addresses.find_or_create(**data_dict)
|
||||||
|
address.save()
|
||||||
|
address.update(is_confirmed=True)
|
||||||
|
address.save()
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True,
|
||||||
|
message="Address created successfully",
|
||||||
|
result=address.get_dict(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddressSearchEventMethods(MethodToEvent):
|
||||||
|
"""Event methods for searching addresses.
|
||||||
|
|
||||||
|
This class handles address search functionality including text search
|
||||||
|
and filtering.
|
||||||
|
"""
|
||||||
|
|
||||||
|
event_type = "SEARCH"
|
||||||
|
event_description = "Search for addresses using text and filters"
|
||||||
|
event_category = "Address"
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"e0ac1269-e9a7-4806-9962-219ac224b0d0": "search_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"e0ac1269-e9a7-4806-9962-219ac224b0d0": SearchAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_order_clause(
|
||||||
|
cls, filter_list: Dict[str, Any], schemas: List[str], filter_table: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Build the ORDER BY clause for the query.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filter_list: Dictionary of filter options
|
||||||
|
schemas: List of available schema fields
|
||||||
|
filter_table: SQLAlchemy table to query
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SQLAlchemy order_by clause
|
||||||
|
"""
|
||||||
|
# Default to ordering by UUID if field not in schema
|
||||||
|
if filter_list.get("order_field") not in schemas:
|
||||||
|
filter_list["order_field"] = "uu_id"
|
||||||
|
else:
|
||||||
|
# Extract table and field from order field
|
||||||
|
table_name, field_name = str(filter_list.get("order_field")).split(".")
|
||||||
|
filter_table = getattr(databases.sql_models, table_name)
|
||||||
|
filter_list["order_field"] = field_name
|
||||||
|
|
||||||
|
# Build order clause
|
||||||
|
field = getattr(filter_table, filter_list.get("order_field"))
|
||||||
|
return (
|
||||||
|
field.desc()
|
||||||
|
if str(filter_list.get("order_type"))[0] == "d"
|
||||||
|
else field.asc()
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _format_record(cls, record: Any, schemas: List[str]) -> Dict[str, str]:
|
||||||
|
"""Format a database record into a dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
record: Database record to format
|
||||||
|
schemas: List of schema fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted record dictionary
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for index, schema in enumerate(schemas):
|
||||||
|
value = str(record[index])
|
||||||
|
# Special handling for UUID fields
|
||||||
|
if "uu_id" in value:
|
||||||
|
value = str(value)
|
||||||
|
result[schema] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def search_address(
|
||||||
|
cls,
|
||||||
|
data: SearchAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
) -> JSONResponse:
|
||||||
|
"""Search for addresses using text search and filters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Search parameters including text and filters
|
||||||
|
token_dict: Authentication token
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response with search results
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPExceptionApi: If search fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Start performance measurement
|
||||||
|
start_time = perf_counter()
|
||||||
|
|
||||||
|
# Get initial query
|
||||||
|
search_result = AddressStreet.search_address_text(search_text=data.search)
|
||||||
|
if not search_result:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="No addresses found matching search criteria",
|
||||||
|
)
|
||||||
|
|
||||||
|
query = search_result.get("query")
|
||||||
|
schemas = search_result.get("schema")
|
||||||
|
|
||||||
|
# Apply filters
|
||||||
|
filter_list = data.list_options.dump()
|
||||||
|
filter_table = AddressStreet
|
||||||
|
|
||||||
|
# Build and apply order clause
|
||||||
|
order = cls._build_order_clause(filter_list, schemas, filter_table)
|
||||||
|
|
||||||
|
# Apply pagination
|
||||||
|
page_size = int(filter_list.get("size"))
|
||||||
|
offset = (int(filter_list.get("page")) - 1) * page_size
|
||||||
|
|
||||||
|
# Execute query
|
||||||
|
query = (
|
||||||
|
query.order_by(order)
|
||||||
|
.limit(page_size)
|
||||||
|
.offset(offset)
|
||||||
|
.populate_existing()
|
||||||
|
)
|
||||||
|
records = list(query.all())
|
||||||
|
|
||||||
|
# Format results
|
||||||
|
results = [cls._format_record(record, schemas) for record in records]
|
||||||
|
|
||||||
|
# Log performance
|
||||||
|
duration = perf_counter() - start_time
|
||||||
|
print(f"Address search completed in {duration:.3f}s")
|
||||||
|
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True, message="Address search results", result=results
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPExceptionApi as e:
|
||||||
|
# Re-raise HTTP exceptions
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
# Log and wrap other errors
|
||||||
|
print(f"Address search error: {str(e)}")
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Failed to search addresses",
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
class AddressUpdateEventMethods(MethodToEvent):
|
||||||
|
|
||||||
|
event_type = "UPDATE"
|
||||||
|
event_description = ""
|
||||||
|
event_category = ""
|
||||||
|
|
||||||
|
__event_keys__ = {
|
||||||
|
"1f9c3a9c-e5bd-4dcd-9b9a-3742d7e03a27": "update_address",
|
||||||
|
}
|
||||||
|
__event_validation__ = {
|
||||||
|
"1f9c3a9c-e5bd-4dcd-9b9a-3742d7e03a27": UpdateAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_address(
|
||||||
|
cls,
|
||||||
|
address_uu_id: str,
|
||||||
|
data: UpdateAddress,
|
||||||
|
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||||
|
):
|
||||||
|
if isinstance(token_dict, EmployeeTokenObject):
|
||||||
|
address = Addresses.filter_one(
|
||||||
|
Addresses.uu_id == address_uu_id,
|
||||||
|
).data
|
||||||
|
if not address:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Address not found. User can not update with given address uuid : {address_uu_id}",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_dict = data.excluded_dump()
|
||||||
|
updated_address = address.update(**data_dict)
|
||||||
|
updated_address.save()
|
||||||
|
return AlchemyJsonResponse(
|
||||||
|
completed=True,
|
||||||
|
message="Address updated successfully",
|
||||||
|
result=updated_address.get_dict(),
|
||||||
|
)
|
||||||
|
elif isinstance(token_dict, OccupantTokenObject):
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
status_code=403,
|
||||||
|
detail="Occupant can not update address.",
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Event Service API initialization"""
|
"""Validation Service API initialization"""
|
||||||
|
|
||||||
from .route_configs import get_route_configs
|
from .route_configs import get_route_configs
|
||||||
|
|
||||||
__all__ = ["get_route_configs"]
|
__all__ = ["get_route_configs"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from .models import ValidationsPydantic
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
|
||||||
|
from .validation import (
|
||||||
|
ValidationsBoth,
|
||||||
|
ValidationsHeaders,
|
||||||
|
ValidationsValidations,
|
||||||
|
)
|
||||||
|
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig
|
||||||
|
from ApiEvents.base_request_model import EndpointBaseRequestModel
|
||||||
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
|
|
||||||
|
from Services.PostgresDb.Models.alchemy_response import DictJsonResponse
|
||||||
|
from fastapi import Request, Path, Body
|
||||||
|
|
||||||
|
from middleware.token_event_middleware import TokenEventMiddleware
|
||||||
|
|
||||||
|
|
||||||
|
prefix = "/validation"
|
||||||
|
|
||||||
|
|
||||||
|
@TokenEventMiddleware.validation_required
|
||||||
|
async def validations_validations_select(request: Request, data: EndpointBaseRequestModel) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Select validations.
|
||||||
|
"""
|
||||||
|
wrapped_context = getattr(validations_validations_select, "__wrapped__", None)
|
||||||
|
auth_context = getattr(wrapped_context, "auth", None)
|
||||||
|
validation_code = getattr(validations_validations_select, "validation_code", {"validation_code": None})
|
||||||
|
if not validation_code:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Validation code not found",
|
||||||
|
)
|
||||||
|
validations_pydantic = ValidationsPydantic(
|
||||||
|
class_model=validation_code.get("class", None),
|
||||||
|
reachable_event_code=validation_code.get("reachable_event_code", None),
|
||||||
|
lang=getattr(auth_context, "lang", None),
|
||||||
|
)
|
||||||
|
validations_both = ValidationsBoth.retrieve_both_validations_and_headers(validations_pydantic)
|
||||||
|
return {"status": "OK", "validation_code": validation_code, **validations_both }
|
||||||
|
|
||||||
|
|
||||||
|
@TokenEventMiddleware.validation_required
|
||||||
|
async def validations_headers_select(request: Request, data: EndpointBaseRequestModel) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Select headers.
|
||||||
|
"""
|
||||||
|
ValidationsHeaders.retrieve_headers()
|
||||||
|
return {
|
||||||
|
"status": "OK",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@TokenEventMiddleware.validation_required
|
||||||
|
async def validations_validations_and_headers_select(request: Request, data: EndpointBaseRequestModel) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Select validations and headers.
|
||||||
|
"""
|
||||||
|
ValidationsBoth.retrieve_both_validations_and_headers()
|
||||||
|
return {
|
||||||
|
"status": "OK",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VALIDATION_CONFIG_MAIN =RouteFactoryConfig(
|
||||||
|
name="validations",
|
||||||
|
prefix=prefix,
|
||||||
|
tags=["Validation"],
|
||||||
|
include_in_schema=True,
|
||||||
|
endpoints=[
|
||||||
|
EndpointFactoryConfig(
|
||||||
|
url_prefix=prefix,
|
||||||
|
url_endpoint="/select",
|
||||||
|
url_of_endpoint=f"{prefix}/validations/select",
|
||||||
|
endpoint="/select",
|
||||||
|
method="POST",
|
||||||
|
summary="Select company or occupant type",
|
||||||
|
description="Select company or occupant type",
|
||||||
|
is_auth_required=False, # Needs token_dict
|
||||||
|
is_event_required=False,
|
||||||
|
endpoint_function=validations_validations_select,
|
||||||
|
),
|
||||||
|
EndpointFactoryConfig(
|
||||||
|
url_prefix=prefix,
|
||||||
|
url_endpoint="/headers/select",
|
||||||
|
url_of_endpoint=f"{prefix}/headers/select",
|
||||||
|
endpoint="/headers/select",
|
||||||
|
method="POST",
|
||||||
|
summary="Select company or occupant type",
|
||||||
|
description="Select company or occupant type",
|
||||||
|
is_auth_required=False, # Needs token_dict
|
||||||
|
is_event_required=False,
|
||||||
|
endpoint_function=validations_headers_select,
|
||||||
|
),
|
||||||
|
EndpointFactoryConfig(
|
||||||
|
url_prefix=prefix,
|
||||||
|
url_endpoint="/both/select",
|
||||||
|
url_of_endpoint=f"{prefix}/validationsAndHeaders/select",
|
||||||
|
endpoint="/both/select",
|
||||||
|
method="POST",
|
||||||
|
summary="Select company or occupant type",
|
||||||
|
description="Select company or occupant type",
|
||||||
|
is_auth_required=False, # Needs token_dict
|
||||||
|
is_event_required=False,
|
||||||
|
endpoint_function=validations_validations_and_headers_select,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
VALIDATION_CONFIG = VALIDATION_CONFIG_MAIN.as_dict()
|
||||||
|
|
||||||
|
VALIDATION_ENDPOINTS = [endpoint.url_of_endpoint for endpoint in VALIDATION_CONFIG_MAIN.endpoints]
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""
|
||||||
|
Validation records request and response models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any
|
||||||
|
from pydantic import BaseModel, Field, RootModel
|
||||||
|
from ApiEvents.base_request_model import BaseRequestModel
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ApiValidations.Request import (
|
||||||
|
ListOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ValidationsPydantic(BaseModel):
|
||||||
|
class_model: str
|
||||||
|
reachable_event_code: str
|
||||||
|
lang: str
|
||||||
|
|
||||||
|
|
||||||
|
class InsertValidationRecordRequestModel(BaseRequestModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateValidationRecordRequestModel(BaseRequestModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ListOptionsValidationRecordRequestModel(BaseRequestModel):
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
"""
|
||||||
|
Validation request models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, Any, Literal, Optional, TypedDict, Union
|
||||||
|
from pydantic import BaseModel, Field, model_validator, RootModel, ConfigDict
|
||||||
|
|
||||||
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
|
from ApiValidations.Custom.validation_response import ValidationModel, ValidationParser
|
||||||
|
|
||||||
|
from ApiEvents.abstract_class import MethodToEvent
|
||||||
|
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||||
|
|
||||||
|
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||||
|
from ApiValidations.Request.base_validations import ListOptions
|
||||||
|
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from .models import ValidationsPydantic
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from fastapi import Request
|
||||||
|
|
||||||
|
|
||||||
|
class AllModelsImport:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_all_models(cls):
|
||||||
|
from ApiEvents.events.account.account_records import (
|
||||||
|
AccountListEventMethod,
|
||||||
|
AccountUpdateEventMethod,
|
||||||
|
AccountCreateEventMethod,
|
||||||
|
)
|
||||||
|
from ApiEvents.events.address.address import (
|
||||||
|
AddressListEventMethod,
|
||||||
|
AddressUpdateEventMethod,
|
||||||
|
AddressCreateEventMethod,
|
||||||
|
AddressSearchEventMethod,
|
||||||
|
)
|
||||||
|
return dict(
|
||||||
|
AccountListEventMethod=AccountListEventMethod,
|
||||||
|
AccountUpdateEventMethod=AccountUpdateEventMethod,
|
||||||
|
AccountCreateEventMethod=AccountCreateEventMethod,
|
||||||
|
AddressListEventMethod=AddressListEventMethod,
|
||||||
|
AddressUpdateEventMethod=AddressUpdateEventMethod,
|
||||||
|
AddressCreateEventMethod=AddressCreateEventMethod,
|
||||||
|
AddressSearchEventMethod=AddressSearchEventMethod,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationsBoth(MethodToEvent):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_both_validations_and_headers(
|
||||||
|
cls, event: ValidationsPydantic
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
EVENT_MODELS = AllModelsImport.import_all_models()
|
||||||
|
return_single_model = EVENT_MODELS.get(event.class_model, None)
|
||||||
|
print("return_single_model", return_single_model, type(return_single_model))
|
||||||
|
# event_class_validation = getattr(return_single_model, "__event_validation__", None)
|
||||||
|
if not return_single_model:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Validation code not found",
|
||||||
|
)
|
||||||
|
response_model = return_single_model.retrieve_event_response_model(event.reachable_event_code)
|
||||||
|
language_model_all = return_single_model.retrieve_language_parameters(function_code=event.reachable_event_code, language=event.lang)
|
||||||
|
language_model = language_model_all.get("language_model", None)
|
||||||
|
language_models = language_model_all.get("language_models", None)
|
||||||
|
|
||||||
|
validation = ValidationModel(response_model, language_model, language_models)
|
||||||
|
return {
|
||||||
|
"headers": validation.headers,
|
||||||
|
"validation": validation.validation,
|
||||||
|
"language_models": language_model_all,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationsValidations(MethodToEvent):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_validations(
|
||||||
|
cls, event: ValidationsPydantic
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationsHeaders(MethodToEvent):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_headers(
|
||||||
|
cls, event: ValidationsPydantic
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
return {}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationActions:
|
||||||
|
|
||||||
|
def __init__(self, function_code: str, func: Callable = None):
|
||||||
|
self.function_code = function_code
|
||||||
|
self.func = func
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_validation(cls):
|
||||||
|
"""
|
||||||
|
Retrieve validation [] by validation via [Response Model of Table]
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_headers(cls):
|
||||||
|
"""
|
||||||
|
Retrieve headers for validations [] by event function code [Response Model of Table]
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_validations_and_headers(cls):
|
||||||
|
"""
|
||||||
|
Retrieve validations and headers [] via event function code [Response Model of Table][]
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton class
|
||||||
|
validation_action = ValidationActions(function_code="")
|
||||||
|
|
@ -5,13 +5,13 @@ This module collects and registers all route configurations from different modul
|
||||||
to be used by the dynamic route creation system.
|
to be used by the dynamic route creation system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, List, Any, TypeVar
|
||||||
|
from .events.validation.endpoints import VALIDATION_CONFIG
|
||||||
|
|
||||||
# Registry of all route configurations
|
# Registry of all route configurations
|
||||||
ROUTE_CONFIGS = []
|
ROUTE_CONFIGS = [VALIDATION_CONFIG]
|
||||||
|
|
||||||
|
|
||||||
def get_route_configs() -> List[Dict[str, Any]]:
|
def get_route_configs() -> List[Dict[str, Any]]:
|
||||||
"""Get all registered route configurations."""
|
"""Get all registered route configurations."""
|
||||||
return []
|
return ROUTE_CONFIGS
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ from fastapi import Request, Depends, APIRouter
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from Schemas.rules.rules import EndpointRestriction
|
||||||
|
|
||||||
|
|
||||||
ResponseModel = TypeVar("ResponseModel", bound=BaseModel)
|
ResponseModel = TypeVar("ResponseModel", bound=BaseModel)
|
||||||
|
|
||||||
|
|
@ -239,14 +243,91 @@ class MethodToEvent:
|
||||||
event_description: ClassVar[str] = ""
|
event_description: ClassVar[str] = ""
|
||||||
event_category: ClassVar[str] = ""
|
event_category: ClassVar[str] = ""
|
||||||
__event_keys__: ClassVar[Dict[str, str]] = {}
|
__event_keys__: ClassVar[Dict[str, str]] = {}
|
||||||
__event_validation__: ClassVar[Dict[str, Tuple[Type[ResponseModel], List[Any]]]] = (
|
__event_validation__: Dict[str, Tuple[Type, Union[List, tuple]]] = {}
|
||||||
{}
|
|
||||||
)
|
@classmethod
|
||||||
|
def retrieve_event_response_model(cls, function_code: str) -> Tuple:
|
||||||
|
"""Retrieve event validation for a specific function.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function_code: Function identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple containing response model and language models
|
||||||
|
"""
|
||||||
|
event_validation_list = cls.__event_validation__.get(function_code, None)
|
||||||
|
if not event_validation_list:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function not found",
|
||||||
|
)
|
||||||
|
return event_validation_list[0]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_event_languages(cls, function_code: str) -> Union[List, tuple]:
|
||||||
|
"""Retrieve event description for a specific function.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function_code: Function identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Event description
|
||||||
|
"""
|
||||||
|
event_keys_list = cls.__event_validation__.get(function_code, None)
|
||||||
|
print('event_keys_list', event_keys_list)
|
||||||
|
if not event_keys_list:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function not found",
|
||||||
|
)
|
||||||
|
function_language_models: Union[List, tuple] = event_keys_list[1]
|
||||||
|
if not function_language_models:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function not found",
|
||||||
|
)
|
||||||
|
return function_language_models
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def merge_models(language_model: List) -> Tuple:
|
||||||
|
merged_models = {"tr": {}, "en": {}}
|
||||||
|
for model in language_model:
|
||||||
|
for lang in dict(model).keys():
|
||||||
|
if lang not in merged_models:
|
||||||
|
merged_models[lang] = model[lang]
|
||||||
|
else:
|
||||||
|
merged_models[lang].update(model[lang])
|
||||||
|
return merged_models
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def retrieve_language_parameters(
|
def retrieve_event_function(cls, function_code: str) -> Dict[str, str]:
|
||||||
cls, language: str, function_code: str
|
"""Retrieve event parameters for a specific function.
|
||||||
) -> Dict[str, str]:
|
|
||||||
|
Args:
|
||||||
|
function_code: Function identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary of event parameters
|
||||||
|
"""
|
||||||
|
function_event = cls.__event_keys__[function_code]
|
||||||
|
function_itself = getattr(cls, function_event, None)
|
||||||
|
if not function_itself:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function not found",
|
||||||
|
)
|
||||||
|
return function_itself
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_language_parameters(cls, function_code: str, language: str = "tr") -> Dict[str, str]:
|
||||||
"""Retrieve language-specific parameters for an event.
|
"""Retrieve language-specific parameters for an event.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -256,20 +337,24 @@ class MethodToEvent:
|
||||||
Returns:
|
Returns:
|
||||||
Dictionary of language-specific field mappings
|
Dictionary of language-specific field mappings
|
||||||
"""
|
"""
|
||||||
validation_dict = dict(cls.__event_validation__)
|
event_language_models = cls.retrieve_event_languages(function_code)
|
||||||
if function_code not in validation_dict:
|
event_response_model = cls.retrieve_event_response_model(function_code)
|
||||||
return {}
|
event_response_model_merged = cls.merge_models(event_language_models)
|
||||||
|
event_response_model_merged_lang = event_response_model_merged[language]
|
||||||
event_response_model, event_language_models = validation_dict[function_code]
|
|
||||||
|
|
||||||
# Collect language-specific field mappings
|
|
||||||
language_models = {}
|
|
||||||
for model in event_language_models:
|
|
||||||
language_models.update(model.get(language, model.get("tr", {})))
|
|
||||||
|
|
||||||
# Map response model fields to language-specific values
|
# Map response model fields to language-specific values
|
||||||
return {
|
print('event_response_model', dict(
|
||||||
field: language_models[field]
|
event_response_model=event_response_model,
|
||||||
|
event_response_model_merged_lang=event_response_model_merged_lang,
|
||||||
|
event_response_model_merged=event_response_model_merged,
|
||||||
|
language=language,
|
||||||
|
function_code=function_code,
|
||||||
|
))
|
||||||
|
only_language_dict = {
|
||||||
|
field: event_response_model_merged_lang[field]
|
||||||
for field in event_response_model.model_fields
|
for field in event_response_model.model_fields
|
||||||
if field in language_models
|
if field in event_response_model_merged_lang
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"language_model": only_language_dict,
|
||||||
|
"language_models": event_response_model_merged,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ class UserLoginModule:
|
||||||
async def login_user_via_credentials(self, access_data: "Login") -> Dict[str, Any]:
|
async def login_user_via_credentials(self, access_data: "Login") -> Dict[str, Any]:
|
||||||
"""Login user via credentials."""
|
"""Login user via credentials."""
|
||||||
# Get the actual data from the BaseRequestModel if needed
|
# Get the actual data from the BaseRequestModel if needed
|
||||||
print("access_data", access_data)
|
|
||||||
if hasattr(access_data, "data"):
|
if hasattr(access_data, "data"):
|
||||||
access_data = access_data.data
|
access_data = access_data.data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
"""Token service for handling authentication tokens and user sessions."""
|
"""Token service for handling authentication tokens and user sessions."""
|
||||||
|
|
||||||
import json
|
|
||||||
from typing import List, Union, TypeVar, Dict, Any, Optional, TYPE_CHECKING
|
from typing import List, Union, TypeVar, Dict, Any, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from AllConfigs.Token.config import Auth
|
from AllConfigs.Token.config import Auth
|
||||||
|
|
@ -75,7 +74,6 @@ class TokenService:
|
||||||
)
|
)
|
||||||
|
|
||||||
occupants_selection_dict: Dict[str, Any] = {}
|
occupants_selection_dict: Dict[str, Any] = {}
|
||||||
|
|
||||||
for living_space in living_spaces:
|
for living_space in living_spaces:
|
||||||
build_parts_selection = BuildParts.filter_all(
|
build_parts_selection = BuildParts.filter_all(
|
||||||
BuildParts.id == living_space.build_parts_id,
|
BuildParts.id == living_space.build_parts_id,
|
||||||
|
|
@ -221,7 +219,6 @@ class TokenService:
|
||||||
company_address = Addresses.filter_by_one(
|
company_address = Addresses.filter_by_one(
|
||||||
id=company.official_address_id, db=db_session
|
id=company.official_address_id, db=db_session
|
||||||
).data
|
).data
|
||||||
|
|
||||||
companies_list.append(
|
companies_list.append(
|
||||||
{
|
{
|
||||||
"uu_id": str(company.uu_id),
|
"uu_id": str(company.uu_id),
|
||||||
|
|
@ -287,15 +284,14 @@ class TokenService:
|
||||||
Users.client_arrow = DateTimeLocal(is_client=True, timezone=user.local_timezone)
|
Users.client_arrow = DateTimeLocal(is_client=True, timezone=user.local_timezone)
|
||||||
db_session = UsersTokens.new_session()
|
db_session = UsersTokens.new_session()
|
||||||
# Handle login based on user type
|
# Handle login based on user type
|
||||||
login_dict = (
|
if user.is_occupant:
|
||||||
cls.do_occupant_login(request=request, user=user, domain=domain)
|
login_dict = cls.do_occupant_login(
|
||||||
if user.is_occupant
|
request=request, user=user, domain=domain
|
||||||
else (
|
)
|
||||||
cls.do_employee_login(request=request, user=user, domain=domain)
|
elif user.is_employee:
|
||||||
if user.is_employee
|
login_dict = cls.do_employee_login(
|
||||||
else {}
|
request=request, user=user, domain=domain
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# Handle remember me functionality
|
# Handle remember me functionality
|
||||||
if remember:
|
if remember:
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class OccupantToken(BaseModel):
|
||||||
responsible_employee_id: Optional[int] = None
|
responsible_employee_id: Optional[int] = None
|
||||||
responsible_employee_uuid: Optional[str] = None
|
responsible_employee_uuid: Optional[str] = None
|
||||||
|
|
||||||
reachable_event_list_id: Optional[list] = None # ID list of reachable modules
|
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
# reachable_event_list_uu_id: Optional[list] = None # UUID list of reachable modules
|
# reachable_event_list_uu_id: Optional[list] = None # UUID list of reachable modules
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@ class CompanyToken(BaseModel): # Required Company Object for an employee
|
||||||
|
|
||||||
bulk_duties_id: int
|
bulk_duties_id: int
|
||||||
|
|
||||||
reachable_event_list_id: Optional[list] = None # ID list of reachable modules
|
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
# reachable_event_list_uu_id: Optional[list] = None # UUID list of reachable modules
|
# reachable_event_list_uu_id: Optional[list] = None # UUID list of reachable modules
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import json
|
||||||
|
from typing import Any, ClassVar, TypeVar, Dict, Tuple, List
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from ErrorHandlers import HTTPExceptionApi
|
||||||
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
|
from ApiValidations.Request.base_validations import CrudRecords, PydanticBaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationParser:
|
||||||
|
|
||||||
|
def __init__(self, active_validation: BaseModel):
|
||||||
|
self.core_validation = active_validation
|
||||||
|
self.annotations = active_validation.model_json_schema()
|
||||||
|
self.annotations = json.loads(json.dumps(self.annotations))
|
||||||
|
self.schema = {}
|
||||||
|
self.parse()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
from ApiValidations.Request.base_validations import CrudRecords, PydanticBaseModel
|
||||||
|
|
||||||
|
properties = dict(self.annotations.get("properties")).items()
|
||||||
|
total_class_annotations = {
|
||||||
|
**self.core_validation.__annotations__,
|
||||||
|
**PydanticBaseModel.__annotations__,
|
||||||
|
**CrudRecords.__annotations__,
|
||||||
|
}
|
||||||
|
for key, value in properties:
|
||||||
|
default, required, possible_types = dict(value).get("default", None), True, []
|
||||||
|
if dict(value).get("anyOf", None):
|
||||||
|
for _ in dict(value).get("anyOf") or []:
|
||||||
|
type_opt = json.loads(json.dumps(_))
|
||||||
|
if not type_opt.get("type") == "null":
|
||||||
|
possible_types.append(type_opt.get("type"))
|
||||||
|
field_type = possible_types[0]
|
||||||
|
required = False
|
||||||
|
else:
|
||||||
|
field_type = dict(value).get("type", "string")
|
||||||
|
attribute_of_class = total_class_annotations.get(key, None)
|
||||||
|
aoc = str(attribute_of_class) if attribute_of_class else None
|
||||||
|
if attribute_of_class:
|
||||||
|
if aoc in ("<class 'str'>", "typing.Optional[str]"):
|
||||||
|
field_type, required = "string", aoc == "<class 'str'>"
|
||||||
|
elif aoc in ("<class 'int'>", "typing.Optional[int]"):
|
||||||
|
field_type, required = "integer", aoc == "<class 'int'>"
|
||||||
|
elif aoc in ("<class 'bool'>", "typing.Optional[bool]"):
|
||||||
|
field_type, required = "boolean", aoc == "<class 'bool'>"
|
||||||
|
elif aoc in ("<class 'float'>", "typing.Optional[float]"):
|
||||||
|
field_type, required = "float", aoc == "<class 'float'>"
|
||||||
|
elif aoc in ("<class 'datetime.datetime'>", "typing.Optional[datetime.datetime]"):
|
||||||
|
field_type, required = "datetime", aoc == "<class 'datetime.datetime'>"
|
||||||
|
self.schema[key] = {
|
||||||
|
"type": field_type, "required": required, "default": default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationModel:
|
||||||
|
|
||||||
|
def __init__(self, response_model: BaseModel, language_model, language_models):
|
||||||
|
self.response_model = response_model
|
||||||
|
self.validation = None
|
||||||
|
self.headers = language_model
|
||||||
|
self.language_models = language_models
|
||||||
|
self.get_validation()
|
||||||
|
|
||||||
|
def get_validation(self) -> Tuple:
|
||||||
|
self.headers = self.language_models
|
||||||
|
self.validation = ValidationParser(self.response_model).schema
|
||||||
|
|
@ -61,12 +61,21 @@ class OccupantSelection(BaseModel, OccupantSelectionValidation):
|
||||||
|
|
||||||
model_config = ConfigDict(
|
model_config = ConfigDict(
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"example": {
|
"example": [
|
||||||
"build_living_space_uu_id": "987fcdeb-51a2-43e7-9876-543210987654"
|
{"company_uu_id": "abcdef12-3456-7890-abcd-ef1234567890"},
|
||||||
}
|
{"build_living_space_uu_id": "987fcdeb-51a2-43e7-9876-543210987654"},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_employee(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_occupant(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class EmployeeSelectionValidation:
|
class EmployeeSelectionValidation:
|
||||||
tr = {"company_uu_id": "Şirket UU ID"}
|
tr = {"company_uu_id": "Şirket UU ID"}
|
||||||
|
|
@ -78,10 +87,21 @@ class EmployeeSelection(BaseModel, EmployeeSelectionValidation):
|
||||||
|
|
||||||
model_config = ConfigDict(
|
model_config = ConfigDict(
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"example": {"company_uu_id": "abcdef12-3456-7890-abcd-ef1234567890"}
|
"example": [
|
||||||
|
{"company_uu_id": "abcdef12-3456-7890-abcd-ef1234567890"},
|
||||||
|
{"build_living_space_uu_id": "987fcdeb-51a2-43e7-9876-543210987654"},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_employee(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_occupant(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class LoginValidation:
|
class LoginValidation:
|
||||||
tr = {
|
tr = {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,16 @@
|
||||||
from .account_responses import AccountRecordResponse
|
from .account_responses import AccountRecordResponse
|
||||||
|
from .address_responses import ListAddressResponse
|
||||||
|
from .auth_responses import (
|
||||||
|
AuthenticationLoginResponse,
|
||||||
|
AuthenticationRefreshResponse,
|
||||||
|
AuthenticationUserInfoResponse
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = ["AccountRecordResponse"]
|
|
||||||
|
__all__ = [
|
||||||
|
"AccountRecordResponse",
|
||||||
|
"ListAddressResponse",
|
||||||
|
"AuthenticationLoginResponse",
|
||||||
|
"AuthenticationRefreshResponse",
|
||||||
|
"AuthenticationUserInfoResponse",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
from typing import Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class ListAddressResponse(BaseModel):
|
||||||
|
build_number: Optional[str] = None
|
||||||
|
door_number: Optional[str] = None
|
||||||
|
floor_number: Optional[str] = None
|
||||||
|
comment_address: Optional[str] = None
|
||||||
|
letter_address: Optional[str] = None
|
||||||
|
short_letter_address: Optional[str] = None
|
||||||
|
latitude: Optional[float] = None
|
||||||
|
longitude: Optional[float] = None
|
||||||
|
street_uu_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class AddressPostCodeResponse:
|
||||||
|
street_id: Optional[int] = None
|
||||||
|
street_uu_id: Optional[str] = None
|
||||||
|
postcode: Optional[str] = None
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, List, Dict, Any
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationLoginResponse(BaseModel):
|
||||||
|
"""Response model for authentication login endpoint"""
|
||||||
|
|
||||||
|
token: str
|
||||||
|
refresh_token: str
|
||||||
|
token_type: str
|
||||||
|
expires_in: int
|
||||||
|
user_info: Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationRefreshResponse(BaseModel):
|
||||||
|
"""Response model for authentication refresh endpoint"""
|
||||||
|
|
||||||
|
token: str
|
||||||
|
refresh_token: str
|
||||||
|
token_type: str
|
||||||
|
expires_in: int
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationUserInfoResponse(BaseModel):
|
||||||
|
"""Response model for authentication user info endpoint"""
|
||||||
|
|
||||||
|
user_id: int
|
||||||
|
username: str
|
||||||
|
email: str
|
||||||
|
first_name: str
|
||||||
|
last_name: str
|
||||||
|
is_active: bool
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, TypeVar, Generic, List
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class BaseResponse(BaseModel):
|
||||||
|
"""Base response model that all response models inherit from.
|
||||||
|
|
||||||
|
This model provides common fields that are present in all database records,
|
||||||
|
including tracking information (created/updated timestamps), user actions
|
||||||
|
(created by, updated by, confirmed by), and record status (active, deleted).
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
uu_id (str): Unique identifier for the record, typically a UUID
|
||||||
|
created_at (datetime): Timestamp when the record was created
|
||||||
|
updated_at (Optional[datetime]): Timestamp when the record was last updated
|
||||||
|
created_by (Optional[str]): Username or identifier of the user who created the record
|
||||||
|
updated_by (Optional[str]): Username or identifier of the user who last updated the record
|
||||||
|
confirmed_by (Optional[str]): Username or identifier of the user who confirmed the record
|
||||||
|
is_confirmed (Optional[bool]): Whether the record has been confirmed/approved
|
||||||
|
active (Optional[bool]): Whether the record is currently active
|
||||||
|
deleted (Optional[bool]): Whether the record has been marked as deleted
|
||||||
|
expiry_starts (Optional[datetime]): When the record becomes valid/active
|
||||||
|
expiry_ends (Optional[datetime]): When the record expires/becomes inactive
|
||||||
|
is_notification_send (Optional[bool]): Whether notifications have been sent for this record
|
||||||
|
is_email_send (Optional[bool]): Whether emails have been sent for this record
|
||||||
|
"""
|
||||||
|
|
||||||
|
uu_id: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
created_by: Optional[str]
|
||||||
|
updated_by: Optional[str]
|
||||||
|
confirmed_by: Optional[str]
|
||||||
|
is_confirmed: Optional[bool] = None
|
||||||
|
active: Optional[bool] = True
|
||||||
|
deleted: Optional[bool] = False
|
||||||
|
expiry_starts: Optional[datetime]
|
||||||
|
expiry_ends: Optional[datetime]
|
||||||
|
is_notification_send: Optional[bool] = False
|
||||||
|
is_email_send: Optional[bool] = False
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Pydantic configuration for the base response model.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
from_attributes (bool): Enables ORM mode for SQLAlchemy integration
|
||||||
|
"""
|
||||||
|
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class CrudCollection(BaseModel, Generic[T]):
|
||||||
|
"""Base collection model for paginated responses.
|
||||||
|
|
||||||
|
This model is used to return collections of items with pagination information.
|
||||||
|
It is generic over the type of items in the collection, allowing it to be
|
||||||
|
used with any response model.
|
||||||
|
|
||||||
|
Type Parameters:
|
||||||
|
T: The type of items in the collection
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
page (int): Current page number, 1-based indexing
|
||||||
|
size (int): Number of items per page
|
||||||
|
total (int): Total number of items across all pages
|
||||||
|
order_field (str): Field used for sorting the collection
|
||||||
|
order_type (str): Sort direction ('asc' or 'desc')
|
||||||
|
items (List[T]): List of items in the current page
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
class UserResponse(BaseResponse):
|
||||||
|
name: str
|
||||||
|
email: str
|
||||||
|
|
||||||
|
users = CrudCollection[UserResponse](
|
||||||
|
page=1,
|
||||||
|
size=10,
|
||||||
|
total=100,
|
||||||
|
order_field="name",
|
||||||
|
order_type="asc",
|
||||||
|
items=[...]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
page: int = 1
|
||||||
|
size: int = 10
|
||||||
|
total: int = 0
|
||||||
|
order_field: str = "id"
|
||||||
|
order_type: str = "asc"
|
||||||
|
items: List[T] = []
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Pydantic configuration for the collection model.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
from_attributes (bool): Enables ORM mode for SQLAlchemy integration
|
||||||
|
"""
|
||||||
|
|
||||||
|
from_attributes = True
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, List
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
from decimal import Decimal
|
||||||
|
from .base_responses import BaseResponse, CrudCollection
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetBooksResponse(BaseResponse):
|
||||||
|
"""Response model for decision book budget books"""
|
||||||
|
|
||||||
|
country: str
|
||||||
|
branch_type: int = 0
|
||||||
|
company_id: int
|
||||||
|
company_uu_id: str
|
||||||
|
branch_id: Optional[int]
|
||||||
|
branch_uu_id: Optional[str]
|
||||||
|
build_decision_book_id: int
|
||||||
|
build_decision_book_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetBooksCollection(
|
||||||
|
CrudCollection[DecisionBookBudgetBooksResponse]
|
||||||
|
):
|
||||||
|
"""Collection of decision book budget books"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetCodesResponse(BaseResponse):
|
||||||
|
"""Response model for decision book budget codes"""
|
||||||
|
|
||||||
|
budget_code: str
|
||||||
|
comment_line: str
|
||||||
|
budget_type: str
|
||||||
|
budget_code_seperator: str = "."
|
||||||
|
system_id: int = 0
|
||||||
|
locked: bool = False
|
||||||
|
company_id: Optional[int]
|
||||||
|
company_uu_id: str
|
||||||
|
customer_id: Optional[int]
|
||||||
|
customer_uu_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetCodesCollection(
|
||||||
|
CrudCollection[DecisionBookBudgetCodesResponse]
|
||||||
|
):
|
||||||
|
"""Collection of decision book budget codes"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetMasterResponse(BaseResponse):
|
||||||
|
"""Response model for decision book budget master"""
|
||||||
|
|
||||||
|
budget_type: str
|
||||||
|
currency: str = "TRY"
|
||||||
|
total_budget: Decimal
|
||||||
|
tracking_period_id: Optional[int]
|
||||||
|
tracking_period_uu_id: Optional[str]
|
||||||
|
budget_books_id: int
|
||||||
|
budget_books_uu_id: Optional[str]
|
||||||
|
department_id: int
|
||||||
|
department_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetMasterCollection(
|
||||||
|
CrudCollection[DecisionBookBudgetMasterResponse]
|
||||||
|
):
|
||||||
|
"""Collection of decision book budget masters"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetsResponse(BaseResponse):
|
||||||
|
"""Response model for decision book budgets"""
|
||||||
|
|
||||||
|
process_date: datetime
|
||||||
|
budget_codes_id: int
|
||||||
|
total_budget: Decimal
|
||||||
|
used_budget: Decimal = Decimal("0")
|
||||||
|
remaining_budget: Decimal = Decimal("0")
|
||||||
|
decision_book_budget_master_id: int
|
||||||
|
decision_book_budget_master_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionBookBudgetsCollection(CrudCollection[DecisionBookBudgetsResponse]):
|
||||||
|
"""Collection of decision book budgets"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
from typing import Optional, List, Generic
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from api_validations.validations_response.base_responses import (
|
||||||
|
BaseResponse,
|
||||||
|
CrudCollection,
|
||||||
|
)
|
||||||
|
from api_validations.validations_request import PydanticBaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class ListBuildingResponse(PydanticBaseModel):
|
||||||
|
|
||||||
|
gov_address_code: str
|
||||||
|
build_name: str
|
||||||
|
build_types_uu_id: Optional[str] = None
|
||||||
|
build_no: Optional[str] = None
|
||||||
|
max_floor: Optional[int] = None
|
||||||
|
underground_floor: Optional[int] = None
|
||||||
|
address_uu_id: Optional[str] = None
|
||||||
|
build_date: Optional[str] = None
|
||||||
|
decision_period_date: Optional[str] = None
|
||||||
|
tax_no: Optional[str] = None
|
||||||
|
lift_count: Optional[int] = None
|
||||||
|
heating_system: Optional[bool] = None
|
||||||
|
cooling_system: Optional[bool] = None
|
||||||
|
hot_water_system: Optional[bool] = None
|
||||||
|
block_service_man_count: Optional[int] = None
|
||||||
|
security_service_man_count: Optional[int] = None
|
||||||
|
garage_count: Optional[int] = None
|
||||||
|
site_uu_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BuildAreaListResponse(BaseResponse):
|
||||||
|
"""Response model for building area list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: str
|
||||||
|
area_name: str
|
||||||
|
area_value: float
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class BuildAreaListCollection(CrudCollection[BuildAreaListResponse]):
|
||||||
|
"""Collection of building area list"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildSitesListResponse(BaseResponse):
|
||||||
|
"""Response model for building sites list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
address_id: int
|
||||||
|
site_name: str
|
||||||
|
site_value: float
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class BuildSitesListCollection(CrudCollection[BuildSitesListResponse]):
|
||||||
|
"""Collection of building sites list"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildTypesListResponse(BaseResponse):
|
||||||
|
"""Response model for building types list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
type_name: str
|
||||||
|
type_value: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class BuildTypesListCollection(CrudCollection[BuildTypesListResponse]):
|
||||||
|
"""Collection of building types list"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildTypesResponse(BaseResponse):
|
||||||
|
"""Response model for building types"""
|
||||||
|
|
||||||
|
function_code: str
|
||||||
|
type_code: str
|
||||||
|
lang: str = "TR"
|
||||||
|
|
||||||
|
|
||||||
|
class BuildTypesCollection(CrudCollection[BuildTypesResponse]):
|
||||||
|
"""Collection of building types"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Part2EmployeeResponse(BaseResponse):
|
||||||
|
"""Response model for part to employee mapping"""
|
||||||
|
|
||||||
|
build_id: int
|
||||||
|
part_id: int
|
||||||
|
employee_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class Part2EmployeeCollection(CrudCollection[Part2EmployeeResponse]):
|
||||||
|
"""Collection of part to employee mappings"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipEmployee2BuildResponse(BaseResponse):
|
||||||
|
"""Response model for employee to build relationship"""
|
||||||
|
|
||||||
|
company_id: int
|
||||||
|
employee_id: int
|
||||||
|
member_id: int
|
||||||
|
relationship_type: Optional[str] = "Employee"
|
||||||
|
show_only: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipEmployee2BuildCollection(
|
||||||
|
CrudCollection[RelationshipEmployee2BuildResponse]
|
||||||
|
):
|
||||||
|
"""Collection of employee to build relationships"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildResponse(BaseResponse):
|
||||||
|
"""Response model for buildings"""
|
||||||
|
|
||||||
|
gov_address_code: str = ""
|
||||||
|
build_name: str
|
||||||
|
build_no: str
|
||||||
|
max_floor: int = 1
|
||||||
|
underground_floor: int = 0
|
||||||
|
build_date: datetime
|
||||||
|
decision_period_date: datetime
|
||||||
|
tax_no: str = ""
|
||||||
|
lift_count: int = 0
|
||||||
|
heating_system: bool = True
|
||||||
|
cooling_system: bool = False
|
||||||
|
hot_water_system: bool = False
|
||||||
|
block_service_man_count: int = 0
|
||||||
|
security_service_man_count: int = 0
|
||||||
|
garage_count: int = 0
|
||||||
|
management_room_id: Optional[int]
|
||||||
|
site_id: Optional[int]
|
||||||
|
site_uu_id: Optional[str]
|
||||||
|
address_id: int
|
||||||
|
address_uu_id: str
|
||||||
|
build_types_id: int
|
||||||
|
build_types_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildCollection(CrudCollection[BuildResponse]):
|
||||||
|
"""Collection of buildings"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPartsResponse(BaseResponse):
|
||||||
|
"""Response model for building parts"""
|
||||||
|
|
||||||
|
address_gov_code: str
|
||||||
|
part_no: int = 0
|
||||||
|
part_level: int = 0
|
||||||
|
part_code: str
|
||||||
|
part_gross_size: int = 0
|
||||||
|
part_net_size: int = 0
|
||||||
|
default_accessory: str = "0"
|
||||||
|
human_livable: bool = True
|
||||||
|
due_part_key: str
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: str
|
||||||
|
part_direction_id: Optional[int]
|
||||||
|
part_direction_uu_id: Optional[str]
|
||||||
|
part_type_id: int
|
||||||
|
part_type_uu_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPartsCollection(CrudCollection[BuildPartsResponse]):
|
||||||
|
"""Collection of building parts"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildLivingSpaceResponse(BaseResponse):
|
||||||
|
"""Response model for building living space"""
|
||||||
|
|
||||||
|
fix_value: Decimal = Decimal("0")
|
||||||
|
fix_percent: Decimal = Decimal("0")
|
||||||
|
agreement_no: str = ""
|
||||||
|
marketing_process: bool = False
|
||||||
|
marketing_layer: int = 0
|
||||||
|
build_parts_id: int
|
||||||
|
build_parts_uu_id: str
|
||||||
|
person_id: int
|
||||||
|
person_uu_id: str
|
||||||
|
occupant_type: int
|
||||||
|
occupant_type_uu_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class BuildLivingSpaceCollection(CrudCollection[BuildLivingSpaceResponse]):
|
||||||
|
"""Collection of building living spaces"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildManagementResponse(BaseResponse):
|
||||||
|
"""Response model for building management"""
|
||||||
|
|
||||||
|
discounted_percentage: Decimal = Decimal("0.00")
|
||||||
|
discounted_price: Decimal = Decimal("0.00")
|
||||||
|
calculated_price: Decimal = Decimal("0.00")
|
||||||
|
occupant_type: int
|
||||||
|
occupant_type_uu_id: str
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: str
|
||||||
|
build_parts_id: int
|
||||||
|
build_parts_uu_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class BuildManagementCollection(CrudCollection[BuildManagementResponse]):
|
||||||
|
"""Collection of building management records"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildAreaResponse(BaseResponse):
|
||||||
|
"""Response model for building area"""
|
||||||
|
|
||||||
|
area_name: str = ""
|
||||||
|
area_code: str = ""
|
||||||
|
area_type: str = "GREEN"
|
||||||
|
area_direction: str = "NN"
|
||||||
|
area_gross_size: Decimal = Decimal("0")
|
||||||
|
area_net_size: Decimal = Decimal("0")
|
||||||
|
width: int = 0
|
||||||
|
size: int = 0
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: str
|
||||||
|
part_type_id: Optional[int]
|
||||||
|
part_type_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildAreaCollection(CrudCollection[BuildAreaResponse]):
|
||||||
|
"""Collection of building areas"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildSitesResponse(BaseResponse):
|
||||||
|
"""Response model for building sites"""
|
||||||
|
|
||||||
|
site_name: str
|
||||||
|
site_no: str
|
||||||
|
address_id: int
|
||||||
|
address_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildSitesCollection(CrudCollection[BuildSitesResponse]):
|
||||||
|
"""Collection of building sites"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildCompaniesProvidingResponse(BaseResponse):
|
||||||
|
"""Response model for building companies providing services"""
|
||||||
|
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: Optional[str]
|
||||||
|
company_id: int
|
||||||
|
company_uu_id: Optional[str]
|
||||||
|
provide_id: Optional[int]
|
||||||
|
provide_uu_id: Optional[str]
|
||||||
|
contract_id: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildCompaniesProvidingCollection(
|
||||||
|
CrudCollection[BuildCompaniesProvidingResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building companies providing services"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPersonProvidingResponse(BaseResponse):
|
||||||
|
"""Response model for building person providing services"""
|
||||||
|
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: Optional[str]
|
||||||
|
people_id: int
|
||||||
|
people_uu_id: Optional[str]
|
||||||
|
provide_id: Optional[int]
|
||||||
|
provide_uu_id: Optional[str]
|
||||||
|
contract_id: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPersonProvidingCollection(CrudCollection[BuildPersonProvidingResponse]):
|
||||||
|
"""Collection of building person providing services"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, List
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyListResponse(BaseModel):
|
||||||
|
"""Response model for company list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
company_name: str
|
||||||
|
company_code: str
|
||||||
|
company_email: str
|
||||||
|
company_phone: str
|
||||||
|
company_address: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyDepartmentListResponse(BaseModel):
|
||||||
|
"""Response model for company department list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
department_name: str
|
||||||
|
department_code: str
|
||||||
|
company_id: int
|
||||||
|
company_uu_id: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyDutyListResponse(BaseModel):
|
||||||
|
"""Response model for company duty list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
duty_name: str
|
||||||
|
duty_code: str
|
||||||
|
department_id: int
|
||||||
|
department_uu_id: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyEmployeeListResponse(BaseModel):
|
||||||
|
"""Response model for company employee list endpoint"""
|
||||||
|
|
||||||
|
uu_id: UUID
|
||||||
|
employee_id: int
|
||||||
|
employee_uu_id: str
|
||||||
|
company_id: int
|
||||||
|
company_uu_id: str
|
||||||
|
duty_id: int
|
||||||
|
duty_uu_id: str
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: Optional[datetime]
|
||||||
|
deleted: bool = False
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, List
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
from decimal import Decimal
|
||||||
|
from .base_responses import BaseResponse, CrudCollection
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book"""
|
||||||
|
|
||||||
|
decision_book_pdf_path: Optional[str] = ""
|
||||||
|
resp_company_fix_wage: float = 0
|
||||||
|
contact_agreement_path: Optional[str] = ""
|
||||||
|
contact_agreement_date: Optional[datetime]
|
||||||
|
meeting_date: Optional[str]
|
||||||
|
decision_type: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookCollection(CrudCollection[BuildDecisionBookResponse]):
|
||||||
|
"""Collection of building decision books"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookInvitationsResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book invitations"""
|
||||||
|
|
||||||
|
build_id: int
|
||||||
|
build_uu_id: Optional[str]
|
||||||
|
decision_book_id: int
|
||||||
|
decision_book_uu_id: Optional[str]
|
||||||
|
invitation_type: str
|
||||||
|
invitation_attempt: int = 1
|
||||||
|
living_part_count: int = 1
|
||||||
|
living_part_percentage: Decimal = Decimal("0.51")
|
||||||
|
message: Optional[str]
|
||||||
|
planned_date: datetime
|
||||||
|
planned_date_expires: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookInvitationsCollection(
|
||||||
|
CrudCollection[BuildDecisionBookInvitationsResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building decision book invitations"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPersonResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book person"""
|
||||||
|
|
||||||
|
dues_percent_discount: int = 0
|
||||||
|
dues_fix_discount: Decimal = Decimal("0")
|
||||||
|
dues_discount_approval_date: datetime
|
||||||
|
send_date: datetime
|
||||||
|
is_attending: bool = False
|
||||||
|
confirmed_date: Optional[datetime]
|
||||||
|
token: str = ""
|
||||||
|
vicarious_person_id: Optional[int]
|
||||||
|
vicarious_person_uu_id: Optional[str]
|
||||||
|
invite_id: int
|
||||||
|
invite_uu_id: str
|
||||||
|
build_decision_book_id: int
|
||||||
|
build_decision_book_uu_id: str
|
||||||
|
build_living_space_id: int
|
||||||
|
build_living_space_uu_id: Optional[str]
|
||||||
|
person_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPersonCollection(
|
||||||
|
CrudCollection[BuildDecisionBookPersonResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building decision book persons"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPersonOccupantsResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book person occupants"""
|
||||||
|
|
||||||
|
build_decision_book_person_id: int
|
||||||
|
build_decision_book_person_uu_id: Optional[str]
|
||||||
|
invite_id: Optional[int]
|
||||||
|
invite_uu_id: Optional[str]
|
||||||
|
occupant_type_id: int
|
||||||
|
occupant_type_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPersonOccupantsCollection(
|
||||||
|
CrudCollection[BuildDecisionBookPersonOccupantsResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building decision book person occupants"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookItemsResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book items"""
|
||||||
|
|
||||||
|
item_order: int
|
||||||
|
item_comment: str
|
||||||
|
item_objection: Optional[str]
|
||||||
|
info_is_completed: bool = False
|
||||||
|
is_payment_created: bool = False
|
||||||
|
info_type_id: Optional[int]
|
||||||
|
info_type_uu_id: Optional[str]
|
||||||
|
build_decision_book_id: int
|
||||||
|
build_decision_book_uu_id: Optional[str]
|
||||||
|
item_short_comment: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookItemsCollection(CrudCollection[BuildDecisionBookItemsResponse]):
|
||||||
|
"""Collection of building decision book items"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookItemsUnapprovedResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book items unapproved"""
|
||||||
|
|
||||||
|
item_objection: str
|
||||||
|
item_order: int
|
||||||
|
decision_book_item_id: int
|
||||||
|
decision_book_item_uu_id: Optional[str]
|
||||||
|
person_id: int
|
||||||
|
person_uu_id: Optional[str]
|
||||||
|
build_decision_book_item: int
|
||||||
|
build_decision_book_item_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookItemsUnapprovedCollection(
|
||||||
|
CrudCollection[BuildDecisionBookItemsUnapprovedResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building decision book items unapproved"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPaymentsResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book payments"""
|
||||||
|
|
||||||
|
payment_plan_time_periods: str
|
||||||
|
process_date: datetime
|
||||||
|
payment_amount: Decimal
|
||||||
|
currency: str = "TRY"
|
||||||
|
payment_types_id: Optional[int]
|
||||||
|
payment_types_uu_id: Optional[str]
|
||||||
|
period_time: str
|
||||||
|
process_date_y: int
|
||||||
|
process_date_m: int
|
||||||
|
build_decision_book_item_id: int
|
||||||
|
build_decision_book_item_uu_id: str
|
||||||
|
build_parts_id: int
|
||||||
|
build_parts_uu_id: str
|
||||||
|
decision_book_project_id: Optional[int]
|
||||||
|
decision_book_project_uu_id: Optional[str]
|
||||||
|
account_records_id: Optional[int]
|
||||||
|
account_records_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookPaymentsCollection(
|
||||||
|
CrudCollection[BuildDecisionBookPaymentsResponse]
|
||||||
|
):
|
||||||
|
"""Collection of building decision book payments"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookLegalResponse(BaseResponse):
|
||||||
|
"""Response model for building decision book legal"""
|
||||||
|
|
||||||
|
period_start_date: datetime
|
||||||
|
lawsuits_decision_number: str
|
||||||
|
lawsuits_decision_date: datetime
|
||||||
|
period_stop_date: datetime
|
||||||
|
decision_book_pdf_path: Optional[str] = ""
|
||||||
|
resp_company_total_wage: Optional[Decimal] = Decimal("0")
|
||||||
|
contact_agreement_path: Optional[str] = ""
|
||||||
|
contact_agreement_date: Optional[datetime]
|
||||||
|
meeting_date: str
|
||||||
|
lawsuits_type: str = "C"
|
||||||
|
lawsuits_name: str
|
||||||
|
lawsuits_note: str
|
||||||
|
lawyer_cost: Decimal
|
||||||
|
mediator_lawyer_cost: Decimal
|
||||||
|
other_cost: Decimal
|
||||||
|
legal_cost: Decimal
|
||||||
|
approved_cost: Decimal
|
||||||
|
total_price: Decimal
|
||||||
|
build_db_item_id: int
|
||||||
|
build_db_item_uu_id: Optional[str]
|
||||||
|
resp_attorney_id: int
|
||||||
|
resp_attorney_uu_id: Optional[str]
|
||||||
|
resp_attorney_company_id: int
|
||||||
|
resp_attorney_company_uu_id: Optional[str]
|
||||||
|
mediator_lawyer_person_id: int
|
||||||
|
mediator_lawyer_person_uu_id: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDecisionBookLegalCollection(CrudCollection[BuildDecisionBookLegalResponse]):
|
||||||
|
"""Collection of building decision book legal records"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import Optional
|
||||||
|
from api_validations.core_validations import BaseModelRegular
|
||||||
|
from api_validations.validations_request import (
|
||||||
|
CrudRecordValidation,
|
||||||
|
CrudRecords,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LivingSpaceListValidation:
|
||||||
|
tr = {
|
||||||
|
**CrudRecordValidation.tr,
|
||||||
|
"fix_value": "Sabit Değer",
|
||||||
|
"fix_percent": "Sabit Yüzde",
|
||||||
|
"agreement_no": "Anlaşma No",
|
||||||
|
"marketing_process": "Pazarlama Süreci",
|
||||||
|
"marketing_layer": "Pazarlama Katmanı",
|
||||||
|
"build_parts_id": "Bölüm ID",
|
||||||
|
"build_parts_uu_id": "Bölüm UUID",
|
||||||
|
"person_id": "Sorumlu Kişi ID",
|
||||||
|
"person_uu_id": "Sorumlu Kişi UUID",
|
||||||
|
"occupant_type": "Kiracı Tipi",
|
||||||
|
"occupant_type_uu_id": "Kiracı Tipi UUID",
|
||||||
|
}
|
||||||
|
en = {
|
||||||
|
**CrudRecordValidation.en,
|
||||||
|
"fix_value": "Fixed Value",
|
||||||
|
"fix_percent": "Fixed Percent",
|
||||||
|
"agreement_no": "Agreement No",
|
||||||
|
"marketing_process": "Marketing Process",
|
||||||
|
"marketing_layer": "Marketing Layer",
|
||||||
|
"build_parts_id": "Part ID",
|
||||||
|
"build_parts_uu_id": "Part UUID",
|
||||||
|
"person_id": "Responsible Person ID",
|
||||||
|
"person_uu_id": "Responsible Person UUID",
|
||||||
|
"occupant_type": "Occupant Type",
|
||||||
|
"occupant_type_uu_id": "Occupant Type UUID",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LivingSpaceListResponse(BaseModelRegular, CrudRecords, LivingSpaceListValidation):
|
||||||
|
|
||||||
|
fix_value: Optional[float] = None
|
||||||
|
fix_percent: Optional[float] = None
|
||||||
|
agreement_no: Optional[str] = None
|
||||||
|
marketing_process: Optional[str] = None
|
||||||
|
marketing_layer: Optional[str] = None
|
||||||
|
build_parts_id: Optional[int] = None
|
||||||
|
build_parts_uu_id: Optional[str] = None
|
||||||
|
person_id: Optional[int] = None
|
||||||
|
person_uu_id: Optional[str] = None
|
||||||
|
occupant_type: Optional[str] = None
|
||||||
|
occupant_type_uu_id: Optional[str] = None
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
from typing import Optional
|
||||||
|
from api_validations.core_validations import BaseModelRegular
|
||||||
|
from api_validations.validations_request import (
|
||||||
|
CrudRecordValidation,
|
||||||
|
CrudRecords,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPartsListValidation:
|
||||||
|
tr = {
|
||||||
|
**CrudRecordValidation.tr,
|
||||||
|
"address_gov_code": "Adres Kapı Kodu",
|
||||||
|
"part_no": "Bölüm No",
|
||||||
|
"part_level": "Bölüm Seviyesi",
|
||||||
|
"part_code": "Bölüm Kodu",
|
||||||
|
"part_gross": "Bölüm Brüt",
|
||||||
|
"part_net": "Bölüm Net",
|
||||||
|
"default_accessory": "Varsayılan Aksesuar",
|
||||||
|
"human_livable": "İnsan Yaşamı",
|
||||||
|
"due_part_key": "Sabit Ödeme Grubu",
|
||||||
|
"build_uu_id": "Bina UUID",
|
||||||
|
"part_direction_uu_id": "Bölüm Yönü UUID",
|
||||||
|
"part_type_uu_id": "Bölüm Tipi UUID",
|
||||||
|
}
|
||||||
|
en = {
|
||||||
|
**CrudRecordValidation.en,
|
||||||
|
"address_gov_code": "Address Government Code",
|
||||||
|
"part_no": "Part Number",
|
||||||
|
"part_level": "Part Level",
|
||||||
|
"part_code": "Part Code",
|
||||||
|
"part_gross": "Part Gross",
|
||||||
|
"part_net": "Part Net",
|
||||||
|
"default_accessory": "Default Accessory",
|
||||||
|
"human_livable": "Human Livable",
|
||||||
|
"due_part_key": "Constant Payment Group",
|
||||||
|
"build_uu_id": "Building UUID",
|
||||||
|
"part_direction_uu_id": "Part Direction UUID",
|
||||||
|
"part_type_uu_id": "Part Type UUID",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BuildPartsListResponse(BaseModelRegular, CrudRecords, BuildPartsListValidation):
|
||||||
|
address_gov_code: Optional[str] = None
|
||||||
|
part_no: Optional[int] = None
|
||||||
|
part_level: Optional[int] = None
|
||||||
|
part_code: Optional[str] = None
|
||||||
|
part_gross: Optional[int] = None
|
||||||
|
part_net: Optional[int] = None
|
||||||
|
default_accessory: Optional[str] = None
|
||||||
|
human_livable: Optional[bool] = None
|
||||||
|
due_part_key: Optional[str] = None
|
||||||
|
build_uu_id: Optional[str] = None
|
||||||
|
part_direction_uu_id: Optional[str] = None
|
||||||
|
part_type_uu_id: Optional[str] = None
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
from typing import Optional
|
||||||
|
from api_validations.core_validations import BaseModelRegular
|
||||||
|
from api_validations.validations_request import (
|
||||||
|
CrudRecordValidation,
|
||||||
|
CrudRecords,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PeopleListValidation:
|
||||||
|
tr = {
|
||||||
|
**CrudRecordValidation.tr,
|
||||||
|
"firstname": "Ad",
|
||||||
|
"surname": "Soyad",
|
||||||
|
"middle_name": "Orta İsim",
|
||||||
|
"sex_code": "Cinsiyet Kodu",
|
||||||
|
"person_ref": "Kişi Referansı",
|
||||||
|
"person_tag": "Kişi Etiketi",
|
||||||
|
"father_name": "Baba Adı",
|
||||||
|
"mother_name": "Anne Adı",
|
||||||
|
"country_code": "Ülke Kodu",
|
||||||
|
"national_identity_id": "Kimlik Numarası",
|
||||||
|
"birth_place": "Doğum Yeri",
|
||||||
|
"birth_date": "Doğum Tarihi",
|
||||||
|
"tax_no": "Vergi Numarası",
|
||||||
|
}
|
||||||
|
en = {
|
||||||
|
**CrudRecordValidation.en,
|
||||||
|
"firstname": "First Name",
|
||||||
|
"surname": "Surname",
|
||||||
|
"middle_name": "Middle Name",
|
||||||
|
"sex_code": "Sex Code",
|
||||||
|
"person_ref": "Person Reference",
|
||||||
|
"person_tag": "Person Tag",
|
||||||
|
"father_name": "Father's Name",
|
||||||
|
"mother_name": "Mother's Name",
|
||||||
|
"country_code": "Country Code",
|
||||||
|
"national_identity_id": "National Identity ID",
|
||||||
|
"birth_place": "Birth Place",
|
||||||
|
"birth_date": "Birth Date",
|
||||||
|
"tax_no": "Tax Number",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PeopleListResponse(BaseModelRegular, CrudRecords, PeopleListValidation):
|
||||||
|
firstname: Optional[str] = None
|
||||||
|
surname: Optional[str] = None
|
||||||
|
middle_name: Optional[str] = None
|
||||||
|
sex_code: Optional[str] = None
|
||||||
|
person_ref: Optional[str] = None
|
||||||
|
person_tag: Optional[str] = None
|
||||||
|
father_name: Optional[str] = None
|
||||||
|
mother_name: Optional[str] = None
|
||||||
|
country_code: Optional[str] = None
|
||||||
|
national_identity_id: Optional[str] = None
|
||||||
|
birth_place: Optional[str] = None
|
||||||
|
birth_date: Optional[str] = None
|
||||||
|
tax_no: Optional[str] = None
|
||||||
|
|
@ -14,6 +14,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
from ApiLibrary.common.line_number import get_line_number_for_error
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
from ErrorHandlers.ErrorHandlers.api_exc_handler import HTTPExceptionApi
|
from ErrorHandlers.ErrorHandlers.api_exc_handler import HTTPExceptionApi
|
||||||
|
from AllConfigs.Token.config import Auth
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,9 +87,8 @@ class MiddlewareModule:
|
||||||
async def wrapper(request: Request, *args, **kwargs):
|
async def wrapper(request: Request, *args, **kwargs):
|
||||||
# Get and validate token context from request
|
# Get and validate token context from request
|
||||||
# Create auth context and Attach auth context to both wrapper and original function
|
# Create auth context and Attach auth context to both wrapper and original function
|
||||||
func.auth = cls.get_user_from_request(
|
func.auth = cls.get_user_from_request(request)
|
||||||
request
|
wrapper.auth = func.auth
|
||||||
) # This ensures the context is available in both places
|
|
||||||
# Call the original endpoint function
|
# Call the original endpoint function
|
||||||
if inspect.iscoroutinefunction(func):
|
if inspect.iscoroutinefunction(func):
|
||||||
return await func(request, *args, **kwargs)
|
return await func(request, *args, **kwargs)
|
||||||
|
|
@ -141,10 +141,22 @@ class LoggerTimingMiddleware(BaseHTTPMiddleware):
|
||||||
|
|
||||||
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
||||||
# Log the request
|
# Log the request
|
||||||
print(f"Handling request: {request.method} {request.url}")
|
import arrow
|
||||||
|
|
||||||
|
headers = dict(request.headers)
|
||||||
response = await call_next(request)
|
response = await call_next(request)
|
||||||
# Log the response
|
# Log the response
|
||||||
print(
|
print(
|
||||||
f"Completed request: {request.method} {request.url} with status {response.status_code}"
|
"Loggers :",
|
||||||
|
{
|
||||||
|
"url": request.url,
|
||||||
|
"method": request.method,
|
||||||
|
"access_token": headers.get(Auth.ACCESS_TOKEN_TAG, ""),
|
||||||
|
"referer": headers.get("referer", ""),
|
||||||
|
"origin": headers.get("origin", ""),
|
||||||
|
"user-agent": headers.get("user-agent", ""),
|
||||||
|
"datetime": arrow.now().format("YYYY-MM-DD HH:mm:ss ZZ"),
|
||||||
|
"status_code": response.status_code,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,196 @@
|
||||||
Token event middleware for handling authentication and event tracking.
|
Token event middleware for handling authentication and event tracking.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
from typing import Callable, Dict, Any
|
|
||||||
from .auth_middleware import MiddlewareModule
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable, Dict, Any, Optional, Union
|
||||||
|
from fastapi import Request
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||||
|
from ApiServices.Token.token_handler import TokenService
|
||||||
|
from ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
|
||||||
|
from Schemas.rules.rules import EndpointRestriction
|
||||||
|
|
||||||
|
from .auth_middleware import MiddlewareModule
|
||||||
|
from Schemas import Events
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class EventFunctions:
|
||||||
|
|
||||||
|
def __init__(self, endpoint: str, request: Request):
|
||||||
|
self.endpoint = endpoint
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
|
||||||
|
def match_endpoint_with_accesiable_event(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Match an endpoint with accessible events.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
endpoint: The endpoint to match
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the endpoint registration data
|
||||||
|
None if endpoint is not found in database
|
||||||
|
"""
|
||||||
|
access_token = TokenService.get_access_token_from_request(self.request)
|
||||||
|
token_context = TokenService.get_object_via_access_key(
|
||||||
|
access_token=access_token
|
||||||
|
)
|
||||||
|
if token_context.is_employee:
|
||||||
|
reachable_event_codes: list[str] = (
|
||||||
|
token_context.selected_company.reachable_event_codes
|
||||||
|
)
|
||||||
|
elif token_context.is_occupant:
|
||||||
|
reachable_event_codes: list[str] = (
|
||||||
|
token_context.selected_occupant.reachable_event_codes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Token not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not access_token:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Token not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
db = EndpointRestriction.new_session()
|
||||||
|
restriction = EndpointRestriction.filter_one(
|
||||||
|
EndpointRestriction.endpoint_name == self.endpoint,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
if not restriction:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function code not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
event_related = Events.filter_all(
|
||||||
|
Events.endpoint_id == restriction.id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
if not event_related:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="No event is registered for this user.",
|
||||||
|
)
|
||||||
|
an_event = event_related[0]
|
||||||
|
event_related_codes: list[str] = [
|
||||||
|
event.function_code for event in event_related
|
||||||
|
]
|
||||||
|
intersected_code: set = set(reachable_event_codes).intersection(
|
||||||
|
set(event_related_codes)
|
||||||
|
)
|
||||||
|
if not len(list(intersected_code)) == 1:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="No event is registered for this user.",
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"endpoint_url": self.endpoint,
|
||||||
|
"reachable_event_code": list(intersected_code)[0],
|
||||||
|
"class": an_event.function_class,
|
||||||
|
}
|
||||||
|
|
||||||
|
def retrieve_function_dict(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Retrieve function dictionary for a given endpoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
endpoint: The endpoint to retrieve the function dictionary for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary containing the function dictionary
|
||||||
|
None if endpoint is not found
|
||||||
|
"""
|
||||||
|
access_token = TokenService.get_access_token_from_request(self.request)
|
||||||
|
token_context = TokenService.get_object_via_access_key(
|
||||||
|
access_token=access_token
|
||||||
|
)
|
||||||
|
if token_context.is_employee:
|
||||||
|
reachable_event_codes: list[str] = (
|
||||||
|
token_context.selected_company.reachable_event_codes
|
||||||
|
)
|
||||||
|
elif token_context.is_occupant:
|
||||||
|
reachable_event_codes: list[str] = (
|
||||||
|
token_context.selected_occupant.reachable_event_codes
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Token not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not access_token:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Token not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
db = EndpointRestriction.new_session()
|
||||||
|
restriction = EndpointRestriction.filter_one(
|
||||||
|
EndpointRestriction.endpoint_name == self.endpoint,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
if not restriction:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function code not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
event_related = Events.filter_all(
|
||||||
|
Events.endpoint_id == restriction.id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
if not event_related:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="No event is registered for this user.",
|
||||||
|
)
|
||||||
|
an_event = event_related[0]
|
||||||
|
event_related_codes: list[str] = [
|
||||||
|
event.function_code for event in event_related
|
||||||
|
]
|
||||||
|
intersected_code: set = set(reachable_event_codes).intersection(
|
||||||
|
set(event_related_codes)
|
||||||
|
)
|
||||||
|
if not len(list(intersected_code)) == 1:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="No event is registered for this user.",
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"endpoint_url": self.endpoint,
|
||||||
|
"reachable_event_code": list(intersected_code)[0],
|
||||||
|
"class": an_event.function_class,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TokenEventMiddleware:
|
class TokenEventMiddleware:
|
||||||
"""
|
"""
|
||||||
|
|
@ -37,17 +222,71 @@ class TokenEventMiddleware:
|
||||||
authenticated_func = MiddlewareModule.auth_required(func)
|
authenticated_func = MiddlewareModule.auth_required(func)
|
||||||
|
|
||||||
@wraps(authenticated_func)
|
@wraps(authenticated_func)
|
||||||
async def wrapper(*args, **kwargs) -> Dict[str, Any]:
|
async def wrapper(request: Request, *args, **kwargs) -> Dict[str, Any]:
|
||||||
# Create handler with context
|
|
||||||
function_code = (
|
|
||||||
"7192c2aa-5352-4e36-98b3-dafb7d036a3d" # Keep function_code as URL
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Get function code from the function's metadata
|
||||||
|
endpoint_url = getattr(authenticated_func, "url_of_endpoint", {})
|
||||||
|
if not endpoint_url:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Function code not found",
|
||||||
|
)
|
||||||
|
|
||||||
# Make handler available to all functions in the chain
|
# Make handler available to all functions in the chain
|
||||||
func.func_code = {"function_code": function_code}
|
func.func_code = EventFunctions(endpoint_url, request).match_endpoint_with_accesiable_event()
|
||||||
# Call the authenticated function
|
# Call the authenticated function
|
||||||
if inspect.iscoroutinefunction(authenticated_func):
|
if inspect.iscoroutinefunction(authenticated_func):
|
||||||
return await authenticated_func(*args, **kwargs)
|
return await authenticated_func(request, *args, **kwargs)
|
||||||
return authenticated_func(*args, **kwargs)
|
return authenticated_func(request, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validation_required(
|
||||||
|
func: Callable[..., Dict[str, Any]]
|
||||||
|
) -> Callable[..., Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Decorator for endpoints with token and event requirements.
|
||||||
|
This decorator:
|
||||||
|
1. First validates authentication using MiddlewareModule.auth_required
|
||||||
|
2. Then adds event tracking context
|
||||||
|
|
||||||
|
Args:
|
||||||
|
func: The function to be decorated
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Callable: The wrapped function with both auth and event handling
|
||||||
|
"""
|
||||||
|
# First apply authentication
|
||||||
|
authenticated_func = MiddlewareModule.auth_required(func)
|
||||||
|
|
||||||
|
@wraps(authenticated_func)
|
||||||
|
async def wrapper(
|
||||||
|
request: Request, *args: Any, **kwargs: Any
|
||||||
|
) -> Union[Dict[str, Any], BaseModel]:
|
||||||
|
# Handle both async and sync functions
|
||||||
|
endpoint_asked = getattr(kwargs.get("data", None), "data", None).get("endpoint", None)
|
||||||
|
if not endpoint_asked:
|
||||||
|
raise HTTPExceptionApi(
|
||||||
|
error_code="",
|
||||||
|
lang="en",
|
||||||
|
loc=get_line_number_for_error(),
|
||||||
|
sys_msg="Endpoint not found",
|
||||||
|
)
|
||||||
|
wrapper.validation_code = EventFunctions(endpoint_asked, request).retrieve_function_dict()
|
||||||
|
if inspect.iscoroutinefunction(authenticated_func):
|
||||||
|
result = await authenticated_func(request, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
result = authenticated_func(request, *args, **kwargs)
|
||||||
|
function_auth = getattr(authenticated_func, "auth", None)
|
||||||
|
wrapper.auth = function_auth
|
||||||
|
func.auth = function_auth
|
||||||
|
authenticated_func.auth = function_auth
|
||||||
|
# If result is a coroutine, await it
|
||||||
|
if inspect.iscoroutine(result):
|
||||||
|
result = await result
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class OpenAPISchemaCreator:
|
||||||
"""
|
"""
|
||||||
self.app = app
|
self.app = app
|
||||||
_, self.protected_routes = get_all_routers()
|
_, self.protected_routes = get_all_routers()
|
||||||
self.tags_metadata = self._create_tags_metadata()
|
# self.tags_metadata = self._create_tags_metadata()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_tags_metadata() -> List[Dict[str, str]]:
|
def _create_tags_metadata() -> List[Dict[str, str]]:
|
||||||
|
|
@ -232,7 +232,6 @@ class OpenAPISchemaCreator:
|
||||||
description=Config.DESCRIPTION,
|
description=Config.DESCRIPTION,
|
||||||
version="1.1.1",
|
version="1.1.1",
|
||||||
routes=self.app.routes,
|
routes=self.app.routes,
|
||||||
tags=self.tags_metadata,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add security schemes
|
# Add security schemes
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,6 @@ COPY ApiEvents/AuthServiceApi /app/ApiEvents
|
||||||
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
||||||
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
||||||
|
|
||||||
# Create empty __init__.py files to make directories into Python packages
|
|
||||||
RUN touch /app/ApiEvents/__init__.py
|
|
||||||
|
|
||||||
# Set Python path to include app directory
|
# Set Python path to include app directory
|
||||||
ENV PYTHONPATH=/app \
|
ENV PYTHONPATH=/app \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,6 @@ COPY ApiEvents/EventServiceApi /app/ApiEvents
|
||||||
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
||||||
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
||||||
|
|
||||||
# Create empty __init__.py files to make directories into Python packages
|
|
||||||
RUN touch /app/ApiEvents/__init__.py
|
|
||||||
|
|
||||||
# Set Python path to include app directory
|
# Set Python path to include app directory
|
||||||
ENV PYTHONPATH=/app \
|
ENV PYTHONPATH=/app \
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ COPY Services /app/Services
|
||||||
COPY ApiServices /app/ApiServices
|
COPY ApiServices /app/ApiServices
|
||||||
|
|
||||||
# Copy Events structure with consistent naming
|
# Copy Events structure with consistent naming
|
||||||
COPY ApiEvents/EventServiceApi /app/ApiEvents
|
COPY ApiEvents/ValidationServiceApi /app/ApiEvents
|
||||||
|
ADD ApiEvents/AuthServiceApi/events /app/ApiEvents/events
|
||||||
|
ADD ApiEvents/EventServiceApi/events /app/ApiEvents/events
|
||||||
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
COPY ApiEvents/abstract_class.py /app/ApiEvents/abstract_class.py
|
||||||
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
COPY ApiEvents/base_request_model.py /app/ApiEvents/base_request_model.py
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,26 +250,33 @@ class Event2Employee(CrudCollection):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_event_id_by_employee_id(cls, employee_id) -> list:
|
def get_event_codes(cls, employee_id: int) -> list:
|
||||||
db = cls.new_session()
|
db = cls.new_session()
|
||||||
occupant_events = cls.filter_all(
|
employee_events = cls.filter_all(
|
||||||
cls.employee_id == employee_id,
|
cls.employee_id == employee_id,
|
||||||
db=db,
|
db=db,
|
||||||
).data
|
).data
|
||||||
active_events = Service2Events.filter_all(
|
active_event_ids = Service2Events.filter_all(
|
||||||
Service2Events.service_id.in_(
|
Service2Events.service_id.in_(
|
||||||
[event.event_service_id for event in occupant_events]
|
[event.event_service_id for event in employee_events]
|
||||||
),
|
),
|
||||||
db=db,
|
db=db,
|
||||||
system=True,
|
system=True,
|
||||||
).data
|
).data
|
||||||
active_events_id = [event.event_id for event in active_events]
|
active_events = Events.filter_all(
|
||||||
|
Events.id.in_([event.event_id for event in active_event_ids]),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
if extra_events := Event2EmployeeExtra.filter_all(
|
if extra_events := Event2EmployeeExtra.filter_all(
|
||||||
Event2EmployeeExtra.employee_id == employee_id,
|
Event2EmployeeExtra.employee_id == employee_id,
|
||||||
db=db,
|
db=db,
|
||||||
).data:
|
).data:
|
||||||
active_events_id.extend([event.event_id for event in extra_events])
|
events_extra = Events.filter_all(
|
||||||
return active_events_id
|
Events.id.in_([event.event_id for event in extra_events]),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
active_events.extend(events_extra)
|
||||||
|
return [event.function_code for event in active_events]
|
||||||
|
|
||||||
|
|
||||||
class Event2Occupant(CrudCollection):
|
class Event2Occupant(CrudCollection):
|
||||||
|
|
@ -307,22 +314,33 @@ class Event2Occupant(CrudCollection):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_event_id_by_build_living_space_id(cls, build_living_space_id) -> list:
|
def get_event_codes(cls, build_living_space_id) -> list:
|
||||||
|
db = cls.new_session()
|
||||||
occupant_events = cls.filter_all(
|
occupant_events = cls.filter_all(
|
||||||
cls.build_living_space_id == build_living_space_id,
|
cls.build_living_space_id == build_living_space_id,
|
||||||
|
db=db,
|
||||||
).data
|
).data
|
||||||
active_events = Service2Events.filter_all(
|
active_event_ids = Service2Events.filter_all(
|
||||||
Service2Events.service_id.in_(
|
Service2Events.service_id.in_(
|
||||||
[event.event_service_id for event in occupant_events]
|
[event.event_service_id for event in occupant_events]
|
||||||
),
|
),
|
||||||
|
db=db,
|
||||||
system=True,
|
system=True,
|
||||||
).data
|
).data
|
||||||
active_events_id = [event.event_id for event in active_events]
|
active_events = Events.filter_all(
|
||||||
|
Events.id.in_([event.event_id for event in active_event_ids]),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
if extra_events := Event2OccupantExtra.filter_all(
|
if extra_events := Event2OccupantExtra.filter_all(
|
||||||
Event2OccupantExtra.build_living_space_id == build_living_space_id
|
Event2OccupantExtra.build_living_space_id == build_living_space_id,
|
||||||
|
db=db,
|
||||||
).data:
|
).data:
|
||||||
active_events_id.extend([event.event_id for event in extra_events])
|
events_extra = Events.filter_all(
|
||||||
return active_events_id
|
Events.id.in_([event.event_id for event in extra_events]),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
active_events.extend(events_extra)
|
||||||
|
return [event.function_code for event in active_events]
|
||||||
|
|
||||||
|
|
||||||
class ModulePrice(CrudCollection):
|
class ModulePrice(CrudCollection):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue