Compare commits
33 Commits
d7f1da8de8
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d7a6e6ec0 | |||
| aee9d59750 | |||
| bc43471259 | |||
| e0ae1ee80a | |||
| dd707b2463 | |||
| 0cde34a9bc | |||
| 01f3e82a54 | |||
| ac8c3fe1c3 | |||
| c6b1a2b1e8 | |||
| 734dc59e38 | |||
| 71c808a5c3 | |||
| 1ce28ec5f0 | |||
| 1920c2a25d | |||
| e815251123 | |||
| 36e63960f8 | |||
| f2cc7a69b5 | |||
| 0052c92974 | |||
| 113e43c7d7 | |||
| 2d418644bb | |||
| ac344773c5 | |||
| 346b132f4c | |||
| 090567ade8 | |||
| ba784c40e4 | |||
| a886c2f28c | |||
| f4e43306c1 | |||
| c553975e82 | |||
| 0bdc0d287e | |||
| 7efd6f8941 | |||
| 9f0c42e57a | |||
| b9825bb8e8 | |||
| 0bd8ddce4d | |||
| 9511f81bc0 | |||
| e5f88f2eb4 |
@@ -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
|
||||||
@@ -12,6 +12,10 @@ class CommonHeaders(BaseModel):
|
|||||||
request: Request | None = None
|
request: Request | None = None
|
||||||
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(
|
||||||
@@ -23,26 +27,26 @@ class CommonHeaders(BaseModel):
|
|||||||
tz: str = Header(None, alias="timezone"),
|
tz: str = Header(None, alias="timezone"),
|
||||||
):
|
):
|
||||||
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
||||||
|
|
||||||
# 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,
|
||||||
domain=domain,
|
domain=domain,
|
||||||
timezone=tz,
|
timezone=tz,
|
||||||
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):
|
||||||
"""Convert the headers to a dictionary format used in the application"""
|
"""Convert the headers to a dictionary format used in the application"""
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"language": self.language or "",
|
"language": self.language or "",
|
||||||
"domain": self.domain or "",
|
"domain": self.domain or "",
|
||||||
|
|||||||
@@ -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,
|
||||||
include_args=[
|
|
||||||
Events.function_code,
|
|
||||||
Events.function_class,
|
|
||||||
Events.endpoint_code,
|
|
||||||
Events.endpoint_uu_id,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
if event_to_save_database.meta_data.created:
|
event_found = Events.filter_one(
|
||||||
event_to_save_database.save(db=db_session)
|
Events.function_code == event_dict_to_save["function_code"],
|
||||||
print(
|
db=db_session,
|
||||||
f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}"
|
).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=[
|
||||||
|
Events.function_code,
|
||||||
|
Events.function_class,
|
||||||
|
Events.endpoint_code,
|
||||||
|
Events.endpoint_uu_id,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -23,17 +23,24 @@ 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,
|
|
||||||
)
|
)
|
||||||
if restriction.meta_data.created:
|
endpoint_restriction_found = EndpointRestriction.filter_one_system(
|
||||||
restriction.save(db=db_session)
|
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:
|
||||||
|
restriction.save(db=db_session)
|
||||||
|
|
||||||
def register_routes(self):
|
def register_routes(self):
|
||||||
for router in self.router_list:
|
for router in self.router_list:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
|
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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,10 +632,17 @@ 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):
|
||||||
return True
|
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 False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
29
ApiServices/BuildingService/Dockerfile
Normal file
29
ApiServices/BuildingService/Dockerfile
Normal 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"]
|
||||||
70
ApiServices/BuildingService/Endpoints/area/route.py
Normal file
70
ApiServices/BuildingService/Endpoints/area/route.py
Normal 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)
|
||||||
70
ApiServices/BuildingService/Endpoints/building/route.py
Normal file
70
ApiServices/BuildingService/Endpoints/building/route.py
Normal 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)
|
||||||
29
ApiServices/BuildingService/Endpoints/routes.py
Normal file
29
ApiServices/BuildingService/Endpoints/routes.py
Normal 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"),
|
||||||
|
]
|
||||||
70
ApiServices/BuildingService/Endpoints/sites/route.py
Normal file
70
ApiServices/BuildingService/Endpoints/sites/route.py
Normal 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)
|
||||||
76
ApiServices/BuildingService/Endpoints/spaces/route.py
Normal file
76
ApiServices/BuildingService/Endpoints/spaces/route.py
Normal 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)
|
||||||
70
ApiServices/BuildingService/Endpoints/type/route.py
Normal file
70
ApiServices/BuildingService/Endpoints/type/route.py
Normal 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)
|
||||||
15
ApiServices/BuildingService/Events/__init__.py
Normal file
15
ApiServices/BuildingService/Events/__init__.py
Normal 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",
|
||||||
|
]
|
||||||
26
ApiServices/BuildingService/Events/area/cluster.py
Normal file
26
ApiServices/BuildingService/Events/area/cluster.py
Normal 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)
|
||||||
96
ApiServices/BuildingService/Events/area/supers_events.py
Normal file
96
ApiServices/BuildingService/Events/area/supers_events.py
Normal 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
|
||||||
27
ApiServices/BuildingService/Events/building/cluster.py
Normal file
27
ApiServices/BuildingService/Events/building/cluster.py
Normal 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)
|
||||||
98
ApiServices/BuildingService/Events/building/supers_events.py
Normal file
98
ApiServices/BuildingService/Events/building/supers_events.py
Normal 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
|
||||||
27
ApiServices/BuildingService/Events/sites/cluster.py
Normal file
27
ApiServices/BuildingService/Events/sites/cluster.py
Normal 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)
|
||||||
96
ApiServices/BuildingService/Events/sites/supers_events.py
Normal file
96
ApiServices/BuildingService/Events/sites/supers_events.py
Normal 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
|
||||||
27
ApiServices/BuildingService/Events/spaces/cluster.py
Normal file
27
ApiServices/BuildingService/Events/spaces/cluster.py
Normal 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)
|
||||||
96
ApiServices/BuildingService/Events/spaces/supers_events.py
Normal file
96
ApiServices/BuildingService/Events/spaces/supers_events.py
Normal 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
|
||||||
27
ApiServices/BuildingService/Events/type/cluster.py
Normal file
27
ApiServices/BuildingService/Events/type/cluster.py
Normal 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)
|
||||||
96
ApiServices/BuildingService/Events/type/supers_events.py
Normal file
96
ApiServices/BuildingService/Events/type/supers_events.py
Normal 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
|
||||||
@@ -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
|
||||||
26
ApiServices/BuildingService/Validations/lists/validations.py
Normal file
26
ApiServices/BuildingService/Validations/lists/validations.py
Normal 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"
|
||||||
@@ -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
|
||||||
)
|
# )
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
if service_to_event_found.data:
|
||||||
|
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}"
|
||||||
|
)
|
||||||
|
|
||||||
|
employee_added_application = Service2Application.find_or_create(
|
||||||
|
service_id=service_match.id,
|
||||||
|
service_uu_id=str(service_match.uu_id),
|
||||||
|
application_id=list_of_event_code.id,
|
||||||
|
application_uu_id=str(list_of_event_code.uu_id),
|
||||||
|
application_code=list_of_event_code.application_code,
|
||||||
|
site_url=list_of_event_code.site_url,
|
||||||
is_confirmed=True,
|
is_confirmed=True,
|
||||||
)
|
active=True,
|
||||||
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,
|
db=db_session,
|
||||||
)
|
)
|
||||||
if application_employee_created.meta_data.created:
|
if employee_added_application.meta_data.created:
|
||||||
application_employee_created.save(db=db_session)
|
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(
|
||||||
def init_applications_for_general_manager(
|
service_id=service_match.id,
|
||||||
super_user: Employees, db_session=None
|
service_uu_id=str(service_match.uu_id),
|
||||||
) -> None:
|
employee_id=employee_match.id,
|
||||||
list_of_created_apps = [
|
employee_uu_id=str(employee_match.uu_id),
|
||||||
dict(
|
is_confirmed=True,
|
||||||
name="Dashboard1",
|
db=db_session,
|
||||||
application_code="app000001",
|
)
|
||||||
site_url="/dashboard",
|
if employee_added_service.meta_data.created:
|
||||||
application_type="info",
|
employee_added_service.save(db=db_session)
|
||||||
description="Dashboard Page",
|
print(
|
||||||
),
|
f"UUID: {employee_added_service.uu_id} service is saved to {employee_match.uu_id}"
|
||||||
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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
employee_added_service.save(db=db_session)
|
||||||
|
print(
|
||||||
|
f"UUID: {employee_added_service.uu_id} event is saved to employee {employee_match.uu_id}"
|
||||||
)
|
)
|
||||||
if employee_added_service.meta_data.created:
|
|
||||||
employee_added_service.save(db=db_session)
|
|
||||||
print(
|
|
||||||
f"UUID: {employee_added_service.uu_id} event is saved to {super_user.uu_id}"
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ def people_route_list(
|
|||||||
List people endpoint
|
List people endpoint
|
||||||
"""
|
"""
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
||||||
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(
|
||||||
@@ -41,7 +41,7 @@ def people_route_create(
|
|||||||
Create people endpoint
|
Create people endpoint
|
||||||
"""
|
"""
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
||||||
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleCreate")
|
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleCreate")
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
||||||
@@ -60,7 +60,7 @@ def people_route_update(
|
|||||||
Update people endpoint
|
Update people endpoint
|
||||||
"""
|
"""
|
||||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||||
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object)
|
||||||
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
event_key = TokenProvider.retrieve_event_codes(**event_founder_dict)
|
||||||
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleUpdate")
|
FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleUpdate")
|
||||||
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
event_cluster_matched = FoundCluster.match_event(event_key=event_key)
|
||||||
|
|||||||
@@ -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]]:
|
||||||
|
|||||||
@@ -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")
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
|
from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster
|
||||||
from .supers_events import (
|
from .supers_events import (
|
||||||
SuperUsersListEvent,
|
SuperUsersListEvent,
|
||||||
SuperUsersCreateEvent,
|
SuperUsersCreateEvent,
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -444,14 +444,29 @@ 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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one(
|
||||||
document={
|
{"user_uu_id": str(gen_manager_user.uu_id)}
|
||||||
"user_uu_id": str(gen_manager_user.uu_id),
|
|
||||||
"other_domains_list": [main_domain],
|
|
||||||
"main_domain": main_domain,
|
|
||||||
"modified_at": arrow.now().timestamp(),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if not existing_record:
|
||||||
|
mongo_engine.insert_one(
|
||||||
|
document={
|
||||||
|
"user_uu_id": str(gen_manager_user.uu_id),
|
||||||
|
"other_domains_list": [main_domain],
|
||||||
|
"main_domain": main_domain,
|
||||||
|
"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,14 +487,29 @@ 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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one(
|
||||||
document={
|
{"user_uu_id": str(app_manager_user.uu_id)}
|
||||||
"user_uu_id": str(app_manager_user.uu_id),
|
|
||||||
"other_domains_list": [main_domain],
|
|
||||||
"main_domain": main_domain,
|
|
||||||
"modified_at": arrow.now().timestamp(),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if not existing_record:
|
||||||
|
mongo_engine.insert_one(
|
||||||
|
document={
|
||||||
|
"user_uu_id": str(app_manager_user.uu_id),
|
||||||
|
"other_domains_list": [main_domain],
|
||||||
|
"main_domain": main_domain,
|
||||||
|
"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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one(
|
||||||
document={
|
{"user_uu_id": str(sup_manager_employee.uu_id)}
|
||||||
"user_uu_id": str(sup_manager_employee.uu_id),
|
|
||||||
"other_domains_list": [main_domain],
|
|
||||||
"main_domain": main_domain,
|
|
||||||
"modified_at": arrow.now().timestamp(),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if not existing_record:
|
||||||
|
print("insert sup existing record", existing_record)
|
||||||
|
mongo_engine.insert_one(
|
||||||
|
document={
|
||||||
|
"user_uu_id": str(sup_manager_employee.uu_id),
|
||||||
|
"other_domains_list": [main_domain, "management.com.tr"],
|
||||||
|
"main_domain": main_domain,
|
||||||
|
"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")
|
||||||
|
|||||||
@@ -239,34 +239,75 @@ 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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one(
|
||||||
document={
|
{"user_uu_id": str(user_build_manager.uu_id)}
|
||||||
"user_uu_id": str(user_build_manager.uu_id),
|
|
||||||
"other_domains_list": [main_domain],
|
|
||||||
"main_domain": main_domain,
|
|
||||||
"modified_at": arrow.now().timestamp(),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if not existing_record:
|
||||||
|
mongo_engine.insert_one(
|
||||||
|
document={
|
||||||
|
"user_uu_id": str(user_build_manager.uu_id),
|
||||||
|
"other_domains_list": [main_domain],
|
||||||
|
"main_domain": main_domain,
|
||||||
|
"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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one({"user_uu_id": str(user_owner.uu_id)})
|
||||||
document={
|
if not existing_record:
|
||||||
"user_uu_id": str(user_owner.uu_id),
|
mongo_engine.insert_one(
|
||||||
"other_domains_list": [main_domain],
|
document={
|
||||||
"main_domain": main_domain,
|
"user_uu_id": str(user_owner.uu_id),
|
||||||
"modified_at": arrow.now().timestamp(),
|
"other_domains_list": [main_domain],
|
||||||
}
|
"main_domain": main_domain,
|
||||||
)
|
"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:
|
||||||
mongo_engine.insert_one(
|
existing_record = mongo_engine.find_one({"user_uu_id": str(user_tenant.uu_id)})
|
||||||
document={
|
if not existing_record:
|
||||||
"user_uu_id": str(user_tenant.uu_id),
|
mongo_engine.insert_one(
|
||||||
"other_domains_list": [main_domain],
|
document={
|
||||||
"main_domain": main_domain,
|
"user_uu_id": str(user_tenant.uu_id),
|
||||||
"modified_at": arrow.now().timestamp(),
|
"other_domains_list": [main_domain],
|
||||||
}
|
"main_domain": main_domain,
|
||||||
)
|
"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,
|
||||||
|
|||||||
29
ApiServices/ManagementService/Dockerfile
Normal file
29
ApiServices/ManagementService/Dockerfile
Normal 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"]
|
||||||
1
ApiServices/ManagementService/Endpoints/__init__.py
Normal file
1
ApiServices/ManagementService/Endpoints/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
197
ApiServices/ManagementService/Endpoints/application/route.py
Normal file
197
ApiServices/ManagementService/Endpoints/application/route.py
Normal 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)
|
||||||
142
ApiServices/ManagementService/Endpoints/event_endpoints/route.py
Normal file
142
ApiServices/ManagementService/Endpoints/event_endpoints/route.py
Normal 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)
|
||||||
25
ApiServices/ManagementService/Endpoints/routes.py
Normal file
25
ApiServices/ManagementService/Endpoints/routes.py
Normal 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"),
|
||||||
|
]
|
||||||
@@ -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)
|
||||||
@@ -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"]
|
||||||
|
)
|
||||||
9
ApiServices/ManagementService/Events/__init__.py
Normal file
9
ApiServices/ManagementService/Events/__init__.py
Normal 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",
|
||||||
|
]
|
||||||
55
ApiServices/ManagementService/Events/application/cluster.py
Normal file
55
ApiServices/ManagementService/Events/application/cluster.py
Normal 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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
1
ApiServices/ManagementService/Validations/__init__.py
Normal file
1
ApiServices/ManagementService/Validations/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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")
|
||||||
@@ -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
|
||||||
13
ApiServices/ManagementService/__init__.py
Normal file
13
ApiServices/ManagementService/__init__.py
Normal 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"]
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
from .template.cluster import (
|
|
||||||
TemplateEventClusterSet
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"TemplateEventClusterSet",
|
|
||||||
]
|
|
||||||
|
|
||||||
def retrieve_all_clusters():
|
|
||||||
return [TemplateEventClusterSet]
|
|
||||||
@@ -30,46 +30,46 @@ T = TypeVar("T")
|
|||||||
|
|
||||||
class EmailProcessingContext:
|
class EmailProcessingContext:
|
||||||
"""Context manager for email processing that marks emails as unread if an error occurs."""
|
"""Context manager for email processing that marks emails as unread if an error occurs."""
|
||||||
|
|
||||||
def __init__(self, email_message, mark_as_read: bool = True):
|
def __init__(self, email_message, mark_as_read: bool = True):
|
||||||
self.email_message = email_message
|
self.email_message = email_message
|
||||||
self.mark_as_read = mark_as_read
|
self.mark_as_read = mark_as_read
|
||||||
self.success = False
|
self.success = False
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
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
|
||||||
is_base64 = False
|
is_base64 = False
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"payload": encoded_payload,
|
"payload": encoded_payload,
|
||||||
@@ -79,12 +79,14 @@ def publish_payload_to_redis(
|
|||||||
"uuid": str(uuid4()), # Use UUID
|
"uuid": str(uuid4()), # Use UUID
|
||||||
**mail_info,
|
**mail_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Publish to Redis channel
|
# Publish to Redis channel
|
||||||
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}")
|
||||||
@@ -126,7 +128,7 @@ def app():
|
|||||||
port = Config.EMAIL_PORT
|
port = Config.EMAIL_PORT
|
||||||
username = Config.EMAIL_USERNAME
|
username = Config.EMAIL_USERNAME
|
||||||
password = Config.EMAIL_PASSWORD
|
password = Config.EMAIL_PASSWORD
|
||||||
|
|
||||||
box = EmailBox(host=host, port=port, username=username, password=password)
|
box = EmailBox(host=host, port=port, username=username, password=password)
|
||||||
if not box:
|
if not box:
|
||||||
return Exception("Mailbox not found")
|
return Exception("Mailbox not found")
|
||||||
@@ -136,41 +138,51 @@ def app():
|
|||||||
filter_mail = OR(FROM(Config.MAILBOX), FROM(Config.MAIN_MAIL))
|
filter_mail = OR(FROM(Config.MAILBOX), FROM(Config.MAIN_MAIL))
|
||||||
filter_print = f"{Config.MAILBOX} & {Config.MAIN_MAIL}"
|
filter_print = f"{Config.MAILBOX} & {Config.MAIN_MAIL}"
|
||||||
|
|
||||||
# Determine if this is the first run of the day
|
# Determine if this is the first run of the day
|
||||||
# Store last run date in a file
|
# Store last run date in a file
|
||||||
last_run_file = "/tmp/email_service_last_run.json"
|
last_run_file = "/tmp/email_service_last_run.json"
|
||||||
current_date = datetime.now().strftime("%Y-%m-%d")
|
current_date = datetime.now().strftime("%Y-%m-%d")
|
||||||
days_to_check, full_check = 7, 90 # Default to 7 days
|
days_to_check, full_check = 7, 90 # Default to 7 days
|
||||||
|
|
||||||
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":
|
||||||
# Search for emails since the calculated date
|
# Search for emails since the calculated date
|
||||||
@@ -184,27 +196,33 @@ 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"],
|
||||||
"subject": headers["subject"],
|
"subject": headers["subject"],
|
||||||
"date": str(headers["date"]),
|
"date": str(headers["date"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Process the email and publish to Redis
|
# Process the email and publish to Redis
|
||||||
success = read_email_and_publish_to_redis(
|
success = read_email_and_publish_to_redis(
|
||||||
email_message=email_message, mail_info=mail_info
|
email_message=email_message, mail_info=mail_info
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set success flag for the context manager
|
# Set success flag for the context manager
|
||||||
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)}")
|
||||||
# The context manager will mark the email as unread
|
# The context manager will mark the email as unread
|
||||||
@@ -213,8 +231,8 @@ def app():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("=== Starting Email Service with Redis Pub/Sub ===")
|
print("=== Starting Email Service with Redis Pub/Sub ===")
|
||||||
print(f"Publishing to channel: {REDIS_CHANNEL}")
|
print(f"Publishing to channel: {REDIS_CHANNEL}")
|
||||||
time.sleep(20) # Wait for 20 seconds to other services to kick in
|
time.sleep(20) # Wait for 20 seconds to other services to kick in
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
print("\n[EMAIL_SERVICE] Checking for new emails...")
|
print("\n[EMAIL_SERVICE] Checking for new emails...")
|
||||||
app()
|
app()
|
||||||
|
|||||||
@@ -18,14 +18,16 @@ 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:
|
||||||
data: Original message data from Redis
|
data: Original message data from Redis
|
||||||
collected_data_dict: Parsed data from Excel file
|
collected_data_dict: Parsed data from Excel file
|
||||||
filename: Name of the processed file
|
filename: Name of the processed file
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: Success status
|
bool: Success status
|
||||||
"""
|
"""
|
||||||
@@ -40,16 +42,18 @@ def publish_parsed_data_to_redis(data, collected_data_dict: list[dict], filename
|
|||||||
else:
|
else:
|
||||||
message["parsed"] = None
|
message["parsed"] = None
|
||||||
message["stage"] = "not found" # Mark as 'not found' if parsing failed
|
message["stage"] = "not found" # Mark as 'not found' if parsing failed
|
||||||
|
|
||||||
# Add processing timestamp
|
# Add processing timestamp
|
||||||
message["parsed_at"] = str(arrow.now())
|
message["parsed_at"] = str(arrow.now())
|
||||||
message["filename"] = filename
|
message["filename"] = filename
|
||||||
|
|
||||||
# Publish to Redis channel
|
# Publish to Redis channel
|
||||||
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}")
|
||||||
@@ -58,10 +62,10 @@ def publish_parsed_data_to_redis(data, collected_data_dict: list[dict], filename
|
|||||||
|
|
||||||
def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
|
def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
|
||||||
"""Parse Excel file data.
|
"""Parse Excel file data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
excel_frame: DataFrame containing Excel data
|
excel_frame: DataFrame containing Excel data
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[dict]: List of parsed data dictionaries
|
list[dict]: List of parsed data dictionaries
|
||||||
"""
|
"""
|
||||||
@@ -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
|
||||||
@@ -100,13 +110,13 @@ def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
|
|||||||
|
|
||||||
def process_message(message):
|
def process_message(message):
|
||||||
"""Process a message from Redis.
|
"""Process a message from Redis.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Message data from Redis subscriber
|
message: Message data from Redis subscriber
|
||||||
"""
|
"""
|
||||||
# Extract the message data
|
# Extract the message data
|
||||||
data = message["data"]
|
data = message["data"]
|
||||||
|
|
||||||
# If data is a string, parse it as JSON
|
# If data is a string, parse it as JSON
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
try:
|
try:
|
||||||
@@ -114,7 +124,7 @@ def process_message(message):
|
|||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"[PARSER_SERVICE] Error parsing message data: {e}")
|
print(f"[PARSER_SERVICE] Error parsing message data: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if stage is 'red' before processing
|
# Check if stage is 'red' before processing
|
||||||
if data.get("stage") == "red":
|
if data.get("stage") == "red":
|
||||||
try:
|
try:
|
||||||
@@ -122,91 +132,109 @@ def process_message(message):
|
|||||||
payload = data.get("payload")
|
payload = data.get("payload")
|
||||||
is_base64 = data.get("is_base64", False)
|
is_base64 = data.get("is_base64", False)
|
||||||
print(f"[PARSER_SERVICE] Processing file: {filename}")
|
print(f"[PARSER_SERVICE] Processing file: {filename}")
|
||||||
|
|
||||||
# Handle base64-encoded payload
|
# Handle base64-encoded payload
|
||||||
if is_base64 and isinstance(payload, str):
|
if is_base64 and isinstance(payload, str):
|
||||||
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
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# 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)),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Try each approach until one works
|
# Try each approach until one works
|
||||||
for i, approach in enumerate(approaches):
|
for i, approach in enumerate(approaches):
|
||||||
try:
|
try:
|
||||||
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)}")
|
||||||
|
|
||||||
# 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():
|
||||||
"""Main application function."""
|
"""Main application function."""
|
||||||
print("[PARSER_SERVICE] Starting Parser Service")
|
print("[PARSER_SERVICE] Starting Parser Service")
|
||||||
|
|
||||||
# Subscribe to the input channel
|
# Subscribe to the input channel
|
||||||
result = redis_pubsub.subscriber.subscribe(REDIS_CHANNEL_IN, process_message)
|
result = redis_pubsub.subscriber.subscribe(REDIS_CHANNEL_IN, process_message)
|
||||||
|
|
||||||
if result.status:
|
if result.status:
|
||||||
print(f"[PARSER_SERVICE] Subscribed to channel: {REDIS_CHANNEL_IN}")
|
print(f"[PARSER_SERVICE] Subscribed to channel: {REDIS_CHANNEL_IN}")
|
||||||
else:
|
else:
|
||||||
print(f"[PARSER_SERVICE] Subscribe error: {result.error}")
|
print(f"[PARSER_SERVICE] Subscribe error: {result.error}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Start listening for messages
|
# Start listening for messages
|
||||||
listen_result = redis_pubsub.subscriber.start_listening(in_thread=True)
|
listen_result = redis_pubsub.subscriber.start_listening(in_thread=True)
|
||||||
|
|
||||||
if listen_result.status:
|
if listen_result.status:
|
||||||
print("[PARSER_SERVICE] Listening for messages")
|
print("[PARSER_SERVICE] Listening for messages")
|
||||||
else:
|
else:
|
||||||
@@ -217,7 +245,7 @@ def app():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Initialize the app once
|
# Initialize the app once
|
||||||
app()
|
app()
|
||||||
|
|
||||||
# Keep the main thread alive
|
# Keep the main thread alive
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ def render_email_template(
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Render the HTML email template with the provided data.
|
Render the HTML email template with the provided data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
headers: List of column headers for the table
|
headers: List of column headers for the table
|
||||||
rows: List of data rows for the table
|
rows: List of data rows for the table
|
||||||
balance_error: Flag indicating if there's a balance discrepancy
|
balance_error: Flag indicating if there's a balance discrepancy
|
||||||
bank_balance: Current bank balance formatted as string
|
bank_balance: Current bank balance formatted as string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Rendered HTML template as string
|
Rendered HTML template as string
|
||||||
"""
|
"""
|
||||||
@@ -25,7 +25,7 @@ def render_email_template(
|
|||||||
# Look for template in ServiceDepends directory
|
# Look for template in ServiceDepends directory
|
||||||
env = Environment(loader=FileSystemLoader("/"))
|
env = Environment(loader=FileSystemLoader("/"))
|
||||||
template = env.get_template("template_accounts.html")
|
template = env.get_template("template_accounts.html")
|
||||||
|
|
||||||
# Render template with variables
|
# Render template with variables
|
||||||
return template.render(
|
return template.render(
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@@ -35,97 +35,109 @@ 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
|
||||||
|
|
||||||
|
|
||||||
def send_email_to_given_address(send_to: str, html_template: str) -> bool:
|
def send_email_to_given_address(send_to: str, html_template: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Send email with the rendered HTML template to the specified address.
|
Send email with the rendered HTML template to the specified address.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
send_to: Email address of the recipient
|
send_to: Email address of the recipient
|
||||||
html_template: Rendered HTML template content
|
html_template: Rendered HTML template content
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating if the email was sent successfully
|
Boolean indicating if the email was sent successfully
|
||||||
"""
|
"""
|
||||||
today = arrow.now()
|
today = arrow.now()
|
||||||
subject = f"{str(today.date())} Gunes Apt. Cari Durum Bilgilendirme Raporu"
|
subject = f"{str(today.date())} Gunes Apt. Cari Durum Bilgilendirme Raporu"
|
||||||
|
|
||||||
# Create email parameters using EmailSendModel
|
# Create email parameters using EmailSendModel
|
||||||
email_params = EmailSendModel(
|
email_params = EmailSendModel(
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def set_account_records_to_send_email() -> bool:
|
def set_account_records_to_send_email() -> bool:
|
||||||
"""
|
"""
|
||||||
Retrieve account records from the database, format them, and send an email report.
|
Retrieve account records from the database, format them, and send an email report.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
from app import set_account_records_to_send_email
|
from app import set_account_records_to_send_email
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating if the process completed successfully
|
Boolean indicating if the process completed successfully
|
||||||
"""
|
"""
|
||||||
# Get database session and retrieve records
|
# Get database session and retrieve records
|
||||||
with AccountRecords.new_session() as db_session:
|
with AccountRecords.new_session() as db_session:
|
||||||
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()
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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.process_comment,
|
record.bank_date.strftime("%d/%m/%Y %H:%M"),
|
||||||
f"{record.currency_value:,.2f}",
|
record.process_comment,
|
||||||
f"{record.bank_balance:,.2f}"
|
f"{record.currency_value:,.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"
|
||||||
|
|
||||||
# Render email template
|
# Render email template
|
||||||
html_template = render_email_template(
|
html_template = render_email_template(
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@@ -133,12 +145,13 @@ def set_account_records_to_send_email() -> bool:
|
|||||||
balance_error=balance_error,
|
balance_error=balance_error,
|
||||||
bank_balance=f"{last_bank_balance:,.2f}",
|
bank_balance=f"{last_bank_balance:,.2f}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Send the email
|
# Send the email
|
||||||
return send_email_to_given_address(send_to=send_to, html_template=html_template)
|
return send_email_to_given_address(send_to=send_to, html_template=html_template)
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
29
BankServices/RoutineEmailService/entrypoint.sh
Normal file
29
BankServices/RoutineEmailService/entrypoint.sh
Normal 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
|
||||||
24
BankServices/RoutineEmailService/run_app.sh
Normal file
24
BankServices/RoutineEmailService/run_app.sh
Normal 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
|
||||||
@@ -12,13 +12,13 @@ def render_email_template(
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Render the HTML email template with the provided data.
|
Render the HTML email template with the provided data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
headers: List of column headers for the table
|
headers: List of column headers for the table
|
||||||
rows: List of data rows for the table
|
rows: List of data rows for the table
|
||||||
balance_error: Flag indicating if there's a balance discrepancy
|
balance_error: Flag indicating if there's a balance discrepancy
|
||||||
bank_balance: Current bank balance formatted as string
|
bank_balance: Current bank balance formatted as string
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Rendered HTML template as string
|
Rendered HTML template as string
|
||||||
"""
|
"""
|
||||||
@@ -26,7 +26,7 @@ def render_email_template(
|
|||||||
# Look for template in ServiceDepends directory
|
# Look for template in ServiceDepends directory
|
||||||
env = Environment(loader=FileSystemLoader("/"))
|
env = Environment(loader=FileSystemLoader("/"))
|
||||||
template = env.get_template("template_accounts.html")
|
template = env.get_template("template_accounts.html")
|
||||||
|
|
||||||
return template.render(
|
return template.render(
|
||||||
headers=headers,
|
headers=headers,
|
||||||
rows=rows,
|
rows=rows,
|
||||||
@@ -35,32 +35,34 @@ 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.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
send_to: Email address of the recipient
|
send_to: Email address of the recipient
|
||||||
html_template: Rendered HTML template content
|
html_template: Rendered HTML template content
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating if the email was sent successfully
|
Boolean indicating if the email was sent successfully
|
||||||
"""
|
"""
|
||||||
today = arrow.now()
|
today = arrow.now()
|
||||||
subject = f"{str(today.date())} Gunes Apt. Cari Durum Kayıt Giriş Raporu"
|
subject = f"{str(today.date())} Gunes Apt. Cari Durum Kayıt Giriş Raporu"
|
||||||
|
|
||||||
# Create email parameters using EmailSendModel
|
# Create email parameters using EmailSendModel
|
||||||
email_params = EmailSendModel(
|
email_params = EmailSendModel(
|
||||||
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:
|
||||||
# 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:
|
||||||
@@ -69,17 +71,17 @@ 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
|
||||||
|
|
||||||
|
|
||||||
def process_unsent_email_records() -> bool:
|
def process_unsent_email_records() -> bool:
|
||||||
"""
|
"""
|
||||||
Process account records that haven't been emailed yet.
|
Process account records that haven't been emailed yet.
|
||||||
|
|
||||||
Finds records with is_email_send=False, formats them into an email,
|
Finds records with is_email_send=False, formats them into an email,
|
||||||
sends the email, and updates the records as sent if successful.
|
sends the email, and updates the records as sent if successful.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if email was sent successfully, False otherwise
|
bool: True if email was sent successfully, False otherwise
|
||||||
"""
|
"""
|
||||||
@@ -87,42 +89,55 @@ 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.is_email_send == False,
|
AccountRecords.filter_all(
|
||||||
db=db_session,
|
AccountRecords.is_email_send == False,
|
||||||
).query.order_by(AccountRecords.bank_date.asc()).limit(20)
|
db=db_session,
|
||||||
|
)
|
||||||
|
.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:
|
||||||
print("No unsent email records found")
|
print("No unsent email records found")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Get the IDs of the records we're processing
|
# Get the IDs of the records we're processing
|
||||||
record_ids = [record.id for record in account_records]
|
record_ids = [record.id for record in account_records]
|
||||||
print(f"Found {len(account_records)} unsent email records")
|
print(f"Found {len(account_records)} unsent email records")
|
||||||
|
|
||||||
# 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.process_comment,
|
record.bank_date.strftime("%d/%m/%Y %H:%M"),
|
||||||
f"{record.currency_value:,.2f}",
|
record.process_comment,
|
||||||
f"{record.bank_balance:,.2f}"
|
f"{record.currency_value:,.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"
|
||||||
|
|
||||||
# Render email template
|
# Render email template
|
||||||
html_template = render_email_template(
|
html_template = render_email_template(
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@@ -130,32 +145,35 @@ def process_unsent_email_records() -> bool:
|
|||||||
balance_error=False,
|
balance_error=False,
|
||||||
bank_balance=f"{last_bank_balance:,.2f}",
|
bank_balance=f"{last_bank_balance:,.2f}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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
|
||||||
update_query.update({"is_email_send": True}, synchronize_session=False)
|
update_query.update({"is_email_send": True}, synchronize_session=False)
|
||||||
AccountRecords.save(db_session)
|
AccountRecords.save(db_session)
|
||||||
print(f"Successfully marked {len(account_records)} records as sent")
|
print(f"Successfully marked {len(account_records)} records as sent")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
print("Email sending failed, records not updated")
|
print("Email sending failed, records not updated")
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("Starting Email Sender Service")
|
print("Starting Email Sender Service")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
result = process_unsent_email_records()
|
result = process_unsent_email_records()
|
||||||
@@ -165,7 +183,7 @@ if __name__ == "__main__":
|
|||||||
print("No emails sent in this iteration")
|
print("No emails sent in this iteration")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Unexpected error in main loop: {e}")
|
print(f"Unexpected error in main loop: {e}")
|
||||||
|
|
||||||
# Sleep for 60 seconds before next check
|
# Sleep for 60 seconds before next check
|
||||||
print("Sleeping for 60 seconds")
|
print("Sleeping for 60 seconds")
|
||||||
time.sleep(60)
|
time.sleep(60)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Config:
|
|||||||
|
|
||||||
class EmailConfig:
|
class EmailConfig:
|
||||||
|
|
||||||
EMAIL_HOST: str = os.getenv("EMAIL_HOST", "10.10.2.34")
|
EMAIL_HOST: str = os.getenv("EMAIL_HOST", "10.10.2.34")
|
||||||
EMAIL_USERNAME: str = Config.EMAIL_SENDER_USERNAME
|
EMAIL_USERNAME: str = Config.EMAIL_SENDER_USERNAME
|
||||||
EMAIL_PASSWORD: str = Config.EMAIL_PASSWORD
|
EMAIL_PASSWORD: str = Config.EMAIL_PASSWORD
|
||||||
EMAIL_PORT: int = Config.EMAIL_SEND_PORT
|
EMAIL_PORT: int = Config.EMAIL_SEND_PORT
|
||||||
|
|||||||
@@ -18,28 +18,30 @@ delimiter = "|"
|
|||||||
|
|
||||||
def publish_written_data_to_redis(data: Dict[str, Any], file_name: str) -> bool:
|
def publish_written_data_to_redis(data: Dict[str, Any], file_name: str) -> bool:
|
||||||
"""Publish written data status to Redis.
|
"""Publish written data status to Redis.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: Original message data from Redis
|
data: Original message data from Redis
|
||||||
file_name: Name of the processed file
|
file_name: Name of the processed file
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: Success status
|
bool: Success status
|
||||||
"""
|
"""
|
||||||
# Create a copy of the original message to preserve metadata
|
# Create a copy of the original message to preserve metadata
|
||||||
message = data.copy() if isinstance(data, dict) else {}
|
message = data.copy() if isinstance(data, dict) else {}
|
||||||
|
|
||||||
# Update stage to 'written'
|
# Update stage to 'written'
|
||||||
message["stage"] = "written"
|
message["stage"] = "written"
|
||||||
|
|
||||||
# Add processing timestamp
|
# Add processing timestamp
|
||||||
message["written_at"] = str(arrow.now())
|
message["written_at"] = str(arrow.now())
|
||||||
|
|
||||||
# Publish to Redis channel
|
# Publish to Redis channel
|
||||||
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}")
|
||||||
@@ -48,10 +50,10 @@ def publish_written_data_to_redis(data: Dict[str, Any], file_name: str) -> bool:
|
|||||||
|
|
||||||
def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> bool:
|
def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> bool:
|
||||||
"""Write parsed data to account records database.
|
"""Write parsed data to account records database.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data_dict: Parsed data dictionary
|
data_dict: Parsed data dictionary
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if record was created or already exists, False on error
|
bool: True if record was created or already exists, False on error
|
||||||
"""
|
"""
|
||||||
@@ -61,8 +63,8 @@ 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"]))
|
||||||
data_dict["bank_date_w"] = bank_date.weekday()
|
data_dict["bank_date_w"] = bank_date.weekday()
|
||||||
@@ -70,7 +72,7 @@ def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> boo
|
|||||||
data_dict["bank_date_d"] = bank_date.day
|
data_dict["bank_date_d"] = bank_date.day
|
||||||
data_dict["bank_date_y"] = bank_date.year
|
data_dict["bank_date_y"] = bank_date.year
|
||||||
data_dict["bank_date"] = str(bank_date)
|
data_dict["bank_date"] = str(bank_date)
|
||||||
|
|
||||||
# Add build information if available
|
# Add build information if available
|
||||||
if build_iban := BuildIbans.filter_by_one(
|
if build_iban := BuildIbans.filter_by_one(
|
||||||
iban=data_dict["iban"], db=db_session
|
iban=data_dict["iban"], db=db_session
|
||||||
@@ -81,7 +83,7 @@ def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> boo
|
|||||||
"build_uu_id": build_iban.build_uu_id,
|
"build_uu_id": build_iban.build_uu_id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create new record or find existing one using specific fields for matching
|
# Create new record or find existing one using specific fields for matching
|
||||||
new_account_record = AccountRecords.find_or_create(
|
new_account_record = AccountRecords.find_or_create(
|
||||||
db=db_session,
|
db=db_session,
|
||||||
@@ -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)}")
|
||||||
@@ -108,13 +114,13 @@ def write_parsed_data_to_account_records(data_dict: dict, file_name: str) -> boo
|
|||||||
|
|
||||||
def process_message(message):
|
def process_message(message):
|
||||||
"""Process a message from Redis.
|
"""Process a message from Redis.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Message data from Redis subscriber
|
message: Message data from Redis subscriber
|
||||||
"""
|
"""
|
||||||
# Extract the message data
|
# Extract the message data
|
||||||
data = message["data"]
|
data = message["data"]
|
||||||
|
|
||||||
# If data is a string, parse it as JSON
|
# If data is a string, parse it as JSON
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
try:
|
try:
|
||||||
@@ -122,51 +128,55 @@ def process_message(message):
|
|||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"[WRITER_SERVICE] Error parsing message data: {e}")
|
print(f"[WRITER_SERVICE] Error parsing message data: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if stage is 'parsed' before processing
|
# Check if stage is 'parsed' before processing
|
||||||
if data.get("stage") == "parsed":
|
if data.get("stage") == "parsed":
|
||||||
try:
|
try:
|
||||||
file_name = data.get("filename")
|
file_name = data.get("filename")
|
||||||
parsed_data = data.get("parsed")
|
parsed_data = data.get("parsed")
|
||||||
|
|
||||||
print(f"[WRITER_SERVICE] Processing file: {file_name}")
|
print(f"[WRITER_SERVICE] Processing file: {file_name}")
|
||||||
|
|
||||||
if not parsed_data:
|
if not parsed_data:
|
||||||
print(f"[WRITER_SERVICE] No parsed data found for {file_name}")
|
print(f"[WRITER_SERVICE] No parsed data found for {file_name}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
# Publish status update to Redis if all records were processed
|
# Publish status update to Redis if all records were processed
|
||||||
if success:
|
if success:
|
||||||
publish_written_data_to_redis(data=data, file_name=file_name)
|
publish_written_data_to_redis(data=data, file_name=file_name)
|
||||||
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():
|
||||||
"""Main application function."""
|
"""Main application function."""
|
||||||
print("[WRITER_SERVICE] Starting Writer Service")
|
print("[WRITER_SERVICE] Starting Writer Service")
|
||||||
|
|
||||||
# Subscribe to the input channel
|
# Subscribe to the input channel
|
||||||
result = redis_pubsub.subscriber.subscribe(REDIS_CHANNEL_IN, process_message)
|
result = redis_pubsub.subscriber.subscribe(REDIS_CHANNEL_IN, process_message)
|
||||||
|
|
||||||
if result.status:
|
if result.status:
|
||||||
print(f"[WRITER_SERVICE] Subscribed to channel: {REDIS_CHANNEL_IN}")
|
print(f"[WRITER_SERVICE] Subscribed to channel: {REDIS_CHANNEL_IN}")
|
||||||
else:
|
else:
|
||||||
print(f"[WRITER_SERVICE] Subscribe error: {result.error}")
|
print(f"[WRITER_SERVICE] Subscribe error: {result.error}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Start listening for messages
|
# Start listening for messages
|
||||||
listen_result = redis_pubsub.subscriber.start_listening(in_thread=True)
|
listen_result = redis_pubsub.subscriber.start_listening(in_thread=True)
|
||||||
|
|
||||||
if listen_result.status:
|
if listen_result.status:
|
||||||
print("[WRITER_SERVICE] Listening for messages")
|
print("[WRITER_SERVICE] Listening for messages")
|
||||||
else:
|
else:
|
||||||
@@ -177,7 +187,7 @@ def app():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Initialize the app once
|
# Initialize the app once
|
||||||
app()
|
app()
|
||||||
|
|
||||||
# Keep the main thread alive
|
# Keep the main thread alive
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -27,17 +27,27 @@ 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]
|
||||||
self.email_sender.send(
|
|
||||||
subject=params.subject,
|
# Ensure connection is established before sending
|
||||||
receivers=receivers,
|
try:
|
||||||
text=params.text + f" : Gonderilen [{str(receivers)}]",
|
# Check if connection exists, if not establish it
|
||||||
html=params.html,
|
if not hasattr(self.email_sender, '_connected') or not self.email_sender._connected:
|
||||||
cc=params.cc,
|
self.email_sender.connect()
|
||||||
bcc=params.bcc,
|
|
||||||
headers=params.headers or {},
|
self.email_sender.send(
|
||||||
attachments=params.attachments or {},
|
subject=params.subject,
|
||||||
)
|
receivers=receivers,
|
||||||
return True
|
text=params.text + f" : Gonderilen [{str(receivers)}]",
|
||||||
|
html=params.html,
|
||||||
|
cc=params.cc,
|
||||||
|
bcc=params.bcc,
|
||||||
|
headers=params.headers or {},
|
||||||
|
attachments=params.attachments or {},
|
||||||
|
)
|
||||||
|
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:
|
||||||
email_sender.close()
|
# Only close if connection was successfully established
|
||||||
|
if connection_established:
|
||||||
|
try:
|
||||||
|
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:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class MongoDBHandler:
|
|||||||
if not hasattr(self, "_initialized") or not self._initialized:
|
if not hasattr(self, "_initialized") or not self._initialized:
|
||||||
self._debug_mode = debug_mode
|
self._debug_mode = debug_mode
|
||||||
self._mock_mode = mock_mode
|
self._mock_mode = mock_mode
|
||||||
|
|
||||||
if mock_mode:
|
if mock_mode:
|
||||||
# In mock mode, we don't need a real connection string
|
# In mock mode, we don't need a real connection string
|
||||||
self.uri = "mongodb://mock:27017/mockdb"
|
self.uri = "mongodb://mock:27017/mockdb"
|
||||||
@@ -76,7 +76,7 @@ class MongoDBHandler:
|
|||||||
# Use the configured connection string with authentication
|
# Use the configured connection string with authentication
|
||||||
self.uri = mongo_configs.url
|
self.uri = mongo_configs.url
|
||||||
print(f"Connecting to MongoDB: {self.uri}")
|
print(f"Connecting to MongoDB: {self.uri}")
|
||||||
|
|
||||||
# Define MongoDB client options with increased timeouts for better reliability
|
# Define MongoDB client options with increased timeouts for better reliability
|
||||||
self.client_options = {
|
self.client_options = {
|
||||||
"maxPoolSize": 5,
|
"maxPoolSize": 5,
|
||||||
@@ -132,11 +132,13 @@ class CollectionContext:
|
|||||||
# If we're in mock mode, return a mock collection immediately
|
# If we're in mock mode, return a mock collection immediately
|
||||||
if self.db_handler._mock_mode:
|
if self.db_handler._mock_mode:
|
||||||
return self._create_mock_collection()
|
return self._create_mock_collection()
|
||||||
|
|
||||||
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
|
||||||
db_name = mongo_configs.DB
|
db_name = mongo_configs.DB
|
||||||
@@ -148,7 +150,7 @@ class CollectionContext:
|
|||||||
except Exception:
|
except Exception:
|
||||||
db_name = mongo_configs.DB
|
db_name = mongo_configs.DB
|
||||||
print(f"Using fallback database '{db_name}'")
|
print(f"Using fallback database '{db_name}'")
|
||||||
|
|
||||||
self.collection = self.client[db_name][self.collection_name]
|
self.collection = self.client[db_name][self.collection_name]
|
||||||
|
|
||||||
# Enhance collection methods with retry capabilities
|
# Enhance collection methods with retry capabilities
|
||||||
@@ -159,13 +161,17 @@ class CollectionContext:
|
|||||||
if "Authentication failed" in str(e):
|
if "Authentication failed" in str(e):
|
||||||
print(f"MongoDB authentication error: {e}")
|
print(f"MongoDB authentication error: {e}")
|
||||||
print("Attempting to reconnect with direct connection...")
|
print("Attempting to reconnect with direct connection...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 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:
|
||||||
@@ -181,54 +187,56 @@ class CollectionContext:
|
|||||||
if self.client:
|
if self.client:
|
||||||
self.client.close()
|
self.client.close()
|
||||||
self.client = None
|
self.client = None
|
||||||
|
|
||||||
return self._create_mock_collection()
|
return self._create_mock_collection()
|
||||||
|
|
||||||
def _create_mock_collection(self):
|
def _create_mock_collection(self):
|
||||||
"""
|
"""
|
||||||
Create a mock collection for testing or graceful degradation.
|
Create a mock collection for testing or graceful degradation.
|
||||||
This prevents the application from crashing when MongoDB is unavailable.
|
This prevents the application from crashing when MongoDB is unavailable.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A mock MongoDB collection with simulated behaviors
|
A mock MongoDB collection with simulated behaviors
|
||||||
"""
|
"""
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
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:
|
||||||
self.db_handler._mock_storage[self.collection_name] = []
|
self.db_handler._mock_storage[self.collection_name] = []
|
||||||
|
|
||||||
mock_collection = MagicMock()
|
mock_collection = MagicMock()
|
||||||
mock_data = self.db_handler._mock_storage[self.collection_name]
|
mock_data = self.db_handler._mock_storage[self.collection_name]
|
||||||
|
|
||||||
# Define behavior for find operations
|
# Define behavior for find operations
|
||||||
def mock_find(query=None, *args, **kwargs):
|
def mock_find(query=None, *args, **kwargs):
|
||||||
# Simple implementation that returns all documents
|
# Simple implementation that returns all documents
|
||||||
return mock_data
|
return mock_data
|
||||||
|
|
||||||
def mock_find_one(query=None, *args, **kwargs):
|
def mock_find_one(query=None, *args, **kwargs):
|
||||||
# Simple implementation that returns the first matching document
|
# Simple implementation that returns the first matching document
|
||||||
if not mock_data:
|
if not mock_data:
|
||||||
return None
|
return None
|
||||||
return mock_data[0]
|
return mock_data[0]
|
||||||
|
|
||||||
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):
|
||||||
inserted_ids = []
|
inserted_ids = []
|
||||||
for doc in documents:
|
for doc in documents:
|
||||||
@@ -237,40 +245,40 @@ class CollectionContext:
|
|||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.inserted_ids = inserted_ids
|
result.inserted_ids = inserted_ids
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def mock_update_one(query, update, *args, **kwargs):
|
def mock_update_one(query, update, *args, **kwargs):
|
||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.modified_count = 1
|
result.modified_count = 1
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def mock_update_many(query, update, *args, **kwargs):
|
def mock_update_many(query, update, *args, **kwargs):
|
||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.modified_count = len(mock_data)
|
result.modified_count = len(mock_data)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def mock_delete_one(query, *args, **kwargs):
|
def mock_delete_one(query, *args, **kwargs):
|
||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.deleted_count = 1
|
result.deleted_count = 1
|
||||||
if mock_data:
|
if mock_data:
|
||||||
mock_data.pop(0) # Just remove the first item for simplicity
|
mock_data.pop(0) # Just remove the first item for simplicity
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def mock_delete_many(query, *args, **kwargs):
|
def mock_delete_many(query, *args, **kwargs):
|
||||||
count = len(mock_data)
|
count = len(mock_data)
|
||||||
mock_data.clear()
|
mock_data.clear()
|
||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.deleted_count = count
|
result.deleted_count = count
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def mock_count_documents(query, *args, **kwargs):
|
def mock_count_documents(query, *args, **kwargs):
|
||||||
return len(mock_data)
|
return len(mock_data)
|
||||||
|
|
||||||
def mock_aggregate(pipeline, *args, **kwargs):
|
def mock_aggregate(pipeline, *args, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def mock_create_index(keys, **kwargs):
|
def mock_create_index(keys, **kwargs):
|
||||||
return f"mock_index_{keys}"
|
return f"mock_index_{keys}"
|
||||||
|
|
||||||
# Assign the mock implementations
|
# Assign the mock implementations
|
||||||
mock_collection.find.side_effect = mock_find
|
mock_collection.find.side_effect = mock_find
|
||||||
mock_collection.find_one.side_effect = mock_find_one
|
mock_collection.find_one.side_effect = mock_find_one
|
||||||
@@ -283,10 +291,10 @@ class CollectionContext:
|
|||||||
mock_collection.count_documents.side_effect = mock_count_documents
|
mock_collection.count_documents.side_effect = mock_count_documents
|
||||||
mock_collection.aggregate.side_effect = mock_aggregate
|
mock_collection.aggregate.side_effect = mock_aggregate
|
||||||
mock_collection.create_index.side_effect = mock_create_index
|
mock_collection.create_index.side_effect = mock_create_index
|
||||||
|
|
||||||
# Add retry capabilities to the mock collection
|
# Add retry capabilities to the mock collection
|
||||||
self._add_retry_capabilities_to_mock(mock_collection)
|
self._add_retry_capabilities_to_mock(mock_collection)
|
||||||
|
|
||||||
self.collection = mock_collection
|
self.collection = mock_collection
|
||||||
return self.collection
|
return self.collection
|
||||||
|
|
||||||
@@ -322,17 +330,25 @@ class CollectionContext:
|
|||||||
"""
|
"""
|
||||||
Add retry capabilities to mock collection methods.
|
Add retry capabilities to mock collection methods.
|
||||||
This is a simplified version that just wraps the mock methods.
|
This is a simplified version that just wraps the mock methods.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
mock_collection: The mock collection to enhance
|
mock_collection: The mock collection to enhance
|
||||||
"""
|
"""
|
||||||
# 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
|
||||||
for method_name in methods:
|
for method_name in methods:
|
||||||
if hasattr(mock_collection, method_name):
|
if hasattr(mock_collection, method_name):
|
||||||
@@ -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):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ def test_basic_crud_operations():
|
|||||||
# First, clear any existing data
|
# First, clear any existing data
|
||||||
users_collection.delete_many({})
|
users_collection.delete_many({})
|
||||||
print("Cleared existing data")
|
print("Cleared existing data")
|
||||||
|
|
||||||
# Insert multiple documents
|
# Insert multiple documents
|
||||||
insert_result = users_collection.insert_many(
|
insert_result = users_collection.insert_many(
|
||||||
[
|
[
|
||||||
@@ -58,7 +58,7 @@ def test_basic_crud_operations():
|
|||||||
condition2 = admin_users and admin_users[0].get("username") == "jane"
|
condition2 = admin_users and admin_users[0].get("username") == "jane"
|
||||||
condition3 = update_result.modified_count == 2
|
condition3 = update_result.modified_count == 2
|
||||||
condition4 = delete_result.deleted_count == 1
|
condition4 = delete_result.deleted_count == 1
|
||||||
|
|
||||||
print(f"Condition 1 (admin count): {condition1}")
|
print(f"Condition 1 (admin count): {condition1}")
|
||||||
print(f"Condition 2 (admin is jane): {condition2}")
|
print(f"Condition 2 (admin is jane): {condition2}")
|
||||||
print(f"Condition 3 (updated 2 users): {condition3}")
|
print(f"Condition 3 (updated 2 users): {condition3}")
|
||||||
@@ -80,7 +80,7 @@ def test_nested_documents():
|
|||||||
# Clear any existing data
|
# Clear any existing data
|
||||||
products_collection.delete_many({})
|
products_collection.delete_many({})
|
||||||
print("Cleared existing data")
|
print("Cleared existing data")
|
||||||
|
|
||||||
# Insert a product with nested data
|
# Insert a product with nested data
|
||||||
insert_result = products_collection.insert_one(
|
insert_result = products_collection.insert_one(
|
||||||
{
|
{
|
||||||
@@ -110,15 +110,17 @@ 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}")
|
||||||
print(f"Condition 3 (update modified 1 doc): {condition3}")
|
print(f"Condition 3 (update modified 1 doc): {condition3}")
|
||||||
@@ -140,7 +142,7 @@ def test_array_operations():
|
|||||||
# Clear any existing data
|
# Clear any existing data
|
||||||
orders_collection.delete_many({})
|
orders_collection.delete_many({})
|
||||||
print("Cleared existing data")
|
print("Cleared existing data")
|
||||||
|
|
||||||
# Insert an order with array of items
|
# Insert an order with array of items
|
||||||
insert_result = orders_collection.insert_one(
|
insert_result = orders_collection.insert_one(
|
||||||
{
|
{
|
||||||
@@ -170,10 +172,12 @@ def test_array_operations():
|
|||||||
# Verify the update
|
# Verify the update
|
||||||
updated_order = orders_collection.find_one({"order_id": "ORD001"})
|
updated_order = orders_collection.find_one({"order_id": "ORD001"})
|
||||||
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,9 +185,13 @@ 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}")
|
||||||
print(f"Condition 3 (order has 3 items): {condition3}")
|
print(f"Condition 3 (order has 3 items): {condition3}")
|
||||||
@@ -205,7 +213,7 @@ def test_aggregation():
|
|||||||
# Clear any existing data
|
# Clear any existing data
|
||||||
sales_collection.delete_many({})
|
sales_collection.delete_many({})
|
||||||
print("Cleared existing data")
|
print("Cleared existing data")
|
||||||
|
|
||||||
# Insert sample sales data
|
# Insert sample sales data
|
||||||
insert_result = sales_collection.insert_many(
|
insert_result = sales_collection.insert_many(
|
||||||
[
|
[
|
||||||
@@ -219,13 +227,13 @@ 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
|
||||||
sales_summary = list(sales_collection.aggregate(pipeline))
|
sales_summary = list(sales_collection.aggregate(pipeline))
|
||||||
print(f"Aggregation returned {len(sales_summary)} results")
|
print(f"Aggregation returned {len(sales_summary)} results")
|
||||||
|
|
||||||
# Print the results for debugging
|
# Print the results for debugging
|
||||||
for item in sales_summary:
|
for item in sales_summary:
|
||||||
print(f"Product: {item.get('_id')}, Total: {item.get('total')}")
|
print(f"Product: {item.get('_id')}, Total: {item.get('total')}")
|
||||||
@@ -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,10 +250,11 @@ 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
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Condition 1 (3 summary items): {condition1}")
|
print(f"Condition 1 (3 summary items): {condition1}")
|
||||||
print(f"Condition 2 (laptop total correct): {condition2}")
|
print(f"Condition 2 (laptop total correct): {condition2}")
|
||||||
print(f"Condition 3 (mouse total correct): {condition3}")
|
print(f"Condition 3 (mouse total correct): {condition3}")
|
||||||
@@ -325,35 +335,37 @@ 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
|
||||||
updated_product = products_collection.find_one({"name": "Cheap Mouse"})
|
updated_product = products_collection.find_one({"name": "Cheap Mouse"})
|
||||||
|
|
||||||
# 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')}")
|
||||||
print(f"Updated product discount: {updated_product.get('discount')}")
|
print(f"Updated product discount: {updated_product.get('discount')}")
|
||||||
|
|
||||||
# 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
|
||||||
@@ -368,48 +380,51 @@ def run_concurrent_operation_test(num_threads=100):
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
print(f"\nStarting concurrent operation test with {num_threads} threads...")
|
print(f"\nStarting concurrent operation test with {num_threads} threads...")
|
||||||
|
|
||||||
# Results tracking
|
# Results tracking
|
||||||
results = {"passed": 0, "failed": 0, "errors": []}
|
results = {"passed": 0, "failed": 0, "errors": []}
|
||||||
results_lock = threading.Lock()
|
results_lock = threading.Lock()
|
||||||
|
|
||||||
def worker(thread_id):
|
def worker(thread_id):
|
||||||
# Create a unique collection name for this thread
|
# Create a unique collection name for this thread
|
||||||
collection_name = f"concurrent_test_{thread_id}"
|
collection_name = f"concurrent_test_{thread_id}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Generate unique data for this thread
|
# Generate unique data for this thread
|
||||||
unique_id = str(uuid.uuid4())
|
unique_id = str(uuid.uuid4())
|
||||||
|
|
||||||
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,
|
{
|
||||||
"uuid": unique_id,
|
"thread_id": thread_id,
|
||||||
"timestamp": time.time()
|
"uuid": unique_id,
|
||||||
})
|
"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
|
||||||
updated_doc = collection.find_one({"thread_id": thread_id})
|
updated_doc = collection.find_one({"thread_id": thread_id})
|
||||||
|
|
||||||
# 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:
|
||||||
if success:
|
if success:
|
||||||
@@ -421,15 +436,15 @@ def run_concurrent_operation_test(num_threads=100):
|
|||||||
with results_lock:
|
with results_lock:
|
||||||
results["failed"] += 1
|
results["failed"] += 1
|
||||||
results["errors"].append(f"Thread {thread_id} exception: {str(e)}")
|
results["errors"].append(f"Thread {thread_id} exception: {str(e)}")
|
||||||
|
|
||||||
# Create and start threads using a thread pool
|
# Create and start threads using a thread pool
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||||
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
||||||
|
|
||||||
# Calculate execution time
|
# Calculate execution time
|
||||||
execution_time = time.time() - start_time
|
execution_time = time.time() - start_time
|
||||||
|
|
||||||
# Print results
|
# Print results
|
||||||
print(f"\nConcurrent Operation Test Results:")
|
print(f"\nConcurrent Operation Test Results:")
|
||||||
print(f"Total threads: {num_threads}")
|
print(f"Total threads: {num_threads}")
|
||||||
@@ -437,14 +452,16 @@ def run_concurrent_operation_test(num_threads=100):
|
|||||||
print(f"Failed: {results['failed']}")
|
print(f"Failed: {results['failed']}")
|
||||||
print(f"Execution time: {execution_time:.2f} seconds")
|
print(f"Execution time: {execution_time:.2f} seconds")
|
||||||
print(f"Operations per second: {num_threads / execution_time:.2f}")
|
print(f"Operations per second: {num_threads / execution_time:.2f}")
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
return results["failed"] == 0
|
return results["failed"] == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -493,10 +510,10 @@ def run_all_tests():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mongo_handler = MongoDBHandler()
|
mongo_handler = MongoDBHandler()
|
||||||
|
|
||||||
# Run standard tests first
|
# Run standard tests first
|
||||||
passed, failed = run_all_tests()
|
passed, failed = run_all_tests()
|
||||||
|
|
||||||
# If all tests pass, run the concurrent operation test
|
# If all tests pass, run the concurrent operation test
|
||||||
if failed == 0:
|
if failed == 0:
|
||||||
run_concurrent_operation_test(10000)
|
run_concurrent_operation_test(10000)
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
"""
|
"""
|
||||||
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."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize with a direct MongoDB URI."""
|
"""Initialize with a direct MongoDB URI."""
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
@@ -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,35 +32,36 @@ 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...")
|
||||||
|
|
||||||
# Create a local handler
|
# Create a local handler
|
||||||
local_handler = create_local_handler()
|
local_handler = create_local_handler()
|
||||||
|
|
||||||
# Add connection tracking to the handler
|
# Add connection tracking to the handler
|
||||||
local_handler._open_connections = 0
|
local_handler._open_connections = 0
|
||||||
|
|
||||||
# Modify the CollectionContext class to track connections
|
# Modify the CollectionContext class to track connections
|
||||||
original_enter = CollectionContext.__enter__
|
original_enter = CollectionContext.__enter__
|
||||||
original_exit = CollectionContext.__exit__
|
original_exit = CollectionContext.__exit__
|
||||||
|
|
||||||
def tracked_enter(self):
|
def tracked_enter(self):
|
||||||
result = original_enter(self)
|
result = original_enter(self)
|
||||||
self.db_handler._open_connections += 1
|
self.db_handler._open_connections += 1
|
||||||
print(f"Connection opened. Total open: {self.db_handler._open_connections}")
|
print(f"Connection opened. Total open: {self.db_handler._open_connections}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def tracked_exit(self, exc_type, exc_val, exc_tb):
|
def tracked_exit(self, exc_type, exc_val, exc_tb):
|
||||||
self.db_handler._open_connections -= 1
|
self.db_handler._open_connections -= 1
|
||||||
print(f"Connection closed. Total open: {self.db_handler._open_connections}")
|
print(f"Connection closed. Total open: {self.db_handler._open_connections}")
|
||||||
return original_exit(self, exc_type, exc_val, exc_tb)
|
return original_exit(self, exc_type, exc_val, exc_tb)
|
||||||
|
|
||||||
# Apply the tracking methods
|
# Apply the tracking methods
|
||||||
CollectionContext.__enter__ = tracked_enter
|
CollectionContext.__enter__ = tracked_enter
|
||||||
CollectionContext.__exit__ = tracked_exit
|
CollectionContext.__exit__ = tracked_exit
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Test with multiple operations
|
# Test with multiple operations
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
@@ -72,18 +76,19 @@ def test_connection_monitoring():
|
|||||||
print(f"Operation failed: {e}")
|
print(f"Operation failed: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Connection failed: {e}")
|
print(f"Connection failed: {e}")
|
||||||
|
|
||||||
# Final connection count
|
# Final connection count
|
||||||
print(f"\nFinal open connections: {local_handler._open_connections}")
|
print(f"\nFinal open connections: {local_handler._open_connections}")
|
||||||
if local_handler._open_connections == 0:
|
if local_handler._open_connections == 0:
|
||||||
print("✅ All connections were properly closed")
|
print("✅ All connections were properly closed")
|
||||||
else:
|
else:
|
||||||
print(f"❌ {local_handler._open_connections} connections remain open")
|
print(f"❌ {local_handler._open_connections} connections remain open")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Restore original methods
|
# Restore original methods
|
||||||
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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -240,10 +240,10 @@ class CRUDModel:
|
|||||||
|
|
||||||
exclude_args = exclude_args or []
|
exclude_args = exclude_args or []
|
||||||
exclude_args = [exclude_arg.key for exclude_arg in exclude_args]
|
exclude_args = [exclude_arg.key for exclude_arg in exclude_args]
|
||||||
|
|
||||||
include_args = include_args or []
|
include_args = include_args or []
|
||||||
include_args = [include_arg.key for include_arg in include_args]
|
include_args = [include_arg.key for include_arg in include_args]
|
||||||
|
|
||||||
# If include_args is provided, only use those fields for matching
|
# If include_args is provided, only use those fields for matching
|
||||||
# Otherwise, use all fields except those in exclude_args
|
# Otherwise, use all fields except those in exclude_args
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
except Exception as e:
|
||||||
return tuple(cls.filter_expr(**smart_options))
|
# 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(
|
||||||
|
|||||||
@@ -478,46 +478,48 @@ def run_simple_concurrent_test(num_threads=10):
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
print(f"\nStarting simple concurrent test with {num_threads} threads...")
|
print(f"\nStarting simple concurrent test with {num_threads} threads...")
|
||||||
|
|
||||||
# Results tracking
|
# Results tracking
|
||||||
results = {"passed": 0, "failed": 0, "errors": []}
|
results = {"passed": 0, "failed": 0, "errors": []}
|
||||||
results_lock = threading.Lock()
|
results_lock = threading.Lock()
|
||||||
|
|
||||||
def worker(thread_id):
|
def worker(thread_id):
|
||||||
try:
|
try:
|
||||||
# Simple query to test connection pooling
|
# Simple query to test connection pooling
|
||||||
with EndpointRestriction.new_session() as db_session:
|
with EndpointRestriction.new_session() as db_session:
|
||||||
# Just run a simple count query
|
# Just run a simple count query
|
||||||
count_query = db_session.query(EndpointRestriction).count()
|
count_query = db_session.query(EndpointRestriction).count()
|
||||||
|
|
||||||
# Small delay to simulate work
|
# Small delay to simulate work
|
||||||
time.sleep(random.uniform(0.01, 0.05))
|
time.sleep(random.uniform(0.01, 0.05))
|
||||||
|
|
||||||
# Simple success criteria
|
# Simple success criteria
|
||||||
success = count_query >= 0
|
success = count_query >= 0
|
||||||
|
|
||||||
# Update results with thread safety
|
# Update results with thread safety
|
||||||
with results_lock:
|
with results_lock:
|
||||||
if success:
|
if success:
|
||||||
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
|
||||||
results["errors"].append(f"Thread {thread_id} exception: {str(e)}")
|
results["errors"].append(f"Thread {thread_id} exception: {str(e)}")
|
||||||
|
|
||||||
# Create and start threads using a thread pool
|
# Create and start threads using a thread pool
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||||
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
||||||
|
|
||||||
# Calculate execution time
|
# Calculate execution time
|
||||||
execution_time = time.time() - start_time
|
execution_time = time.time() - start_time
|
||||||
|
|
||||||
# Print results
|
# Print results
|
||||||
print(f"\nConcurrent Operation Test Results:")
|
print(f"\nConcurrent Operation Test Results:")
|
||||||
print(f"Total threads: {num_threads}")
|
print(f"Total threads: {num_threads}")
|
||||||
@@ -525,21 +527,23 @@ def run_simple_concurrent_test(num_threads=10):
|
|||||||
print(f"Failed: {results['failed']}")
|
print(f"Failed: {results['failed']}")
|
||||||
print(f"Execution time: {execution_time:.2f} seconds")
|
print(f"Execution time: {execution_time:.2f} seconds")
|
||||||
print(f"Operations per second: {num_threads / execution_time:.2f}")
|
print(f"Operations per second: {num_threads / execution_time:.2f}")
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
return results["failed"] == 0
|
return results["failed"] == 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
generate_table_in_postgres()
|
generate_table_in_postgres()
|
||||||
passed, failed = run_all_tests()
|
passed, failed = run_all_tests()
|
||||||
|
|
||||||
# If all tests pass, run the simple concurrent test
|
# If all tests pass, run the simple concurrent test
|
||||||
if failed == 0:
|
if failed == 0:
|
||||||
run_simple_concurrent_test(100)
|
run_simple_concurrent_test(100)
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ from Controllers.Redis.response import RedisResponse
|
|||||||
|
|
||||||
class RedisPublisher:
|
class RedisPublisher:
|
||||||
"""Redis Publisher class for broadcasting messages to channels."""
|
"""Redis Publisher class for broadcasting messages to channels."""
|
||||||
|
|
||||||
def __init__(self, redis_client=redis_cli):
|
def __init__(self, redis_client=redis_cli):
|
||||||
self.redis_client = redis_client
|
self.redis_client = redis_client
|
||||||
|
|
||||||
def publish(self, channel: str, message: Union[Dict, List, str]) -> RedisResponse:
|
def publish(self, channel: str, message: Union[Dict, List, str]) -> RedisResponse:
|
||||||
"""Publish a message to a Redis channel.
|
"""Publish a message to a Redis channel.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
channel: The channel to publish to
|
channel: The channel to publish to
|
||||||
message: The message to publish (will be JSON serialized if dict or list)
|
message: The message to publish (will be JSON serialized if dict or list)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
@@ -26,113 +26,124 @@ class RedisPublisher:
|
|||||||
# Convert dict/list to JSON string if needed
|
# Convert dict/list to JSON string if needed
|
||||||
if isinstance(message, (dict, list)):
|
if isinstance(message, (dict, list)):
|
||||||
message = json.dumps(message)
|
message = json.dumps(message)
|
||||||
|
|
||||||
# Publish the message
|
# Publish the message
|
||||||
recipient_count = self.redis_client.publish(channel, message)
|
recipient_count = self.redis_client.publish(channel, message)
|
||||||
|
|
||||||
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),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RedisSubscriber:
|
class RedisSubscriber:
|
||||||
"""Redis Subscriber class for listening to channels."""
|
"""Redis Subscriber class for listening to channels."""
|
||||||
|
|
||||||
def __init__(self, redis_client=redis_cli):
|
def __init__(self, redis_client=redis_cli):
|
||||||
self.redis_client = redis_client
|
self.redis_client = redis_client
|
||||||
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:
|
||||||
channel: The channel to subscribe to
|
channel: The channel to subscribe to
|
||||||
callback: Function to call when a message is received
|
callback: Function to call when a message is received
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Subscribe to the channel
|
# Subscribe to the channel
|
||||||
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:
|
||||||
pattern: The pattern to subscribe to (e.g., 'user.*')
|
pattern: The pattern to subscribe to (e.g., 'user.*')
|
||||||
callback: Function to call when a message is received
|
callback: Function to call when a message is received
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Subscribe to the pattern
|
# Subscribe to the pattern
|
||||||
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:
|
||||||
# Not JSON, keep as is
|
# Not JSON, keep as is
|
||||||
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
|
||||||
|
|
||||||
def start_listening(self, in_thread: bool = True) -> RedisResponse:
|
def start_listening(self, in_thread: bool = True) -> RedisResponse:
|
||||||
"""Start listening for messages on subscribed channels.
|
"""Start listening for messages on subscribed channels.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
in_thread: If True, start listening in a separate thread
|
in_thread: If True, start listening in a separate thread
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
@@ -140,50 +151,41 @@ 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):
|
||||||
"""Thread function for listening to messages."""
|
"""Thread function for listening to messages."""
|
||||||
self.pubsub.run_in_thread(sleep_time=0.01)
|
self.pubsub.run_in_thread(sleep_time=0.01)
|
||||||
|
|
||||||
def stop_listening(self) -> RedisResponse:
|
def stop_listening(self) -> RedisResponse:
|
||||||
"""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:
|
||||||
"""Unsubscribe from a channel or all channels.
|
"""Unsubscribe from a channel or all channels.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
channel: The channel to unsubscribe from, or None for all channels
|
channel: The channel to unsubscribe from, or None for all channels
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
@@ -194,24 +196,21 @@ class RedisSubscriber:
|
|||||||
else:
|
else:
|
||||||
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:
|
||||||
"""Unsubscribe from a pattern or all patterns.
|
"""Unsubscribe from a pattern or all patterns.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pattern: The pattern to unsubscribe from, or None for all patterns
|
pattern: The pattern to unsubscribe from, or None for all patterns
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RedisResponse with status and message
|
RedisResponse with status and message
|
||||||
"""
|
"""
|
||||||
@@ -222,24 +221,21 @@ class RedisSubscriber:
|
|||||||
else:
|
else:
|
||||||
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),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RedisPubSub:
|
class RedisPubSub:
|
||||||
"""Singleton class that provides both publisher and subscriber functionality."""
|
"""Singleton class that provides both publisher and subscriber functionality."""
|
||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
||||||
def __new__(cls):
|
def __new__(cls):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(RedisPubSub, cls).__new__(cls)
|
cls._instance = super(RedisPubSub, cls).__new__(cls)
|
||||||
|
|||||||
@@ -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,40 +25,43 @@ 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.
|
||||||
Generates mock data and publishes to the reader channel.
|
Generates mock data and publishes to the reader channel.
|
||||||
"""
|
"""
|
||||||
print("[READER] Function started")
|
print("[READER] Function started")
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
# Generate mock data
|
# Generate mock data
|
||||||
message = generate_mock_data()
|
message = generate_mock_data()
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
message["start_time"] = start_time
|
message["start_time"] = start_time
|
||||||
|
|
||||||
# Publish to reader channel
|
# Publish to reader channel
|
||||||
result = redis_pubsub.publisher.publish(CHANNEL_READER, message)
|
result = redis_pubsub.publisher.publish(CHANNEL_READER, message)
|
||||||
|
|
||||||
if result.status:
|
if result.status:
|
||||||
print(f"[READER] {time.time():.6f} | Published UUID: {message['uuid']}")
|
print(f"[READER] {time.time():.6f} | Published UUID: {message['uuid']}")
|
||||||
else:
|
else:
|
||||||
print(f"[READER] Publish error: {result.error}")
|
print(f"[READER] Publish error: {result.error}")
|
||||||
|
|
||||||
# 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
|
||||||
@@ -68,47 +72,51 @@ def processor_function():
|
|||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"[PROCESSOR] Error parsing message data: {e}")
|
print(f"[PROCESSOR] Error parsing message data: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if stage is 'red' before processing
|
# Check if stage is 'red' before processing
|
||||||
if data.get("stage") == "red":
|
if data.get("stage") == "red":
|
||||||
# Process the message
|
# Process the message
|
||||||
data["processor_timestamp"] = datetime.now().isoformat()
|
data["processor_timestamp"] = datetime.now().isoformat()
|
||||||
data["data"]["status"] = "processed"
|
data["data"]["status"] = "processed"
|
||||||
data["data"]["counter"] += 1
|
data["data"]["counter"] += 1
|
||||||
|
|
||||||
# Update stage to 'processed'
|
# Update stage to 'processed'
|
||||||
data["stage"] = "processed"
|
data["stage"] = "processed"
|
||||||
|
|
||||||
# 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:
|
||||||
print(f"[PROCESSOR] Skipped message: {data['uuid']} (stage is not 'red')")
|
print(f"[PROCESSOR] Skipped message: {data['uuid']} (stage is not 'red')")
|
||||||
|
|
||||||
# Subscribe to reader channel
|
# Subscribe to reader channel
|
||||||
result = redis_pubsub.subscriber.subscribe(CHANNEL_READER, on_reader_message)
|
result = redis_pubsub.subscriber.subscribe(CHANNEL_READER, on_reader_message)
|
||||||
|
|
||||||
if result.status:
|
if result.status:
|
||||||
print(f"[PROCESSOR] Subscribed to channel: {CHANNEL_READER}")
|
print(f"[PROCESSOR] Subscribed to channel: {CHANNEL_READER}")
|
||||||
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
|
||||||
@@ -119,42 +127,45 @@ def writer_function():
|
|||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"[WRITER] Error parsing message data: {e}")
|
print(f"[WRITER] Error parsing message data: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if stage is 'processed' before processing
|
# Check if stage is 'processed' before processing
|
||||||
if data.get("stage") == "processed":
|
if data.get("stage") == "processed":
|
||||||
# Process the message
|
# Process the message
|
||||||
data["writer_timestamp"] = datetime.now().isoformat()
|
data["writer_timestamp"] = datetime.now().isoformat()
|
||||||
data["data"]["status"] = "completed"
|
data["data"]["status"] = "completed"
|
||||||
data["data"]["counter"] += 1
|
data["data"]["counter"] += 1
|
||||||
|
|
||||||
# Update stage to 'completed'
|
# Update stage to 'completed'
|
||||||
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)
|
||||||
|
|
||||||
if result.status:
|
if result.status:
|
||||||
print(f"[WRITER] Subscribed to channel: {CHANNEL_PROCESSOR}")
|
print(f"[WRITER] Subscribed to channel: {CHANNEL_PROCESSOR}")
|
||||||
else:
|
else:
|
||||||
@@ -167,18 +178,18 @@ def run_demo():
|
|||||||
print("Chain: READER → PROCESSOR → WRITER")
|
print("Chain: READER → PROCESSOR → WRITER")
|
||||||
print(f"Channels: {CHANNEL_READER} → {CHANNEL_PROCESSOR} → {CHANNEL_WRITER}")
|
print(f"Channels: {CHANNEL_READER} → {CHANNEL_PROCESSOR} → {CHANNEL_WRITER}")
|
||||||
print("Format: [SERVICE] TIMESTAMP | Received/Published UUID | [Elapsed time]")
|
print("Format: [SERVICE] TIMESTAMP | Received/Published UUID | [Elapsed time]")
|
||||||
|
|
||||||
# Start the Redis subscriber listening thread
|
# Start the Redis subscriber listening thread
|
||||||
redis_pubsub.subscriber.start_listening()
|
redis_pubsub.subscriber.start_listening()
|
||||||
|
|
||||||
# Start processor and writer functions (these subscribe to channels)
|
# Start processor and writer functions (these subscribe to channels)
|
||||||
processor_function()
|
processor_function()
|
||||||
writer_function()
|
writer_function()
|
||||||
|
|
||||||
# Create a thread for the reader function (this publishes messages)
|
# Create a thread for the reader function (this publishes messages)
|
||||||
reader_thread = Thread(target=reader_function, daemon=True)
|
reader_thread = Thread(target=reader_function, daemon=True)
|
||||||
reader_thread.start()
|
reader_thread.start()
|
||||||
|
|
||||||
# Keep the main thread alive
|
# Keep the main thread alive
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -41,15 +41,15 @@ class RedisConn:
|
|||||||
# Add connection pooling settings if not provided
|
# Add connection pooling settings if not provided
|
||||||
if "max_connections" not in self.config:
|
if "max_connections" not in self.config:
|
||||||
self.config["max_connections"] = 50 # Increased for better concurrency
|
self.config["max_connections"] = 50 # Increased for better concurrency
|
||||||
|
|
||||||
# Add connection timeout settings
|
# Add connection timeout settings
|
||||||
if "health_check_interval" not in self.config:
|
if "health_check_interval" not in self.config:
|
||||||
self.config["health_check_interval"] = 30 # Health check every 30 seconds
|
self.config["health_check_interval"] = 30 # Health check every 30 seconds
|
||||||
|
|
||||||
# Add retry settings for operations
|
# Add retry settings for operations
|
||||||
if "retry_on_timeout" not in self.config:
|
if "retry_on_timeout" not in self.config:
|
||||||
self.config["retry_on_timeout"] = True
|
self.config["retry_on_timeout"] = True
|
||||||
|
|
||||||
# Add connection pool settings for better performance
|
# Add connection pool settings for better performance
|
||||||
if "socket_keepalive" not in self.config:
|
if "socket_keepalive" not in self.config:
|
||||||
self.config["socket_keepalive"] = True
|
self.config["socket_keepalive"] = True
|
||||||
|
|||||||
@@ -113,56 +113,58 @@ 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 = {
|
||||||
"passed": 0,
|
"passed": 0,
|
||||||
"failed": 0,
|
"failed": 0,
|
||||||
"retried": 0,
|
"retried": 0,
|
||||||
"errors": [],
|
"errors": [],
|
||||||
"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()
|
||||||
|
|
||||||
def worker(thread_id):
|
def worker(thread_id):
|
||||||
# Track operation timing
|
# Track operation timing
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
retry_count = 0
|
retry_count = 0
|
||||||
success = False
|
success = False
|
||||||
error_message = None
|
error_message = None
|
||||||
|
|
||||||
while retry_count <= results["max_retries"] and not success:
|
while retry_count <= results["max_retries"] and not success:
|
||||||
try:
|
try:
|
||||||
# Generate unique key for this thread
|
# Generate unique key for this thread
|
||||||
unique_id = str(uuid.uuid4())[:8]
|
unique_id = str(uuid.uuid4())[:8]
|
||||||
full_key = f"test:concurrent:{thread_id}:{unique_id}"
|
full_key = f"test:concurrent:{thread_id}:{unique_id}"
|
||||||
|
|
||||||
# Simple string operations instead of JSON
|
# Simple string operations instead of JSON
|
||||||
test_value = f"test-value-{thread_id}-{time.time()}"
|
test_value = f"test-value-{thread_id}-{time.time()}"
|
||||||
|
|
||||||
# Set data in Redis with pipeline for efficiency
|
# Set data in Redis with pipeline for efficiency
|
||||||
from Controllers.Redis.database import redis_cli
|
from Controllers.Redis.database import redis_cli
|
||||||
|
|
||||||
# Use pipeline to reduce network overhead
|
# Use pipeline to reduce network overhead
|
||||||
with redis_cli.pipeline() as pipe:
|
with redis_cli.pipeline() as pipe:
|
||||||
pipe.set(full_key, test_value)
|
pipe.set(full_key, test_value)
|
||||||
pipe.get(full_key)
|
pipe.get(full_key)
|
||||||
pipe.delete(full_key)
|
pipe.delete(full_key)
|
||||||
results_list = pipe.execute()
|
results_list = pipe.execute()
|
||||||
|
|
||||||
# Check results
|
# Check results
|
||||||
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
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -170,26 +172,28 @@ 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)
|
||||||
retry_count += 1
|
retry_count += 1
|
||||||
with results_lock:
|
with results_lock:
|
||||||
results["retry_count"] += 1
|
results["retry_count"] += 1
|
||||||
|
|
||||||
# Check if it's a connection error and retry
|
# Check if it's a connection error and retry
|
||||||
if "Too many connections" in str(e) or "Connection" in str(e):
|
if "Too many connections" in str(e) or "Connection" in str(e):
|
||||||
# Exponential backoff for connection issues
|
# Exponential backoff for connection issues
|
||||||
backoff_time = results["retry_delay"] * (2 ** retry_count)
|
backoff_time = results["retry_delay"] * (2**retry_count)
|
||||||
time.sleep(backoff_time)
|
time.sleep(backoff_time)
|
||||||
else:
|
else:
|
||||||
# For other errors, use a smaller delay
|
# For other errors, use a smaller delay
|
||||||
time.sleep(results["retry_delay"])
|
time.sleep(results["retry_delay"])
|
||||||
|
|
||||||
# Record operation time
|
# Record operation time
|
||||||
operation_time = time.time() - start_time
|
operation_time = time.time() - start_time
|
||||||
|
|
||||||
# Update results
|
# Update results
|
||||||
with results_lock:
|
with results_lock:
|
||||||
if success:
|
if success:
|
||||||
@@ -200,26 +204,30 @@ 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()
|
||||||
with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||||
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
futures = [executor.submit(worker, i) for i in range(num_threads)]
|
||||||
concurrent.futures.wait(futures)
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
# Calculate execution time and performance metrics
|
# Calculate execution time and performance metrics
|
||||||
execution_time = time.time() - start_time
|
execution_time = time.time() - start_time
|
||||||
ops_per_second = num_threads / execution_time if execution_time > 0 else 0
|
ops_per_second = num_threads / execution_time if execution_time > 0 else 0
|
||||||
|
|
||||||
# Calculate additional metrics if we have successful operations
|
# Calculate additional metrics if we have successful operations
|
||||||
avg_op_time = 0
|
avg_op_time = 0
|
||||||
min_op_time = 0
|
min_op_time = 0
|
||||||
max_op_time = 0
|
max_op_time = 0
|
||||||
p95_op_time = 0
|
p95_op_time = 0
|
||||||
|
|
||||||
if results["operation_times"]:
|
if results["operation_times"]:
|
||||||
avg_op_time = sum(results["operation_times"]) / len(results["operation_times"])
|
avg_op_time = sum(results["operation_times"]) / len(results["operation_times"])
|
||||||
min_op_time = min(results["operation_times"])
|
min_op_time = min(results["operation_times"])
|
||||||
@@ -227,8 +235,12 @@ 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:")
|
||||||
print(f"Total threads: {num_threads}")
|
print(f"Total threads: {num_threads}")
|
||||||
@@ -237,17 +249,17 @@ def run_concurrent_test(num_threads=100):
|
|||||||
print(f"Operations with retries: {results['retried']}")
|
print(f"Operations with retries: {results['retried']}")
|
||||||
print(f"Total retry attempts: {results['retry_count']}")
|
print(f"Total retry attempts: {results['retry_count']}")
|
||||||
print(f"Success rate: {(results['passed'] / num_threads) * 100:.2f}%")
|
print(f"Success rate: {(results['passed'] / num_threads) * 100:.2f}%")
|
||||||
|
|
||||||
print("\nPerformance Metrics:")
|
print("\nPerformance Metrics:")
|
||||||
print(f"Total execution time: {execution_time:.2f} seconds")
|
print(f"Total execution time: {execution_time:.2f} seconds")
|
||||||
print(f"Operations per second: {ops_per_second:.2f}")
|
print(f"Operations per second: {ops_per_second:.2f}")
|
||||||
|
|
||||||
if results["operation_times"]:
|
if results["operation_times"]:
|
||||||
print(f"Average operation time: {avg_op_time * 1000:.2f} ms")
|
print(f"Average operation time: {avg_op_time * 1000:.2f} ms")
|
||||||
print(f"Minimum operation time: {min_op_time * 1000:.2f} ms")
|
print(f"Minimum operation time: {min_op_time * 1000:.2f} ms")
|
||||||
print(f"Maximum operation time: {max_op_time * 1000:.2f} ms")
|
print(f"Maximum operation time: {max_op_time * 1000:.2f} ms")
|
||||||
print(f"95th percentile operation time: {p95_op_time * 1000:.2f} ms")
|
print(f"95th percentile operation time: {p95_op_time * 1000:.2f} ms")
|
||||||
|
|
||||||
# Print errors (limited to 10 for readability)
|
# Print errors (limited to 10 for readability)
|
||||||
if results["errors"]:
|
if results["errors"]:
|
||||||
print("\nErrors:")
|
print("\nErrors:")
|
||||||
@@ -255,7 +267,7 @@ def run_concurrent_test(num_threads=100):
|
|||||||
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")
|
||||||
|
|
||||||
# Return results for potential further analysis
|
# Return results for potential further analysis
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -263,6 +275,6 @@ def run_concurrent_test(num_threads=100):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Run basic examples
|
# Run basic examples
|
||||||
run_all_examples()
|
run_all_examples()
|
||||||
|
|
||||||
# Run enhanced concurrent test
|
# Run enhanced concurrent test
|
||||||
run_concurrent_test(10000)
|
run_concurrent_test(10000)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()}"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
|
||||||
return applications_dict
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
Index(
|
Index(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from config import api_config
|
|||||||
|
|
||||||
|
|
||||||
def create_events_if_any_cluster_set():
|
def create_events_if_any_cluster_set():
|
||||||
from events import retrieve_all_clusters
|
from events import retrieve_all_clusters
|
||||||
|
|
||||||
for event_cluster in retrieve_all_clusters():
|
for event_cluster in retrieve_all_clusters():
|
||||||
for event in event_cluster.retrieve_all_event_clusters:
|
for event in event_cluster.retrieve_all_event_clusters:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user