Compare commits

...

33 Commits

Author SHA1 Message Date
4d7a6e6ec0 web service tenant first try 2025-05-07 11:41:08 +03:00
aee9d59750 updated all systems 2025-05-06 17:50:24 +03:00
bc43471259 updated client-frontend additions 2025-05-06 17:48:13 +03:00
e0ae1ee80a appender events and applications are updated 2025-05-06 12:03:58 +03:00
dd707b2463 tested appender service to events 2025-05-04 21:24:29 +03:00
0cde34a9bc updated appenders service 2025-05-04 00:21:38 +03:00
01f3e82a54 updated service binders updated 2025-05-03 23:35:03 +03:00
ac8c3fe1c3 updated management service bind 2025-05-03 18:38:50 +03:00
c6b1a2b1e8 updated managment index 2025-05-03 15:24:41 +03:00
734dc59e38 updated mdenu 2025-05-03 15:13:44 +03:00
71c808a5c3 updated components common header layouts 2025-05-03 13:51:02 +03:00
1ce28ec5f0 updated Api Defaults 2025-05-02 20:46:04 +03:00
1920c2a25d updated Identity and managment service 2025-05-01 15:25:15 +03:00
e815251123 event and service sendpoints added 2025-04-30 18:19:31 +03:00
36e63960f8 updated lang change and FormDisplay Components 2025-04-30 14:30:22 +03:00
f2cc7a69b5 components stablized 2025-04-30 00:44:13 +03:00
0052c92974 Card Component implemented 2025-04-29 20:44:39 +03:00
113e43c7d7 updated initilazer 2025-04-29 16:36:42 +03:00
2d418644bb updated pagination commit to carry 2025-04-28 13:19:01 +03:00
ac344773c5 added application page 2025-04-28 02:30:06 +03:00
346b132f4c updated Bank service routine email sender and wag managment application page completed 2025-04-27 21:01:06 +03:00
090567ade8 managment frontend initiated 2025-04-27 14:12:49 +03:00
ba784c40e4 routine crontab service tested and completed added to Readme.md 2025-04-25 16:07:17 +03:00
a886c2f28c updated Dockerfile 2025-04-25 16:01:46 +03:00
f4e43306c1 updated Dockerfile 2025-04-25 15:52:06 +03:00
c553975e82 updated Dockerfile 2025-04-25 15:46:14 +03:00
0bdc0d287e updated Dockerfile 2025-04-25 15:31:22 +03:00
7efd6f8941 updated Dockerfile 2025-04-25 15:22:20 +03:00
9f0c42e57a updated mail Dockerfile with env variables 2025-04-25 15:03:47 +03:00
b9825bb8e8 updated mail config 2025-04-25 14:49:20 +03:00
0bd8ddce4d updated email service 2025-04-24 17:41:14 +03:00
9511f81bc0 created design pattern 2025-04-23 12:26:15 +03:00
e5f88f2eb4 black shift 2025-04-22 11:10:29 +03:00
441 changed files with 24690 additions and 2255 deletions

View File

@@ -1,4 +1,4 @@
from fastapi import Header, Depends, Request, Response from fastapi import Header, Request, Response
from pydantic import BaseModel from pydantic import BaseModel
from ApiDefaults.config import api_config from ApiDefaults.config import api_config
@@ -13,6 +13,10 @@ class CommonHeaders(BaseModel):
response: Response | None = None response: Response | None = None
operation_id: str | None = None operation_id: str | None = None
model_config = {
"arbitrary_types_allowed": True
}
@classmethod @classmethod
def as_dependency( def as_dependency(
cls, cls,
@@ -26,8 +30,8 @@ class CommonHeaders(BaseModel):
# Extract operation_id from the route # Extract operation_id from the route
operation_id = None operation_id = None
if hasattr(request.scope.get('route'), 'operation_id'): if hasattr(request.scope.get("route"), "operation_id"):
operation_id = request.scope.get('route').operation_id operation_id = request.scope.get("route").operation_id
return cls( return cls(
language=language, language=language,
@@ -36,7 +40,7 @@ class CommonHeaders(BaseModel):
token=token, token=token,
request=request, request=request,
response=response, response=response,
operation_id=operation_id operation_id=operation_id,
) )
def get_headers_dict(self): def get_headers_dict(self):

View File

@@ -6,6 +6,8 @@ class EventCluster:
""" """
EventCluster EventCluster
""" """
def __repr__(self):
return f"EventCluster(name={self.name})"
def __init__(self, endpoint_uu_id: str, name: str): def __init__(self, endpoint_uu_id: str, name: str):
self.endpoint_uu_id = endpoint_uu_id self.endpoint_uu_id = endpoint_uu_id
@@ -33,14 +35,12 @@ class EventCluster:
from Schemas import Events, EndpointRestriction from Schemas import Events, EndpointRestriction
with Events.new_session() as db_session: with Events.new_session() as db_session:
print(f"Endpoint UUID: {self.endpoint_uu_id}")
if to_save_endpoint := EndpointRestriction.filter_one( if to_save_endpoint := EndpointRestriction.filter_one(
EndpointRestriction.operation_uu_id == self.endpoint_uu_id, EndpointRestriction.operation_uu_id == self.endpoint_uu_id,
db=db_session, db=db_session,
).data: ).data:
for event in self.events: for event in self.events:
print(f"event:", event.name) event_dict_to_save = dict(
event_to_save_database = Events.find_or_create(
function_code=event.key, function_code=event.key,
function_class=event.name, function_class=event.name,
description=event.description, description=event.description,
@@ -49,18 +49,27 @@ class EventCluster:
endpoint_uu_id=str(to_save_endpoint.uu_id), endpoint_uu_id=str(to_save_endpoint.uu_id),
is_confirmed=True, is_confirmed=True,
db=db_session, db=db_session,
)
event_found = Events.filter_one(
Events.function_code == event_dict_to_save["function_code"],
db=db_session,
).data
if event_found:
event_found.update(**event_dict_to_save)
event_found.save(db=db_session)
else:
event_to_save_database = Events.find_or_create(
**event_dict_to_save,
include_args=[ include_args=[
Events.function_code, Events.function_code,
Events.function_class, Events.function_class,
Events.endpoint_code, Events.endpoint_code,
Events.endpoint_uu_id, Events.endpoint_uu_id,
], ]
) )
if event_to_save_database.meta_data.created: if event_to_save_database.meta_data.created:
print(f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}")
event_to_save_database.save(db=db_session) event_to_save_database.save(db=db_session)
print(
f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}"
)
def match_event(self, event_key: str) -> "Event": def match_event(self, event_key: str) -> "Event":
""" """
@@ -100,15 +109,18 @@ class RouterCluster:
RouterCluster RouterCluster
""" """
event_clusters: dict[str, EventCluster] = {} def __repr__(self):
return f"RouterCluster(name={self.name})"
def __init__(self, name: str): def __init__(self, name: str):
self.name = name self.name = name
self.event_clusters: dict[str, EventCluster] = {}
def set_event_cluster(self, event_cluster: EventCluster): def set_event_cluster(self, event_cluster: EventCluster):
""" """
Add an event cluster to the set Add an event cluster to the set
""" """
print("Setting event cluster:", event_cluster.name)
if event_cluster.name not in self.event_clusters: if event_cluster.name not in self.event_clusters:
self.event_clusters[event_cluster.name] = event_cluster self.event_clusters[event_cluster.name] = event_cluster

View File

@@ -23,15 +23,22 @@ class RouteRegisterController:
for route_method in [ for route_method in [
method.lower() for method in getattr(route, "methods") method.lower() for method in getattr(route, "methods")
]: ]:
restriction = EndpointRestriction.find_or_create( add_or_update_dict = dict(
endpoint_method=route_method, endpoint_method=route_method,
endpoint_name=route_path, endpoint_name=route_path,
endpoint_desc=route_summary.replace("_", " "), endpoint_desc=route_summary.replace("_", " "),
endpoint_function=route_summary, endpoint_function=route_summary,
operation_uu_id=operation_id, # UUID of the endpoint operation_uu_id=operation_id,
is_confirmed=True, is_confirmed=True,
db=db_session,
) )
endpoint_restriction_found = EndpointRestriction.filter_one_system(
EndpointRestriction.operation_uu_id == operation_id, db=db_session,
).data
if endpoint_restriction_found:
endpoint_restriction_found.update(**add_or_update_dict, db=db_session)
endpoint_restriction_found.save(db=db_session)
else:
restriction = EndpointRestriction.find_or_create(**add_or_update_dict, db=db_session)
if restriction.meta_data.created: if restriction.meta_data.created:
restriction.save(db=db_session) restriction.save(db=db_session)

View File

@@ -6,7 +6,6 @@ from Endpoints.routes import get_safe_endpoint_urls
async def token_middleware(request: Request, call_next): async def token_middleware(request: Request, call_next):
base_url = request.url.path base_url = request.url.path
safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()] safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()]
if base_url in safe_endpoints: if base_url in safe_endpoints:

View File

@@ -22,7 +22,7 @@ class Configs(BaseSettings):
EMAIL_HOST: str = "" EMAIL_HOST: str = ""
DATETIME_FORMAT: str = "" DATETIME_FORMAT: str = ""
FORGOT_LINK: str = "" FORGOT_LINK: str = ""
ALLOW_ORIGINS: list = ["http://localhost:3000"] ALLOW_ORIGINS: list = ["http://localhost:3000", "http://localhost:3001"]
VERSION: str = "0.1.001" VERSION: str = "0.1.001"
DESCRIPTION: str = "" DESCRIPTION: str = ""

View File

@@ -3,22 +3,31 @@ 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
cluster_is_set = False
def create_events_if_any_cluster_set(): def create_events_if_any_cluster_set():
import Events import Events
if not Events.__all__:
global cluster_is_set
if not Events.__all__ or cluster_is_set:
return return
router_cluster_stack: list[RouterCluster] = [getattr(Events, e, None) for e in Events.__all__] router_cluster_stack: list[RouterCluster] = [
getattr(Events, e, None) for e in Events.__all__
]
for router_cluster in router_cluster_stack: for router_cluster in router_cluster_stack:
event_cluster_stack: list[EventCluster] = list(router_cluster.event_clusters.values()) event_cluster_stack: list[EventCluster] = list(
router_cluster.event_clusters.values()
)
for event_cluster in event_cluster_stack: for event_cluster in event_cluster_stack:
print(f"Creating event:", event_cluster.name)
try: try:
event_cluster.set_events_to_database() event_cluster.set_events_to_database()
except Exception as e: except Exception as e:
print(f"Error creating event cluster: {e}") print(f"Error creating event cluster: {e}")
cluster_is_set = True
def create_app(): def create_app():
from ApiDefaults.open_api_creator import create_openapi_schema from ApiDefaults.open_api_creator import create_openapi_schema
@@ -53,6 +62,7 @@ def create_app():
route_register = RouteRegisterController(app=application, router_list=get_routes()) route_register = RouteRegisterController(app=application, router_list=get_routes())
application = route_register.register_routes() application = route_register.register_routes()
create_events_if_any_cluster_set() create_events_if_any_cluster_set()
application.openapi = lambda _=application: create_openapi_schema(_) application.openapi = lambda _=application: create_openapi_schema(_)
return application return application

View File

@@ -22,7 +22,7 @@ class Configs(BaseSettings):
EMAIL_HOST: str = "" EMAIL_HOST: str = ""
DATETIME_FORMAT: str = "" DATETIME_FORMAT: str = ""
FORGOT_LINK: str = "" FORGOT_LINK: str = ""
ALLOW_ORIGINS: list = ["http://localhost:3000"] ALLOW_ORIGINS: list = ["http://localhost:3000", "http://localhost:3001"]
VERSION: str = "0.1.001" VERSION: str = "0.1.001"
DESCRIPTION: str = "" DESCRIPTION: str = ""

View File

@@ -304,7 +304,9 @@ def authentication_token_check_post(
status_code=status.HTTP_406_NOT_ACCEPTABLE, status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers, headers=headers,
) )
if AuthHandlers.LoginHandler.authentication_check_token_valid(access_token=token): if AuthHandlers.LoginHandler.authentication_check_token_valid(
domain=domain, access_token=token
):
return JSONResponse( return JSONResponse(
content={"message": "MSG_0001"}, content={"message": "MSG_0001"},
status_code=status.HTTP_202_ACCEPTED, status_code=status.HTTP_202_ACCEPTED,
@@ -438,7 +440,7 @@ def authentication_page_valid(
summary="Lists all sites that are available for user", summary="Lists all sites that are available for user",
description="Lists all sites that are available for user", description="Lists all sites that are available for user",
) )
def authentication_page_valid( def authentication_get_all_sites_list(
request: Request, request: Request,
language: str = Header(None, alias="language"), language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"), domain: str = Header(None, alias="domain"),

View File

@@ -171,8 +171,20 @@ class LoginHandler:
access_key=data.access_key, db_session=db_session access_key=data.access_key, db_session=db_session
) )
other_domains_list, main_domain = [], ""
with mongo_handler.collection(
f"{str(found_user.related_company)}*Domain"
) as collection:
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
if not result:
raise ValueError("EYS_00087")
other_domains_list = result.get("other_domains_list", [])
main_domain = result.get("main_domain", None)
if domain not in other_domains_list or not main_domain:
raise ValueError("EYS_00088")
if not user_handler.check_password_valid( if not user_handler.check_password_valid(
domain=domain or "", domain=main_domain,
id_=str(found_user.uu_id), id_=str(found_user.uu_id),
password=data.password, password=data.password,
password_hashed=found_user.hash_password, password_hashed=found_user.hash_password,
@@ -233,6 +245,7 @@ class LoginHandler:
person_id=found_user.person_id, person_id=found_user.person_id,
person_uu_id=str(person.uu_id), person_uu_id=str(person.uu_id),
request=dict(request.headers), request=dict(request.headers),
domain_list=other_domains_list,
companies_uu_id_list=companies_uu_id_list, companies_uu_id_list=companies_uu_id_list,
companies_id_list=companies_id_list, companies_id_list=companies_id_list,
duty_uu_id_list=duty_uu_id_list, duty_uu_id_list=duty_uu_id_list,
@@ -286,8 +299,20 @@ class LoginHandler:
found_user = user_handler.check_user_exists( found_user = user_handler.check_user_exists(
access_key=data.access_key, db_session=db_session access_key=data.access_key, db_session=db_session
) )
other_domains_list, main_domain = [], ""
with mongo_handler.collection(
f"{str(found_user.related_company)}*Domain"
) as collection:
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
if not result:
raise ValueError("EYS_00087")
other_domains_list = result.get("other_domains_list", [])
main_domain = result.get("main_domain", None)
if domain not in other_domains_list or not main_domain:
raise ValueError("EYS_00088")
if not user_handler.check_password_valid( if not user_handler.check_password_valid(
domain=domain, domain=main_domain,
id_=str(found_user.uu_id), id_=str(found_user.uu_id),
password=data.password, password=data.password,
password_hashed=found_user.hash_password, password_hashed=found_user.hash_password,
@@ -343,6 +368,7 @@ class LoginHandler:
user_id=found_user.id, user_id=found_user.id,
person_id=person.id, person_id=person.id,
person_uu_id=str(person.uu_id), person_uu_id=str(person.uu_id),
domain_list=other_domains_list,
request=dict(request.headers), request=dict(request.headers),
available_occupants=occupants_selection_dict, available_occupants=occupants_selection_dict,
).model_dump() ).model_dump()
@@ -606,9 +632,16 @@ class LoginHandler:
) )
@classmethod @classmethod
def authentication_check_token_valid(cls, access_token: str) -> bool: def authentication_check_token_valid(cls, domain, access_token: str) -> bool:
redis_handler = RedisHandlers() redis_handler = RedisHandlers()
if redis_handler.get_object_from_redis(access_token=access_token): if auth_token := redis_handler.get_object_from_redis(access_token=access_token):
if auth_token.is_employee:
if domain not in auth_token.domain_list:
raise ValueError("EYS_00112")
return True
elif auth_token.is_occupant:
if domain not in auth_token.domain_list:
raise ValueError("EYS_00113")
return True return True
return False return False

View File

@@ -5,6 +5,7 @@ from ..config import api_config
async def token_middleware(request: Request, call_next): async def token_middleware(request: Request, call_next):
print("Token Middleware", dict(request.headers))
base_url = request.url.path base_url = request.url.path
safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()] safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()]
if base_url in safe_endpoints: if base_url in safe_endpoints:

View File

@@ -35,6 +35,7 @@ class ApplicationToken(BaseModel):
person_uu_id: str person_uu_id: str
request: Optional[dict] = None # Request Info of Client request: Optional[dict] = None # Request Info of Client
domain_list: Optional[list[str]] = None
expires_at: Optional[float] = None # Expiry timestamp expires_at: Optional[float] = None # Expiry timestamp

View File

@@ -0,0 +1,29 @@
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 /ApiControllers /ApiControllers
COPY /ApiDefaults /ApiDefaults
COPY /Controllers /Controllers
COPY /Schemas /Schemas
COPY /ApiServices/BuildingService/Endpoints /ApiDefaults/Endpoints
COPY /ApiServices/BuildingService/Events /ApiDefaults/Events
COPY /ApiServices/BuildingService/Validations /ApiDefaults/Validations
# 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", "ApiDefaults/app.py"]

View File

@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly
from Events.area.cluster import BuildAreaRouterCluster
area_route = APIRouter(prefix="/building/area", tags=["Building Area"])
@area_route.post(
path="/list",
description="List building area endpoint",
operation_id="f2831efc-5493-448c-bcf6-4d52d744ba7e",
)
def area_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List building area endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildAreaRouterCluster.get_event_cluster("BuildAreaList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@area_route.post(
path="/create",
description="Create building area endpoint",
operation_id="66e8b310-7a5d-41f4-977c-ee5f3b0603da",
)
def area_create_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create building area endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildAreaRouterCluster.get_event_cluster("BuildAreaCreate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@area_route.post(
path="/update",
description="Update building area endpoint",
operation_id="d7769899-156d-49ff-85f4-06e3faa23e30",
)
def area_update_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update building area endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildAreaRouterCluster.get_event_cluster("BuildAreaUpdate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly
from Events.building.cluster import BuildRouterCluster
building_route = APIRouter(prefix="/building", tags=["Building"])
@building_route.post(
path="/list",
description="List buildings endpoint",
operation_id="72003d1f-79cc-43c3-9150-3a94f318186e",
)
def building_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List buildings endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildRouterCluster.get_event_cluster("BuildList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@building_route.post(
path="/create",
description="Create buildings endpoint",
operation_id="13b86c9c-702b-411f-acb9-9ff50ef86507",
)
def building_create_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create buildings endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildRouterCluster.get_event_cluster("BuildCreate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@building_route.post(
path="/update",
description="Update buildings endpoint",
operation_id="b09fb6fd-a6c9-4c03-8637-9af0570a84e8",
)
def building_update_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update buildings endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildRouterCluster.get_event_cluster("BuildUpdate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,29 @@
from fastapi import APIRouter
def get_routes() -> list[APIRouter]:
from .building.route import building_route
from .area.route import area_route
from .sites.route import sites_route
from .parts.route import parts_route
from .spaces.route import spaces_route
from .type.route import build_types_route
return [
building_route,
area_route,
sites_route,
parts_route,
spaces_route,
build_types_route,
]
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
return [
("/", "GET"),
("/docs", "GET"),
("/redoc", "GET"),
("/openapi.json", "GET"),
("/metrics", "GET"),
]

View File

@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly
from Events.sites.cluster import BuildSitesRouterCluster
sites_route = APIRouter(prefix="/building/sites", tags=["Build Sites"])
@sites_route.post(
path="/list",
description="List build sites endpoint",
operation_id="b64a2479-4160-43bf-9676-cd762a48bf62",
)
def sites_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List build sites endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildSitesRouterCluster.get_event_cluster("BuildSitesList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@sites_route.post(
path="/create",
description="Create build sites endpoint",
operation_id="e63dab4b-d446-430c-ac7d-0cb47a66390c",
)
def sites_create_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create build sites endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildSitesRouterCluster.get_event_cluster("BuildSitesCreate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@sites_route.post(
path="/update",
description="Update build sites endpoint",
operation_id="34ec902c-5ebe-41f6-8294-dfe634d47d98",
)
def sites_update_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update build sites endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildSitesRouterCluster.get_event_cluster("BuildSitesUpdate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,76 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly
from Events.spaces.cluster import BuildLivingSpaceRouterCluster
spaces_route = APIRouter(prefix="/building/spaces", tags=["Build Spaces"])
@spaces_route.post(
path="/list",
description="List build spaces endpoint",
operation_id="9d072fd3-d727-4f71-bf0c-fd74e25ba507",
)
def spaces_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List build spaces endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildLivingSpaceRouterCluster.get_event_cluster(
"BuildLivingSpaceList"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@spaces_route.post(
path="/create",
description="Create build spaces endpoint",
operation_id="4520cfb7-ed45-44d2-a706-e2a8be777419",
)
def spaces_create_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create build spaces endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildLivingSpaceRouterCluster.get_event_cluster(
"BuildLivingSpaceCreate"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@spaces_route.post(
path="/update",
description="Update build spaces endpoint",
operation_id="d8c90f64-0a4c-4a7c-a685-0e1894cbe1c4",
)
def spaces_update_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update build spaces endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildLivingSpaceRouterCluster.get_event_cluster(
"BuildLivingSpaceUpdate"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly
from Events.type.cluster import BuildTypeRouterCluster
build_types_route = APIRouter(prefix="/building/types", tags=["Build Types"])
@build_types_route.post(
path="/list",
description="List build types endpoint",
operation_id="916d2e02-ee83-4099-ba02-63e2a3f83ec4",
)
def build_types_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List build types endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildTypeRouterCluster.get_event_cluster("BuildTypeList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@build_types_route.post(
path="/create",
description="Create build types endpoint",
operation_id="ae7d9e5d-3933-4613-a686-c887d74b85b3",
)
def build_types_create_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create build types endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildTypeRouterCluster.get_event_cluster("BuildTypeCreate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@build_types_route.post(
path="/update",
description="Update build types endpoint",
operation_id="0f304075-efb0-4f9d-a3b2-e60eeb4d9a87",
)
def build_types_update_route(
data: dict,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update build types endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = BuildTypeRouterCluster.get_event_cluster("BuildTypeUpdate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,15 @@
from .building.cluster import BuildRouterCluster
from .area.cluster import BuildAreaRouterCluster
from .parts.cluster import BuildPartsRouterCluster
from .spaces.cluster import BuildLivingSpaceRouterCluster
from .type.cluster import BuildTypeRouterCluster
from .sites.cluster import BuildSitesRouterCluster
__all__ = [
"BuildRouterCluster",
"BuildAreaRouterCluster",
"BuildPartsRouterCluster",
"BuildLivingSpaceRouterCluster",
"BuildTypeRouterCluster",
"BuildSitesRouterCluster",
]

View File

@@ -0,0 +1,26 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
BuildAreaListEvent,
BuildAreaCreateEvent,
BuildAreaUpdateEvent,
)
BuildAreaRouterCluster = RouterCluster(name="BuildAreaRouterCluster")
BuildAreaEventClusterList = EventCluster(
name="BuildAreaList", endpoint_uu_id="cc487a4f-9a45-4072-89c1-a1ad504c79ad"
)
BuildAreaEventClusterCreate = EventCluster(
name="BuildAreaCreate", endpoint_uu_id="bdd58d68-3a7c-4150-9f5b-e322db35b804"
)
BuildAreaEventClusterUpdate = EventCluster(
name="BuildAreaUpdate", endpoint_uu_id="cad0c4e2-36e3-4f80-9ad2-b06bf8cd8d1c"
)
BuildAreaEventClusterList.add_event(BuildAreaListEvent)
BuildAreaEventClusterCreate.add_event(BuildAreaCreateEvent)
BuildAreaEventClusterUpdate.add_event(BuildAreaUpdateEvent)
BuildAreaRouterCluster.set_event_cluster(BuildAreaEventClusterList)
BuildAreaRouterCluster.set_event_cluster(BuildAreaEventClusterCreate)
BuildAreaRouterCluster.set_event_cluster(BuildAreaEventClusterUpdate)

View File

@@ -0,0 +1,96 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import BuildArea
# List endpoint
BuildAreaListEvent = Event(
name="build_area_list",
key="306e6e11-21da-4fbb-b3d6-3a5179ecfe71",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="List events of build area endpoint",
)
# Create endpoint
BuildAreaCreateEvent = Event(
name="build_area_create",
key="aa173f0f-7a97-4e91-97da-173626dd3257",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of build area endpoint",
)
# Update endpoint
BuildAreaUpdateEvent = Event(
name="build_area_update",
key="358404d0-fb80-4d58-8907-0c04948b364e",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of build area endpoint",
)
def build_area_list_callable(list_options: PaginateOnly):
"""
Example callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
with BuildArea.new_session() as db_session:
if list_options.query:
buildings_list = BuildArea.filter_all(
*BuildArea.convert(list_options.query), db=db_session
)
else:
buildings_list = BuildArea.filter_all(db=db_session)
pagination = Pagination(data=buildings_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(
data=buildings_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
BuildAreaListEvent.event_callable = build_area_list_callable
def build_area_create_callable(data: dict):
"""
Example callable method
"""
with BuildArea.new_session() as db_session:
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildAreaCreateEvent.event_callable = build_area_create_callable
def build_area_update_callable(data: dict):
"""
Example callable method
"""
with BuildArea.new_session() as db_session:
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildAreaUpdateEvent.event_callable = build_area_update_callable

View File

@@ -0,0 +1,27 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
BuildingListEvent,
BuildingCreateEvent,
BuildingUpdateEvent,
)
BuildRouterCluster = RouterCluster(name="BuildRouterCluster")
BuildEventClusterList = EventCluster(
name="BuildList", endpoint_uu_id="1a186985-ed2a-434b-af28-22d6773382e9"
)
BuildEventClusterList.add_event(BuildingListEvent)
BuildEventClusterCreate = EventCluster(
name="BuildCreate", endpoint_uu_id="d545c5a9-bbbf-4795-a4de-467db0809a04"
)
BuildEventClusterCreate.add_event(BuildingCreateEvent)
BuildEventClusterUpdate = EventCluster(
name="BuildUpdate", endpoint_uu_id="6166a28e-8da8-4a2c-96c8-0a1651739cf3"
)
BuildEventClusterUpdate.add_event(BuildingUpdateEvent)
BuildRouterCluster.set_event_cluster(BuildEventClusterList)
BuildRouterCluster.set_event_cluster(BuildEventClusterCreate)
BuildRouterCluster.set_event_cluster(BuildEventClusterUpdate)

View File

@@ -0,0 +1,98 @@
from ApiControllers.abstracts.event_clusters import Event
from Validations.building.building.validations import (
REQUESTEWFAZCDMPVZHIWOKZEJBIEUDAFBNXFEEAEGSELVGGCDMWLQPYMRAEEABSRQJUFBIMFEEADXK,
)
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import Build
# List endpoint
BuildingListEvent = Event(
name="building_list",
key="e675d36a-6772-44a8-b153-9a8d55e0f560",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="List events of buildings endpoint",
)
# Create endpoint
BuildingCreateEvent = Event(
name="building_create",
key="d79e3bdc-8f75-41d0-8eeb-dd9893d7ea0d",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of buildings endpoint",
)
# Update endpoint
BuildingUpdateEvent = Event(
name="building_update",
key="45e1bb8c-c521-4013-9e99-d3a58204bc5f",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of buildings endpoint",
)
def building_list_callable(list_options: PaginateOnly):
"""
Example callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
with Build.new_session() as db_session:
if list_options.query:
buildings_list = Build.filter_all(
*Build.convert(list_options.query), db=db_session
)
else:
buildings_list = Build.filter_all(db=db_session)
pagination = Pagination(data=buildings_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(
data=buildings_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
request=REQUESTEWFAZCDMPVZHIWOKZEJBIEUDAFBNXFEEAEGSELVGGCDMWLQPYMRAEEABSRQJUFBIMFEEADXK,
).response
BuildingListEvent.event_callable = building_list_callable
def building_create_callable():
"""
Example callable method
"""
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildingCreateEvent.event_callable = building_create_callable
def building_update_callable():
"""
Example callable method
"""
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildingUpdateEvent.event_callable = building_update_callable

View File

@@ -0,0 +1,27 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
BuildSitesListEvent,
BuildSitesCreateEvent,
BuildSitesUpdateEvent,
)
BuildSitesRouterCluster = RouterCluster(name="BuildSitesRouterCluster")
BuildSitesEventClusterList = EventCluster(
name="BuildSitesList", endpoint_uu_id="1159fb39-ad5d-46fb-8b6b-461021a39da3"
)
BuildSitesEventClusterList.add_event(BuildSitesListEvent)
BuildSitesEventClusterCreate = EventCluster(
name="BuildCreate", endpoint_uu_id="bc71d276-fd51-43bb-90f2-6f4245b59d72"
)
BuildSitesEventClusterCreate.add_event(BuildSitesCreateEvent)
BuildSitesEventClusterUpdate = EventCluster(
name="BuildUpdate", endpoint_uu_id="eca87985-3074-44cb-a947-f01e30769019"
)
BuildSitesEventClusterUpdate.add_event(BuildSitesUpdateEvent)
BuildSitesRouterCluster.set_event_cluster(BuildSitesEventClusterList)
BuildSitesRouterCluster.set_event_cluster(BuildSitesEventClusterCreate)
BuildSitesRouterCluster.set_event_cluster(BuildSitesEventClusterUpdate)

View File

@@ -0,0 +1,96 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import BuildSites
# List endpoint
BuildSitesListEvent = Event(
name="build_sites_list",
key="d16c692f-5597-493a-90fe-b2bfe0d1c163",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="List events of build sites endpoint",
)
# Create endpoint
BuildSitesCreateEvent = Event(
name="build_sites_create",
key="8ed0929e-9c96-4e81-b073-e3578ca13f37",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of build sites endpoint",
)
# Update endpoint
BuildSitesUpdateEvent = Event(
name="build_sites_update",
key="ece55fa6-afba-4c34-bd2e-f39a4cdd6b5c",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of build sites endpoint",
)
def build_sites_list_callable(list_options: PaginateOnly):
"""
Example callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
with BuildSites.new_session() as db_session:
if list_options.query:
buildings_list = BuildSites.filter_all(
*BuildSites.convert(list_options.query), db=db_session
)
else:
buildings_list = BuildSites.filter_all(db=db_session)
pagination = Pagination(data=buildings_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(
data=buildings_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
BuildSitesListEvent.event_callable = build_sites_list_callable
def build_sites_create_callable(data: dict):
"""
Example callable method
"""
with BuildSites.new_session() as db_session:
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildSitesCreateEvent.event_callable = build_sites_create_callable
def build_sites_update_callable(data: dict):
"""
Example callable method
"""
with BuildSites.new_session() as db_session:
return {
"completed": True,
"message": "Example callable method 2",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildSitesUpdateEvent.event_callable = build_sites_update_callable

View File

@@ -0,0 +1,27 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
BuildLivingSpaceListEvent,
BuildLivingSpaceCreateEvent,
BuildLivingSpaceUpdateEvent,
)
BuildLivingSpaceRouterCluster = RouterCluster(name="BuildLivingSpaceRouterCluster")
BuildLivingSpaceEventClusterList = EventCluster(
name="BuildLivingSpaceList", endpoint_uu_id="ad17c019-3050-4c41-ab6f-9ce43290e81a"
)
BuildLivingSpaceEventClusterList.add_event(BuildLivingSpaceListEvent)
BuildLivingSpaceEventClusterCreate = EventCluster(
name="BuildCreate", endpoint_uu_id="ed9a0303-14e2-46fe-bec0-d395c29801ff"
)
BuildLivingSpaceEventClusterCreate.add_event(BuildLivingSpaceCreateEvent)
BuildLivingSpaceEventClusterUpdate = EventCluster(
name="BuildUpdate", endpoint_uu_id="c62bb3dc-03c0-406c-8bbc-0e1f75a6420c"
)
BuildLivingSpaceEventClusterUpdate.add_event(BuildLivingSpaceUpdateEvent)
BuildLivingSpaceRouterCluster.set_event_cluster(BuildLivingSpaceEventClusterList)
BuildLivingSpaceRouterCluster.set_event_cluster(BuildLivingSpaceEventClusterCreate)
BuildLivingSpaceRouterCluster.set_event_cluster(BuildLivingSpaceEventClusterUpdate)

View File

@@ -0,0 +1,96 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import BuildLivingSpace
# List endpoint
BuildLivingSpaceListEvent = Event(
name="build_living_space_list",
key="d16c692f-5597-493a-90fe-b2bfe0d1c163",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="List events of build living spaces endpoint",
)
# Create endpoint
BuildLivingSpaceCreateEvent = Event(
name="build_living_space_create",
key="8ed0929e-9c96-4e81-b073-e3578ca13f37",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of build living spaces endpoint",
)
# Update endpoint
BuildLivingSpaceUpdateEvent = Event(
name="build_living_space_update",
key="ece55fa6-afba-4c34-bd2e-f39a4cdd6b5c",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of build living spaces endpoint",
)
def build_living_space_list_callable(list_options: PaginateOnly):
"""
List callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
with BuildLivingSpace.new_session() as db_session:
if list_options.query:
buildings_list = BuildLivingSpace.filter_all(
*BuildLivingSpace.convert(list_options.query), db=db_session
)
else:
buildings_list = BuildLivingSpace.filter_all(db=db_session)
pagination = Pagination(data=buildings_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(
data=buildings_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
BuildLivingSpaceListEvent.event_callable = build_living_space_list_callable
def build_living_space_create_callable(data: dict):
"""
Create callable method
"""
with BuildLivingSpace.new_session() as db_session:
return {
"completed": True,
"message": "Build living space created",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildLivingSpaceCreateEvent.event_callable = build_living_space_create_callable
def build_living_space_update_callable(data: dict):
"""
Update callable method
"""
with BuildLivingSpace.new_session() as db_session:
return {
"completed": True,
"message": "Build living space updated",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildLivingSpaceUpdateEvent.event_callable = build_living_space_update_callable

View File

@@ -0,0 +1,27 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
BuildTypeListEvent,
BuildTypeCreateEvent,
BuildTypeUpdateEvent,
)
BuildTypeRouterCluster = RouterCluster(name="BuildTypeRouterCluster")
BuildTypeEventClusterList = EventCluster(
name="BuildTypeList", endpoint_uu_id="14f076c2-b118-4cfb-b679-9f2168a2658c"
)
BuildTypeEventClusterList.add_event(BuildTypeListEvent)
BuildTypeEventClusterCreate = EventCluster(
name="BuildCreate", endpoint_uu_id="4c90c4cf-8873-479a-a56b-d12f276efa9d"
)
BuildTypeEventClusterCreate.add_event(BuildTypeCreateEvent)
BuildTypeEventClusterUpdate = EventCluster(
name="BuildUpdate", endpoint_uu_id="cde9db69-6795-4562-802d-540d0b03ceaa"
)
BuildTypeEventClusterUpdate.add_event(BuildTypeUpdateEvent)
BuildTypeRouterCluster.set_event_cluster(BuildTypeEventClusterList)
BuildTypeRouterCluster.set_event_cluster(BuildTypeEventClusterCreate)
BuildTypeRouterCluster.set_event_cluster(BuildTypeEventClusterUpdate)

View File

@@ -0,0 +1,96 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import BuildTypes
# List endpoint
BuildTypeListEvent = Event(
name="build_type_list",
key="e013b43e-3845-4a95-b5b0-409343659c5f",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="List events of build types endpoint",
)
# Create endpoint
BuildTypeCreateEvent = Event(
name="build_type_create",
key="8119a07e-bbb2-4d4c-b529-f923e8cdcc60",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of build types endpoint",
)
# Update endpoint
BuildTypeUpdateEvent = Event(
name="build_type_update",
key="6b7b89d5-6406-4614-aba4-c942f0c081ac",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of build types endpoint",
)
def build_type_list_callable(list_options: PaginateOnly):
"""
List callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
with BuildTypes.new_session() as db_session:
if list_options.query:
buildings_list = BuildTypes.filter_all(
*BuildTypes.convert(list_options.query), db=db_session
)
else:
buildings_list = BuildTypes.filter_all(db=db_session)
pagination = Pagination(data=buildings_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(
data=buildings_list,
pagination=pagination,
# response_model="",
).pagination.as_dict
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
BuildTypeListEvent.event_callable = build_type_list_callable
def build_type_create_callable(data: dict):
"""
Create callable method
"""
with BuildTypes.new_session() as db_session:
return {
"completed": True,
"message": "Build type created",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildTypeCreateEvent.event_callable = build_type_create_callable
def build_type_update_callable(data: dict):
"""
Update callable method
"""
with BuildTypes.new_session() as db_session:
return {
"completed": True,
"message": "Build type updated",
"info": {
"host": "example_host",
"user_agent": "example_user_agent",
},
}
BuildTypeUpdateEvent.event_callable = build_type_update_callable

View File

@@ -0,0 +1,37 @@
from pydantic import BaseModel
from typing import Optional
class REQUESTEWFAZCDMPVZHIWOKZEJBIEUDAFBNXFEEAEGSELVGGCDMWLQPYMRAEEABSRQJUFBIMFEEADXK(
BaseModel
):
gov_address_code: str
build_name: str
build_no: str
max_floor: int
underground_floor: int
build_date: str
decision_period_date: str
tax_no: str
lift_count: int
heating_system: bool
cooling_system: bool
hot_water_system: bool
block_service_man_count: int
security_service_man_count: int
garage_count: int
management_room_id: int
site_uu_id: str
address_uu_id: str
class REQUESTOCARDAJXLDANCXQAJWDBDIWXHUAEKQNUOSBZOCWXDAFGLAAVRBSADHUBDXAREUSESYGNGKBR(
BaseModel
):
pass
class REQUESTAEKUDAILSFMCCLOERBHBFRCIKFCSNCBOSENCAEOIDACPRFZCCWGEDBHBFBMZBFCJHCBVKEFC(
BaseModel
):
pass

View File

@@ -0,0 +1,26 @@
from pydantic import BaseModel
from typing import Optional
class ListOptions(BaseModel):
"""
Query for list option abilities
"""
page: Optional[int] = 1
size: Optional[int] = 10
order_field: Optional[str] = "id"
order_type: Optional[str] = "asc"
# include_joins: Optional[list] = None
query: Optional[dict] = None
class PaginateOnly(BaseModel):
"""
Query for list option abilities
"""
page: Optional[int] = 1
size: Optional[int] = 10
order_field: Optional[str] = "id"
order_type: Optional[str] = "asc"

View File

@@ -1,12 +1,12 @@
from Controllers.Postgres.database import get_db from Controllers.Postgres.database import get_db
from Schemas import Users, Employees, BuildLivingSpace from Schemas import Users, Employees, BuildLivingSpace, Services
from init_service_to_events import init_service_to_event_matches_for_super_user from init_service_to_events import init_service_to_event_matches_for_super_user
from init_applications import ( from init_applications import (
init_applications_for_super_user, init_applications_for_super_user,
init_applications_for_general_manager, # init_applications_for_general_manager,
init_applications_for_build_manager, # init_applications_for_build_manager,
init_applications_for_owner, # init_applications_for_owner,
init_applications_for_tenant, # init_applications_for_tenant,
) )
@@ -16,62 +16,59 @@ if __name__ == "__main__":
Set Events to service | Set Service to employee Set Events to service | Set Service to employee
""" """
with get_db() as db_session: with get_db() as db_session:
if super_man := Users.filter_one( service_super_user = Services.filter_one(Services.service_code == "SRE-SUE", db=db_session).data
Users.email == "karatay.berkay.sup@evyos.com.tr", db=db_session user_super_user = Users.filter_one(Users.email == "karatay.berkay.sup@evyos.com.tr", db=db_session).data
).data: employee_super_user = Employees.filter_one(Employees.people_id == user_super_user.person_id, db=db_session).data
super_employee = Employees.filter_one( if service_super_user and user_super_user and employee_super_user:
Employees.people_id == super_man.person_id, db=db_session
).data
init_service_to_event_matches_for_super_user( init_service_to_event_matches_for_super_user(
super_user=super_employee, db_session=db_session service_match=service_super_user, employee_match=employee_super_user, db_session=db_session
) )
init_applications_for_super_user( init_applications_for_super_user(
super_user=super_employee, db_session=db_session service_match=service_super_user, employee_match=employee_super_user, db_session=db_session
) )
with get_db() as db_session: # with get_db() as db_session:
print("Createing GM") # print("Createing GM")
if gen_man := Users.filter_one( # if gen_man := Users.filter_one(
Users.email == "example.general@evyos.com.tr", db=db_session # Users.email == "example.general@evyos.com.tr", db=db_session
).data: # ).data:
gen_man_employee = Employees.filter_one( # gen_man_employee = Employees.filter_one(
Employees.people_id == gen_man.person_id, db=db_session # Employees.people_id == gen_man.person_id, db=db_session
).data # ).data
print("General Manager : ", gen_man_employee) # print("General Manager : ", gen_man_employee)
init_applications_for_general_manager( # init_applications_for_general_manager(
super_user=gen_man_employee, db_session=db_session # super_user=gen_man_employee, db_session=db_session
) # )
with get_db() as db_session: # with get_db() as db_session:
if build_man := Users.filter_one( # if build_man := Users.filter_one(
Users.email == "example.build.manager@gmail.com", db=db_session # Users.email == "example.build.manager@gmail.com", db=db_session
).data: # ).data:
build_man_employee = BuildLivingSpace.filter_one( # build_man_employee = BuildLivingSpace.filter_one(
BuildLivingSpace.person_id == build_man.person_id, db=db_session # BuildLivingSpace.person_id == build_man.person_id, db=db_session
).data # ).data
init_applications_for_build_manager( # init_applications_for_build_manager(
super_user=build_man_employee, db_session=db_session # super_user=build_man_employee, db_session=db_session
) # )
with get_db() as db_session: # with get_db() as db_session:
if own_flt := Users.filter_one( # if own_flt := Users.filter_one(
Users.email == "example.owner@gmail.com", db=db_session # Users.email == "example.owner@gmail.com", db=db_session
).data: # ).data:
own_flt_employee = BuildLivingSpace.filter_one( # own_flt_employee = BuildLivingSpace.filter_one(
BuildLivingSpace.person_id == own_flt.person_id, db=db_session # BuildLivingSpace.person_id == own_flt.person_id, db=db_session
).data # ).data
init_applications_for_owner( # init_applications_for_owner(
super_user=own_flt_employee, db_session=db_session # super_user=own_flt_employee, db_session=db_session
) # )
with get_db() as db_session: # with get_db() as db_session:
if ten_flt := Users.filter_one( # if ten_flt := Users.filter_one(
Users.email == "example.tenant@gmail.com", db=db_session # Users.email == "example.tenant@gmail.com", db=db_session
).data: # ).data:
ten_flt_employee = BuildLivingSpace.filter_one( # ten_flt_employee = BuildLivingSpace.filter_one(
BuildLivingSpace.person_id == ten_flt.person_id, db=db_session # BuildLivingSpace.person_id == ten_flt.person_id, db=db_session
).data # ).data
init_applications_for_tenant( # init_applications_for_tenant(
super_user=ten_flt_employee, db_session=db_session # super_user=ten_flt_employee, db_session=db_session
) # )

View File

@@ -4,223 +4,193 @@ from Schemas import (
Application2Occupant, Application2Occupant,
Employees, Employees,
BuildLivingSpace, BuildLivingSpace,
Services,
Service2Application,
) )
def init_applications_for_super_user(super_user: Employees, db_session=None) -> None: def init_applications_for_super_user(service_match: Services, employee_match: Employees, db_session=None) -> None:
list_of_created_apps = [ list_of_all_events = Applications.filter_all(db=db_session).data
dict( Service2Application.filter_all(db=db_session).query.delete()
name="Dashboard1", Service2Application.save(db=db_session)
application_code="app000001",
site_url="/dashboard", for list_of_event_code in list_of_all_events:
application_type="info", service_to_event_found = Service2Application.filter_one_system(
description="Dashboard Page", Service2Application.application_id == list_of_event_code.id,
), Service2Application.service_id == service_match.id,
dict(
name="Individual",
application_code="app000003",
site_url="/individual",
application_type="Dash",
description="Individual Page for people",
),
dict(
name="User",
application_code="app000004",
site_url="/user",
application_type="Dash",
description="Individual Page for user",
),
dict(
name="Build",
application_code="app000005",
site_url="/build",
application_type="Dash",
description="Individual Page for build",
),
dict(
name="BuildParts",
application_code="app000006",
site_url="/build/parts",
application_type="Dash",
description="Individual Page for build parts",
),
dict(
name="BuildArea",
application_code="app000007",
site_url="/build/area",
application_type="Dash",
description="Individual Page for build area",
),
dict(
name="ManagementAccounting",
application_code="app000008",
site_url="/management/accounting",
application_type="Dash",
description="Individual Page for management accounting",
),
dict(
name="ManagementBudget",
application_code="app000009",
site_url="/management/budget",
application_type="Dash",
description="Individual Page for management accounting2",
),
dict(
name="ManagementMeetingClose",
application_code="app000010",
site_url="/annual/meeting/close",
application_type="Dash",
description="Individual Page for management accounting3",
),
dict(
name="EmergencyMeeting",
application_code="app000011",
site_url="/emergency/meeting",
application_type="Dash",
description="Individual Page for management accounting4",
),
dict(
name="EmergencyMeetingClose",
application_code="app000012",
site_url="/emergency/meeting/close",
application_type="Dash",
description="Individual Page for management accounting5",
),
dict(
name="MeetingParticipation",
application_code="app000013",
site_url="/meeting/participation",
application_type="Dash",
description="Individual Page for management accounting6",
),
]
for list_of_created_app in list_of_created_apps:
created_page = Applications.find_or_create(
**list_of_created_app,
db=db_session, db=db_session,
is_confirmed=True,
) )
if created_page.meta_data.created: if service_to_event_found.data:
created_page.save(db=db_session) service_to_event_found.destroy(db=db_session)
print(
f"UUID: {service_to_event_found.uu_id} application is deleted from {service_match.service_description}"
)
application_employee_created = Application2Employee.find_or_create( employee_added_application = Service2Application.find_or_create(
employee_id=super_user.id, service_id=service_match.id,
employee_uu_id=str(super_user.uu_id), service_uu_id=str(service_match.uu_id),
site_url=created_page.site_url, application_id=list_of_event_code.id,
application_code=created_page.application_code, application_uu_id=str(list_of_event_code.uu_id),
application_id=created_page.id, application_code=list_of_event_code.application_code,
application_uu_id=str(created_page.uu_id), site_url=list_of_event_code.site_url,
is_confirmed=True,
active=True,
db=db_session,
)
if employee_added_application.meta_data.created:
employee_added_application.save(db=db_session)
print(
f"UUID: {employee_added_application.uu_id} application is saved to {service_match.service_description}"
)
employee_added_service = Application2Employee.find_or_create(
service_id=service_match.id,
service_uu_id=str(service_match.uu_id),
employee_id=employee_match.id,
employee_uu_id=str(employee_match.uu_id),
is_confirmed=True, is_confirmed=True,
db=db_session, db=db_session,
) )
if application_employee_created.meta_data.created: if employee_added_service.meta_data.created:
application_employee_created.save(db=db_session) employee_added_service.save(db=db_session)
print(
f"UUID: {employee_added_service.uu_id} service is saved to {employee_match.uu_id}"
def init_applications_for_general_manager(
super_user: Employees, db_session=None
) -> None:
list_of_created_apps = [
dict(
name="Dashboard1",
application_code="app000001",
site_url="/dashboard",
application_type="info",
description="Dashboard Page",
),
dict(
name="ManagementAccounting",
application_code="app000008",
site_url="/management/accounting",
application_type="Dash",
description="Individual Page for management accounting",
),
]
for list_of_created_app in list_of_created_apps:
created_page = Applications.find_or_create(
**list_of_created_app,
db=db_session,
) )
print("Application : ", created_page)
if created_page.meta_data.created:
created_page.save(db=db_session)
application_employee_created = Application2Employee.find_or_create(
employee_id=super_user.id,
employee_uu_id=str(super_user.uu_id),
site_url=created_page.site_url,
application_code=created_page.application_code,
application_id=created_page.id,
application_uu_id=str(created_page.uu_id),
is_confirmed=True,
db=db_session,
)
print("Application Employee : ", application_employee_created)
if application_employee_created.meta_data.created:
application_employee_created.save(db=db_session)
def init_applications_for_build_manager( # def init_applications_for_general_manager(
super_user: BuildLivingSpace, db_session=None # super_user: Employees, db_session=None
) -> None: # ) -> None:
list_of_created_apps = [] # list_of_created_apps = Applications.filter_all_system(db=db_session).data
# if not list_of_created_apps:
# raise Exception("No applications found")
# for list_of_created_app in list_of_created_apps:
# application_employee_created = Application2Employee.find_or_create(
# employee_id=super_user.id,
# employee_uu_id=str(super_user.uu_id),
# site_url=list_of_created_app.site_url,
# application_code=list_of_created_app.application_code,
# application_id=list_of_created_app.id,
# application_uu_id=str(list_of_created_app.uu_id),
# is_confirmed=True,
# db=db_session,
# )
# if application_employee_created.meta_data.created:
# application_employee_created.save(db=db_session)
def init_applications_for_owner(super_user: BuildLivingSpace, db_session=None) -> None:
pass # def init_applications_for_build_manager(
# super_user: BuildLivingSpace, db_session=None
# ) -> None:
# list_of_created_apps = Applications.filter_all_system(db=db_session).data
# if not list_of_created_apps:
# raise Exception("No applications found")
# for list_of_created_app in list_of_created_apps:
# application_employee_created = Application2Employee.find_or_create(
# employee_id=super_user.id,
# employee_uu_id=str(super_user.uu_id),
# site_url=list_of_created_app.site_url,
# application_code=list_of_created_app.application_code,
# application_id=list_of_created_app.id,
# application_uu_id=str(list_of_created_app.uu_id),
# is_confirmed=True,
# db=db_session,
# )
# if application_employee_created.meta_data.created:
# application_employee_created.save(db=db_session)
def init_applications_for_tenant(super_user: BuildLivingSpace, db_session=None) -> None: # def init_applications_for_owner(super_user: BuildLivingSpace, db_session=None) -> None:
list_of_created_apps = [ # list_of_created_apps = Applications.filter_all_system(db=db_session).data
dict( # if not list_of_created_apps:
name="Dashboard1", # raise Exception("No applications found")
application_code="app000001",
site_url="/dashboard",
application_type="info",
description="Dashboard Page",
),
dict(
name="TenantSendMessageToBuildManager",
application_code="app000022",
site_url="/tenant/messageToBM",
application_type="Dash",
description="Individual Page for tenant send message to build manager",
),
dict(
name="TenantSendMessageToOwner",
application_code="app000018",
site_url="/tenant/messageToOwner",
application_type="Dash",
description="Individual Page for tenant send message to owner",
),
dict(
name="TenantAccountView",
application_code="app000019",
site_url="/tenant/accounting",
application_type="Dash",
description="Individual Page for tenant account view",
),
] # for list_of_created_app in list_of_created_apps:
# application_employee_created = Application2Employee.find_or_create(
# employee_id=super_user.id,
# employee_uu_id=str(super_user.uu_id),
# site_url=list_of_created_app.site_url,
# application_code=list_of_created_app.application_code,
# application_id=list_of_created_app.id,
# application_uu_id=str(list_of_created_app.uu_id),
# is_confirmed=True,
# db=db_session,
# )
# if application_employee_created.meta_data.created:
# application_employee_created.save(db=db_session)
for list_of_created_app in list_of_created_apps:
created_page = Applications.find_or_create(
**list_of_created_app,
db=db_session,
is_confirmed=True,
)
if created_page.meta_data.created:
created_page.save(db=db_session)
application_occupant_created = Application2Occupant.find_or_create( # def init_applications_for_tenant(super_user: BuildLivingSpace, db_session=None) -> None:
build_living_space_id=super_user.id, # list_of_created_apps = Applications.filter_all_system(db=db_session).data
build_living_space_uu_id=str(super_user.uu_id), # if not list_of_created_apps:
site_url=created_page.site_url, # raise Exception("No applications found")
application_code=created_page.application_code,
application_id=created_page.id, # for list_of_created_app in list_of_created_apps:
application_uu_id=str(created_page.uu_id), # application_employee_created = Application2Employee.find_or_create(
is_confirmed=True, # employee_id=super_user.id,
db=db_session, # employee_uu_id=str(super_user.uu_id),
) # site_url=list_of_created_app.site_url,
if application_occupant_created.meta_data.created: # application_code=list_of_created_app.application_code,
application_occupant_created.save(db=db_session) # application_id=list_of_created_app.id,
# application_uu_id=str(list_of_created_app.uu_id),
# is_confirmed=True,
# db=db_session,
# )
# if application_employee_created.meta_data.created:
# application_employee_created.save(db=db_session)
# list_of_created_apps = [
# dict(
# name="Dashboard1",
# application_code="app000001",
# site_url="/dashboard",
# application_type="info",
# description="Dashboard Page",
# ),
# dict(
# name="TenantSendMessageToBuildManager",
# application_code="app000022",
# site_url="/tenant/messageToBM",
# application_type="Dash",
# description="Individual Page for tenant send message to build manager",
# ),
# dict(
# name="TenantSendMessageToOwner",
# application_code="app000018",
# site_url="/tenant/messageToOwner",
# application_type="Dash",
# description="Individual Page for tenant send message to owner",
# ),
# dict(
# name="TenantAccountView",
# application_code="app000019",
# site_url="/tenant/accounting",
# application_type="Dash",
# description="Individual Page for tenant account view",
# ),
# ]
# for list_of_created_app in list_of_created_apps:
# created_page = Applications.find_or_create(
# **list_of_created_app,
# db=db_session,
# is_confirmed=True,
# )
# if created_page.meta_data.created:
# created_page.save(db=db_session)
# application_occupant_created = Application2Occupant.find_or_create(
# build_living_space_id=super_user.id,
# build_living_space_uu_id=str(super_user.uu_id),
# site_url=created_page.site_url,
# application_code=created_page.application_code,
# application_id=created_page.id,
# application_uu_id=str(created_page.uu_id),
# is_confirmed=True,
# db=db_session,
# )
# if application_occupant_created.meta_data.created:
# application_occupant_created.save(db=db_session)

View File

@@ -11,14 +11,23 @@ from Schemas import (
) )
def init_service_to_event_matches_for_super_user(super_user, db_session=None) -> None: def init_service_to_event_matches_for_super_user(service_match: Services, employee_match: Employees, db_session=None) -> None:
service_match = Services.filter_one(
Services.service_name == "Super User",
db=db_session,
).data
list_of_all_events = Events.filter_all(db=db_session).data list_of_all_events = Events.filter_all(db=db_session).data
Service2Events.filter_all(db=db_session).query.delete()
Service2Events.save(db=db_session)
for list_of_event_code in list_of_all_events: for list_of_event_code in list_of_all_events:
created_service = Service2Events.find_or_create( service_to_event_found = Service2Events.filter_one_system(
Service2Events.event_id == list_of_event_code.id,
Service2Events.service_id == service_match.id,
db=db_session,
)
if service_to_event_found.data:
service_to_event_found.destroy(db=db_session)
print(
f"UUID: {service_to_event_found.uu_id} event is deleted from {service_match.service_description}"
)
added_service = Service2Events.find_or_create(
service_id=service_match.id, service_id=service_match.id,
service_uu_id=str(service_match.uu_id), service_uu_id=str(service_match.uu_id),
event_id=list_of_event_code.id, event_id=list_of_event_code.id,
@@ -27,22 +36,22 @@ def init_service_to_event_matches_for_super_user(super_user, db_session=None) ->
active=True, active=True,
db=db_session, db=db_session,
) )
if created_service.meta_data.created: if added_service.meta_data.created:
created_service.save(db=db_session) added_service.save(db=db_session)
print( print(
f"UUID: {created_service.uu_id} event is saved to {service_match.uu_id}" f"UUID: {added_service.uu_id} event is saved to {service_match.service_description}"
) )
employee_added_service = Event2Employee.find_or_create( employee_added_service = Event2Employee.find_or_create(
event_service_id=service_match.id, event_service_id=service_match.id,
event_service_uu_id=str(service_match.uu_id), event_service_uu_id=str(service_match.uu_id),
employee_id=super_user.id, employee_id=employee_match.id,
employee_uu_id=str(super_user.uu_id), employee_uu_id=str(employee_match.uu_id),
is_confirmed=True, is_confirmed=True,
db=db_session, db=db_session,
) )
if employee_added_service.meta_data.created: if employee_added_service.meta_data.created:
employee_added_service.save(db=db_session) employee_added_service.save(db=db_session)
print( print(
f"UUID: {employee_added_service.uu_id} event is saved to {super_user.uu_id}" f"UUID: {employee_added_service.uu_id} event is saved to employee {employee_match.uu_id}"
) )

View File

@@ -26,7 +26,7 @@ def people_route_list(
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleList") FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key) event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable() return event_cluster_matched.event_callable(data=data)
@people_route.post( @people_route.post(

View File

@@ -4,10 +4,8 @@ from fastapi import APIRouter
def get_routes() -> list[APIRouter]: def get_routes() -> list[APIRouter]:
from .people.route import people_route from .people.route import people_route
from .user.route import user_route from .user.route import user_route
return [
user_route, return [user_route, people_route]
people_route
]
def get_safe_endpoint_urls() -> list[tuple[str, str]]: def get_safe_endpoint_urls() -> list[tuple[str, str]]:

View File

@@ -1,11 +1,13 @@
import uuid import uuid
from fastapi import APIRouter, Request, Response, Header from fastapi import APIRouter, Header
from typing import Any
from ApiDefaults.config import api_config from ApiDefaults.config import api_config
from Events.user.cluster import UserRouterCluster from Events.user.cluster import UserRouterCluster
from ApiControllers.providers.token_provider import TokenProvider from ApiControllers.providers.token_provider import TokenProvider
from ApiControllers.abstracts.default_validations import CommonHeaders
from Controllers.Postgres.pagination import PaginateOnly
user_route = APIRouter(prefix="/user", tags=["User"]) user_route = APIRouter(prefix="/user", tags=["User"])
@@ -13,95 +15,58 @@ user_route = APIRouter(prefix="/user", tags=["User"])
@user_route.post( @user_route.post(
path="/list", path="/list",
description="List users endpoint", description="List users endpoint",
operation_id="5bc09312-d3f2-4f47-baba-17c928706da8", operation_id="1aca3094-fe80-4e0f-a460-1a506419082a",
) )
def user_list_route( def user_list_route(
request: Request, data: PaginateOnly,
response: Response, headers: CommonHeaders,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
tz: str = Header(None, alias="timezone"),
): ):
""" """
List users endpoint List users endpoint
""" """
endpoint_code = "5bc09312-d3f2-4f47-baba-17c928706da8" token_object = TokenProvider.get_dict_from_redis(token=headers.token)
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
token_object = TokenProvider.get_dict_from_redis(token=token) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
event_key = TokenProvider.retrieve_event_codes(endpoint_code=endpoint_code, token=token_object) FoundCluster = UserRouterCluster.get_event_cluster("UserList")
headers = { event_cluster_matched = FoundCluster.match_event(event_key=event_key)
"language": language or "", return event_cluster_matched.event_callable(data=data)
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"tz": tz or "GMT+3",
"token": token,
}
event_cluster_matched = UserRouterCluster.get_event_cluster("UserList").match_event(event_key=event_key)
response.headers["X-Header"] = "Test Header GET"
if runner_callable := event_cluster_matched.event_callable():
return runner_callable
raise ValueError("Event key not found or multiple matches found")
@user_route.post( @user_route.post(
path="/create", path="/create",
description="Create users endpoint", description="Create users endpoint",
operation_id="08d4b572-1584-47bb-aa42-8d068e5514e7", operation_id="9686211f-4260-485d-8076-186c22c053aa",
) )
def user_create_route( def user_create_route(
request: Request, data: Any,
response: Response, headers: CommonHeaders,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
tz: str = Header(None, alias="timezone"),
): ):
""" """
Create users endpoint Create users endpoint
""" """
endpoint_code = "08d4b572-1584-47bb-aa42-8d068e5514e7" token_object = TokenProvider.get_dict_from_redis(token=headers.token)
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
token_object = TokenProvider.get_dict_from_redis(token=token) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
event_key = TokenProvider.retrieve_event_codes( FoundCluster = UserRouterCluster.get_event_cluster("UserCreate")
endpoint_code=endpoint_code, token=token_object event_cluster_matched = FoundCluster.match_event(event_key=event_key)
) return event_cluster_matched.event_callable(data=data)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"tz": tz or "GMT+3",
"token": token,
}
event_cluster_matched = UserRouterCluster.get_event_cluster("UserCreate").match_event(event_key=event_key)
response.headers["X-Header"] = "Test Header POST"
if runner_callable := event_cluster_matched.event_callable():
return runner_callable
raise ValueError("Event key not found or multiple matches found")
@user_route.post( @user_route.post(
path="/update", path="/update",
description="Update users endpoint", description="Update users endpoint",
operation_id="b641236a-928d-4f19-a1d2-5edf611d1e56", operation_id="268e887b-5ff5-4f99-b1be-7e127e28a198",
) )
def user_update_route(request: Request, response: Response): def user_update_route(
data: Any,
headers: CommonHeaders,
):
""" """
Update users endpoint Update users endpoint
""" """
endpoint_code = "b641236a-928d-4f19-a1d2-5edf611d1e56" token_object = TokenProvider.get_dict_from_redis(token=headers.token)
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
token_object = TokenProvider.get_dict_from_redis(token=token) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
event_key = TokenProvider.retrieve_event_codes( FoundCluster = UserRouterCluster.get_event_cluster("UserUpdate")
endpoint_code=endpoint_code, token=token_object event_cluster_matched = FoundCluster.match_event(event_key=event_key)
) return event_cluster_matched.event_callable(data=data)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"tz": tz or "GMT+3",
"token": token,
}
event_cluster_matched = UserRouterCluster.get_event_cluster("UserUpdate").match_event(event_key=event_key)
response.headers["X-Header"] = "Test Header POST"
if runner_callable := event_cluster_matched.event_callable():
return runner_callable
raise ValueError("Event key not found or multiple matches found")

View File

@@ -2,31 +2,24 @@ from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import ( from .supers_events import (
SupersPeopleCreateEvent, SupersPeopleCreateEvent,
SupersPeopleUpdateEvent, SupersPeopleUpdateEvent,
SupersPeopleListEvent SupersPeopleListEvent,
) )
PeopleRouterCluster = RouterCluster( PeopleRouterCluster = RouterCluster(name="PeopleRouterCluster")
name="PeopleRouterCluster"
)
PeopleEventClusterList = EventCluster( PeopleEventClusterList = EventCluster(
name="PeopleList", name="PeopleList", endpoint_uu_id="f102db46-031a-43e4-966a-dae6896f985b"
endpoint_uu_id="f102db46-031a-43e4-966a-dae6896f985b"
) )
PeopleEventClusterList.add_event(SupersPeopleListEvent) PeopleEventClusterList.add_event(SupersPeopleListEvent)
PeopleEventClusterCreate = EventCluster( PeopleEventClusterCreate = EventCluster(
name="PeopleCreate", name="PeopleCreate", endpoint_uu_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6"
endpoint_uu_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6"
) )
PeopleEventClusterCreate.add_event(SupersPeopleCreateEvent) PeopleEventClusterCreate.add_event(SupersPeopleCreateEvent)
PeopleEventClusterUpdate = EventCluster( PeopleEventClusterUpdate = EventCluster(
name="PeopleUpdate", name="PeopleUpdate", endpoint_uu_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89"
endpoint_uu_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89"
) )
PeopleEventClusterUpdate.add_event(SupersPeopleUpdateEvent) PeopleEventClusterUpdate.add_event(SupersPeopleUpdateEvent)
PeopleRouterCluster.set_event_cluster(PeopleEventClusterList) PeopleRouterCluster.set_event_cluster(PeopleEventClusterList)
PeopleRouterCluster.set_event_cluster(PeopleEventClusterCreate) PeopleRouterCluster.set_event_cluster(PeopleEventClusterCreate)
PeopleRouterCluster.set_event_cluster(PeopleEventClusterUpdate) PeopleRouterCluster.set_event_cluster(PeopleEventClusterUpdate)

View File

@@ -2,7 +2,12 @@ from ApiControllers.abstracts.event_clusters import Event
from Validations.people.validations import ( from Validations.people.validations import (
REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV, REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV,
) )
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly from Controllers.Postgres.pagination import (
ListOptions,
Pagination,
PaginationResult,
PaginateOnly,
)
from Controllers.Postgres.response import EndpointResponse from Controllers.Postgres.response import EndpointResponse
from Schemas import People from Schemas import People
@@ -12,7 +17,7 @@ SupersPeopleCreateEvent = Event(
key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41", key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41",
request_validator=None, # TODO: Add request validator request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator response_validator=None, # TODO: Add response validator
description="Create events of people endpoint", description="Super Users Create events of people endpoint",
) )
# Update endpoint # Update endpoint
@@ -21,7 +26,7 @@ SupersPeopleUpdateEvent = Event(
key="91e77de4-9f29-4309-b121-4aad256d440c", key="91e77de4-9f29-4309-b121-4aad256d440c",
request_validator=None, # TODO: Add request validator request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator response_validator=None, # TODO: Add response validator
description="Update events of people endpoint", description="Super Users Update events of people endpoint",
) )
# List endpoint # List endpoint
@@ -30,11 +35,11 @@ SupersPeopleListEvent = Event(
key="6828d280-e587-400d-a622-c318277386c3", key="6828d280-e587-400d-a622-c318277386c3",
request_validator=None, # TODO: Add request validator request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator response_validator=None, # TODO: Add response validator
description="List events of people endpoint", description="Super Users List events of people endpoint",
) )
def supers_people_create_callable(list_options): def supers_people_create_callable():
""" """
Example callable method Example callable method
""" """
@@ -70,11 +75,11 @@ def supers_people_update_callable():
SupersPeopleUpdateEvent.event_callable = supers_people_update_callable SupersPeopleUpdateEvent.event_callable = supers_people_update_callable
def supers_people_list_callable(list_options: PaginateOnly): def supers_people_list_callable(data: PaginateOnly):
""" """
Example callable method Example callable method
""" """
list_options = PaginateOnly(**list_options.model_dump()) list_options = PaginateOnly(**data.model_dump())
with People.new_session() as db_session: with People.new_session() as db_session:
if list_options.query: if list_options.query:
people_list = People.filter_all( people_list = People.filter_all(

View File

@@ -5,25 +5,20 @@ from .supers_events import (
SuperUsersUpdateEvent, SuperUsersUpdateEvent,
) )
UserRouterCluster = RouterCluster( UserRouterCluster = RouterCluster(name="UserRouterCluster")
name="UserRouterCluster"
)
UserEventClusterList = EventCluster( UserEventClusterList = EventCluster(
name="UserList", name="UserList", endpoint_uu_id="1aca3094-fe80-4e0f-a460-1a506419082a"
endpoint_uu_id="5bc09312-d3f2-4f47-baba-17c928706da8"
) )
UserEventClusterList.add_event(SuperUsersListEvent) UserEventClusterList.add_event(SuperUsersListEvent)
UserEventClusterCreate = EventCluster( UserEventClusterCreate = EventCluster(
name="UserCreate", name="UserCreate", endpoint_uu_id="9686211f-4260-485d-8076-186c22c053aa"
endpoint_uu_id="08d4b572-1584-47bb-aa42-8d068e5514e7"
) )
UserEventClusterCreate.add_event(SuperUsersCreateEvent) UserEventClusterCreate.add_event(SuperUsersCreateEvent)
UserEventClusterUpdate = EventCluster( UserEventClusterUpdate = EventCluster(
name="UserUpdate", name="UserUpdate", endpoint_uu_id="268e887b-5ff5-4f99-b1be-7e127e28a198"
endpoint_uu_id="b641236a-928d-4f19-a1d2-5edf611d1e56"
) )
UserEventClusterUpdate.add_event(SuperUsersUpdateEvent) UserEventClusterUpdate.add_event(SuperUsersUpdateEvent)

View File

@@ -1,16 +1,34 @@
from ApiControllers.abstracts.event_clusters import Event from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse from Controllers.Postgres.response import EndpointResponse
from Schemas import ( from Schemas import Users
Users,
)
# List endpoint
SuperUsersListEvent = Event( SuperUsersListEvent = Event(
name="supers_users_list", name="supers_users_list",
key="341b394f-9f11-4abb-99e7-4b27fa6bf012", key="341b394f-9f11-4abb-99e7-4b27fa6bf012",
request_validator=None, # TODO: Add request validator request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator response_validator=None, # TODO: Add response validator
description="List events of users endpoint", description="Super User List events of users endpoint",
)
# Create endpoint
SuperUsersCreateEvent = Event(
name="supers_users_create",
key="4e7e189e-e015-4ff8-902d-60138cbc77a6",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super User Create events of users endpoint",
)
# Update endpoint
SuperUsersUpdateEvent = Event(
name="supers_users_update",
key="efa4aa4a-d414-4391-91ee-97eb617b7755",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super User Update events of users endpoint",
) )
@@ -32,31 +50,15 @@ def supers_users_list_callable(list_options: PaginateOnly):
data=users_list, data=users_list,
pagination=pagination, pagination=pagination,
# response_model="", # response_model="",
) ).pagination.as_dict
return EndpointResponse( return EndpointResponse(
message="MSG0003-LIST", message="MSG0003-LIST",
pagination_result=pagination_result, pagination_result=pagination_result,
).response ).response
# return {
# "completed": True,
# "message": "Example callable method 2",
# "info": {
# "host": "example_host",
# "user_agent": "example_user_agent",
# },
# }
SuperUsersListEvent.event_callable = supers_users_list_callable SuperUsersListEvent.event_callable = supers_users_list_callable
SuperUsersCreateEvent = Event(
name="supers_users_create",
key="4e7e189e-e015-4ff8-902d-60138cbc77a6",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Create events of users endpoint",
)
def supers_users_create_callable(): def supers_users_create_callable():
""" """
@@ -71,16 +73,8 @@ def supers_users_create_callable():
}, },
} }
SuperUsersCreateEvent.event_callable = supers_users_create_callable
# Update endpoint SuperUsersCreateEvent.event_callable = supers_users_create_callable
SuperUsersUpdateEvent = Event(
name="supers_users_update",
key="efa4aa4a-d414-4391-91ee-97eb617b7755",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Update events of users endpoint",
)
def supers_users_update_callable(): def supers_users_update_callable():
@@ -96,4 +90,5 @@ def supers_users_update_callable():
}, },
} }
SuperUsersUpdateEvent.event_callable = supers_users_update_callable SuperUsersUpdateEvent.event_callable = supers_users_update_callable

View File

@@ -444,6 +444,10 @@ def create_application_defaults(db_session):
f"{str(company_management.uu_id)}*Domain", f"{str(company_management.uu_id)}*Domain",
) )
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one(
{"user_uu_id": str(gen_manager_user.uu_id)}
)
if not existing_record:
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(gen_manager_user.uu_id), "user_uu_id": str(gen_manager_user.uu_id),
@@ -452,6 +456,17 @@ def create_application_defaults(db_session):
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
mongo_engine.update_one(
{"user_uu_id": str(gen_manager_user.uu_id)},
{
"$set": {
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
app_manager_user = Users.find_or_create( app_manager_user = Users.find_or_create(
person_id=app_manager.id, person_id=app_manager.id,
@@ -472,6 +487,10 @@ def create_application_defaults(db_session):
app_manager_user.password_token = PasswordModule.generate_refresher_token() app_manager_user.password_token = PasswordModule.generate_refresher_token()
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one(
{"user_uu_id": str(app_manager_user.uu_id)}
)
if not existing_record:
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(app_manager_user.uu_id), "user_uu_id": str(app_manager_user.uu_id),
@@ -480,6 +499,17 @@ def create_application_defaults(db_session):
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
mongo_engine.update_one(
{"user_uu_id": str(app_manager_user.uu_id)},
{
"$set": {
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
sup_manager_employee = Users.find_or_create( sup_manager_employee = Users.find_or_create(
person_id=sup_manager.id, person_id=sup_manager.id,
@@ -502,14 +532,32 @@ def create_application_defaults(db_session):
sup_manager_employee.password_expiry_begins = str(arrow.now()) sup_manager_employee.password_expiry_begins = str(arrow.now())
sup_manager_employee.password_token = PasswordModule.generate_refresher_token() sup_manager_employee.password_token = PasswordModule.generate_refresher_token()
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one(
{"user_uu_id": str(sup_manager_employee.uu_id)}
)
if not existing_record:
print("insert sup existing record", existing_record)
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(sup_manager_employee.uu_id), "user_uu_id": str(sup_manager_employee.uu_id),
"other_domains_list": [main_domain], "other_domains_list": [main_domain, "management.com.tr"],
"main_domain": main_domain, "main_domain": main_domain,
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
print("update sup existing record", existing_record)
# Optionally update the existing record if needed
mongo_engine.update_one(
{"user_uu_id": str(sup_manager_employee.uu_id)},
{
"$set": {
"other_domains_list": [main_domain, "management.com.tr"],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
db_session.commit() db_session.commit()
print("All Defaults Create is now completed") print("All Defaults Create is now completed")

View File

@@ -239,6 +239,10 @@ def create_occupant_defaults(db_session):
user_tenant.password_token = PasswordModule.generate_refresher_token() user_tenant.password_token = PasswordModule.generate_refresher_token()
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one(
{"user_uu_id": str(user_build_manager.uu_id)}
)
if not existing_record:
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(user_build_manager.uu_id), "user_uu_id": str(user_build_manager.uu_id),
@@ -247,8 +251,21 @@ def create_occupant_defaults(db_session):
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
mongo_engine.update_one(
{"user_uu_id": str(user_build_manager.uu_id)},
{
"$set": {
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one({"user_uu_id": str(user_owner.uu_id)})
if not existing_record:
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(user_owner.uu_id), "user_uu_id": str(user_owner.uu_id),
@@ -257,8 +274,21 @@ def create_occupant_defaults(db_session):
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
mongo_engine.update_one(
{"user_uu_id": str(user_owner.uu_id)},
{
"$set": {
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
with mongo_handler.collection(collection_name) as mongo_engine: with mongo_handler.collection(collection_name) as mongo_engine:
existing_record = mongo_engine.find_one({"user_uu_id": str(user_tenant.uu_id)})
if not existing_record:
mongo_engine.insert_one( mongo_engine.insert_one(
document={ document={
"user_uu_id": str(user_tenant.uu_id), "user_uu_id": str(user_tenant.uu_id),
@@ -267,6 +297,17 @@ def create_occupant_defaults(db_session):
"modified_at": arrow.now().timestamp(), "modified_at": arrow.now().timestamp(),
} }
) )
else:
mongo_engine.update_one(
{"user_uu_id": str(user_tenant.uu_id)},
{
"$set": {
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
},
)
created_build_living_space_prs = BuildLivingSpace.find_or_create( created_build_living_space_prs = BuildLivingSpace.find_or_create(
build_id=created_build.id, build_id=created_build.id,

View File

@@ -0,0 +1,29 @@
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 /ApiControllers /ApiControllers
COPY /ApiDefaults /ApiDefaults
COPY /Controllers /Controllers
COPY /Schemas /Schemas
COPY /ApiServices/ManagementService/Endpoints /ApiDefaults/Endpoints
COPY /ApiServices/ManagementService/Events /ApiDefaults/Events
COPY /ApiServices/ManagementService/Validations /ApiDefaults/Validations
# 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", "ApiDefaults/app.py"]

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,197 @@
from fastapi import APIRouter, Depends
from typing import Any
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
from Controllers.Postgres.response import EndpointResponse, CreateEndpointResponse
from Schemas import Applications
from Validations.application.validations import RequestApplication
from Events.application.cluster import ApplicationRouterCluster
from Validations.application.validations import AddRemoveService
# Create API router
application_route = APIRouter(prefix="/application", tags=["Application Management"])
@application_route.post(
path="/list/all",
description="List all applications endpoint",
operation_id="fe30481d-802c-4490-897f-a4e95310e6bc",
)
def application_list_all_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List all applications with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationListAll")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@application_route.post(
path="/list/available",
description="List available applications endpoint",
operation_id="7492bb02-a074-4320-b58c-4bc7d9fba3a6",
)
def application_list_available_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List available applications with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationListAvailable")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@application_route.post(
path="/list/appended",
description="List appended applications endpoint",
operation_id="ea7bbd58-da09-407c-a630-c324e0272385",
)
def application_list_appended_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List appended applications with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationListAppended")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@application_route.post(
path="/register/service",
description="Register event to service endpoint",
operation_id="92e0870f-f8bb-4879-ba03-c2901cc70ecc",
)
def application_register_service_route(
data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Register event to service
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationRegisterService")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@application_route.post(
path="/unregister/service",
description="Unregister event from service endpoint",
operation_id="7fa1a183-4c99-4746-b675-148f65ad7ba7",
)
def application_unregister_service_route(
data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Unregister event from service
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationUnRegisterService")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@application_route.post(
path="/create",
description="Create application endpoint",
operation_id="5570be78-030a-438e-8674-7e751447608b",
)
def application_create_route(
data: RequestApplication,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Create a new application
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationCreate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@application_route.post(
path="/update/{application_uuid}",
description="Update application endpoint",
operation_id="87cd4515-73dd-4d11-a01f-562e221d973c",
)
def application_update_route(
data: RequestApplication,
application_uuid: str,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Update an existing application
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationUpdate")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data, uu_id=application_uuid)
@application_route.post(
path="/bind/employee",
description="Bind application to employee endpoint",
operation_id="2bab94fa-becb-4d8e-80f1-f4631119a521",
)
def application_bind_employee_route(
data: Any,
headers: CommonHeaders,
):
"""
Bind application to employee endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationBindEmployee")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@application_route.post(
path="/bind/occupant",
description="Bind application to occupant endpoint",
operation_id="fccf1a59-0650-4e5c-ba8d-f389dadce01c",
)
def application_bind_occupant_route(
data: Any,
headers: CommonHeaders,
):
"""
Bind application to occupant endpoint
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationBindOccupant")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,142 @@
from typing import Any
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
from Controllers.Postgres.response import EndpointResponse
from Validations.service_endpoints.validations import (
Event2Employee,
Event2Occupant,
AddRemoveService,
)
from Events.event_endpoints.cluster import EventsEndpointRouterCluster
# Create API router
event_endpoint_route = APIRouter(prefix="/events", tags=["Event Actions"])
@event_endpoint_route.post(
path="/list/available",
description="List available events endpoint",
operation_id="0659d5e4-671f-466c-a84f-47a1290a6f0d",
)
def event_list_available_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List available events with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventsListAvailable")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@event_endpoint_route.post(
path="/list/appended",
description="List appended events endpoint",
operation_id="4d563973-cdcd-44e1-94e0-4262ffb456a1",
)
def event_list_appended_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List events with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventsListAppended")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(list_options=data)
@event_endpoint_route.post(
path="/register/service",
description="Register event to service endpoint",
operation_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76",
)
def event_register_service_route(
data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Register event to service
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventRegisterService")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@event_endpoint_route.post(
path="/unregister/service",
description="Unregister event from service endpoint",
operation_id="2f16dc9e-de02-449d-9c3f-1a21f87e8794",
)
def event_unregister_service_route(
data: AddRemoveService,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Unregister event from service
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster(
"EventUnregisterService"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@event_endpoint_route.post(
path="/bind/extra/employee",
description="Bind event to employee extra endpoint",
operation_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24",
)
def event_bind_employee_extra_route(
data: Any,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Bind event to employee extra
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster(
"EventBindEmployeeExtra"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@event_endpoint_route.post(
path="/bind/extra/occupant",
description="Bind event to occupant extra endpoint",
operation_id="7794a550-3073-43e3-b0c5-80128f8d3e4b",
)
def event_bind_occupant_extra_route(
data: Any,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
Bind event to occupant extra
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = EventsEndpointRouterCluster.get_event_cluster(
"EventBindOccupantExtra"
)
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,25 @@
from fastapi import APIRouter
def get_routes() -> list[APIRouter]:
from .application.route import application_route
from .service_endpoints.route import service_endpoint_route
from .service_managements.route import service_management_route
from .event_endpoints.route import event_endpoint_route
return [
application_route,
service_endpoint_route,
service_management_route,
event_endpoint_route,
]
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
return [
("/", "GET"),
("/docs", "GET"),
("/redoc", "GET"),
("/openapi.json", "GET"),
("/metrics", "GET"),
]

View File

@@ -0,0 +1,52 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
from Controllers.Postgres.response import EndpointResponse
from Events.service_endpoints.cluster import ServiceEndpointRouterCluster
# Create API router
service_endpoint_route = APIRouter(prefix="/service", tags=["Service Actions"])
@service_endpoint_route.post(
path="/list",
description="List services endpoint",
operation_id="f4e4d332-70b1-4121-9fcc-a08850b72aaa",
)
def service_list_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List services with pagination and filtering options
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ServiceEndpointRouterCluster.get_event_cluster("ServiceList")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)
@service_endpoint_route.post(
path="/to/events",
description="List events of a service endpoint given service UUID",
operation_id="7b6b0c6a-e3db-4353-a7df-ea49d2a67f8a",
)
def service_to_events_route(
data: PaginateOnly,
headers: CommonHeaders = Depends(CommonHeaders.as_dependency),
):
"""
List events of a service given service UUID
"""
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
FoundCluster = ServiceEndpointRouterCluster.get_event_cluster("ServiceToEvents")
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
return event_cluster_matched.event_callable(data=data)

View File

@@ -0,0 +1,26 @@
from fastapi import APIRouter, Depends
from ApiControllers.abstracts.default_validations import CommonHeaders
from ApiControllers.providers.token_provider import TokenProvider
from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult
from Controllers.Postgres.response import EndpointResponse
from Schemas import (
Services,
Employees,
Event2Employee,
Users,
Events,
Service2Events,
Applications,
Application2Employee,
Application2Occupant,
)
from Validations.application.validations import (
RequestApplication,
)
# Create API router
service_management_route = APIRouter(
prefix="/managements/service", tags=["Service Management"]
)

View File

@@ -0,0 +1,9 @@
from .service_endpoints.cluster import ServiceEndpointRouterCluster
from .event_endpoints.cluster import EventsEndpointRouterCluster
from .application.cluster import ApplicationRouterCluster
__all__ = [
"ServiceEndpointRouterCluster",
"EventsEndpointRouterCluster",
"ApplicationRouterCluster",
]

View File

@@ -0,0 +1,55 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
ApplicationListAllEvent,
ApplicationListAvailableEvent,
ApplicationListAppendedEvent,
ApplicationRegisterServiceEvent,
ApplicationUnRegisterServiceEvent,
ApplicationCreateEvent,
ApplicationUpdateEvent,
)
ApplicationRouterCluster = RouterCluster(name="ApplicationRouterCluster")
ApplicationEventClusterListAll = EventCluster(
name="ApplicationListAll", endpoint_uu_id="fe30481d-802c-4490-897f-a4e95310e6bc"
)
ApplicationEventClusterListAll.add_event(ApplicationListAllEvent)
ApplicationEventClusterListAvailable = EventCluster(
name="ApplicationListAvailable", endpoint_uu_id="7492bb02-a074-4320-b58c-4bc7d9fba3a6"
)
ApplicationEventClusterListAvailable.add_event(ApplicationListAvailableEvent)
ApplicationEventClusterListAppended = EventCluster(
name="ApplicationListAppended", endpoint_uu_id="ea7bbd58-da09-407c-a630-c324e0272385"
)
ApplicationEventClusterListAppended.add_event(ApplicationListAppendedEvent)
ApplicationEventClusterRegisterService = EventCluster(
name="ApplicationRegisterService", endpoint_uu_id="92e0870f-f8bb-4879-ba03-c2901cc70ecc"
)
ApplicationEventClusterRegisterService.add_event(ApplicationRegisterServiceEvent)
ApplicationEventClusterUnregisterService = EventCluster(
name="ApplicationUnRegisterService", endpoint_uu_id="7fa1a183-4c99-4746-b675-148f65ad7ba7"
)
ApplicationEventClusterUnregisterService.add_event(ApplicationUnRegisterServiceEvent)
ApplicationEventClusterCreate = EventCluster(
name="ApplicationCreate", endpoint_uu_id="5570be78-030a-438e-8674-7e751447608b"
)
ApplicationEventClusterCreate.add_event(ApplicationCreateEvent)
ApplicationEventClusterUpdate = EventCluster(
name="ApplicationUpdate", endpoint_uu_id="87cd4515-73dd-4d11-a01f-562e221d973c"
)
ApplicationEventClusterUpdate.add_event(ApplicationUpdateEvent)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterListAvailable)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterListAppended)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterListAll)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterRegisterService)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterUnregisterService)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterCreate)
ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterUpdate)

View File

@@ -0,0 +1,320 @@
from typing import Any
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import (
Applications,
Application2Employee,
Application2Occupant,
Service2Application,
Services,
)
# List all endpoint
ApplicationListAllEvent = Event(
name="application_list_all",
key="1971ce4d-4f59-4aa8-83e2-ca19d7da6d11",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List all applications endpoint",
)
# List available endpoint
ApplicationListAvailableEvent = Event(
name="application_list_available",
key="d8e733f5-b53a-4c36-9082-12579bf9cc4a",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List available applications endpoint",
)
# List appended endpoint
ApplicationListAppendedEvent = Event(
name="application_list_appended",
key="ea7bbd58-da09-407c-a630-c324e0272385",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List appended applications endpoint",
)
# Register application to service endpoint
ApplicationRegisterServiceEvent = Event(
name="application_register_service",
key="47d7cfc8-6004-4442-8357-16ceac5d9d18",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Register application to service endpoint",
)
# Unregister application to service endpoint
ApplicationUnRegisterServiceEvent = Event(
name="application_unregister_service",
key="d228ab26-0b74-440f-8f1f-8f40be5a22f2",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Unregister application to service endpoint",
)
# Create endpoint
ApplicationCreateEvent = Event(
name="application_create",
key="f53ca9aa-5536-4d77-9129-78d67e61db4a",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Create applications endpoint",
)
# Update endpoint
ApplicationUpdateEvent = Event(
name="application_update",
key="0e9a855e-4e69-44b5-8ac2-825daa32840c",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Update applications endpoint",
)
def application_list_all_callable(list_options: PaginateOnly):
list_options = PaginateOnly(**list_options.model_dump())
with Applications.new_session() as db_session:
if list_options.query:
applications_list = Applications.filter_all(*Applications.convert(list_options.query), db=db_session)
else:
applications_list = Applications.filter_all(db=db_session)
pagination = Pagination(data=applications_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=applications_list, pagination=pagination)
return EndpointResponse(message="MSG0003-LIST", pagination_result=pagination_result).response
ApplicationListAllEvent.event_callable = application_list_all_callable
def application_list_available_callable(list_options: PaginateOnly):
"""
Example callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
service_uu_id = list_options.query.get("service_uu_id__ilike", None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
list_options.query.pop("service_uu_id__ilike", None)
list_options.query.pop("service_uu_id", None)
with Applications.new_session() as db_session:
service2applications = Service2Application.filter_all(
*Service2Application.convert({"service_uu_id__ilike": service_uu_id}),
db=db_session,
)
already_events = [
service_to_application.application_id for service_to_application in service2applications.data
]
if list_options.query:
applications_list = Applications.filter_all(
*Applications.convert(list_options.query), Applications.id.not_in(already_events), db=db_session
)
else:
applications_list = Applications.filter_all(Applications.id.not_in(already_events), db=db_session)
pagination = Pagination(data=applications_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=applications_list, pagination=pagination)
return EndpointResponse(message="MSG0003-LIST", pagination_result=pagination_result).response
ApplicationListAvailableEvent.event_callable = application_list_available_callable
def application_list_appended_callable(list_options: PaginateOnly):
"""
Example callable method
"""
list_options = PaginateOnly(**list_options.model_dump())
service_uu_id = list_options.query.get("service_uu_id__ilike", None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
list_options.query.pop("service_uu_id__ilike", None)
list_options.query.pop("service_uu_id", None)
with Applications.new_session() as db_session:
service2applications = Service2Application.filter_all(
*Service2Application.convert({"service_uu_id__ilike": service_uu_id}),
db=db_session,
)
already_events = [
service_to_application.application_id for service_to_application in service2applications.data
]
if list_options.query:
applications_list = Applications.filter_all(
*Applications.convert(list_options.query), Applications.id.in_(already_events), db=db_session
)
else:
applications_list = Applications.filter_all(Applications.id.in_(already_events), db=db_session)
pagination = Pagination(data=applications_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=applications_list, pagination=pagination)
return EndpointResponse(message="MSG0003-LIST", pagination_result=pagination_result).response
ApplicationListAppendedEvent.event_callable = application_list_appended_callable
def application_create_callable(data: Any):
"""
Create a new application
"""
with Applications.new_session() as db_session:
created_application_dict = data.model_dump()
created_application = Applications.find_or_create(
db=db_session,
include_args=[Applications.application_for, Applications.application_code, Applications.site_url],
**created_application_dict,
)
if created_application.meta_data.created:
created_application.save(db=db_session)
return {
"completed": True,
"message": "MSG0001-INSERT",
"data": created_application,
}
return {
"completed": False,
"message": "MSG0002-ERROR",
"data": created_application,
}
ApplicationCreateEvent.event_callable = application_create_callable
def application_update_callable(data: Any, uu_id: str):
"""
Update an existing application
"""
with Applications.new_session() as db_session:
updated_application_dict = data.model_dump(
exclude_unset=True, exclude_none=True
)
found_application = Applications.filter_one(
Applications.uu_id == uu_id, db=db_session
).data
if not found_application:
return {
"completed": False,
"message": "MSG0002-FOUND",
"data": found_application,
}
updated_application = found_application.update(
db=db_session, **updated_application_dict
)
updated_application.save(db_session)
if updated_application.meta_data.updated:
return {
"completed": True,
"message": "MSG0003-UPDATE",
"data": updated_application,
}
return {
"completed": False,
"message": "MSG0003-UPDATE",
"data": updated_application,
}
ApplicationUpdateEvent.event_callable = application_update_callable
def application_register_service_callable(data: Any):
"""
Register an application to a service
"""
with Applications.new_session() as db_session:
event = Applications.filter_one_system(Applications.uu_id == data.application_uu_id, db=db_session)
if not event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(Services.uu_id == data.service_uu_id, db=db_session)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_application = Service2Application.find_or_create(
db=db_session,
include_args=[Service2Application.service_uu_id, Service2Application.application_uu_id],
service_id=service.data.id,
service_uu_id=str(service.data.uu_id),
application_id=event.data.id,
application_uu_id=str(event.data.uu_id),
application_code=event.data.application_code,
site_url=event.data.site_url,
is_confirmed=True,
)
if not service_to_application.meta_data.created:
return {
"message": "MSG0003-ALREADY-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_application.save(db=db_session)
return {
"message": "MSG0003-REGISTER",
"data": data.model_dump(),
"completed": True,
}
ApplicationRegisterServiceEvent.event_callable = application_register_service_callable
def application_unregister_service_callable(data: Any):
"""
Unregister an application from a service
"""
with Applications.new_session() as db_session:
application = Applications.filter_one_system(Applications.uu_id == data.application_uu_id, db=db_session)
if not application.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(Services.uu_id == data.service_uu_id, db=db_session)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_application = Service2Application.filter_one_system(
Service2Application.service_id == service.data.id,
Service2Application.application_id == application.data.id,
db=db_session,
)
if not service_to_application.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_application.query.delete()
db_session.commit()
return {
"message": "MSG0003-UNREGISTER",
"data": data.model_dump(),
"completed": True,
}
ApplicationUnRegisterServiceEvent.event_callable = application_unregister_service_callable

View File

@@ -0,0 +1,54 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
EventsListAvailableEvent,
EventsListAppendedEvent,
EventRegisterServiceEvent,
EventUnRegisterServiceEvent,
EventBindEmployeeExtraEvent,
EventBindOccupantExtraEvent,
)
EventsEndpointRouterCluster = RouterCluster(name="EventsEndpointRouterCluster")
EventsEndpointEventClusterListAvailable = EventCluster(
name="EventsListAvailable", endpoint_uu_id="0659d5e4-671f-466c-a84f-47a1290a6f0d"
)
EventsEndpointEventClusterListAvailable.add_event(EventsListAvailableEvent)
EventsEndpointEventClusterListAppended = EventCluster(
name="EventsListAppended", endpoint_uu_id="4d563973-cdcd-44e1-94e0-4262ffb456a1"
)
EventsEndpointEventClusterListAppended.add_event(EventsListAppendedEvent)
EventsEndpointEventClusterRegisterService = EventCluster(
name="EventRegisterService", endpoint_uu_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76"
)
EventsEndpointEventClusterRegisterService.add_event(EventRegisterServiceEvent)
EventsEndpointEventClusterUnregisterService = EventCluster(
name="EventUnregisterService", endpoint_uu_id="2f16dc9e-de02-449d-9c3f-1a21f87e8794"
)
EventsEndpointEventClusterUnregisterService.add_event(EventUnRegisterServiceEvent)
EventsEndpointEventClusterBindEmployeeExtra = EventCluster(
name="EventBindEmployeeExtra", endpoint_uu_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24"
)
EventsEndpointEventClusterBindEmployeeExtra.add_event(EventBindEmployeeExtraEvent)
EventsEndpointEventClusterBindOccupantExtra = EventCluster(
name="EventBindOccupantExtra", endpoint_uu_id="7794a550-3073-43e3-b0c5-80128f8d3e4b"
)
EventsEndpointEventClusterBindOccupantExtra.add_event(EventBindOccupantExtraEvent)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterListAvailable)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterListAppended)
EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterRegisterService)
EventsEndpointRouterCluster.set_event_cluster(
EventsEndpointEventClusterUnregisterService
)
EventsEndpointRouterCluster.set_event_cluster(
EventsEndpointEventClusterBindEmployeeExtra
)
EventsEndpointRouterCluster.set_event_cluster(
EventsEndpointEventClusterBindOccupantExtra
)

View File

@@ -0,0 +1,280 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from typing import Any
from Schemas import (
Events,
Event2Employee,
Event2Occupant,
Event2EmployeeExtra,
Event2OccupantExtra,
Service2Events,
Services,
)
# List available events endpoint
EventsListAvailableEvent = Event(
name="event_endpoint_list_available",
key="d39af512-ec71-4c0f-9b35-e53b0d06d3a4",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List available events endpoint",
)
# List appended events endpoint
EventsListAppendedEvent = Event(
name="event_endpoint_list_appended",
key="bea77d6a-d99f-468b-9002-b3bda6bb6ad0",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List appended events endpoint",
)
# Event Register endpoint
EventRegisterServiceEvent = Event(
name="event_endpoint_register_service",
key="e18e7f89-5708-4a15-9258-99b0903ed43d",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Register service endpoint",
)
# Event Unregister endpoint
EventUnRegisterServiceEvent = Event(
name="service_endpoint_unregister_service",
key="4d693774-4857-435b-a63c-c39baebfe916",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Unregister service endpoint",
)
# Bind employee extra endpoint
EventBindEmployeeExtraEvent = Event(
name="service_endpoint_bind_employee_extra",
key="cd452928-4256-4fb4-b81e-0ca41d723616",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Bind service to employee extra endpoint",
)
# Bind occupant extra endpoint
EventBindOccupantExtraEvent = Event(
name="service_endpoint_bind_occupant_extra",
key="cb11a150-8049-45c9-8cf3-d5290ffd2e4a",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users Bind service to occupant extra endpoint",
)
def events_list_available_callable(list_options: PaginateOnly):
"""
List available events with pagination and filtering options
"""
list_options = PaginateOnly(**list_options.model_dump())
service_uu_id = list_options.query.get("service_uu_id__ilike", None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
list_options.query.pop("service_uu_id__ilike", None)
list_options.query.pop("service_uu_id", None)
with Events.new_session() as db_session:
service2events = Service2Events.filter_all(
*Service2Events.convert({"service_uu_id__ilike": service_uu_id}),
db=db_session,
)
already_events = [
service_to_event.event_id for service_to_event in service2events.data
]
if list_options.query:
events_list = Events.filter_all(
*Events.convert(list_options.query),
Events.id.not_in(already_events),
db=db_session,
)
else:
events_list = Events.filter_all(
Events.id.not_in(already_events), db=db_session
)
pagination = Pagination(data=events_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=events_list, pagination=pagination)
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
EventsListAvailableEvent.event_callable = events_list_available_callable
def events_list_appended_callable(list_options: PaginateOnly):
"""
List appended events with pagination and filtering options
"""
list_options = PaginateOnly(**list_options.model_dump())
service_uu_id = list_options.query.get("service_uu_id__ilike", None)
if not service_uu_id:
return {
"message": "MSG0003-PARAM-MISSING",
"data": list_options.query,
"completed": False,
}
list_options.query.pop("service_uu_id__ilike", None)
list_options.query.pop("service_uu_id", None)
with Events.new_session() as db_session:
service2events = Service2Events.filter_all(
*Service2Events.convert({"service_uu_id__ilike": service_uu_id}),
db=db_session,
)
already_events = [
service_to_event.event_id for service_to_event in service2events.data
]
if list_options.query:
events_list = Events.filter_all(
*Events.convert(list_options.query),
Events.id.in_(already_events),
db=db_session,
)
else:
events_list = Events.filter_all(
Events.id.in_(already_events), db=db_session
)
pagination = Pagination(data=events_list)
pagination.change(**list_options.model_dump())
pagination_result = PaginationResult(data=events_list, pagination=pagination)
return EndpointResponse(
message="MSG0003-LIST",
pagination_result=pagination_result,
).response
EventsListAppendedEvent.event_callable = events_list_appended_callable
def event_register_service_callable(data: Any):
"""
Register event to service
"""
with Events.new_session() as db_session:
event = Events.filter_one_system(
Events.uu_id == data.event_uu_id, db=db_session
)
if not event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(
Services.uu_id == data.service_uu_id, db=db_session
)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event = Service2Events.find_or_create(
db=db_session,
include_args=[
Service2Events.service_uu_id,
Service2Events.event_uu_id,
],
service_id=service.data.id,
event_id=event.data.id,
is_confirmed=True,
service_uu_id=str(service.data.uu_id),
event_uu_id=str(event.data.uu_id),
)
if not service_to_event.meta_data.created:
return {
"message": "MSG0003-ALREADY-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event.save(db=db_session)
return {
"message": "MSG0003-REGISTER",
"data": data.model_dump(),
"completed": True,
}
EventRegisterServiceEvent.event_callable = event_register_service_callable
def event_unregister_service_callable(data: Any):
"""
Unregister event from service
"""
with Events.new_session() as db_session:
event = Events.filter_one_system(
Events.uu_id == data.event_uu_id, db=db_session
)
if not event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service = Services.filter_one_system(
Services.uu_id == data.service_uu_id, db=db_session
)
if not service.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event = Service2Events.filter_one_system(
Service2Events.service_id == service.data.id,
Service2Events.event_id == event.data.id,
db=db_session,
)
if not service_to_event.data:
return {
"message": "MSG0003-NOT-FOUND",
"data": data.model_dump(),
"completed": False,
}
service_to_event.query.delete()
db_session.commit()
return {
"message": "MSG0003-UNREGISTER",
"data": data.model_dump(),
"completed": True,
}
EventUnRegisterServiceEvent.event_callable = event_unregister_service_callable
def event_bind_employee_extra_callable(data: Any):
"""
Bind event to employee extra
"""
return {
"message": "MSG0003-BIND",
"data": data.model_dump(),
"completed": True,
}
EventBindEmployeeExtraEvent.event_callable = event_bind_employee_extra_callable
def event_bind_occupant_extra_callable(data: Any):
"""
Bind event to occupant extra
"""
return EndpointResponse(
message="MSG0003-BIND",
).response
EventBindOccupantExtraEvent.event_callable = event_bind_occupant_extra_callable

View File

@@ -0,0 +1,19 @@
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
from .supers_events import (
ServiceEndpointListEvent,
ServiceEndpointToEventsEvent,
)
ServiceEndpointRouterCluster = RouterCluster(name="ServiceEndpointRouterCluster")
ServiceEndpointEventClusterList = EventCluster(
name="ServiceList", endpoint_uu_id="f4e4d332-70b1-4121-9fcc-a08850b72aaa"
)
ServiceEndpointEventClusterList.add_event(ServiceEndpointListEvent)
ServiceEndpointEventClusterToService = EventCluster(
name="ServiceToEvents", endpoint_uu_id="7b6b0c6a-e3db-4353-a7df-ea49d2a67f8a"
)
ServiceEndpointEventClusterToService.add_event(ServiceEndpointToEventsEvent)
ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterList)
ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterToService)

View File

@@ -0,0 +1,69 @@
from ApiControllers.abstracts.event_clusters import Event
from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly
from Controllers.Postgres.response import EndpointResponse
from Schemas import Services, Service2Events
# List endpoint
ServiceEndpointListEvent = Event(
name="service_endpoint_list",
key="7da6ceac-925a-4faa-9cc5-3f34396b5684",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List services endpoint",
)
def service_endpoint_list_callable(data: PaginateOnly):
"""
List services endpoint callable method
"""
list_options = PaginateOnly(**data.model_dump())
with Services.new_session() as db_session:
if data.query:
services_list = Services.filter_all_system(
*Services.convert(data.query), db=db_session
)
else:
services_list = Services.filter_all_system(db=db_session)
pagination = Pagination(data=services_list)
pagination.change(**data.model_dump())
pagination_result = PaginationResult(data=services_list, pagination=pagination)
return EndpointResponse(
message="MSG0003-LIST", pagination_result=pagination_result
).response
ServiceEndpointListEvent.event_callable = service_endpoint_list_callable
# To events endpoint
ServiceEndpointToEventsEvent = Event(
name="service_endpoint_to_events",
key="7b6b0c6a-e3db-4353-a7df-ea49d2a67f8a",
request_validator=None, # TODO: Add request validator
response_validator=None, # TODO: Add response validator
description="Super Users List events of a service endpoint given service UUID",
)
def service_endpoint_to_events_callable(data: PaginateOnly):
"""
List events of a service given service UUID
"""
list_options = PaginateOnly(**data.model_dump())
with Service2Events.new_session() as db_session:
if data.query:
services_list = Service2Events.filter_all_system(
*Service2Events.convert(data.query), db=db_session
)
else:
services_list = Service2Events.filter_all_system(db=db_session)
pagination = Pagination(data=services_list)
pagination.change(**data.model_dump())
pagination_result = PaginationResult(data=services_list, pagination=pagination)
return EndpointResponse(
message="MSG0003-LIST", pagination_result=pagination_result
).response
ServiceEndpointToEventsEvent.event_callable = service_endpoint_to_events_callable

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,22 @@
from pydantic import BaseModel, Field
from typing import Optional
class RequestApplication(BaseModel):
"""Base model for application data"""
name: str = Field(..., description="Application name")
application_code: str = Field(..., description="Unique application code")
site_url: str = Field(..., description="Application site URL")
application_type: str = Field(
..., description="Application type (info, Dash, Admin)"
)
application_for: str = Field(..., description="Application for (EMP, OCC)")
description: Optional[str] = Field(None, description="Application description")
class AddRemoveService(BaseModel):
"""Base model for add/remove service data"""
application_uu_id: str = Field(..., description="Application UUID")
service_uu_id: str = Field(..., description="Service UUID")

View File

@@ -0,0 +1,14 @@
from pydantic import BaseModel
class Event2Employee(BaseModel):
pass
class Event2Occupant(BaseModel):
pass
class AddRemoveService(BaseModel):
event_uu_id: str
service_uu_id: str

View File

@@ -0,0 +1,13 @@
from fastapi import APIRouter
# Import all routes
from ApiServices.ManagementService.Endpoints.application.route import application_route
# Create main router for ManagementService
management_service_router = APIRouter(prefix="/management", tags=["Management Service"])
# Include all routes
management_service_router.include_router(application_route)
# Export the router
__all__ = ["management_service_router"]

View File

@@ -1,10 +0,0 @@
from .template.cluster import (
TemplateEventClusterSet
)
__all__ = [
"TemplateEventClusterSet",
]
def retrieve_all_clusters():
return [TemplateEventClusterSet]

View File

@@ -43,28 +43,28 @@ class EmailProcessingContext:
if exc_type is not None or not self.success: if exc_type is not None or not self.success:
# If an exception occurred or processing wasn't successful, mark as unread # If an exception occurred or processing wasn't successful, mark as unread
try: try:
if hasattr(self.email_message, 'mark_as_unread'): if hasattr(self.email_message, "mark_as_unread"):
self.email_message.mark_as_unread() self.email_message.mark_as_unread()
print(f"[EMAIL_SERVICE] Marked email as UNREAD due to processing error: {exc_val if exc_val else 'Unknown error'}") print(
f"[EMAIL_SERVICE] Marked email as UNREAD due to processing error: {exc_val if exc_val else 'Unknown error'}"
)
except Exception as e: except Exception as e:
print(f"[EMAIL_SERVICE] Failed to mark email as unread: {str(e)}") print(f"[EMAIL_SERVICE] Failed to mark email as unread: {str(e)}")
elif self.mark_as_read: elif self.mark_as_read:
# If processing was successful and mark_as_read is True, ensure it's marked as read # If processing was successful and mark_as_read is True, ensure it's marked as read
try: try:
if hasattr(self.email_message, 'mark_as_read'): if hasattr(self.email_message, "mark_as_read"):
self.email_message.mark_as_read() self.email_message.mark_as_read()
except Exception as e: except Exception as e:
print(f"[EMAIL_SERVICE] Failed to mark email as read: {str(e)}") print(f"[EMAIL_SERVICE] Failed to mark email as read: {str(e)}")
return False # Don't suppress exceptions return False # Don't suppress exceptions
def publish_payload_to_redis( def publish_payload_to_redis(payload, filename: str, mail_info: dict) -> bool:
payload, filename: str, mail_info: dict
) -> bool:
# Create message document # Create message document
# Use base64 encoding for binary payloads to ensure proper transmission # Use base64 encoding for binary payloads to ensure proper transmission
if isinstance(payload, bytes): if isinstance(payload, bytes):
encoded_payload = base64.b64encode(payload).decode('utf-8') encoded_payload = base64.b64encode(payload).decode("utf-8")
is_base64 = True is_base64 = True
else: else:
encoded_payload = payload encoded_payload = payload
@@ -84,7 +84,9 @@ def publish_payload_to_redis(
result = redis_pubsub.publisher.publish(REDIS_CHANNEL, message) result = redis_pubsub.publisher.publish(REDIS_CHANNEL, message)
if result.status: if result.status:
print(f"[EMAIL_SERVICE] Published message with filename: {filename} to channel: {REDIS_CHANNEL}") print(
f"[EMAIL_SERVICE] Published message with filename: {filename} to channel: {REDIS_CHANNEL}"
)
return True return True
else: else:
print(f"[EMAIL_SERVICE] Publish error: {result.error}") print(f"[EMAIL_SERVICE] Publish error: {result.error}")
@@ -144,32 +146,42 @@ def app():
try: try:
if os.path.exists(last_run_file): if os.path.exists(last_run_file):
with open(last_run_file, 'r') as f: with open(last_run_file, "r") as f:
last_run_data = json.load(f) last_run_data = json.load(f)
last_run_date = last_run_data.get('last_run_date') last_run_date = last_run_data.get("last_run_date")
# If this is the first run of a new day, check 90 days # If this is the first run of a new day, check 90 days
if last_run_date != current_date: if last_run_date != current_date:
days_to_check = full_check days_to_check = full_check
print(f"[EMAIL_SERVICE] First run of the day. Checking emails from the past {days_to_check} days") print(
f"[EMAIL_SERVICE] First run of the day. Checking emails from the past {days_to_check} days"
)
else: else:
print(f"[EMAIL_SERVICE] Subsequent run today. Checking emails from the past {days_to_check} days") print(
f"[EMAIL_SERVICE] Subsequent run today. Checking emails from the past {days_to_check} days"
)
else: else:
# If no last run file exists, this is the first run ever - check 90 days # If no last run file exists, this is the first run ever - check 90 days
days_to_check = full_check days_to_check = full_check
print(f"[EMAIL_SERVICE] First run detected. Checking emails from the past {days_to_check} days") print(
f"[EMAIL_SERVICE] First run detected. Checking emails from the past {days_to_check} days"
)
except Exception as e: except Exception as e:
print(f"[EMAIL_SERVICE] Error reading last run file: {str(e)}. Using default of {days_to_check} days") print(
f"[EMAIL_SERVICE] Error reading last run file: {str(e)}. Using default of {days_to_check} days"
)
# Update the last run file # Update the last run file
try: try:
with open(last_run_file, 'w') as f: with open(last_run_file, "w") as f:
json.dump({'last_run_date': current_date}, f) json.dump({"last_run_date": current_date}, f)
except Exception as e: except Exception as e:
print(f"[EMAIL_SERVICE] Error writing last run file: {str(e)}") print(f"[EMAIL_SERVICE] Error writing last run file: {str(e)}")
# Calculate the date to check from # Calculate the date to check from
check_since_date = (datetime.now() - timedelta(days=days_to_check)).strftime("%d-%b-%Y") check_since_date = (datetime.now() - timedelta(days=days_to_check)).strftime(
"%d-%b-%Y"
)
for folder in mail_folders: for folder in mail_folders:
if folder.name == "INBOX": if folder.name == "INBOX":
@@ -184,7 +196,9 @@ def app():
# Use context manager to handle errors and mark email as unread if needed # Use context manager to handle errors and mark email as unread if needed
with EmailProcessingContext(banks_mail) as ctx: with EmailProcessingContext(banks_mail) as ctx:
try: try:
headers = {k.lower(): v for k, v in banks_mail.headers.items()} headers = {
k.lower(): v for k, v in banks_mail.headers.items()
}
mail_info = { mail_info = {
"from": headers["from"], "from": headers["from"],
"to": headers["to"], "to": headers["to"],
@@ -201,9 +215,13 @@ def app():
ctx.success = success ctx.success = success
if success: if success:
print(f"[EMAIL_SERVICE] Successfully processed email with subject: {mail_info['subject']}") print(
f"[EMAIL_SERVICE] Successfully processed email with subject: {mail_info['subject']}"
)
else: else:
print(f"[EMAIL_SERVICE] No matching attachments found in email with subject: {mail_info['subject']}") print(
f"[EMAIL_SERVICE] No matching attachments found in email with subject: {mail_info['subject']}"
)
except Exception as e: except Exception as e:
print(f"[EMAIL_SERVICE] Error processing email: {str(e)}") print(f"[EMAIL_SERVICE] Error processing email: {str(e)}")

View File

@@ -18,7 +18,9 @@ REDIS_CHANNEL_OUT = "parser" # Publish to Parser Service channel
delimiter = "|" delimiter = "|"
def publish_parsed_data_to_redis(data, collected_data_dict: list[dict], filename: str) -> bool: def publish_parsed_data_to_redis(
data, collected_data_dict: list[dict], filename: str
) -> bool:
"""Publish parsed data to Redis. """Publish parsed data to Redis.
Args: Args:
@@ -49,7 +51,9 @@ def publish_parsed_data_to_redis(data, collected_data_dict: list[dict], filename
result = redis_pubsub.publisher.publish(REDIS_CHANNEL_OUT, message) result = redis_pubsub.publisher.publish(REDIS_CHANNEL_OUT, message)
if result.status: if result.status:
print(f"[PARSER_SERVICE] Published parsed data for {filename} with stage: {message['stage']}") print(
f"[PARSER_SERVICE] Published parsed data for {filename} with stage: {message['stage']}"
)
return True return True
else: else:
print(f"[PARSER_SERVICE] Publish error: {result.error}") print(f"[PARSER_SERVICE] Publish error: {result.error}")
@@ -76,13 +80,17 @@ def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
dict( dict(
iban=str(iban), iban=str(iban),
bank_date=arrow.get( bank_date=arrow.get(
datetime.datetime.strptime(str(row[1]), "%d/%m/%Y-%H:%M:%S") datetime.datetime.strptime(
str(row[1]), "%d/%m/%Y-%H:%M:%S"
)
).__str__(), ).__str__(),
channel_branch=unidecode(str(row[3])), channel_branch=unidecode(str(row[3])),
currency_value=( currency_value=(
float(str(row[4]).replace(",", "")) if row[4] else 0 float(str(row[4]).replace(",", "")) if row[4] else 0
), ),
balance=float(str(row[5]).replace(",", "")) if row[5] else 0, balance=(
float(str(row[5]).replace(",", "")) if row[5] else 0
),
additional_balance=( additional_balance=(
float(str(row[6]).replace(",", "")) if row[6] else 0 float(str(row[6]).replace(",", "")) if row[6] else 0
), ),
@@ -92,7 +100,9 @@ def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
bank_reference_code=str(row[15]), bank_reference_code=str(row[15]),
) )
) )
print(f"[PARSER_SERVICE] Successfully parsed {len(data_list)} records from Excel file") print(
f"[PARSER_SERVICE] Successfully parsed {len(data_list)} records from Excel file"
)
except Exception as e: except Exception as e:
print(f"[PARSER_SERVICE] Error parsing Excel file: {str(e)}") print(f"[PARSER_SERVICE] Error parsing Excel file: {str(e)}")
return data_list return data_list
@@ -128,12 +138,14 @@ def process_message(message):
try: try:
# Decode base64 string to bytes # Decode base64 string to bytes
payload = base64.b64decode(payload) payload = base64.b64decode(payload)
print(f"[PARSER_SERVICE] Successfully decoded base64 payload, size: {len(payload)} bytes") print(
f"[PARSER_SERVICE] Successfully decoded base64 payload, size: {len(payload)} bytes"
)
except Exception as e: except Exception as e:
print(f"[PARSER_SERVICE] Error decoding base64 payload: {str(e)}") print(f"[PARSER_SERVICE] Error decoding base64 payload: {str(e)}")
# Convert regular string payload to bytes if needed # Convert regular string payload to bytes if needed
elif isinstance(payload, str): elif isinstance(payload, str):
payload = payload.encode('utf-8') payload = payload.encode("utf-8")
# Create an in-memory file-like object and try multiple approaches # Create an in-memory file-like object and try multiple approaches
excel_frame = None excel_frame = None
@@ -142,20 +154,32 @@ def process_message(message):
# Save payload to a temporary file for debugging if needed # Save payload to a temporary file for debugging if needed
temp_file_path = f"/tmp/{filename}" temp_file_path = f"/tmp/{filename}"
try: try:
with open(temp_file_path, 'wb') as f: with open(temp_file_path, "wb") as f:
f.write(payload) f.write(payload)
print(f"[PARSER_SERVICE] Saved payload to {temp_file_path} for debugging") print(
f"[PARSER_SERVICE] Saved payload to {temp_file_path} for debugging"
)
except Exception as e: except Exception as e:
print(f"[PARSER_SERVICE] Could not save debug file: {str(e)}") print(f"[PARSER_SERVICE] Could not save debug file: {str(e)}")
# Try different approaches to read the Excel file # Try different approaches to read the Excel file
approaches = [ approaches = [
# Approach 1: Try xlrd for .xls files # Approach 1: Try xlrd for .xls files
lambda: DataFrame(read_excel(io.BytesIO(payload), engine='xlrd')) if filename.lower().endswith('.xls') else None, lambda: (
DataFrame(read_excel(io.BytesIO(payload), engine="xlrd"))
if filename.lower().endswith(".xls")
else None
),
# Approach 2: Try openpyxl for .xlsx files # Approach 2: Try openpyxl for .xlsx files
lambda: DataFrame(read_excel(io.BytesIO(payload), engine='openpyxl')) if filename.lower().endswith('.xlsx') else None, lambda: (
DataFrame(read_excel(io.BytesIO(payload), engine="openpyxl"))
if filename.lower().endswith(".xlsx")
else None
),
# Approach 3: Try xlrd with explicit sheet name # Approach 3: Try xlrd with explicit sheet name
lambda: DataFrame(read_excel(io.BytesIO(payload), engine='xlrd', sheet_name=0)), lambda: DataFrame(
read_excel(io.BytesIO(payload), engine="xlrd", sheet_name=0)
),
# Approach 4: Try with temporary file # Approach 4: Try with temporary file
lambda: DataFrame(read_excel(temp_file_path)), lambda: DataFrame(read_excel(temp_file_path)),
] ]
@@ -166,7 +190,9 @@ def process_message(message):
result = approach() result = approach()
if result is not None: if result is not None:
excel_frame = result excel_frame = result
print(f"[PARSER_SERVICE] Successfully read Excel file using approach {i+1}") print(
f"[PARSER_SERVICE] Successfully read Excel file using approach {i+1}"
)
break break
except Exception as e: except Exception as e:
errors.append(f"Approach {i+1}: {str(e)}") errors.append(f"Approach {i+1}: {str(e)}")
@@ -174,21 +200,23 @@ def process_message(message):
# If all approaches failed, raise an exception # If all approaches failed, raise an exception
if excel_frame is None: if excel_frame is None:
error_details = "\n".join(errors) error_details = "\n".join(errors)
raise Exception(f"Failed to read Excel file using all approaches:\n{error_details}") raise Exception(
f"Failed to read Excel file using all approaches:\n{error_details}"
)
# Extract data from the Excel file # Extract data from the Excel file
collected_data_dict = parse_excel_file(excel_frame) collected_data_dict = parse_excel_file(excel_frame)
# Publish parsed data to Redis # Publish parsed data to Redis
publish_parsed_data_to_redis( publish_parsed_data_to_redis(
data=data, data=data, collected_data_dict=collected_data_dict, filename=filename
collected_data_dict=collected_data_dict,
filename=filename
) )
except Exception as e: except Exception as e:
print(f"[PARSER_SERVICE] Error processing message: {str(e)}") print(f"[PARSER_SERVICE] Error processing message: {str(e)}")
else: else:
print(f"[PARSER_SERVICE] Skipped message with UUID: {data.get('uuid')} (stage is not 'red')") print(
f"[PARSER_SERVICE] Skipped message with UUID: {data.get('uuid')} (stage is not 'red')"
)
def app(): def app():

View File

@@ -14,12 +14,12 @@ RUN poetry config virtualenvs.create false && poetry install --no-interaction --
# Install cron for scheduling tasks # Install cron for scheduling tasks
RUN apt-get update && apt-get install -y cron RUN apt-get update && apt-get install -y cron
# 11:00 Istanbul Time (UTC+3) system time is 08:00 UTC
RUN echo "0 8 * * * /usr/local/bin/python /app.py >> /var/log/cron.log 2>&1" > /tmp/crontab_list && crontab /tmp/crontab_list
# Copy application code # Copy application code
COPY /BankServices/RoutineEmailService / COPY /BankServices/RoutineEmailService /
# Make run_app.sh executable
RUN chmod +x /run_app.sh
COPY /Schemas /Schemas COPY /Schemas /Schemas
COPY /Controllers /Controllers COPY /Controllers /Controllers
COPY /BankServices/ServiceDepends / COPY /BankServices/ServiceDepends /
@@ -30,5 +30,8 @@ ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# Create log file to grab cron logs # Create log file to grab cron logs
RUN touch /var/log/cron.log RUN touch /var/log/cron.log
# Run cron setup and tail the log file for user to monitor logs # Make entrypoint script executable
CMD cron && tail -f /var/log/cron.log RUN chmod +x /entrypoint.sh
# Use entrypoint script to update run_app.sh with environment variables and start cron
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -0,0 +1,69 @@
# Routine Email Service
## Overview
This service sends automated email reports about account records at scheduled times using cron. It retrieves account records from a PostgreSQL database, formats them into an HTML email, and sends them to specified recipients.
## Environment Setup
The service requires the following environment variables:
### Email Configuration
- `EMAIL_HOST`: SMTP server address (e.g., "10.10.2.34")
- `EMAIL_USERNAME`: Email sender address (e.g., "example@domain.com")
- `EMAIL_PASSWORD`: Email password (sensitive)
- `EMAIL_PORT`: SMTP port (e.g., 587)
- `EMAIL_SEND`: Flag to enable/disable email sending (1 = enabled)
### Database Configuration
- `DB_HOST`: PostgreSQL server address (e.g., "10.10.2.14")
- `DB_USER`: Database username (e.g., "postgres")
- `DB_PASSWORD`: Database password (sensitive)
- `DB_PORT`: Database port (e.g., 5432)
- `DB_NAME`: Database name (e.g., "postgres")
## Cron Job Configuration
The service is configured to run daily at 11:00 Istanbul Time (08:00 UTC). This is set up in the entrypoint.sh script.
## Docker Container Setup
### Key Files
1. **Dockerfile**: Defines the container image with Python and cron
2. **entrypoint.sh**: Container entrypoint script that:
- Creates an environment file (/env.sh) with all configuration variables
- Sets up the crontab to run run_app.sh at the scheduled time
- Starts the cron service
- Tails the log file for monitoring
3. **run_app.sh**: Script executed by cron that:
- Sources the environment file to get all configuration
- Exports variables to make them available to the Python script
- Runs the Python application
- Logs environment and execution results
### Environment Variable Handling
Cron jobs run with a minimal environment that doesn't automatically include Docker container environment variables. Our solution:
1. Captures all environment variables from Docker to a file at container startup
2. Has the run_app.sh script source this file before execution
3. Explicitly exports all variables to ensure they're available to the Python script
## Logs
Logs are written to `/var/log/cron.log` and can be viewed with:
```bash
docker exec routine_email_service tail -f /var/log/cron.log
```
## Manual Execution
To run the service manually:
```bash
docker exec routine_email_service /run_app.sh
```
## Docker Compose Configuration
In the docker-compose.yml file, the service needs an explicit entrypoint configuration:
```yaml
entrypoint: ["/entrypoint.sh"]
```
This ensures the entrypoint script runs when the container starts.

View File

@@ -35,8 +35,7 @@ def render_email_template(
today=str(arrow.now().date()), today=str(arrow.now().date()),
) )
except Exception as e: except Exception as e:
print('Exception render template:',e) print("Exception render template:", e)
err = e
raise raise
@@ -59,17 +58,16 @@ def send_email_to_given_address(send_to: str, html_template: str) -> bool:
subject=subject, subject=subject,
html=html_template, html=html_template,
receivers=[send_to], receivers=[send_to],
text=f"Gunes Apt. Cari Durum Bilgilendirme Raporu - {today.date()}" text=f"Gunes Apt. Cari Durum Bilgilendirme Raporu - {today.date()}",
) )
try: try:
# Use the context manager to handle connection errors # Use the context manager to handle connection errors
with EmailService.new_session() as email_session: with EmailService.new_session() as email_session:
# Send email through the service # Send email through the service
EmailService.send_email(email_session, email_params) return EmailService.send_email(email_session, email_params)
return True
except Exception as e: except Exception as e:
print(f'Exception send email: {e}') print(f"Exception send email: {e}")
return False return False
@@ -88,10 +86,10 @@ def set_account_records_to_send_email() -> bool:
account_records_query = AccountRecords.filter_all(db=db_session).query account_records_query = AccountRecords.filter_all(db=db_session).query
# Get the 3 most recent records # Get the 3 most recent records
account_records: List[AccountRecords] | [] = ( account_records: List[AccountRecords] = (
account_records_query.order_by( account_records_query.order_by(
AccountRecords.bank_date.desc(), AccountRecords.bank_date.desc(),
AccountRecords.bank_reference_code.desc() AccountRecords.iban.desc(),
) )
.limit(3) .limit(3)
.all() .all()
@@ -99,29 +97,43 @@ def set_account_records_to_send_email() -> bool:
# Check if we have enough records # Check if we have enough records
if len(account_records) < 2: if len(account_records) < 2:
print(f"Not enough records found: {len(account_records)}")
return False return False
# Check for balance discrepancy # Check for balance discrepancy
first_record, second_record = account_records[0], account_records[1] first_record, second_record = account_records[0], account_records[1]
expected_second_balance = first_record.bank_balance - first_record.currency_value expected_second_balance = (
first_record.bank_balance - first_record.currency_value
)
balance_error = expected_second_balance != second_record.bank_balance balance_error = expected_second_balance != second_record.bank_balance
if balance_error: if balance_error:
return False print(
f"Balance error detected {expected_second_balance} != {second_record.bank_balance}"
)
# Format rows for the email template # Format rows for the email template
list_of_rows = [] list_of_rows = []
for record in account_records: for record in account_records:
list_of_rows.append([ list_of_rows.append(
[
record.bank_date.strftime("%d/%m/%Y %H:%M"), record.bank_date.strftime("%d/%m/%Y %H:%M"),
record.process_comment, record.process_comment,
f"{record.currency_value:,.2f}", f"{record.currency_value:,.2f}",
f"{record.bank_balance:,.2f}" f"{record.bank_balance:,.2f}",
]) ]
)
# Get the most recent bank balance # Get the most recent bank balance
last_bank_balance = sorted(account_records, key=lambda x: x.bank_date, reverse=True)[0].bank_balance last_bank_balance = sorted(
account_records, key=lambda x: x.bank_date, reverse=True
)[0].bank_balance
# Define headers for the table # Define headers for the table
headers = ["Ulaştığı Tarih", "Banka Transaksiyonu Ek Bilgi", "Aktarım Değeri", "Banka Bakiyesi"] headers = [
"Ulaştığı Tarih",
"Banka Transaksiyonu Ek Bilgi",
"Aktarım Değeri",
"Banka Bakiyesi",
]
# Recipient email address # Recipient email address
send_to = "karatay@mehmetkaratay.com.tr" send_to = "karatay@mehmetkaratay.com.tr"
@@ -140,5 +152,6 @@ def set_account_records_to_send_email() -> bool:
if __name__ == "__main__": if __name__ == "__main__":
success = set_account_records_to_send_email() success = set_account_records_to_send_email()
print("Email sent successfully" if success else "Failed to send email")
exit_code = 0 if success else 1 exit_code = 0 if success else 1
exit(exit_code) exit(exit_code)

View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Create environment file that will be available to cron jobs
echo "# Environment variables for cron jobs" > /env.sh
echo "EMAIL_HOST=\"$EMAIL_HOST\"" >> /env.sh
echo "EMAIL_USERNAME=\"$EMAIL_USERNAME\"" >> /env.sh
echo "EMAIL_PASSWORD=\"$EMAIL_PASSWORD\"" >> /env.sh
echo "EMAIL_PORT=$EMAIL_PORT" >> /env.sh
echo "EMAIL_SEND=$EMAIL_SEND" >> /env.sh
echo "DB_HOST=\"$DB_HOST\"" >> /env.sh
echo "DB_USER=\"$DB_USER\"" >> /env.sh
echo "DB_PASSWORD=\"$DB_PASSWORD\"" >> /env.sh
echo "DB_PORT=$DB_PORT" >> /env.sh
echo "DB_NAME=\"$DB_NAME\"" >> /env.sh
# Add Python environment variables
echo "PYTHONPATH=/" >> /env.sh
echo "PYTHONUNBUFFERED=1" >> /env.sh
echo "PYTHONDONTWRITEBYTECODE=1" >> /env.sh
# Make the environment file available to cron
echo "0 8 * * * /run_app.sh >> /var/log/cron.log 2>&1" > /tmp/crontab_list
crontab /tmp/crontab_list
# Start cron
cron
# Tail the log file
tail -f /var/log/cron.log

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Source the environment file directly
. /env.sh
# Re-export all variables to ensure they're available to the Python script
export EMAIL_HOST
export EMAIL_USERNAME
export EMAIL_PASSWORD
export EMAIL_PORT
export EMAIL_SEND
export DB_HOST
export DB_USER
export DB_PASSWORD
export DB_PORT
export DB_NAME
# Python environment variables
export PYTHONPATH
export PYTHONUNBUFFERED
export PYTHONDONTWRITEBYTECODE
env >> /var/log/cron.log
/usr/local/bin/python /app.py

View File

@@ -35,11 +35,13 @@ def render_email_template(
today=str(arrow.now().date()), today=str(arrow.now().date()),
) )
except Exception as e: except Exception as e:
print(f'Template rendering failed: {e}') print(f"Template rendering failed: {e}")
raise raise
def send_email_to_given_address(send_to: str, html_template: str, count_of_records: int) -> bool: def send_email_to_given_address(
send_to: str, html_template: str, count_of_records: int
) -> bool:
""" """
Send email with the rendered HTML template to the specified address. Send email with the rendered HTML template to the specified address.
@@ -58,7 +60,7 @@ def send_email_to_given_address(send_to: str, html_template: str, count_of_recor
subject=subject + f" ({count_of_records} kayıt)", subject=subject + f" ({count_of_records} kayıt)",
html=html_template, html=html_template,
receivers=[send_to], receivers=[send_to],
text=f"Gunes Apt. Cari Durum Kayıt Giriş Raporu - {today.date()}" text=f"Gunes Apt. Cari Durum Kayıt Giriş Raporu - {today.date()}",
) )
try: try:
@@ -69,7 +71,7 @@ def send_email_to_given_address(send_to: str, html_template: str, count_of_recor
print(f"Email successfully sent to: {send_to}") print(f"Email successfully sent to: {send_to}")
return True return True
except Exception as e: except Exception as e:
print(f'Failed to send email: {e}') print(f"Failed to send email: {e}")
return False return False
@@ -87,10 +89,14 @@ def process_unsent_email_records() -> bool:
# Use the context manager to handle database connections # Use the context manager to handle database connections
with AccountRecords.new_session() as db_session: with AccountRecords.new_session() as db_session:
# Query un-sent mail rows - with limit for display only # Query un-sent mail rows - with limit for display only
account_records_query = AccountRecords.filter_all( account_records_query = (
AccountRecords.filter_all(
AccountRecords.is_email_send == False, AccountRecords.is_email_send == False,
db=db_session, db=db_session,
).query.order_by(AccountRecords.bank_date.asc()).limit(20) )
.query.order_by(AccountRecords.bank_date.asc())
.limit(20)
)
account_records: List[AccountRecords] = account_records_query.all() account_records: List[AccountRecords] = account_records_query.all()
if not account_records: if not account_records:
@@ -104,21 +110,30 @@ def process_unsent_email_records() -> bool:
# Format rows for the email template # Format rows for the email template
list_of_rows = [] list_of_rows = []
for record in account_records: for record in account_records:
list_of_rows.append([ list_of_rows.append(
[
record.bank_date.strftime("%d/%m/%Y %H:%M"), record.bank_date.strftime("%d/%m/%Y %H:%M"),
record.process_comment, record.process_comment,
f"{record.currency_value:,.2f}", f"{record.currency_value:,.2f}",
f"{record.bank_balance:,.2f}" f"{record.bank_balance:,.2f}",
]) ]
)
# Reverse list by date # Reverse list by date
list_of_rows = list_of_rows[::-1] list_of_rows = list_of_rows[::-1]
# Get the most recent bank balance # Get the most recent bank balance
last_bank_balance = sorted(account_records, key=lambda x: x.bank_date, reverse=True)[0].bank_balance last_bank_balance = sorted(
account_records, key=lambda x: x.bank_date, reverse=True
)[0].bank_balance
# Define headers for the table # Define headers for the table
headers = ["Ulaştığı Tarih", "Banka Transaksiyonu Ek Bilgi", "Aktarım Değeri", "Banka Bakiyesi"] headers = [
"Ulaştığı Tarih",
"Banka Transaksiyonu Ek Bilgi",
"Aktarım Değeri",
"Banka Bakiyesi",
]
# Recipient email address # Recipient email address
send_to = "karatay@mehmetkaratay.com.tr" send_to = "karatay@mehmetkaratay.com.tr"
@@ -132,11 +147,14 @@ def process_unsent_email_records() -> bool:
) )
# Send the email # Send the email
if send_email_to_given_address(send_to=send_to, html_template=html_template, count_of_records=len(list_of_rows)): if send_email_to_given_address(
send_to=send_to,
html_template=html_template,
count_of_records=len(list_of_rows),
):
# Create a new query without limit for updating # Create a new query without limit for updating
update_query = AccountRecords.filter_all( update_query = AccountRecords.filter_all(
AccountRecords.id.in_(record_ids), AccountRecords.id.in_(record_ids), db=db_session
db=db_session
).query ).query
# Update records as sent # Update records as sent
@@ -149,7 +167,7 @@ def process_unsent_email_records() -> bool:
return False return False
except Exception as e: except Exception as e:
print(f'Error processing unsent email records: {e}') print(f"Error processing unsent email records: {e}")
return False return False

View File

@@ -39,7 +39,9 @@ def publish_written_data_to_redis(data: Dict[str, Any], file_name: str) -> bool:
result = redis_pubsub.publisher.publish(REDIS_CHANNEL_OUT, message) result = redis_pubsub.publisher.publish(REDIS_CHANNEL_OUT, message)
if result.status: if result.status:
print(f"[WRITER_SERVICE] Published written status for {file_name} with stage: written") print(
f"[WRITER_SERVICE] Published written status for {file_name} with stage: written"
)
return True return True
else: else:
print(f"[WRITER_SERVICE] Publish error: {result.error}") print(f"[WRITER_SERVICE] Publish error: {result.error}")
@@ -61,7 +63,7 @@ def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> boo
data_dict["bank_balance"] = data_dict.pop("balance") data_dict["bank_balance"] = data_dict.pop("balance")
data_dict["import_file_name"] = file_name data_dict["import_file_name"] = file_name
data_dict = BankReceive(**data_dict).model_dump() data_dict = BankReceive(**data_dict).model_dump()
print('data_dict', data_dict) print("data_dict", data_dict)
# Process date fields # Process date fields
bank_date = arrow.get(str(data_dict["bank_date"])) bank_date = arrow.get(str(data_dict["bank_date"]))
@@ -90,16 +92,20 @@ def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> boo
AccountRecords.bank_date, AccountRecords.bank_date,
AccountRecords.iban, AccountRecords.iban,
AccountRecords.bank_reference_code, AccountRecords.bank_reference_code,
AccountRecords.bank_balance AccountRecords.bank_balance,
] ],
) )
if new_account_record.meta_data.created: if new_account_record.meta_data.created:
new_account_record.is_confirmed = True new_account_record.is_confirmed = True
new_account_record.save(db=db_session) new_account_record.save(db=db_session)
print(f"[WRITER_SERVICE] Created new record in database: {new_account_record.id}") print(
f"[WRITER_SERVICE] Created new record in database: {new_account_record.id}"
)
return True return True
else: else:
print(f"[WRITER_SERVICE] Record already exists in database: {new_account_record.id}") print(
f"[WRITER_SERVICE] Record already exists in database: {new_account_record.id}"
)
return False return False
except Exception as e: except Exception as e:
print(f"[WRITER_SERVICE] Error writing to database: {str(e)}") print(f"[WRITER_SERVICE] Error writing to database: {str(e)}")
@@ -138,7 +144,9 @@ def process_message(message):
# Process each parsed data item # Process each parsed data item
success = True success = True
for item in parsed_data: for item in parsed_data:
result = write_parsed_data_to_account_records(data_dict=item, file_name=file_name) result = write_parsed_data_to_account_records(
data_dict=item, file_name=file_name
)
if not result: if not result:
success = False success = False
@@ -148,7 +156,9 @@ def process_message(message):
except Exception as e: except Exception as e:
print(f"[WRITER_SERVICE] Error processing message: {str(e)}") print(f"[WRITER_SERVICE] Error processing message: {str(e)}")
else: else:
print(f"[WRITER_SERVICE] Skipped message with UUID: {data.get('uuid')} (stage is not 'parsed')") print(
f"[WRITER_SERVICE] Skipped message with UUID: {data.get('uuid')} (stage is not 'parsed')"
)
def app(): def app():

View File

@@ -10,7 +10,7 @@ class Configs(BaseSettings):
USERNAME: str = "" USERNAME: str = ""
PASSWORD: str = "" PASSWORD: str = ""
PORT: int = 0 PORT: int = 0
SEND: bool = 0 SEND: bool = True
@property @property
def is_send(self): def is_send(self):

View File

@@ -27,6 +27,13 @@ class EmailSession:
print("Email sending is disabled", params) print("Email sending is disabled", params)
return False return False
receivers = [email_configs.USERNAME] receivers = [email_configs.USERNAME]
# Ensure connection is established before sending
try:
# Check if connection exists, if not establish it
if not hasattr(self.email_sender, '_connected') or not self.email_sender._connected:
self.email_sender.connect()
self.email_sender.send( self.email_sender.send(
subject=params.subject, subject=params.subject,
receivers=receivers, receivers=receivers,
@@ -38,6 +45,9 @@ class EmailSession:
attachments=params.attachments or {}, attachments=params.attachments or {},
) )
return True return True
except Exception as e:
print(f"Error sending email: {e}")
raise
class EmailService: class EmailService:
@@ -54,14 +64,25 @@ class EmailService:
"""Create and yield a new email session with active connection.""" """Create and yield a new email session with active connection."""
email_sender = EmailSender(**email_configs.as_dict()) email_sender = EmailSender(**email_configs.as_dict())
session = EmailSession(email_sender) session = EmailSession(email_sender)
connection_established = False
try: try:
# Establish connection and set flag
email_sender.connect() email_sender.connect()
# Set a flag to track connection state
email_sender._connected = True
connection_established = True
yield session yield session
except Exception as e: except Exception as e:
print(f"Error with email connection: {e}") print(f"Error with email connection: {e}")
raise raise
finally: finally:
# Only close if connection was successfully established
if connection_established:
try:
email_sender.close() email_sender.close()
email_sender._connected = False
except Exception as e:
print(f"Error closing email connection: {e}")
@classmethod @classmethod
def send_email(cls, session: EmailSession, params: EmailSendModel) -> bool: def send_email(cls, session: EmailSession, params: EmailSendModel) -> bool:

View File

@@ -135,7 +135,9 @@ class CollectionContext:
try: try:
# Create a new client connection # Create a new client connection
self.client = MongoClient(self.db_handler.uri, **self.db_handler.client_options) self.client = MongoClient(
self.db_handler.uri, **self.db_handler.client_options
)
if self.db_handler._debug_mode: if self.db_handler._debug_mode:
# In debug mode, we explicitly use the configured DB # In debug mode, we explicitly use the configured DB
@@ -164,8 +166,12 @@ class CollectionContext:
# Try a direct connection without authentication for testing # Try a direct connection without authentication for testing
direct_uri = f"mongodb://{mongo_configs.HOST}:{mongo_configs.PORT}/{mongo_configs.DB}" direct_uri = f"mongodb://{mongo_configs.HOST}:{mongo_configs.PORT}/{mongo_configs.DB}"
print(f"Trying direct connection: {direct_uri}") print(f"Trying direct connection: {direct_uri}")
self.client = MongoClient(direct_uri, **self.db_handler.client_options) self.client = MongoClient(
self.collection = self.client[mongo_configs.DB][self.collection_name] direct_uri, **self.db_handler.client_options
)
self.collection = self.client[mongo_configs.DB][
self.collection_name
]
self._add_retry_capabilities() self._add_retry_capabilities()
return self.collection return self.collection
except Exception as inner_e: except Exception as inner_e:
@@ -197,10 +203,12 @@ class CollectionContext:
if self.db_handler._mock_mode: if self.db_handler._mock_mode:
print(f"MOCK MODE: Using mock collection '{self.collection_name}'") print(f"MOCK MODE: Using mock collection '{self.collection_name}'")
else: else:
print(f"Using mock MongoDB collection '{self.collection_name}' for graceful degradation") print(
f"Using mock MongoDB collection '{self.collection_name}' for graceful degradation"
)
# Create in-memory storage for this mock collection # Create in-memory storage for this mock collection
if not hasattr(self.db_handler, '_mock_storage'): if not hasattr(self.db_handler, "_mock_storage"):
self.db_handler._mock_storage = {} self.db_handler._mock_storage = {}
if self.collection_name not in self.db_handler._mock_storage: if self.collection_name not in self.db_handler._mock_storage:
@@ -222,11 +230,11 @@ class CollectionContext:
def mock_insert_one(document, *args, **kwargs): def mock_insert_one(document, *args, **kwargs):
# Add _id if not present # Add _id if not present
if '_id' not in document: if "_id" not in document:
document['_id'] = f"mock_id_{len(mock_data)}" document["_id"] = f"mock_id_{len(mock_data)}"
mock_data.append(document) mock_data.append(document)
result = MagicMock() result = MagicMock()
result.inserted_id = document['_id'] result.inserted_id = document["_id"]
return result return result
def mock_insert_many(documents, *args, **kwargs): def mock_insert_many(documents, *args, **kwargs):
@@ -328,9 +336,17 @@ class CollectionContext:
""" """
# List of common MongoDB collection methods to add retry capabilities to # List of common MongoDB collection methods to add retry capabilities to
methods = [ methods = [
'insert_one', 'insert_many', 'find_one', 'find', "insert_one",
'update_one', 'update_many', 'delete_one', 'delete_many', "insert_many",
'replace_one', 'count_documents', 'aggregate' "find_one",
"find",
"update_one",
"update_many",
"delete_one",
"delete_many",
"replace_one",
"count_documents",
"aggregate",
] ]
# Add retry decorator to each method # Add retry decorator to each method
@@ -340,7 +356,7 @@ class CollectionContext:
setattr( setattr(
mock_collection, mock_collection,
method_name, method_name,
retry_operation(max_retries=1, retry_interval=0)(original_method) retry_operation(max_retries=1, retry_interval=0)(original_method),
) )
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):

View File

@@ -110,14 +110,16 @@ def test_nested_documents():
print(f"Found updated laptop: {updated_laptop is not None}") print(f"Found updated laptop: {updated_laptop is not None}")
if updated_laptop: if updated_laptop:
print(f"Updated laptop specs: {updated_laptop.get('specs')}") print(f"Updated laptop specs: {updated_laptop.get('specs')}")
if 'specs' in updated_laptop: if "specs" in updated_laptop:
print(f"Updated RAM: {updated_laptop['specs'].get('ram')}") print(f"Updated RAM: {updated_laptop['specs'].get('ram')}")
# Check each condition separately # Check each condition separately
condition1 = laptop is not None condition1 = laptop is not None
condition2 = laptop and laptop.get('specs', {}).get('ram') == "16GB" condition2 = laptop and laptop.get("specs", {}).get("ram") == "16GB"
condition3 = update_result.modified_count == 1 condition3 = update_result.modified_count == 1
condition4 = updated_laptop and updated_laptop.get('specs', {}).get('ram') == "32GB" condition4 = (
updated_laptop and updated_laptop.get("specs", {}).get("ram") == "32GB"
)
print(f"Condition 1 (laptop found): {condition1}") print(f"Condition 1 (laptop found): {condition1}")
print(f"Condition 2 (original RAM is 16GB): {condition2}") print(f"Condition 2 (original RAM is 16GB): {condition2}")
@@ -172,8 +174,10 @@ def test_array_operations():
print(f"Found updated order: {updated_order is not None}") print(f"Found updated order: {updated_order is not None}")
if updated_order: if updated_order:
print(f"Number of items in order: {len(updated_order.get('items', []))}") print(
items = updated_order.get('items', []) f"Number of items in order: {len(updated_order.get('items', []))}"
)
items = updated_order.get("items", [])
if items: if items:
last_item = items[-1] if items else None last_item = items[-1] if items else None
print(f"Last item in order: {last_item}") print(f"Last item in order: {last_item}")
@@ -181,8 +185,12 @@ def test_array_operations():
# Check each condition separately # Check each condition separately
condition1 = len(laptop_orders) == 1 condition1 = len(laptop_orders) == 1
condition2 = update_result.modified_count == 1 condition2 = update_result.modified_count == 1
condition3 = updated_order and len(updated_order.get('items', [])) == 3 condition3 = updated_order and len(updated_order.get("items", [])) == 3
condition4 = updated_order and updated_order.get('items', []) and updated_order['items'][-1].get('product') == "Keyboard" condition4 = (
updated_order
and updated_order.get("items", [])
and updated_order["items"][-1].get("product") == "Keyboard"
)
print(f"Condition 1 (found 1 laptop order): {condition1}") print(f"Condition 1 (found 1 laptop order): {condition1}")
print(f"Condition 2 (update modified 1 doc): {condition2}") print(f"Condition 2 (update modified 1 doc): {condition2}")
@@ -219,7 +227,7 @@ def test_aggregation():
# Calculate total sales by product - use a simpler aggregation pipeline # Calculate total sales by product - use a simpler aggregation pipeline
pipeline = [ pipeline = [
{"$match": {}}, # Match all documents {"$match": {}}, # Match all documents
{"$group": {"_id": "$product", "total": {"$sum": "$amount"}}} {"$group": {"_id": "$product", "total": {"$sum": "$amount"}}},
] ]
# Execute the aggregation # Execute the aggregation
@@ -233,7 +241,8 @@ def test_aggregation():
# Check each condition separately # Check each condition separately
condition1 = len(sales_summary) == 3 condition1 = len(sales_summary) == 3
condition2 = any( condition2 = any(
item.get("_id") == "Laptop" and abs(item.get("total", 0) - 999.99) < 0.01 item.get("_id") == "Laptop"
and abs(item.get("total", 0) - 999.99) < 0.01
for item in sales_summary for item in sales_summary
) )
condition3 = any( condition3 = any(
@@ -241,7 +250,8 @@ def test_aggregation():
for item in sales_summary for item in sales_summary
) )
condition4 = any( condition4 = any(
item.get("_id") == "Keyboard" and abs(item.get("total", 0) - 59.99) < 0.01 item.get("_id") == "Keyboard"
and abs(item.get("total", 0) - 59.99) < 0.01
for item in sales_summary for item in sales_summary
) )
@@ -325,14 +335,12 @@ def test_complex_queries():
# Update with multiple conditions - split into separate operations for better compatibility # Update with multiple conditions - split into separate operations for better compatibility
# First set the discount # First set the discount
products_collection.update_many( products_collection.update_many(
{"price": {"$lt": 100}, "in_stock": True}, {"price": {"$lt": 100}, "in_stock": True}, {"$set": {"discount": 0.1}}
{"$set": {"discount": 0.1}}
) )
# Then update the price # Then update the price
update_result = products_collection.update_many( update_result = products_collection.update_many(
{"price": {"$lt": 100}, "in_stock": True}, {"price": {"$lt": 100}, "in_stock": True}, {"$inc": {"price": -10}}
{"$inc": {"price": -10}}
) )
# Verify the update # Verify the update
@@ -341,7 +349,9 @@ def test_complex_queries():
# Print debug information # Print debug information
print(f"Found expensive electronics: {len(expensive_electronics)}") print(f"Found expensive electronics: {len(expensive_electronics)}")
if expensive_electronics: if expensive_electronics:
print(f"First expensive product: {expensive_electronics[0].get('name')}") print(
f"First expensive product: {expensive_electronics[0].get('name')}"
)
print(f"Modified count: {update_result.modified_count}") print(f"Modified count: {update_result.modified_count}")
if updated_product: if updated_product:
print(f"Updated product price: {updated_product.get('price')}") print(f"Updated product price: {updated_product.get('price')}")
@@ -350,10 +360,12 @@ def test_complex_queries():
# More flexible verification with approximate float comparison # More flexible verification with approximate float comparison
success = ( success = (
len(expensive_electronics) >= 1 len(expensive_electronics) >= 1
and expensive_electronics[0].get("name") in ["Expensive Laptop", "Laptop"] and expensive_electronics[0].get("name")
in ["Expensive Laptop", "Laptop"]
and update_result.modified_count >= 1 and update_result.modified_count >= 1
and updated_product is not None and updated_product is not None
and updated_product.get("discount", 0) > 0 # Just check that discount exists and is positive and updated_product.get("discount", 0)
> 0 # Just check that discount exists and is positive
) )
print(f"Test {'passed' if success else 'failed'}") print(f"Test {'passed' if success else 'failed'}")
return success return success
@@ -385,19 +397,20 @@ def run_concurrent_operation_test(num_threads=100):
with mongo_handler.collection(collection_name) as collection: with mongo_handler.collection(collection_name) as collection:
# Insert a document # Insert a document
collection.insert_one({ collection.insert_one(
{
"thread_id": thread_id, "thread_id": thread_id,
"uuid": unique_id, "uuid": unique_id,
"timestamp": time.time() "timestamp": time.time(),
}) }
)
# Find the document # Find the document
doc = collection.find_one({"thread_id": thread_id}) doc = collection.find_one({"thread_id": thread_id})
# Update the document # Update the document
collection.update_one( collection.update_one(
{"thread_id": thread_id}, {"thread_id": thread_id}, {"$set": {"updated": True}}
{"$set": {"updated": True}}
) )
# Verify update # Verify update
@@ -406,9 +419,11 @@ def run_concurrent_operation_test(num_threads=100):
# Clean up # Clean up
collection.delete_many({"thread_id": thread_id}) collection.delete_many({"thread_id": thread_id})
success = (doc is not None and success = (
updated_doc is not None and doc is not None
updated_doc.get("updated") is True) and updated_doc is not None
and updated_doc.get("updated") is True
)
# Update results with thread safety # Update results with thread safety
with results_lock: with results_lock:
@@ -440,7 +455,9 @@ def run_concurrent_operation_test(num_threads=100):
if results["failed"] > 0: if results["failed"] > 0:
print("\nErrors:") print("\nErrors:")
for error in results["errors"][:10]: # Show only first 10 errors to avoid flooding output for error in results["errors"][
:10
]: # Show only first 10 errors to avoid flooding output
print(f"- {error}") print(f"- {error}")
if len(results["errors"]) > 10: if len(results["errors"]) > 10:
print(f"- ... and {len(results['errors']) - 10} more errors") print(f"- ... and {len(results['errors']) - 10} more errors")

View File

@@ -1,10 +1,12 @@
""" """
Test script for MongoDB handler with a local MongoDB instance. Test script for MongoDB handler with a local MongoDB instance.
""" """
import os import os
from Controllers.Mongo.database import MongoDBHandler, CollectionContext from Controllers.Mongo.database import MongoDBHandler, CollectionContext
from datetime import datetime from datetime import datetime
# Create a custom handler class for local testing # Create a custom handler class for local testing
class LocalMongoDBHandler(MongoDBHandler): class LocalMongoDBHandler(MongoDBHandler):
"""A MongoDB handler for local testing without authentication.""" """A MongoDB handler for local testing without authentication."""
@@ -22,6 +24,7 @@ class LocalMongoDBHandler(MongoDBHandler):
} }
self._initialized = True self._initialized = True
# Create a custom handler for local testing # Create a custom handler for local testing
def create_local_handler(): def create_local_handler():
"""Create a MongoDB handler for local testing.""" """Create a MongoDB handler for local testing."""
@@ -29,6 +32,7 @@ def create_local_handler():
handler = LocalMongoDBHandler() handler = LocalMongoDBHandler()
return handler return handler
def test_connection_monitoring(): def test_connection_monitoring():
"""Test connection monitoring with the MongoDB handler.""" """Test connection monitoring with the MongoDB handler."""
print("\nTesting connection monitoring...") print("\nTesting connection monitoring...")
@@ -85,5 +89,6 @@ def test_connection_monitoring():
CollectionContext.__enter__ = original_enter CollectionContext.__enter__ = original_enter
CollectionContext.__exit__ = original_exit CollectionContext.__exit__ = original_exit
if __name__ == "__main__": if __name__ == "__main__":
test_connection_monitoring() test_connection_monitoring()

View File

@@ -38,4 +38,4 @@ class Configs(BaseSettings):
# singleton instance of the POSTGRESQL configuration settings # singleton instance of the POSTGRESQL configuration settings
postgres_configs = Configs() postgres_configs = Configs()
print('url', postgres_configs.url) print("url", postgres_configs.url)

View File

@@ -270,6 +270,7 @@ class CRUDModel:
except Exception as e: except Exception as e:
db.rollback() db.rollback()
print('e', e)
cls.raise_exception( cls.raise_exception(
f"Failed to find or create record: {str(e)}", status_code=500 f"Failed to find or create record: {str(e)}", status_code=500
) )

View File

@@ -4,8 +4,8 @@ Advanced filtering functionality for SQLAlchemy models.
This module provides a comprehensive set of filtering capabilities for SQLAlchemy models, This module provides a comprehensive set of filtering capabilities for SQLAlchemy models,
including pagination, ordering, and complex query building. including pagination, ordering, and complex query building.
""" """
from __future__ import annotations from __future__ import annotations
import arrow import arrow
from typing import Any, TypeVar, Type, Union, Optional from typing import Any, TypeVar, Type, Union, Optional
@@ -23,7 +23,7 @@ T = TypeVar("T", bound="QueryModel")
class QueryModel: class QueryModel:
__abstract__ = True __abstract__ = True
pre_query = None pre_query: Optional[Query] = None
@classmethod @classmethod
def _query(cls: Type[T], db: Session) -> Query: def _query(cls: Type[T], db: Session) -> Query:
@@ -149,11 +149,29 @@ class QueryModel:
Returns: Returns:
Tuple of SQLAlchemy filter expressions or None if validation fails Tuple of SQLAlchemy filter expressions or None if validation fails
""" """
if validate_model is not None: try:
# Add validation logic here if needed # Let SQLAlchemy handle the validation by attempting to create the filter expressions
pass
return tuple(cls.filter_expr(**smart_options)) return tuple(cls.filter_expr(**smart_options))
except Exception as e:
# If there's an error, provide a helpful message with valid columns and relationships
valid_columns = set()
relationship_names = set()
# Get column names if available
if hasattr(cls, '__table__') and hasattr(cls.__table__, 'columns'):
valid_columns = set(column.key for column in cls.__table__.columns)
# Get relationship names if available
if hasattr(cls, '__mapper__') and hasattr(cls.__mapper__, 'relationships'):
relationship_names = set(rel.key for rel in cls.__mapper__.relationships)
# Create a helpful error message
error_msg = f"Error in filter expression: {str(e)}\n"
error_msg += f"Attempted to filter with: {smart_options}\n"
error_msg += f"Valid columns are: {', '.join(valid_columns)}\n"
error_msg += f"Valid relationships are: {', '.join(relationship_names)}"
raise ValueError(error_msg) from e
@classmethod @classmethod
def filter_by_one( def filter_by_one(

View File

@@ -504,7 +504,9 @@ def run_simple_concurrent_test(num_threads=10):
results["passed"] += 1 results["passed"] += 1
else: else:
results["failed"] += 1 results["failed"] += 1
results["errors"].append(f"Thread {thread_id} failed to get count") results["errors"].append(
f"Thread {thread_id} failed to get count"
)
except Exception as e: except Exception as e:
with results_lock: with results_lock:
results["failed"] += 1 results["failed"] += 1
@@ -528,7 +530,9 @@ def run_simple_concurrent_test(num_threads=10):
if results["failed"] > 0: if results["failed"] > 0:
print("\nErrors:") print("\nErrors:")
for error in results["errors"][:10]: # Show only first 10 errors to avoid flooding output for error in results["errors"][
:10
]: # Show only first 10 errors to avoid flooding output
print(f"- {error}") print(f"- {error}")
if len(results["errors"]) > 10: if len(results["errors"]) > 10:
print(f"- ... and {len(results['errors']) - 10} more errors") print(f"- ... and {len(results['errors']) - 10} more errors")

View File

@@ -144,6 +144,19 @@ class Pagination:
self.orderField = "uu_id" self.orderField = "uu_id"
self.orderType = "asc" self.orderType = "asc"
@property
def next_available(self) -> bool:
if self.page < self.total_pages:
return True
return False
@property
def back_available(self) -> bool:
if self.page > 1:
return True
return False
@property
def as_dict(self) -> Dict[str, Any]: def as_dict(self) -> Dict[str, Any]:
"""Convert pagination state to dictionary format.""" """Convert pagination state to dictionary format."""
self.refresh() self.refresh()
@@ -156,6 +169,8 @@ class Pagination:
"pageCount": self.page_count, "pageCount": self.page_count,
"orderField": self.orderField, "orderField": self.orderField,
"orderType": self.orderType, "orderType": self.orderType,
"next": self.next_available,
"back": self.back_available,
} }

