updated events initializer
This commit is contained in:
parent
f284d4c61b
commit
b1c8203a33
|
|
@ -8,7 +8,10 @@ class RouteRegisterController:
|
||||||
self.router_list = router_list
|
self.router_list = router_list
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register_routes(self):
|
def register_routes(self):
|
||||||
for router in self.router_list:
|
for router in self.router_list:
|
||||||
self.app.include_router(router)
|
self.app.include_router(router)
|
||||||
|
self.add_router_to_database(router)
|
||||||
return self.app
|
return self.app
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from .auth.route import auth_route
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"auth_route",
|
||||||
|
]
|
||||||
|
|
@ -41,6 +41,7 @@ def authentication_login_post(
|
||||||
"language": language or "",
|
"language": language or "",
|
||||||
"domain": domain or "",
|
"domain": domain or "",
|
||||||
"eys-ext": f"{str(uuid.uuid4())}",
|
"eys-ext": f"{str(uuid.uuid4())}",
|
||||||
|
"timezone": tz or "GMT+3",
|
||||||
}
|
}
|
||||||
if not domain or not language:
|
if not domain or not language:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,564 @@
|
||||||
|
from typing import Any, List, Dict, Optional, Union
|
||||||
|
from ApiServices.AuthService.validations.custom.token import (
|
||||||
|
EmployeeTokenObject,
|
||||||
|
OccupantTokenObject,
|
||||||
|
CompanyToken,
|
||||||
|
OccupantToken,
|
||||||
|
UserType,
|
||||||
|
)
|
||||||
|
from ApiServices.TemplateService.config import api_config
|
||||||
|
from Schemas import (
|
||||||
|
Users,
|
||||||
|
People,
|
||||||
|
UsersTokens,
|
||||||
|
Credentials,
|
||||||
|
BuildLivingSpace,
|
||||||
|
BuildParts,
|
||||||
|
OccupantTypes,
|
||||||
|
Employees,
|
||||||
|
Addresses,
|
||||||
|
Companies,
|
||||||
|
Staff,
|
||||||
|
Duty,
|
||||||
|
Duties,
|
||||||
|
Departments,
|
||||||
|
Event2Employee,
|
||||||
|
)
|
||||||
|
from Modules.Token.password_module import PasswordModule
|
||||||
|
from Controllers.Redis.database import RedisActions
|
||||||
|
from Schemas.building.build import RelationshipEmployee2Build
|
||||||
|
from Schemas.event.event import Event2Occupant
|
||||||
|
|
||||||
|
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
|
||||||
|
|
||||||
|
class RedisHandlers:
|
||||||
|
AUTH_TOKEN: str = "AUTH_TOKEN"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def process_redis_object(cls, redis_object: Dict[str, Any]) -> TokenDictType:
|
||||||
|
"""Process Redis object and return appropriate token object."""
|
||||||
|
if not redis_object.get("selected_company"):
|
||||||
|
redis_object["selected_company"] = None
|
||||||
|
if not redis_object.get("selected_occupant"):
|
||||||
|
redis_object["selected_occupant"] = None
|
||||||
|
if redis_object.get("user_type") == UserType.employee.value:
|
||||||
|
return EmployeeTokenObject(**redis_object)
|
||||||
|
elif redis_object.get("user_type") == UserType.occupant.value:
|
||||||
|
return OccupantTokenObject(**redis_object)
|
||||||
|
raise ValueError("Invalid user type")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_object_from_redis(cls, access_token: str) -> TokenDictType:
|
||||||
|
redis_response = RedisActions.get_json(
|
||||||
|
list_keys=[RedisHandlers.AUTH_TOKEN, access_token, "*"]
|
||||||
|
)
|
||||||
|
if not redis_response.status:
|
||||||
|
raise ValueError("EYS_0001")
|
||||||
|
if redis_object := redis_response.first:
|
||||||
|
return cls.process_redis_object(redis_object)
|
||||||
|
raise ValueError("EYS_0002")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_object_to_redis(cls, user: Users, token, header_info):
|
||||||
|
result_delete = RedisActions.delete(
|
||||||
|
list_keys=[RedisHandlers.AUTH_TOKEN, "*", str(user.uu_id)]
|
||||||
|
)
|
||||||
|
print('result_delete', result_delete)
|
||||||
|
generated_access_token = PasswordModule.generate_access_token()
|
||||||
|
keys = [RedisHandlers.AUTH_TOKEN, generated_access_token, str(user.uu_id)]
|
||||||
|
RedisActions.set_json(
|
||||||
|
list_keys=keys,
|
||||||
|
value={**token, **header_info},
|
||||||
|
expires={"hours": 1, "minutes": 30},
|
||||||
|
)
|
||||||
|
return generated_access_token
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken]):
|
||||||
|
if already_token_data := RedisActions.get_json(
|
||||||
|
list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"]
|
||||||
|
).first:
|
||||||
|
already_token = cls.process_redis_object(**already_token_data)
|
||||||
|
if already_token.is_employee:
|
||||||
|
already_token.selected_company = add_payload
|
||||||
|
elif already_token.is_occupant:
|
||||||
|
already_token.selected_occupant = add_payload
|
||||||
|
result = RedisActions.set_json(
|
||||||
|
list_keys=[RedisHandlers.AUTH_TOKEN, token, str(already_token.user_uu_id)],
|
||||||
|
value=already_token.model_dump(),
|
||||||
|
expires={"hours": 1, "minutes": 30},
|
||||||
|
)
|
||||||
|
return result.first
|
||||||
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
|
|
||||||
|
class UserHandlers:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_user_exists(access_key: str, db_session) -> Users:
|
||||||
|
"""
|
||||||
|
Check if the user exists in the database.
|
||||||
|
"""
|
||||||
|
if "@" in access_key:
|
||||||
|
found_user: Users = Users.filter_one(
|
||||||
|
Users.email == access_key.lower(), db=db_session
|
||||||
|
).data
|
||||||
|
else:
|
||||||
|
found_user: Users = Users.filter_one(
|
||||||
|
Users.phone_number == access_key.replace(" ", ""), db=db_session
|
||||||
|
).data
|
||||||
|
if not found_user:
|
||||||
|
ValueError("EYS_0003")
|
||||||
|
return found_user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_password_valid(
|
||||||
|
domain: str, id_: str, password: str, password_hashed: str
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the password is valid.
|
||||||
|
"""
|
||||||
|
if PasswordModule.check_password(
|
||||||
|
domain=domain, id_=id_, password=password, password_hashed=password_hashed
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
raise ValueError("EYS_0004")
|
||||||
|
|
||||||
|
|
||||||
|
class LoginHandler:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_occupant(email: str):
|
||||||
|
return not str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_employee(email: str):
|
||||||
|
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_employee_login(
|
||||||
|
cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Handle employee login.
|
||||||
|
"""
|
||||||
|
language = extra_dict.get("language", "tr")
|
||||||
|
domain = extra_dict.get("domain", None)
|
||||||
|
timezone = extra_dict.get("tz", None) or "GMT+3"
|
||||||
|
|
||||||
|
user_handler = UserHandlers()
|
||||||
|
with Users.new_session() as db_session:
|
||||||
|
found_user = user_handler.check_user_exists(
|
||||||
|
access_key=data.access_key, db_session=db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
if not user_handler.check_password_valid(
|
||||||
|
domain=data.domain,
|
||||||
|
id_=str(found_user.uu_id),
|
||||||
|
password=data.password,
|
||||||
|
password_hashed=found_user.hash_password
|
||||||
|
):
|
||||||
|
raise ValueError("EYS_0005")
|
||||||
|
|
||||||
|
list_employee = Employees.filter_all(
|
||||||
|
Employees.people_id == found_user.person_id, db=db_session
|
||||||
|
).data
|
||||||
|
|
||||||
|
companies_uu_id_list: list = []
|
||||||
|
companies_id_list: list = []
|
||||||
|
companies_list: list = []
|
||||||
|
duty_uu_id_list: list = []
|
||||||
|
duty_id_list: list = []
|
||||||
|
|
||||||
|
for employee in list_employee:
|
||||||
|
staff = Staff.filter_one(
|
||||||
|
Staff.id == employee.staff_id, db=db_session
|
||||||
|
).data
|
||||||
|
if duties := Duties.filter_one(
|
||||||
|
Duties.id == staff.duties_id, db=db_session
|
||||||
|
).data:
|
||||||
|
if duty_found := Duty.filter_by_one(
|
||||||
|
id=duties.duties_id, db=db_session
|
||||||
|
).data:
|
||||||
|
duty_uu_id_list.append(str(duty_found.uu_id))
|
||||||
|
duty_id_list.append(duty_found.id)
|
||||||
|
|
||||||
|
department = Departments.filter_one(
|
||||||
|
Departments.id == duties.department_id, db=db_session
|
||||||
|
).data
|
||||||
|
|
||||||
|
if company := Companies.filter_one(
|
||||||
|
Companies.id == department.company_id, db=db_session
|
||||||
|
).data:
|
||||||
|
companies_uu_id_list.append(str(company.uu_id))
|
||||||
|
companies_id_list.append(company.id)
|
||||||
|
company_address = Addresses.filter_by_one(
|
||||||
|
id=company.official_address_id, db=db_session
|
||||||
|
).data
|
||||||
|
companies_list.append(
|
||||||
|
{
|
||||||
|
"uu_id": str(company.uu_id),
|
||||||
|
"public_name": company.public_name,
|
||||||
|
"company_type": company.company_type,
|
||||||
|
"company_address": company_address,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
person = People.filter_one(
|
||||||
|
People.id == found_user.person_id, db=db_session
|
||||||
|
).data
|
||||||
|
model_value = EmployeeTokenObject(
|
||||||
|
user_type=UserType.employee.value,
|
||||||
|
user_uu_id=str(found_user.uu_id),
|
||||||
|
user_id=found_user.id,
|
||||||
|
person_id=found_user.person_id,
|
||||||
|
person_uu_id=str(person.uu_id),
|
||||||
|
request=dict(request.headers),
|
||||||
|
companies_uu_id_list=companies_uu_id_list,
|
||||||
|
companies_id_list=companies_id_list,
|
||||||
|
duty_uu_id_list=duty_uu_id_list,
|
||||||
|
duty_id_list=duty_id_list,
|
||||||
|
).model_dump()
|
||||||
|
|
||||||
|
set_to_redis_dict = dict(
|
||||||
|
user=found_user,
|
||||||
|
token=model_value,
|
||||||
|
header_info=dict(language=language, domain=domain, timezone=timezone),
|
||||||
|
)
|
||||||
|
redis_handler = RedisHandlers()
|
||||||
|
if access_token := redis_handler.set_object_to_redis(**set_to_redis_dict):
|
||||||
|
return {
|
||||||
|
"access_token": access_token,
|
||||||
|
"user_type": UserType.employee.name,
|
||||||
|
"selection_list": companies_list,
|
||||||
|
}
|
||||||
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_employee_occupant(
|
||||||
|
cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Handle occupant login.
|
||||||
|
"""
|
||||||
|
language = extra_dict.get("language", "tr")
|
||||||
|
domain = extra_dict.get("domain", None)
|
||||||
|
timezone = extra_dict.get("tz", None) or "GMT+3"
|
||||||
|
|
||||||
|
user_handler = UserHandlers()
|
||||||
|
with Users.new_session() as db_session:
|
||||||
|
found_user = user_handler.check_user_exists(
|
||||||
|
access_key=data.access_key, db_session=db_session
|
||||||
|
)
|
||||||
|
if not user_handler.check_password_valid(
|
||||||
|
domain=data.domain,
|
||||||
|
id_=str(found_user.uu_id),
|
||||||
|
password=data.password,
|
||||||
|
password_hashed=found_user.hash_password
|
||||||
|
):
|
||||||
|
raise ValueError("EYS_0005")
|
||||||
|
|
||||||
|
occupants_selection_dict: Dict[str, Any] = {}
|
||||||
|
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_all(
|
||||||
|
BuildLivingSpace.person_id == found_user.person_id, db=db_session
|
||||||
|
).data
|
||||||
|
|
||||||
|
if not living_spaces:
|
||||||
|
raise ValueError("EYS_0006")
|
||||||
|
for living_space in living_spaces:
|
||||||
|
build_parts_selection = BuildParts.filter_all(
|
||||||
|
BuildParts.id == living_space.build_parts_id,
|
||||||
|
db=db_session,
|
||||||
|
).data
|
||||||
|
if not build_parts_selection:
|
||||||
|
raise ValueError("EYS_0007")
|
||||||
|
|
||||||
|
build_part = build_parts_selection[0]
|
||||||
|
|
||||||
|
build = build_part.buildings
|
||||||
|
occupant_type = OccupantTypes.filter_by_one(
|
||||||
|
id=living_space.occupant_type,
|
||||||
|
db=db_session,
|
||||||
|
system=True,
|
||||||
|
).data
|
||||||
|
|
||||||
|
occupant_data = {
|
||||||
|
"part_uu_id": str(build_part.uu_id),
|
||||||
|
"part_name": build_part.part_name,
|
||||||
|
"part_level": build_part.part_level,
|
||||||
|
"uu_id": str(occupant_type.uu_id),
|
||||||
|
"description": occupant_type.occupant_description,
|
||||||
|
"code": occupant_type.occupant_code,
|
||||||
|
}
|
||||||
|
|
||||||
|
build_key = str(build.uu_id)
|
||||||
|
if build_key not in occupants_selection_dict:
|
||||||
|
occupants_selection_dict[build_key] = {
|
||||||
|
"build_uu_id": build_key,
|
||||||
|
"build_name": build.build_name,
|
||||||
|
"build_no": build.build_no,
|
||||||
|
"occupants": [occupant_data],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
occupants_selection_dict[build_key]["occupants"].append(occupant_data)
|
||||||
|
|
||||||
|
person = found_user.person
|
||||||
|
model_value = OccupantTokenObject(
|
||||||
|
user_type=UserType.occupant.value,
|
||||||
|
user_uu_id=str(found_user.uu_id),
|
||||||
|
user_id=found_user.id,
|
||||||
|
person_id=person.id,
|
||||||
|
person_uu_id=str(person.uu_id),
|
||||||
|
request=dict(request.headers),
|
||||||
|
available_occupants=occupants_selection_dict,
|
||||||
|
).model_dump()
|
||||||
|
redis_handler = RedisHandlers()
|
||||||
|
if access_token := redis_handler.set_object_to_redis(
|
||||||
|
user=found_user,
|
||||||
|
token=model_value,
|
||||||
|
header_info=dict(language=language, domain=domain, timezone=timezone),
|
||||||
|
):
|
||||||
|
return {
|
||||||
|
"access_token": access_token,
|
||||||
|
"user_type": UserType.occupant.name,
|
||||||
|
"selection_list": occupants_selection_dict,
|
||||||
|
}
|
||||||
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def authentication_login_with_domain_and_creds(cls, request: Any, data: Any):
|
||||||
|
"""
|
||||||
|
Authenticate user with domain and credentials.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: FastAPI request object
|
||||||
|
data: Request body containing login credentials
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"domain": "evyos.com.tr",
|
||||||
|
"access_key": "karatay.berkay.sup@evyos.com.tr",
|
||||||
|
"password": "string",
|
||||||
|
"remember_me": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Returns:
|
||||||
|
SuccessResponse containing authentication token and user info
|
||||||
|
"""
|
||||||
|
language = request.headers("language", "tr")
|
||||||
|
domain = request.headers("domain", None)
|
||||||
|
timezone = request.headers("tz", None) or "GMT+3"
|
||||||
|
|
||||||
|
if cls.is_employee(data.access_key):
|
||||||
|
return cls.do_employee_login(
|
||||||
|
request=request,
|
||||||
|
data=data,
|
||||||
|
extra_dict=dict(
|
||||||
|
language=language,
|
||||||
|
domain=domain,
|
||||||
|
timezone=timezone,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
elif cls.is_occupant(data.access_key):
|
||||||
|
return cls.do_employee_login(
|
||||||
|
request=request,
|
||||||
|
data=data,
|
||||||
|
extra_dict=dict(
|
||||||
|
language=language,
|
||||||
|
domain=domain,
|
||||||
|
timezone=timezone,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid email format")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def raise_error_if_request_has_no_token(cls, request: Any) -> None:
|
||||||
|
"""Validate request has required token headers."""
|
||||||
|
if not hasattr(request, "headers"):
|
||||||
|
raise ValueError("Request has no headers")
|
||||||
|
if not request.headers.get(api_config.ACCESS_TOKEN_TAG):
|
||||||
|
raise ValueError("Request has no access token")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_access_token_from_request(cls, request: Any) -> str:
|
||||||
|
"""Extract access token from request headers."""
|
||||||
|
cls.raise_error_if_request_has_no_token(request=request)
|
||||||
|
return request.headers.get(api_config.ACCESS_TOKEN_TAG)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||||
|
with Users.new_session() as db:
|
||||||
|
if data.company_uu_id not in token_dict.companies_uu_id_list:
|
||||||
|
ValueError("EYS_0011")
|
||||||
|
selected_company: Companies = Companies.filter_one(
|
||||||
|
Companies.uu_id == data.company_uu_id, db=db
|
||||||
|
).data
|
||||||
|
if not selected_company:
|
||||||
|
ValueError("EYS_0009")
|
||||||
|
|
||||||
|
# Get duties IDs for the company
|
||||||
|
duties_ids = [
|
||||||
|
duty.id
|
||||||
|
for duty in Duties.filter_all(
|
||||||
|
Duties.company_id == selected_company.id, db=db
|
||||||
|
).data
|
||||||
|
]
|
||||||
|
|
||||||
|
# Get staff IDs
|
||||||
|
staff_ids = [
|
||||||
|
staff.id
|
||||||
|
for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids), db=db).data
|
||||||
|
]
|
||||||
|
|
||||||
|
# Get employee
|
||||||
|
employee: Employees = Employees.filter_one(
|
||||||
|
Employees.people_id == token_dict.person_id,
|
||||||
|
Employees.staff_id.in_(staff_ids),
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
|
||||||
|
if not employee:
|
||||||
|
ValueError("EYS_0010")
|
||||||
|
|
||||||
|
# Get reachable events
|
||||||
|
reachable_event_codes = Event2Employee.get_event_codes(
|
||||||
|
employee_id=employee.id, db=db
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get staff and duties
|
||||||
|
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db).data
|
||||||
|
duties = Duties.filter_one(Duties.id == staff.duties_id, db=db).data
|
||||||
|
department = Departments.filter_one(
|
||||||
|
Departments.id == duties.department_id, db=db
|
||||||
|
).data
|
||||||
|
|
||||||
|
# Get bulk duty
|
||||||
|
bulk_id = Duty.filter_by_one_system(duty_code="BULK", db=db).data
|
||||||
|
bulk_duty_id = Duties.filter_by_one(
|
||||||
|
company_id=selected_company.id,
|
||||||
|
duties_id=bulk_id.id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
|
||||||
|
# Create company token
|
||||||
|
company_token = CompanyToken(
|
||||||
|
company_uu_id=selected_company.uu_id.__str__(),
|
||||||
|
company_id=selected_company.id,
|
||||||
|
department_id=department.id,
|
||||||
|
department_uu_id=department.uu_id.__str__(),
|
||||||
|
duty_id=duties.id,
|
||||||
|
duty_uu_id=duties.uu_id.__str__(),
|
||||||
|
bulk_duties_id=bulk_duty_id.id,
|
||||||
|
staff_id=staff.id,
|
||||||
|
staff_uu_id=staff.uu_id.__str__(),
|
||||||
|
employee_id=employee.id,
|
||||||
|
employee_uu_id=employee.uu_id.__str__(),
|
||||||
|
reachable_event_codes=reachable_event_codes,
|
||||||
|
)
|
||||||
|
redis_handler = RedisHandlers()
|
||||||
|
try: # Update Redis
|
||||||
|
return redis_handler.update_token_at_redis(
|
||||||
|
token=access_token, add_payload=company_token
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
err = e
|
||||||
|
ValueError("EYS_0008")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def handle_occupant_selection(
|
||||||
|
cls, access_token: str, data: Any, token_dict: TokenDictType
|
||||||
|
):
|
||||||
|
"""Handle occupant type selection"""
|
||||||
|
with BuildLivingSpace.new_session() as db:
|
||||||
|
# Get selected occupant type
|
||||||
|
selected_build_living_space: BuildLivingSpace = BuildLivingSpace.filter_one(
|
||||||
|
BuildLivingSpace.uu_id == data.build_living_space_uu_id,
|
||||||
|
db=db,
|
||||||
|
).data
|
||||||
|
if not selected_build_living_space:
|
||||||
|
raise ValueError("EYS_0012")
|
||||||
|
|
||||||
|
# Get reachable events
|
||||||
|
reachable_event_codes = Event2Occupant.get_event_codes(
|
||||||
|
build_living_space_id=selected_build_living_space.id, db=db
|
||||||
|
)
|
||||||
|
occupant_type = OccupantTypes.filter_one_system(
|
||||||
|
OccupantTypes.id == selected_build_living_space.occupant_type_id,
|
||||||
|
db=db,
|
||||||
|
).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,
|
||||||
|
)
|
||||||
|
redis_handler = RedisHandlers()
|
||||||
|
try: # Update Redis
|
||||||
|
return redis_handler.update_token_at_redis(
|
||||||
|
token=access_token, add_payload=occupant_token
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError("EYS_0008")
|
||||||
|
|
||||||
|
@classmethod # Requires auth context
|
||||||
|
def authentication_select_company_or_occupant_type(cls, request: Any, data: Any):
|
||||||
|
"""
|
||||||
|
Handle selection of company or occupant type
|
||||||
|
{"data": {"build_living_space_uu_id": ""}} | {"data": {"company_uu_id": ""}}
|
||||||
|
{
|
||||||
|
"data": {"company_uu_id": "e9869a25-ba4d-49dc-bb0d-8286343b184b"}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"data": {"build_living_space_uu_id": "e9869a25-ba4d-49dc-bb0d-8286343b184b"}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
access_token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
||||||
|
if not access_token:
|
||||||
|
raise ValueError("EYS_0001")
|
||||||
|
|
||||||
|
token_object = RedisHandlers().get_object_from_redis(access_token=access_token)
|
||||||
|
if token_object.is_employee and isinstance(data, CompanyToken):
|
||||||
|
return cls.handle_employee_selection(
|
||||||
|
access_token=access_token, data=data, token_dict=token_object,
|
||||||
|
)
|
||||||
|
elif token_object.is_occupant and isinstance(data, OccupantToken):
|
||||||
|
return cls.handle_occupant_selection(
|
||||||
|
access_token=access_token, data=data, token_dict=token_object,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthHandlers:
|
class AuthHandlers:
|
||||||
pass
|
LoginHandler: LoginHandler = LoginHandler()
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ class OccupantToken(BaseModel):
|
||||||
responsible_employee_uuid: Optional[str] = None
|
responsible_employee_uuid: Optional[str] = None
|
||||||
|
|
||||||
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
|
reachable_app_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
|
|
||||||
|
|
||||||
class CompanyToken(BaseModel):
|
class CompanyToken(BaseModel):
|
||||||
|
|
@ -83,6 +84,7 @@ class CompanyToken(BaseModel):
|
||||||
bulk_duties_id: int
|
bulk_duties_id: int
|
||||||
|
|
||||||
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
|
reachable_app_codes: Optional[list[str]] = None # ID list of reachable modules
|
||||||
|
|
||||||
|
|
||||||
class OccupantTokenObject(ApplicationToken):
|
class OccupantTokenObject(ApplicationToken):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
# Install system dependencies and Poetry
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry
|
||||||
|
|
||||||
|
# Copy Poetry configuration
|
||||||
|
COPY /pyproject.toml ./pyproject.toml
|
||||||
|
|
||||||
|
# Configure Poetry and install dependencies with optimizations
|
||||||
|
RUN poetry config virtualenvs.create false \
|
||||||
|
&& poetry install --no-interaction --no-ansi --no-root --only main \
|
||||||
|
&& pip cache purge && rm -rf ~/.cache/pypoetry
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY /ApiServices/InitialService /ApiServices/InitialService
|
||||||
|
COPY /Controllers /Controllers
|
||||||
|
COPY /Schemas/building /Schemas/building
|
||||||
|
COPY /Schemas/company /Schemas/company
|
||||||
|
COPY /Schemas/identity /Schemas/identity
|
||||||
|
|
||||||
|
# Set Python path to include app directory
|
||||||
|
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# Run the application using the configured uvicorn server
|
||||||
|
CMD ["poetry", "run", "python", "ApiServices/InitialService/app.py"]
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
from Schemas import (
|
||||||
|
BuildLivingSpace,
|
||||||
|
BuildParts,
|
||||||
|
Companies,
|
||||||
|
Departments,
|
||||||
|
Duties,
|
||||||
|
Duty,
|
||||||
|
Staff,
|
||||||
|
Employees,
|
||||||
|
Event2Employee,
|
||||||
|
Event2Occupant,
|
||||||
|
OccupantTypes,
|
||||||
|
Users,
|
||||||
|
UsersTokens,
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
|
|
@ -17,3 +17,5 @@ AccountCreate:
|
||||||
|
|
||||||
|
|
||||||
ApplicationService -> Serves only app pages that are reachable for Client Side
|
ApplicationService -> Serves only app pages that are reachable for Client Side
|
||||||
|
|
||||||
|
IdentityService -> Super User Attach related events to user
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
|
||||||
|
|
||||||
from ApiServices.TemplateService.create_route import RouteRegisterController
|
|
||||||
from ApiServices.TemplateService.endpoints.routes import get_routes
|
from ApiServices.TemplateService.endpoints.routes import get_routes
|
||||||
from ApiServices.TemplateService.open_api_creator import create_openapi_schema
|
from ApiServices.TemplateService.open_api_creator import create_openapi_schema
|
||||||
from ApiServices.TemplateService.middlewares.token_middleware import token_middleware
|
from ApiServices.TemplateService.middlewares.token_middleware import token_middleware
|
||||||
from ApiServices.TemplateService.config import template_api_config
|
from ApiServices.TemplateService.initializer.create_route import RouteRegisterController
|
||||||
|
|
||||||
|
from .config import api_config
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
|
|
||||||
application = FastAPI(**template_api_config.api_info)
|
application = FastAPI(**api_config.api_info)
|
||||||
# application.mount(
|
# application.mount(
|
||||||
# "/application/static",
|
# "/application/static",
|
||||||
# StaticFiles(directory="application/static"),
|
# StaticFiles(directory="application/static"),
|
||||||
|
|
@ -20,7 +20,7 @@ def create_app():
|
||||||
# )
|
# )
|
||||||
application.add_middleware(
|
application.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=template_api_config.ALLOW_ORIGINS,
|
allow_origins=api_config.ALLOW_ORIGINS,
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
from typing import List
|
|
||||||
from fastapi import APIRouter, FastAPI
|
|
||||||
|
|
||||||
|
|
||||||
class RouteRegisterController:
|
|
||||||
|
|
||||||
def __init__(self, app: FastAPI, router_list: List[APIRouter]):
|
|
||||||
self.router_list = router_list
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
def register_routes(self):
|
|
||||||
for router in self.router_list:
|
|
||||||
self.app.include_router(router)
|
|
||||||
return self.app
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
from fastapi import APIRouter, Request, Response
|
from fastapi import APIRouter, Request, Response
|
||||||
|
|
||||||
|
from ApiServices.TemplateService.events.events_setter import event_cluster
|
||||||
|
|
||||||
test_template_route = APIRouter(prefix="/test", tags=["Test"])
|
test_template_route = APIRouter(prefix="/test", tags=["Test"])
|
||||||
|
|
||||||
|
|
||||||
@test_template_route.get(path="/template", description="Test Template Route")
|
@test_template_route.get(
|
||||||
|
path="/template",
|
||||||
|
description="Test Template Route",
|
||||||
|
operation_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5"
|
||||||
|
)
|
||||||
def test_template(request: Request, response: Response):
|
def test_template(request: Request, response: Response):
|
||||||
"""
|
"""
|
||||||
Test Template Route
|
Test Template Route
|
||||||
"""
|
"""
|
||||||
headers = dict(request.headers)
|
headers = dict(request.headers)
|
||||||
|
event_cluster_matched = event_cluster.match_event(
|
||||||
|
event_keys=[
|
||||||
|
"3f510dcf-9f84-4eb9-b919-f582f30adab1",
|
||||||
|
"9f403034-deba-4e1f-b43e-b25d3c808d39",
|
||||||
|
"b8ec6e64-286a-4f60-8554-7a3865454944"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
event_cluster_matched.example_callable()
|
||||||
response.headers["X-Header"] = "Test Header GET"
|
response.headers["X-Header"] = "Test Header GET"
|
||||||
return {
|
return {
|
||||||
"completed": True,
|
"completed": True,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
from ApiServices.TemplateService.initializer.event_clusters import EventCluster, Event
|
||||||
|
|
||||||
|
|
||||||
|
single_event = Event(
|
||||||
|
name="example_event",
|
||||||
|
key="176b829c-7622-4cf2-b474-411e5acb637c",
|
||||||
|
request_validator=None, # TODO: Add request validator
|
||||||
|
response_validator=None, # TODO: Add response validator
|
||||||
|
description="Example event description",
|
||||||
|
)
|
||||||
|
|
||||||
|
def example_callable():
|
||||||
|
"""
|
||||||
|
Example callable method
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"completed": True,
|
||||||
|
"message": "Example callable method 2",
|
||||||
|
"info": {
|
||||||
|
"host": "example_host",
|
||||||
|
"user_agent": "example_user_agent",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
single_event.event_callable = example_callable
|
||||||
|
|
||||||
|
other_event = Event(
|
||||||
|
name="example_event-2",
|
||||||
|
key="176b829c-7622-4cf2-b474-421e5acb637c",
|
||||||
|
request_validator=None, # TODO: Add request validator
|
||||||
|
response_validator=None, # TODO: Add response validator
|
||||||
|
description="Example event 2 description",
|
||||||
|
)
|
||||||
|
def example_callable_other():
|
||||||
|
"""
|
||||||
|
Example callable method
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"completed": True,
|
||||||
|
"message": "Example callable method 1",
|
||||||
|
"info": {
|
||||||
|
"host": "example_host",
|
||||||
|
"user_agent": "example_user_agent",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
other_event.event_callable = example_callable_other
|
||||||
|
|
||||||
|
tokens_in_redis = [
|
||||||
|
"3f510dcf-9f84-4eb9-b919-f582f30adab1",
|
||||||
|
"9f403034-deba-4e1f-b43e-b25d3c808d39",
|
||||||
|
"b8ec6e64-286a-4f60-8554-7a3865454944",
|
||||||
|
"176b829c-7622-4cf2-b474-421e5acb637c",
|
||||||
|
]
|
||||||
|
template_event_cluster = EventCluster(endpoint_uu_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5")
|
||||||
|
template_event_cluster.add_event([single_event, other_event])
|
||||||
|
matched_event = template_event_cluster.match_event(event_keys=tokens_in_redis)
|
||||||
|
|
||||||
|
print('event_callable', matched_event.event_callable())
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
from typing import List
|
||||||
|
from fastapi import APIRouter, FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
class RouteRegisterController:
|
||||||
|
|
||||||
|
def __init__(self, app: FastAPI, router_list: List[APIRouter]):
|
||||||
|
self.router_list = router_list
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_router_with_event_to_database(route: APIRouter):
|
||||||
|
from Schemas import EndpointRestriction
|
||||||
|
|
||||||
|
with EndpointRestriction.new_session() as db_session:
|
||||||
|
route_path = str(getattr(route, "path"))
|
||||||
|
route_summary = str(getattr(route, "name")) or ""
|
||||||
|
operation_id = str(getattr(route, "operation_id")) or ""
|
||||||
|
if not operation_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
for route_method in [method.lower() for method in getattr(route, "methods")]:
|
||||||
|
restriction = EndpointRestriction.find_or_create(
|
||||||
|
**dict(
|
||||||
|
endpoint_method=route_method,
|
||||||
|
endpoint_name=route_path,
|
||||||
|
endpoint_desc=route_summary.replace("_", " "),
|
||||||
|
endpoint_function=route_summary,
|
||||||
|
operation_uu_id=operation_id, # UUID of the endpoint
|
||||||
|
is_confirmed=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not restriction.meta_data.created:
|
||||||
|
restriction.endpoint_code = f"AR{str(restriction.id).zfill(3)}"
|
||||||
|
restriction.save(db=db_session)
|
||||||
|
|
||||||
|
def register_routes(self):
|
||||||
|
for router in self.router_list:
|
||||||
|
self.app.include_router(router)
|
||||||
|
self.add_router_with_event_to_database(router)
|
||||||
|
return self.app
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
|
||||||
|
class EventCluster:
|
||||||
|
|
||||||
|
def __init__(self, endpoint_uu_id: str):
|
||||||
|
self.endpoint_uu_id = endpoint_uu_id
|
||||||
|
self.events = []
|
||||||
|
|
||||||
|
def add_event(self, list_of_events: list["Event"]):
|
||||||
|
"""
|
||||||
|
Add an event to the cluster
|
||||||
|
"""
|
||||||
|
for event in list_of_events:
|
||||||
|
self.events.append(event)
|
||||||
|
self.events = list(set(self.events))
|
||||||
|
|
||||||
|
def get_event(self, event_key: str):
|
||||||
|
"""
|
||||||
|
Get an event by its key
|
||||||
|
"""
|
||||||
|
|
||||||
|
for event in self.events:
|
||||||
|
if event.key == event_key:
|
||||||
|
return event
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_events_to_database(self):
|
||||||
|
from Schemas import Events, EndpointRestriction
|
||||||
|
with Events.new_session() as db_session:
|
||||||
|
|
||||||
|
if to_save_endpoint := EndpointRestriction.filter_one(
|
||||||
|
EndpointRestriction.uu_id == self.endpoint_uu_id,
|
||||||
|
db=db_session,
|
||||||
|
).data:
|
||||||
|
for event in self.events:
|
||||||
|
event_obj = Events.find_or_create(
|
||||||
|
function_code=event.key,
|
||||||
|
function_class=event.name,
|
||||||
|
description=event.description,
|
||||||
|
endpoint_id=to_save_endpoint.id,
|
||||||
|
endpoint_uu_id=str(to_save_endpoint.uu_id),
|
||||||
|
is_confirmed=True,
|
||||||
|
active=True,
|
||||||
|
db=db_session,
|
||||||
|
)
|
||||||
|
event_obj.save()
|
||||||
|
print(f'UUID: {event_obj.uu_id} event is saved to {to_save_endpoint.uu_id}')
|
||||||
|
|
||||||
|
def match_event(self, event_keys: list[str]) -> "Event":
|
||||||
|
"""
|
||||||
|
Match an event by its key
|
||||||
|
"""
|
||||||
|
print('set(event_keys)', set(event_keys))
|
||||||
|
print('event.keys', set([event.key for event in self.events]))
|
||||||
|
intersection_of_key: set[str] = set(event_keys) & set([event.key for event in self.events])
|
||||||
|
if not len(intersection_of_key) == 1:
|
||||||
|
raise ValueError(
|
||||||
|
f"Event key not found or multiple matches found: {intersection_of_key}"
|
||||||
|
)
|
||||||
|
return self.get_event(event_key=list(intersection_of_key)[0])
|
||||||
|
|
||||||
|
|
||||||
|
class Event:
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
key: str,
|
||||||
|
request_validator: str = None,
|
||||||
|
response_validator: str = None,
|
||||||
|
description: str = "",
|
||||||
|
):
|
||||||
|
self.name = name
|
||||||
|
self.key = key
|
||||||
|
self.request_validator = request_validator
|
||||||
|
self.response_validator = response_validator
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
|
def event_callable(self):
|
||||||
|
"""
|
||||||
|
Example callable method
|
||||||
|
"""
|
||||||
|
print(self.name)
|
||||||
|
return {}
|
||||||
|
|
@ -1,23 +1,13 @@
|
||||||
import arrow
|
import arrow
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from typing import Optional, Any, Dict, List
|
from typing import Optional, Any, Dict
|
||||||
from sqlalchemy.orm import Session, Mapped
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from fastapi.exceptions import HTTPException
|
||||||
|
|
||||||
from sqlalchemy import TIMESTAMP, NUMERIC
|
from sqlalchemy import TIMESTAMP, NUMERIC
|
||||||
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
||||||
|
from sqlalchemy.orm import Session, Mapped
|
||||||
|
|
||||||
class Credentials(BaseModel):
|
|
||||||
"""
|
|
||||||
Class to store user credentials.
|
|
||||||
"""
|
|
||||||
|
|
||||||
person_id: int
|
|
||||||
person_name: str
|
|
||||||
full_name: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class MetaData:
|
class MetaData:
|
||||||
|
|
@ -27,6 +17,7 @@ class MetaData:
|
||||||
|
|
||||||
created: bool = False
|
created: bool = False
|
||||||
updated: bool = False
|
updated: bool = False
|
||||||
|
deleted: bool = False
|
||||||
|
|
||||||
|
|
||||||
class CRUDModel:
|
class CRUDModel:
|
||||||
|
|
@ -43,7 +34,7 @@ class CRUDModel:
|
||||||
|
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|
||||||
creds: Credentials = None
|
# creds: Credentials = None
|
||||||
meta_data: MetaData = MetaData()
|
meta_data: MetaData = MetaData()
|
||||||
|
|
||||||
# Define required columns for CRUD operations
|
# Define required columns for CRUD operations
|
||||||
|
|
@ -57,23 +48,6 @@ class CRUDModel:
|
||||||
"deleted": bool,
|
"deleted": bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_credentials(cls, record_created) -> None:
|
|
||||||
"""
|
|
||||||
Save user credentials for tracking.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
record_created: Record that created or updated
|
|
||||||
"""
|
|
||||||
if not cls.creds:
|
|
||||||
return
|
|
||||||
|
|
||||||
if getattr(cls.creds, "person_id", None) and getattr(
|
|
||||||
cls.creds, "person_name", None
|
|
||||||
):
|
|
||||||
record_created.created_by_id = cls.creds.person_id
|
|
||||||
record_created.created_by = cls.creds.person_name
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def raise_exception(
|
def raise_exception(
|
||||||
cls, message: str = "Exception raised.", status_code: int = 400
|
cls, message: str = "Exception raised.", status_code: int = 400
|
||||||
|
|
@ -126,7 +100,6 @@ class CRUDModel:
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(created_record, key, value)
|
setattr(created_record, key, value)
|
||||||
|
|
||||||
cls.create_credentials(created_record)
|
|
||||||
db.add(created_record)
|
db.add(created_record)
|
||||||
db.flush()
|
db.flush()
|
||||||
return created_record
|
return created_record
|
||||||
|
|
@ -194,6 +167,7 @@ class CRUDModel:
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
err = e
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
def get_dict(
|
def get_dict(
|
||||||
|
|
@ -234,6 +208,7 @@ class CRUDModel:
|
||||||
return return_dict
|
return return_dict
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
err = e
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -278,7 +253,6 @@ class CRUDModel:
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(created_record, key, value)
|
setattr(created_record, key, value)
|
||||||
|
|
||||||
cls.create_credentials(created_record)
|
|
||||||
db.add(created_record)
|
db.add(created_record)
|
||||||
db.flush()
|
db.flush()
|
||||||
cls.meta_data.created = True
|
cls.meta_data.created = True
|
||||||
|
|
@ -308,7 +282,6 @@ class CRUDModel:
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
self.update_credentials()
|
|
||||||
db.flush()
|
db.flush()
|
||||||
self.meta_data.updated = True
|
self.meta_data.updated = True
|
||||||
return self
|
return self
|
||||||
|
|
@ -317,17 +290,3 @@ class CRUDModel:
|
||||||
self.meta_data.updated = False
|
self.meta_data.updated = False
|
||||||
db.rollback()
|
db.rollback()
|
||||||
self.raise_exception(f"Failed to update record: {str(e)}", status_code=500)
|
self.raise_exception(f"Failed to update record: {str(e)}", status_code=500)
|
||||||
|
|
||||||
def update_credentials(self) -> None:
|
|
||||||
"""
|
|
||||||
Save user credentials for tracking.
|
|
||||||
"""
|
|
||||||
if not self.creds:
|
|
||||||
return
|
|
||||||
|
|
||||||
person_id = getattr(self.creds, "person_id", None)
|
|
||||||
person_name = getattr(self.creds, "person_name", None)
|
|
||||||
|
|
||||||
if person_id and person_name:
|
|
||||||
self.updated_by_id = self.creds.person_id
|
|
||||||
self.updated_by = self.creds.person_name
|
|
||||||
|
|
|
||||||
|
|
@ -72,27 +72,6 @@ class CrudMixin(BasicMixin):
|
||||||
comment="Record validity end timestamp",
|
comment="Record validity end timestamp",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CrudCollection(CrudMixin):
|
|
||||||
"""
|
|
||||||
Full-featured model class with all common fields.
|
|
||||||
|
|
||||||
Includes:
|
|
||||||
- UUID and reference ID
|
|
||||||
- Timestamps
|
|
||||||
- User tracking
|
|
||||||
- Confirmation status
|
|
||||||
- Soft delete
|
|
||||||
- Notification flags
|
|
||||||
"""
|
|
||||||
|
|
||||||
__abstract__ = True
|
|
||||||
__repr__ = ReprMixin.__repr__
|
|
||||||
|
|
||||||
ref_id: Mapped[str] = mapped_column(
|
|
||||||
String(100), nullable=True, index=True, comment="External reference ID"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
created_at: Mapped[TIMESTAMP] = mapped_column(
|
created_at: Mapped[TIMESTAMP] = mapped_column(
|
||||||
TIMESTAMP(timezone=True),
|
TIMESTAMP(timezone=True),
|
||||||
|
|
@ -110,36 +89,51 @@ class CrudCollection(CrudMixin):
|
||||||
comment="Last update timestamp",
|
comment="Last update timestamp",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CrudCollection(CrudMixin):
|
||||||
|
"""
|
||||||
|
Full-featured model class with all common fields.
|
||||||
|
|
||||||
|
Includes:
|
||||||
|
- UUID and reference ID
|
||||||
|
- Timestamps
|
||||||
|
- User tracking
|
||||||
|
- Confirmation status
|
||||||
|
- Soft delete
|
||||||
|
- Notification flags
|
||||||
|
"""
|
||||||
|
|
||||||
|
__abstract__ = True
|
||||||
|
__repr__ = ReprMixin.__repr__
|
||||||
|
|
||||||
|
# Outer reference fields
|
||||||
|
ref_id: Mapped[str] = mapped_column(
|
||||||
|
String(100), nullable=True, index=True, comment="External reference ID"
|
||||||
|
)
|
||||||
|
replication_id: Mapped[int] = mapped_column(
|
||||||
|
SmallInteger, server_default="0", comment="Replication identifier"
|
||||||
|
)
|
||||||
|
|
||||||
# Cryptographic and user tracking
|
# Cryptographic and user tracking
|
||||||
cryp_uu_id: Mapped[str] = mapped_column(
|
cryp_uu_id: Mapped[str] = mapped_column(
|
||||||
String, nullable=True, index=True, comment="Cryptographic UUID"
|
String, nullable=True, index=True, comment="Cryptographic UUID"
|
||||||
)
|
)
|
||||||
# created_by: Mapped[str] = mapped_column(
|
|
||||||
# String, nullable=True, comment="Creator name"
|
# Token fields of modification
|
||||||
# )
|
created_credentials_token: Mapped[str] = mapped_column(
|
||||||
# created_by_id: Mapped[int] = mapped_column(
|
String, nullable=True, comment="Created Credentials token"
|
||||||
# Integer, nullable=True, comment="Creator ID"
|
|
||||||
# )
|
|
||||||
# updated_by: Mapped[str] = mapped_column(
|
|
||||||
# String, nullable=True, comment="Last modifier name"
|
|
||||||
# )
|
|
||||||
# updated_by_id: Mapped[int] = mapped_column(
|
|
||||||
# Integer, nullable=True, comment="Last modifier ID"
|
|
||||||
# )
|
|
||||||
confirmed_by: Mapped[str] = mapped_column(
|
|
||||||
String, nullable=True, comment="Confirmer name"
|
|
||||||
)
|
)
|
||||||
confirmed_by_id: Mapped[int] = mapped_column(
|
updated_credentials_token: Mapped[str] = mapped_column(
|
||||||
Integer, nullable=True, comment="Confirmer ID"
|
String, nullable=True, comment="Updated Credentials token"
|
||||||
|
)
|
||||||
|
confirmed_credentials_token: Mapped[str] = mapped_column(
|
||||||
|
String, nullable=True, comment="Confirmed Credentials token"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Status flags
|
# Status flags
|
||||||
is_confirmed: Mapped[bool] = mapped_column(
|
is_confirmed: Mapped[bool] = mapped_column(
|
||||||
Boolean, server_default="0", comment="Record confirmation status"
|
Boolean, server_default="0", comment="Record confirmation status"
|
||||||
)
|
)
|
||||||
replication_id: Mapped[int] = mapped_column(
|
|
||||||
SmallInteger, server_default="0", comment="Replication identifier"
|
|
||||||
)
|
|
||||||
deleted: Mapped[bool] = mapped_column(
|
deleted: Mapped[bool] = mapped_column(
|
||||||
Boolean, server_default="0", comment="Soft delete flag"
|
Boolean, server_default="0", comment="Soft delete flag"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class PasswordModule:
|
||||||
i, random.choice(merged_letters), 1
|
i, random.choice(merged_letters), 1
|
||||||
)
|
)
|
||||||
return token_generated
|
return token_generated
|
||||||
|
raise ValueError("EYS_0004")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_access_token(cls) -> str:
|
def generate_access_token(cls) -> str:
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ from Schemas.identity.identity import (
|
||||||
OccupantTypes,
|
OccupantTypes,
|
||||||
People,
|
People,
|
||||||
Users,
|
Users,
|
||||||
|
Credentials,
|
||||||
RelationshipDutyPeople,
|
RelationshipDutyPeople,
|
||||||
Contracts,
|
Contracts,
|
||||||
)
|
)
|
||||||
|
|
@ -188,6 +189,7 @@ __all__ = [
|
||||||
"OccupantTypes",
|
"OccupantTypes",
|
||||||
"People",
|
"People",
|
||||||
"Users",
|
"Users",
|
||||||
|
"Credentials",
|
||||||
"RelationshipDutyPeople",
|
"RelationshipDutyPeople",
|
||||||
"RelationshipEmployee2PostCode",
|
"RelationshipEmployee2PostCode",
|
||||||
"Contracts",
|
"Contracts",
|
||||||
|
|
|
||||||
|
|
@ -230,8 +230,7 @@ class Event2Employee(CrudCollection):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_event_codes(cls, employee_id: int) -> list:
|
def get_event_codes(cls, employee_id: int, db) -> list:
|
||||||
db = cls.new_session()
|
|
||||||
employee_events = cls.filter_all(
|
employee_events = cls.filter_all(
|
||||||
cls.employee_id == employee_id,
|
cls.employee_id == employee_id,
|
||||||
db=db,
|
db=db,
|
||||||
|
|
@ -328,8 +327,7 @@ class Event2Occupant(CrudCollection):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_event_codes(cls, build_living_space_id) -> list:
|
def get_event_codes(cls, build_living_space_id, db) -> 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,
|
db=db,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ class EndpointRestriction(CrudCollection):
|
||||||
__tablename__ = "endpoint_restriction"
|
__tablename__ = "endpoint_restriction"
|
||||||
__exclude__fields__ = []
|
__exclude__fields__ = []
|
||||||
|
|
||||||
|
operation_uu_id: Mapped[UUID] = mapped_column(
|
||||||
|
String, comment="UUID of the operation",
|
||||||
|
)
|
||||||
endpoint_function: Mapped[str] = mapped_column(
|
endpoint_function: Mapped[str] = mapped_column(
|
||||||
String, server_default="", comment="Function name of the API endpoint"
|
String, server_default="", comment="Function name of the API endpoint"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue