updated events initializer

This commit is contained in:
berkay 2025-04-04 12:03:00 +03:00
parent f284d4c61b
commit b1c8203a33
24 changed files with 874 additions and 114 deletions

View File

@ -8,7 +8,10 @@ class RouteRegisterController:
self.router_list = router_list
self.app = app
def register_routes(self):
for router in self.router_list:
self.app.include_router(router)
self.add_router_to_database(router)
return self.app

View File

@ -0,0 +1,5 @@
from .auth.route import auth_route
__all__ = [
"auth_route",
]

View File

@ -41,6 +41,7 @@ def authentication_login_post(
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"timezone": tz or "GMT+3",
}
if not domain or not language:
return JSONResponse(

View File

@ -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:
pass
LoginHandler: LoginHandler = LoginHandler()

View File

@ -60,6 +60,7 @@ class OccupantToken(BaseModel):
responsible_employee_uuid: Optional[str] = None
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):
@ -83,6 +84,7 @@ class CompanyToken(BaseModel):
bulk_duties_id: int
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):

View File

@ -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"]

View File

@ -0,0 +1,18 @@
from Schemas import (
BuildLivingSpace,
BuildParts,
Companies,
Departments,
Duties,
Duty,
Staff,
Employees,
Event2Employee,
Event2Occupant,
OccupantTypes,
Users,
UsersTokens,
)
if __name__ == "__main__":
pass

View File

@ -17,3 +17,5 @@ AccountCreate:
ApplicationService -> Serves only app pages that are reachable for Client Side
IdentityService -> Super User Attach related events to user

View File

View File

@ -1,18 +1,18 @@
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
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.open_api_creator import create_openapi_schema
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():
application = FastAPI(**template_api_config.api_info)
application = FastAPI(**api_config.api_info)
# application.mount(
# "/application/static",
# StaticFiles(directory="application/static"),
@ -20,7 +20,7 @@ def create_app():
# )
application.add_middleware(
CORSMiddleware,
allow_origins=template_api_config.ALLOW_ORIGINS,
allow_origins=api_config.ALLOW_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],

View File

@ -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

View File

@ -1,14 +1,28 @@
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.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):
"""
Test Template Route
"""
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"
return {
"completed": True,

View File

@ -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())

View File

@ -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

View File

@ -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 {}

0
ApiServices/__init__.py Normal file
View File

View File

@ -1,23 +1,13 @@
import arrow
import datetime
from typing import Optional, Any, Dict, List
from sqlalchemy.orm import Session, Mapped
from pydantic import BaseModel
from fastapi.exceptions import HTTPException
from typing import Optional, Any, Dict
from decimal import Decimal
from fastapi.exceptions import HTTPException
from sqlalchemy import TIMESTAMP, NUMERIC
from sqlalchemy.orm.attributes import InstrumentedAttribute
class Credentials(BaseModel):
"""
Class to store user credentials.
"""
person_id: int
person_name: str
full_name: Optional[str] = None
from sqlalchemy.orm import Session, Mapped
class MetaData:
@ -27,6 +17,7 @@ class MetaData:
created: bool = False
updated: bool = False
deleted: bool = False
class CRUDModel:
@ -43,7 +34,7 @@ class CRUDModel:
__abstract__ = True
creds: Credentials = None
# creds: Credentials = None
meta_data: MetaData = MetaData()
# Define required columns for CRUD operations
@ -57,23 +48,6 @@ class CRUDModel:
"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
def raise_exception(
cls, message: str = "Exception raised.", status_code: int = 400
@ -126,7 +100,6 @@ class CRUDModel:
for key, value in kwargs.items():
setattr(created_record, key, value)
cls.create_credentials(created_record)
db.add(created_record)
db.flush()
return created_record
@ -194,6 +167,7 @@ class CRUDModel:
return False, None
except Exception as e:
err = e
return False, None
def get_dict(
@ -234,6 +208,7 @@ class CRUDModel:
return return_dict
except Exception as e:
err = e
return {}
@classmethod
@ -278,7 +253,6 @@ class CRUDModel:
for key, value in kwargs.items():
setattr(created_record, key, value)
cls.create_credentials(created_record)
db.add(created_record)
db.flush()
cls.meta_data.created = True
@ -308,7 +282,6 @@ class CRUDModel:
for key, value in kwargs.items():
setattr(self, key, value)
self.update_credentials()
db.flush()
self.meta_data.updated = True
return self
@ -317,17 +290,3 @@ class CRUDModel:
self.meta_data.updated = False
db.rollback()
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

View File

@ -72,27 +72,6 @@ class CrudMixin(BasicMixin):
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
created_at: Mapped[TIMESTAMP] = mapped_column(
TIMESTAMP(timezone=True),
@ -110,36 +89,51 @@ class CrudCollection(CrudMixin):
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
cryp_uu_id: Mapped[str] = mapped_column(
String, nullable=True, index=True, comment="Cryptographic UUID"
)
# created_by: Mapped[str] = mapped_column(
# String, nullable=True, comment="Creator name"
# )
# created_by_id: Mapped[int] = mapped_column(
# 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"
# Token fields of modification
created_credentials_token: Mapped[str] = mapped_column(
String, nullable=True, comment="Created Credentials token"
)
confirmed_by_id: Mapped[int] = mapped_column(
Integer, nullable=True, comment="Confirmer ID"
updated_credentials_token: Mapped[str] = mapped_column(
String, nullable=True, comment="Updated Credentials token"
)
confirmed_credentials_token: Mapped[str] = mapped_column(
String, nullable=True, comment="Confirmed Credentials token"
)
# Status flags
is_confirmed: Mapped[bool] = mapped_column(
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(
Boolean, server_default="0", comment="Soft delete flag"
)

View File

@ -25,6 +25,7 @@ class PasswordModule:
i, random.choice(merged_letters), 1
)
return token_generated
raise ValueError("EYS_0004")
@classmethod
def generate_access_token(cls) -> str:

View File

@ -86,6 +86,7 @@ from Schemas.identity.identity import (
OccupantTypes,
People,
Users,
Credentials,
RelationshipDutyPeople,
Contracts,
)
@ -188,6 +189,7 @@ __all__ = [
"OccupantTypes",
"People",
"Users",
"Credentials",
"RelationshipDutyPeople",
"RelationshipEmployee2PostCode",
"Contracts",

View File

@ -230,8 +230,7 @@ class Event2Employee(CrudCollection):
)
@classmethod
def get_event_codes(cls, employee_id: int) -> list:
db = cls.new_session()
def get_event_codes(cls, employee_id: int, db) -> list:
employee_events = cls.filter_all(
cls.employee_id == employee_id,
db=db,
@ -328,8 +327,7 @@ class Event2Occupant(CrudCollection):
)
@classmethod
def get_event_codes(cls, build_living_space_id) -> list:
db = cls.new_session()
def get_event_codes(cls, build_living_space_id, db) -> list:
occupant_events = cls.filter_all(
cls.build_living_space_id == build_living_space_id,
db=db,

View File

@ -18,6 +18,9 @@ class EndpointRestriction(CrudCollection):
__tablename__ = "endpoint_restriction"
__exclude__fields__ = []
operation_uu_id: Mapped[UUID] = mapped_column(
String, comment="UUID of the operation",
)
endpoint_function: Mapped[str] = mapped_column(
String, server_default="", comment="Function name of the API endpoint"
)