View File

@@ -112,6 +112,8 @@ class PostgresResponse(Generic[T]):
class EndpointResponse(BaseModel): class EndpointResponse(BaseModel):
"""Endpoint response model."""
completed: bool = True completed: bool = True
message: str = "Success" message: str = "Success"
pagination_result: Any pagination_result: Any
@@ -119,9 +121,46 @@ class EndpointResponse(BaseModel):
@property @property
def response(self): def response(self):
"""Convert response to dictionary format.""" """Convert response to dictionary format."""
result_data = getattr(self.pagination_result, "data", None)
if not result_data:
return {
"completed": False,
"message": "MSG0004-NODATA",
"data": None,
"pagination": {
"page": 1,
"size": 10,
"total_count": 0,
"total_pages": 0,
},
}
result_pagination = getattr(self.pagination_result, "pagination", None)
if not result_pagination:
raise ValueError("Invalid pagination result pagination.")
pagination_dict = getattr(result_pagination, "as_dict", None)
if not pagination_dict:
raise ValueError("Invalid pagination result as_dict.")
return { return {
"completed": self.completed, "completed": self.completed,
"message": self.message, "message": self.message,
"data": self.pagination_result.data, "data": result_data,
"pagination": self.pagination_result.pagination.as_dict(), "pagination": pagination_dict,
} }
class CreateEndpointResponse(BaseModel):
"""Create endpoint response model."""
completed: bool = True
message: str = "Success"
data: Any
@property
def response(self):
"""Convert response to dictionary format."""
return {
"completed": self.completed,
"message": self.message,
"data": self.data,
}

View File

@@ -33,13 +33,13 @@ class RedisPublisher:
return RedisResponse( return RedisResponse(
status=True, status=True,
message=f"Message published successfully to {channel}.", message=f"Message published successfully to {channel}.",
data={"recipients": recipient_count} data={"recipients": recipient_count},
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False,
message=f"Failed to publish message to {channel}.", message=f"Failed to publish message to {channel}.",
error=str(e) error=str(e),
) )
@@ -51,7 +51,9 @@ class RedisSubscriber:
self.pubsub = self.redis_client.pubsub() self.pubsub = self.redis_client.pubsub()
self.active_threads = {} self.active_threads = {}
def subscribe(self, channel: str, callback: Callable[[Dict], None]) -> RedisResponse: def subscribe(
self, channel: str, callback: Callable[[Dict], None]
) -> RedisResponse:
"""Subscribe to a Redis channel with a callback function. """Subscribe to a Redis channel with a callback function.
Args: Args:
@@ -66,17 +68,16 @@ class RedisSubscriber:
self.pubsub.subscribe(**{channel: self._message_handler(callback)}) self.pubsub.subscribe(**{channel: self._message_handler(callback)})
return RedisResponse( return RedisResponse(
status=True, status=True, message=f"Successfully subscribed to {channel}."
message=f"Successfully subscribed to {channel}."
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False, message=f"Failed to subscribe to {channel}.", error=str(e)
message=f"Failed to subscribe to {channel}.",
error=str(e)
) )
def psubscribe(self, pattern: str, callback: Callable[[Dict], None]) -> RedisResponse: def psubscribe(
self, pattern: str, callback: Callable[[Dict], None]
) -> RedisResponse:
"""Subscribe to Redis channels matching a pattern. """Subscribe to Redis channels matching a pattern.
Args: Args:
@@ -91,27 +92,27 @@ class RedisSubscriber:
self.pubsub.psubscribe(**{pattern: self._message_handler(callback)}) self.pubsub.psubscribe(**{pattern: self._message_handler(callback)})
return RedisResponse( return RedisResponse(
status=True, status=True, message=f"Successfully pattern-subscribed to {pattern}."
message=f"Successfully pattern-subscribed to {pattern}."
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False,
message=f"Failed to pattern-subscribe to {pattern}.", message=f"Failed to pattern-subscribe to {pattern}.",
error=str(e) error=str(e),
) )
def _message_handler(self, callback: Callable[[Dict], None]): def _message_handler(self, callback: Callable[[Dict], None]):
"""Create a message handler function for the subscription.""" """Create a message handler function for the subscription."""
def handler(message): def handler(message):
# Skip subscription confirmation messages # Skip subscription confirmation messages
if message['type'] in ('subscribe', 'psubscribe'): if message["type"] in ("subscribe", "psubscribe"):
return return
# Parse JSON if the message is a JSON string # Parse JSON if the message is a JSON string
data = message['data'] data = message["data"]
if isinstance(data, bytes): if isinstance(data, bytes):
data = data.decode('utf-8') data = data.decode("utf-8")
try: try:
data = json.loads(data) data = json.loads(data)
except json.JSONDecodeError: except json.JSONDecodeError:
@@ -119,11 +120,21 @@ class RedisSubscriber:
pass pass
# Call the callback with the message data # Call the callback with the message data
callback({ callback(
'channel': message.get('channel', b'').decode('utf-8') if isinstance(message.get('channel', b''), bytes) else message.get('channel', ''), {
'pattern': message.get('pattern', b'').decode('utf-8') if isinstance(message.get('pattern', b''), bytes) else message.get('pattern', ''), "channel": (
'data': data message.get("channel", b"").decode("utf-8")
}) if isinstance(message.get("channel", b""), bytes)
else message.get("channel", "")
),
"pattern": (
message.get("pattern", b"").decode("utf-8")
if isinstance(message.get("pattern", b""), bytes)
else message.get("pattern", "")
),
"data": data,
}
)
return handler return handler
@@ -140,23 +151,19 @@ class RedisSubscriber:
if in_thread: if in_thread:
thread = Thread(target=self._listen_thread, daemon=True) thread = Thread(target=self._listen_thread, daemon=True)
thread.start() thread.start()
self.active_threads['listener'] = thread self.active_threads["listener"] = thread
return RedisResponse( return RedisResponse(
status=True, status=True, message="Listening thread started successfully."
message="Listening thread started successfully."
) )
else: else:
# This will block the current thread # This will block the current thread
self._listen_thread() self._listen_thread()
return RedisResponse( return RedisResponse(
status=True, status=True, message="Listening started successfully (blocking)."
message="Listening started successfully (blocking)."
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False, message="Failed to start listening.", error=str(e)
message="Failed to start listening.",
error=str(e)
) )
def _listen_thread(self): def _listen_thread(self):
@@ -167,15 +174,10 @@ class RedisSubscriber:
"""Stop listening for messages.""" """Stop listening for messages."""
try: try:
self.pubsub.close() self.pubsub.close()
return RedisResponse( return RedisResponse(status=True, message="Successfully stopped listening.")
status=True,
message="Successfully stopped listening."
)
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False, message="Failed to stop listening.", error=str(e)
message="Failed to stop listening.",
error=str(e)
) )
def unsubscribe(self, channel: Optional[str] = None) -> RedisResponse: def unsubscribe(self, channel: Optional[str] = None) -> RedisResponse:
@@ -195,15 +197,12 @@ class RedisSubscriber:
self.pubsub.unsubscribe() self.pubsub.unsubscribe()
message = "Successfully unsubscribed from all channels." message = "Successfully unsubscribed from all channels."
return RedisResponse( return RedisResponse(status=True, message=message)
status=True,
message=message
)
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False,
message=f"Failed to unsubscribe from {'channel' if channel else 'all channels'}.", message=f"Failed to unsubscribe from {'channel' if channel else 'all channels'}.",
error=str(e) error=str(e),
) )
def punsubscribe(self, pattern: Optional[str] = None) -> RedisResponse: def punsubscribe(self, pattern: Optional[str] = None) -> RedisResponse:
@@ -223,15 +222,12 @@ class RedisSubscriber:
self.pubsub.punsubscribe() self.pubsub.punsubscribe()
message = "Successfully unsubscribed from all patterns." message = "Successfully unsubscribed from all patterns."
return RedisResponse( return RedisResponse(status=True, message=message)
status=True,
message=message
)
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
status=False, status=False,
message=f"Failed to unsubscribe from {'pattern' if pattern else 'all patterns'}.", message=f"Failed to unsubscribe from {'pattern' if pattern else 'all patterns'}.",
error=str(e) error=str(e),
) )

View File

@@ -15,6 +15,7 @@ CHANNEL_WRITER = "chain:writer"
# Flag to control the demo # Flag to control the demo
running = True running = True
def generate_mock_data(): def generate_mock_data():
"""Generate a mock message with UUID, timestamp, and sample data.""" """Generate a mock message with UUID, timestamp, and sample data."""
return { return {
@@ -24,10 +25,11 @@ def generate_mock_data():
"data": { "data": {
"value": f"Sample data {int(time.time())}", "value": f"Sample data {int(time.time())}",
"status": "new", "status": "new",
"counter": 0 "counter": 0,
} },
} }
def reader_function(): def reader_function():
""" """
First function in the chain. First function in the chain.
@@ -52,12 +54,14 @@ def reader_function():
# Wait before generating next message # Wait before generating next message
time.sleep(2) time.sleep(2)
def processor_function(): def processor_function():
""" """
Second function in the chain. Second function in the chain.
Subscribes to reader channel, processes messages, and publishes to processor channel. Subscribes to reader channel, processes messages, and publishes to processor channel.
""" """
print("[PROCESSOR] Function started") print("[PROCESSOR] Function started")
def on_reader_message(message): def on_reader_message(message):
# The message structure from the subscriber has 'data' containing our actual message # The message structure from the subscriber has 'data' containing our actual message
# If data is a string, parse it as JSON # If data is a string, parse it as JSON
@@ -82,14 +86,16 @@ def processor_function():
# Add some processing metadata # Add some processing metadata
data["processing"] = { data["processing"] = {
"duration_ms": 150, # Mock processing time "duration_ms": 150, # Mock processing time
"processor_id": "main-processor" "processor_id": "main-processor",
} }
# Publish to processor channel # Publish to processor channel
result = redis_pubsub.publisher.publish(CHANNEL_PROCESSOR, data) result = redis_pubsub.publisher.publish(CHANNEL_PROCESSOR, data)
if result.status: if result.status:
print(f"[PROCESSOR] {time.time():.6f} | Received UUID: {data['uuid']} | Published UUID: {data['uuid']}") print(
f"[PROCESSOR] {time.time():.6f} | Received UUID: {data['uuid']} | Published UUID: {data['uuid']}"
)
else: else:
print(f"[PROCESSOR] Publish error: {result.error}") print(f"[PROCESSOR] Publish error: {result.error}")
else: else:
@@ -103,12 +109,14 @@ def processor_function():
else: else:
print(f"[PROCESSOR] Subscribe error: {result.error}") print(f"[PROCESSOR] Subscribe error: {result.error}")
def writer_function(): def writer_function():
""" """
Third function in the chain. Third function in the chain.
Subscribes to processor channel and performs final processing. Subscribes to processor channel and performs final processing.
""" """
print("[WRITER] Function started") print("[WRITER] Function started")
def on_processor_message(message): def on_processor_message(message):
# The message structure from the subscriber has 'data' containing our actual message # The message structure from the subscriber has 'data' containing our actual message
# If data is a string, parse it as JSON # If data is a string, parse it as JSON
@@ -131,26 +139,29 @@ def writer_function():
data["stage"] = "completed" data["stage"] = "completed"
# Add some writer metadata # Add some writer metadata
data["storage"] = { data["storage"] = {"location": "main-db", "partition": "events-2025-04"}
"location": "main-db",
"partition": "events-2025-04"
}
# Calculate elapsed time if start_time is available # Calculate elapsed time if start_time is available
current_time = time.time() current_time = time.time()
elapsed_ms = "" elapsed_ms = ""
if "start_time" in data: if "start_time" in data:
elapsed_ms = f" | Elapsed: {(current_time - data['start_time']) * 1000:.2f}ms" elapsed_ms = (
f" | Elapsed: {(current_time - data['start_time']) * 1000:.2f}ms"
)
# Optionally publish to writer channel for any downstream listeners # Optionally publish to writer channel for any downstream listeners
result = redis_pubsub.publisher.publish(CHANNEL_WRITER, data) result = redis_pubsub.publisher.publish(CHANNEL_WRITER, data)
if result.status: if result.status:
print(f"[WRITER] {current_time:.6f} | Received UUID: {data['uuid']} | Published UUID: {data['uuid']}{elapsed_ms}") print(
f"[WRITER] {current_time:.6f} | Received UUID: {data['uuid']} | Published UUID: {data['uuid']}{elapsed_ms}"
)
else: else:
print(f"[WRITER] Publish error: {result.error}") print(f"[WRITER] Publish error: {result.error}")
else: else:
print(f"[WRITER] Skipped message: {data['uuid']} (stage is not 'processed')") print(
f"[WRITER] Skipped message: {data['uuid']} (stage is not 'processed')"
)
# Subscribe to processor channel # Subscribe to processor channel
result = redis_pubsub.subscriber.subscribe(CHANNEL_PROCESSOR, on_processor_message) result = redis_pubsub.subscriber.subscribe(CHANNEL_PROCESSOR, on_processor_message)

View File

@@ -113,7 +113,9 @@ def run_all_examples() -> None:
def run_concurrent_test(num_threads=100): def run_concurrent_test(num_threads=100):
"""Run a comprehensive concurrent test with multiple threads to verify Redis connection handling.""" """Run a comprehensive concurrent test with multiple threads to verify Redis connection handling."""
print(f"\nStarting comprehensive Redis concurrent test with {num_threads} threads...") print(
f"\nStarting comprehensive Redis concurrent test with {num_threads} threads..."
)
# Results tracking with detailed metrics # Results tracking with detailed metrics
results = { results = {
@@ -124,7 +126,7 @@ def run_concurrent_test(num_threads=100):
"operation_times": [], "operation_times": [],
"retry_count": 0, "retry_count": 0,
"max_retries": 3, "max_retries": 3,
"retry_delay": 0.1 "retry_delay": 0.1,
} }
results_lock = threading.Lock() results_lock = threading.Lock()
@@ -158,7 +160,7 @@ def run_concurrent_test(num_threads=100):
set_ok = results_list[0] set_ok = results_list[0]
retrieved_value = results_list[1] retrieved_value = results_list[1]
if isinstance(retrieved_value, bytes): if isinstance(retrieved_value, bytes):
retrieved_value = retrieved_value.decode('utf-8') retrieved_value = retrieved_value.decode("utf-8")
# Verify data # Verify data
success = set_ok and retrieved_value == test_value success = set_ok and retrieved_value == test_value
@@ -170,7 +172,9 @@ def run_concurrent_test(num_threads=100):
retry_count += 1 retry_count += 1
with results_lock: with results_lock:
results["retry_count"] += 1 results["retry_count"] += 1
time.sleep(results["retry_delay"] * (2 ** retry_count)) # Exponential backoff time.sleep(
results["retry_delay"] * (2**retry_count)
) # Exponential backoff
except Exception as e: except Exception as e:
error_message = str(e) error_message = str(e)
@@ -200,9 +204,13 @@ def run_concurrent_test(num_threads=100):
else: else:
results["failed"] += 1 results["failed"] += 1
if error_message: if error_message:
results["errors"].append(f"Thread {thread_id} failed after {retry_count} retries: {error_message}") results["errors"].append(
f"Thread {thread_id} failed after {retry_count} retries: {error_message}"
)
else: else:
results["errors"].append(f"Thread {thread_id} failed after {retry_count} retries with unknown error") results["errors"].append(
f"Thread {thread_id} failed after {retry_count} retries with unknown error"
)
# Create and start threads using a thread pool # Create and start threads using a thread pool
start_time = time.time() start_time = time.time()
@@ -227,7 +235,11 @@ def run_concurrent_test(num_threads=100):
# Calculate 95th percentile # Calculate 95th percentile
sorted_times = sorted(results["operation_times"]) sorted_times = sorted(results["operation_times"])
p95_index = int(len(sorted_times) * 0.95) p95_index = int(len(sorted_times) * 0.95)
p95_op_time = sorted_times[p95_index] if p95_index < len(sorted_times) else sorted_times[-1] p95_op_time = (
sorted_times[p95_index]
if p95_index < len(sorted_times)
else sorted_times[-1]
)
# Print detailed results # Print detailed results
print("\nConcurrent Redis Test Results:") print("\nConcurrent Redis Test Results:")

View File

@@ -75,6 +75,7 @@ from Schemas.event.event import (
Modules, Modules,
Services, Services,
Service2Events, Service2Events,
Service2Application,
Events, Events,
Event2Occupant, Event2Occupant,
Event2Employee, Event2Employee,
@@ -83,6 +84,8 @@ from Schemas.event.event import (
Applications, Applications,
Application2Employee, Application2Employee,
Application2Occupant, Application2Occupant,
Application2EmployeeExtra,
Application2OccupantExtra,
) )
from Schemas.identity.identity import ( from Schemas.identity.identity import (
UsersTokens, UsersTokens,

View File

@@ -343,7 +343,7 @@ class BuildParts(CrudCollection):
) )
__table_args__ = ( __table_args__ = (
Index("build_parts_ndx_01", build_id, part_no, unique=True), Index("build_parts_ndx_1", build_id, part_no, unique=True),
{"comment": "Part objects that are belong to building objects"}, {"comment": "Part objects that are belong to building objects"},
) )
@@ -351,9 +351,7 @@ class BuildParts(CrudCollection):
if build_type := BuildTypes.filter_by_one( if build_type := BuildTypes.filter_by_one(
system=True, id=self.part_type_id, db=db system=True, id=self.part_type_id, db=db
).data: ).data:
return ( return f"{str(build_type.type_name).upper()} : {str(self.part_no).upper()}"
f"{str(build_type.type_name).upper()} : {str(self.part_no).upper()}"
)
return f"Undefined:{str(build_type.type_name).upper()}" return f"Undefined:{str(build_type.type_name).upper()}"

View File

@@ -27,6 +27,9 @@ class Applications(CrudCollection):
String, nullable=False, comment="Application Code" String, nullable=False, comment="Application Code"
) )
application_type: Mapped[str] = mapped_column(String, comment="Application Type") application_type: Mapped[str] = mapped_column(String, comment="Application Type")
application_for: Mapped[str] = mapped_column(
String, server_default="EMP", comment="Application For"
)
description: Mapped[str] = mapped_column(String, comment="Application Description") description: Mapped[str] = mapped_column(String, comment="Application Description")
@@ -198,8 +201,30 @@ class Service2Events(CrudCollection):
__table_args__ = ({"comment": "Service2Events Information"},) __table_args__ = ({"comment": "Service2Events Information"},)
# class Service2Application(CrudCollection): class Service2Application(CrudCollection):
# pass """
Service2Application class based on declarative_base and BaseMixin via session
"""
__tablename__ = "services2applications"
__exclude__fields__ = []
application_id: Mapped[int] = mapped_column(
ForeignKey("applications.id"), nullable=False
)
application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID"
)
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
service_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Service UUID"
)
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
__table_args__ = {"comment": "Service2Applications Information"}
class Event2OccupantExtra(CrudCollection): class Event2OccupantExtra(CrudCollection):
@@ -395,45 +420,54 @@ class Application2Employee(CrudCollection):
__exclude__fields__ = [] __exclude__fields__ = []
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False) employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
employee_uu_id: Mapped[str] = mapped_column( employee_uu_id = mapped_column(String, nullable=False, comment="Employee UUID")
String, nullable=False, comment="Employee UUID" service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
) service_uu_id = mapped_column(String, nullable=False, comment="Service UUID")
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
)
@classmethod @classmethod
def get_application_codes(cls, employee_id: int, db) -> dict[str, str]: def get_application_codes(cls, employee_id: int, db) -> list[int]:
print("employee_id", employee_id) employee_services = cls.filter_all(
employee_applications = cls.filter_all( cls.employee_id == employee_id,
Application2Employee.employee_id == employee_id,
db=db, db=db,
).data ).data
service_ids = [service.service_id for service in employee_services]
active_applications = Service2Application.filter_all(
Service2Application.service_id.in_(service_ids),
db=db,
).data
applications = Applications.filter_all(
Applications.id.in_(
[application.application_id for application in active_applications]
),
db=db,
).data
if extra_applications := Application2EmployeeExtra.filter_all(
Application2EmployeeExtra.employee_id == employee_id,
db=db,
).data:
applications_extra = Applications.filter_all(
Applications.id.in_(
[application.application_id for application in extra_applications]
),
db=db,
).data
applications.extend(applications_extra)
applications_dict = {} applications_dict = {}
print("employee_applications", employee_applications) for application in applications:
for employee_application in employee_applications: if not application.site_url in applications_dict:
if employee_application.site_url not in applications_dict: applications_dict[str(application.site_url)] = str(application.application_code)
applications_dict[str(employee_application.site_url)] = str( else:
employee_application.application_code ValueError("Duplicate application code found for single endpoint")
)
return applications_dict return applications_dict
__table_args__ = ( __table_args__ = (
Index( Index(
"application_to_employee", "application2employee_employee_to_service",
employee_id, employee_uu_id,
site_url, service_uu_id,
application_id,
unique=True, unique=True,
), ),
{"comment": "Application2Employee Information"}, {"comment": "Application to Employee Information"},
) )
@@ -448,33 +482,114 @@ class Application2Occupant(CrudCollection):
build_living_space_id: Mapped[int] = mapped_column( build_living_space_id: Mapped[int] = mapped_column(
ForeignKey("build_living_space.id"), nullable=False ForeignKey("build_living_space.id"), nullable=False
) )
build_living_space_uu_id: Mapped[str] = mapped_column( build_living_space_uu_id = mapped_column(
String, nullable=False, comment="Build Living Space UUID" String, nullable=False, comment="Build Living Space UUID"
) )
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
service_uu_id = mapped_column(String, nullable=False, comment="Service UUID")
@classmethod
def get_application_codes(cls, build_living_space_id: int, db) -> list[int]:
occupant_services = cls.filter_all(
cls.build_living_space_id == build_living_space_id,
db=db,
).data
service_ids = [service.service_id for service in occupant_services]
active_applications = Service2Application.filter_all(
Service2Application.service_id.in_(service_ids),
db=db,
).data
applications = Applications.filter_all(
Applications.id.in_(
[application.application_id for application in active_applications]
),
db=db,
).data
if extra_applications := Application2OccupantExtra.filter_all(
Application2OccupantExtra.build_living_space_id == build_living_space_id,
db=db,
).data:
applications_extra = Applications.filter_all(
Applications.id.in_(
[application.application_id for application in extra_applications]
),
db=db,
).data
applications.extend(applications_extra)
applications_dict = {}
for application in applications:
if not application.site_url in applications_dict:
applications_dict[str(application.site_url)] = str(application.application_code)
else:
ValueError("Duplicate application code found for single endpoint")
return applications_dict
__table_args__ = (
Index(
"application2occupant_occupant_to_service",
build_living_space_uu_id,
service_uu_id,
unique=True,
),
{"comment": "Application to Occupant Information"},
)
class Application2EmployeeExtra(CrudCollection):
"""
Application2EmployeeExtra class based on declarative_base and BaseMixin via session
"""
__tablename__ = "application2employee_extra"
__exclude__fields__ = []
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
employee_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Employee UUID"
)
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id")) application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
application_uu_id: Mapped[str] = mapped_column( application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID" String, nullable=False, comment="Application UUID"
) )
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL") site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column( application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code" String, nullable=False, comment="Application Code"
) )
@classmethod __table_args__ = (
def get_application_codes(cls, build_living_space_id: int, db) -> dict[str, str]: Index(
occupant_applications = cls.filter_all( "application_to_employee",
cls.build_living_space_id == build_living_space_id, employee_id,
db=db, site_url,
).data application_id,
applications_dict = {} unique=True,
for occupant_application in occupant_applications: ),
if occupant_application.site_url not in applications_dict: {"comment": "Application2Employee Information"},
applications_dict[str(occupant_application.site_url)] = str( )
occupant_application.application_code
class Application2OccupantExtra(CrudCollection):
"""
Application2OccupantExtra class based on declarative_base and BaseMixin via session
"""
__tablename__ = "application2occupant_extra"
__exclude__fields__ = []
build_living_space_id: Mapped[int] = mapped_column(
ForeignKey("build_living_space.id"), nullable=False
)
build_living_space_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Build Living Space UUID"
)
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
) )
return applications_dict
__table_args__ = ( __table_args__ = (
Index( Index(

Some files were not shown because too many files have changed in this diff Show More