diff --git a/ApiServices/api_handlers/__init__.py b/ApiServices/api_handlers/__init__.py index 4569148..41b2818 100644 --- a/ApiServices/api_handlers/__init__.py +++ b/ApiServices/api_handlers/__init__.py @@ -1,3 +1,3 @@ from .core_response import AlchemyJsonResponse -__all__ = ["AlchemyJsonResponse"] \ No newline at end of file +__all__ = ["AlchemyJsonResponse"] diff --git a/ApiServices/api_handlers/auth_actions/auth.py b/ApiServices/api_handlers/auth_actions/auth.py index 8001fc8..6f80b7b 100644 --- a/ApiServices/api_handlers/auth_actions/auth.py +++ b/ApiServices/api_handlers/auth_actions/auth.py @@ -22,7 +22,7 @@ def save_access_token_to_redis( Employees, Staff, Addresses, - OccupantTypes + OccupantTypes, ) if not found_user: @@ -32,7 +32,9 @@ def save_access_token_to_redis( ) # Check user is already logged in or has a previous session - already_tokens = AccessObjectActions.get_object_via_user_uu_id(user_id=found_user.uu_id) + already_tokens = AccessObjectActions.get_object_via_user_uu_id( + user_id=found_user.uu_id + ) for key, token_user in already_tokens.items(): if token_user.get("domain", "") == domain: redis_cli.delete(key) diff --git a/ApiServices/api_handlers/auth_actions/token.py b/ApiServices/api_handlers/auth_actions/token.py index 7d227bc..a9033d3 100644 --- a/ApiServices/api_handlers/auth_actions/token.py +++ b/ApiServices/api_handlers/auth_actions/token.py @@ -9,10 +9,10 @@ class AccessObjectActions: @classmethod def save_object_to_redis( - cls, - access_token, - model_object: typing.Union[OccupantTokenObject, EmployeeTokenObject], - expiry_minutes: int = Auth.TOKEN_EXPIRE_MINUTES_30.total_seconds() // 60 + cls, + access_token, + model_object: typing.Union[OccupantTokenObject, EmployeeTokenObject], + expiry_minutes: int = Auth.TOKEN_EXPIRE_MINUTES_30.total_seconds() // 60, ) -> bool: """Save access token object to Redis with expiry Args: @@ -28,17 +28,14 @@ class AccessObjectActions: RedisActions.save_object_to_redis( access_token=access_token, model_object=model_object, - expiry_minutes=expiry_minutes + expiry_minutes=expiry_minutes, ) return True except Exception as e: print("Save Object to Redis Error: ", e) raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail=dict( - message="Failed to save token to Redis", - error=str(e) - ), + detail=dict(message="Failed to save token to Redis", error=str(e)), ) @classmethod @@ -64,23 +61,21 @@ class AccessObjectActions: if not hasattr(request, "headers"): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail=dict( - message="Headers not found in request" - ) + detail=dict(message="Headers not found in request"), ) - + access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) if not access_token: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail=dict( - message="Unauthorized user, please login" - ), + detail=dict(message="Unauthorized user, please login"), ) return access_token @classmethod - def get_token_object(cls, request) -> typing.Union[OccupantTokenObject, EmployeeTokenObject]: + def get_token_object( + cls, request + ) -> typing.Union[OccupantTokenObject, EmployeeTokenObject]: """Get and validate token object from request Args: request: The request object @@ -93,18 +88,16 @@ class AccessObjectActions: return RedisActions.get_object_via_access_key(request) except Exception as e: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=dict( - message=str(e) - ) + status_code=status.HTTP_401_UNAUTHORIZED, detail=dict(message=str(e)) ) - @classmethod def get_object_via_access_key( - cls, request, + cls, + request, ) -> typing.Union[EmployeeTokenObject, OccupantTokenObject, None]: from api_configs import Auth + access_object = RedisActions.get_with_regex( value_regex=str(request.headers.get(Auth.ACCESS_TOKEN_TAG) + ":*") ).data @@ -120,7 +113,7 @@ class AccessObjectActions: status_code=401, detail=dict( message="User type is not found in the token object. Please reach to your administrator." - ) + ), ) diff --git a/api_configs/configs.py b/api_configs/configs.py index b435493..ab9c8ee 100644 --- a/api_configs/configs.py +++ b/api_configs/configs.py @@ -36,7 +36,7 @@ class Config: # Timezone Configuration DEFAULT_TIMEZONE = "GMT+3" # Default timezone for the application - SYSTEM_TIMEZONE = "GMT+0" # System timezone (used for internal operations) + SYSTEM_TIMEZONE = "GMT+0" # System timezone (used for internal operations) SUPPORTED_TIMEZONES = ["GMT+0", "GMT+3"] # List of supported timezones diff --git a/api_events/events/abstract_class.py b/api_events/events/abstract_class.py index 0b06571..03d8132 100644 --- a/api_events/events/abstract_class.py +++ b/api_events/events/abstract_class.py @@ -6,16 +6,18 @@ from typing import TypeVar, Union, Dict, Any, Optional, Type from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject -TokenType = TypeVar('TokenType', bound=Union[EmployeeTokenObject, OccupantTokenObject]) +TokenType = TypeVar("TokenType", bound=Union[EmployeeTokenObject, OccupantTokenObject]) + class ActionsSchema(ABC): """Base class for defining API action schemas. - + This class handles endpoint registration and validation in the database. """ + def __init__(self, endpoint: str): """Initialize with an API endpoint path. - + Args: endpoint: The API endpoint path (e.g. "/users/create") """ @@ -23,10 +25,10 @@ class ActionsSchema(ABC): def retrieve_action_from_endpoint(self) -> Dict[str, Any]: """Retrieve the endpoint registration from the database. - + Returns: Dict containing the endpoint registration data - + Raises: HTTPException: If endpoint is not found in database """ @@ -38,21 +40,22 @@ class ActionsSchema(ABC): "endpoint_desc": "Temporary endpoint", "endpoint_code": "dummy_code", "id": 1, - "uu_id": "dummy_uuid" + "uu_id": "dummy_uuid", } class ActionsSchemaFactory: """Factory class for creating action schemas. - + This class validates and initializes action schemas for API endpoints. """ + def __init__(self, action: ActionsSchema): """Initialize with an action schema. - + Args: action: The action schema to initialize - + Raises: HTTPException: If action initialization fails """ @@ -67,17 +70,18 @@ class ActionsSchemaFactory: print(f"ActionsSchemaFactory Error: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to initialize action schema" + detail="Failed to initialize action schema", ) from e class MethodToEvent(ABC, ActionsSchemaFactory): """Base class for mapping methods to API events. - + This class handles method registration and validation for API events. """ + action_key: Optional[str] = None - event_type: Optional[str] = None + event_type: Optional[str] = None event_description: str = "" event_category: str = "" @@ -87,42 +91,40 @@ class MethodToEvent(ABC, ActionsSchemaFactory): @classmethod def call_event_method(cls, method_uu_id: str, *args: Any, **kwargs: Any) -> Any: """Call an event method by its UUID. - + Args: method_uu_id: UUID of the method to call *args: Positional arguments to pass to method **kwargs: Keyword arguments to pass to method - + Returns: The result of the called method - + Raises: AttributeError: If method UUID is not found """ function_name = cls.__event_keys__.get(method_uu_id) if not function_name: raise AttributeError(f"No method found for UUID: {method_uu_id}") - + return getattr(cls, function_name)(*args, **kwargs) @classmethod - def ban_token_objects( - cls, - token: TokenType, - ban_list: Type[TokenType] - ) -> None: + def ban_token_objects(cls, token: TokenType, ban_list: Type[TokenType]) -> None: """Check if a token type is banned from accessing an event. - + Args: token: The token to check ban_list: The token type that is banned - + Raises: HTTPException: If token type matches banned type """ if isinstance(token, ban_list): - user_type = "employee" if isinstance(token, EmployeeTokenObject) else "occupant" + user_type = ( + "employee" if isinstance(token, EmployeeTokenObject) else "occupant" + ) raise HTTPException( status_code=status.HTTP_406_NOT_ACCEPTABLE, - detail=f"No {user_type} can reach this event. A notification has been sent to admin." + detail=f"No {user_type} can reach this event. A notification has been sent to admin.", ) diff --git a/api_events/events/account/account_records.py b/api_events/events/account/account_records.py index 87dbf89..94616cf 100644 --- a/api_events/events/account/account_records.py +++ b/api_events/events/account/account_records.py @@ -54,7 +54,7 @@ class AccountRecordsListEventMethods(MethodToEvent): result=records, cls_object=AccountRecords, filter_attributes=list_options, - response_model=AccountRecordResponse + response_model=AccountRecordResponse, ) @classmethod @@ -224,7 +224,9 @@ class AccountRecordsCreateEventMethods(MethodToEvent): ) account_record = AccountRecords.find_or_create(**data.excluded_dump()) return AlchemyJsonResponse( - completed=True, message="Account record created successfully", result=account_record + completed=True, + message="Account record created successfully", + result=account_record, ) elif isinstance(token_dict, EmployeeTokenObject): # Build.pre_query = Build.select_action( @@ -266,7 +268,9 @@ class AccountRecordsCreateEventMethods(MethodToEvent): account_record = AccountRecords.insert_one(data_dict).data return AlchemyJsonResponse( - completed=True, message="Account record created successfully", result=account_record + completed=True, + message="Account record created successfully", + result=account_record, ) @@ -297,7 +301,9 @@ class AccountRecordsUpdateEventMethods(MethodToEvent): account_record = AccountRecords.update_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, message="Account record updated successfully", result=account_record + completed=True, + message="Account record updated successfully", + result=account_record, ) @@ -323,7 +329,9 @@ class AccountRecordsPatchEventMethods(MethodToEvent): ): account_record = AccountRecords.patch_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, message="Account record patched successfully", result=account_record + completed=True, + message="Account record patched successfully", + result=account_record, ) diff --git a/api_events/events/address/address.py b/api_events/events/address/address.py index 37230a1..0f57ed8 100644 --- a/api_events/events/address/address.py +++ b/api_events/events/address/address.py @@ -138,16 +138,19 @@ class AddressCreateEventMethods(MethodToEvent): address.update(is_confirmed=True) address.save() return AlchemyJsonResponse( - completed=True, message="Address created successfully", result=address.get_dict() + completed=True, + message="Address created successfully", + result=address.get_dict(), ) class AddressSearchEventMethods(MethodToEvent): """Event methods for searching addresses. - + This class handles address search functionality including text search and filtering. """ + event_type = "SEARCH" event_description = "Search for addresses using text and filters" event_category = "Address" @@ -161,18 +164,15 @@ class AddressSearchEventMethods(MethodToEvent): @classmethod def _build_order_clause( - cls, - filter_list: Dict[str, Any], - schemas: List[str], - filter_table: Any + cls, filter_list: Dict[str, Any], schemas: List[str], filter_table: Any ) -> Any: """Build the ORDER BY clause for the query. - + Args: filter_list: Dictionary of filter options schemas: List of available schema fields filter_table: SQLAlchemy table to query - + Returns: SQLAlchemy order_by clause """ @@ -187,16 +187,20 @@ class AddressSearchEventMethods(MethodToEvent): # Build order clause field = getattr(filter_table, filter_list.get("order_field")) - return field.desc() if str(filter_list.get("order_type"))[0] == "d" else field.asc() + return ( + field.desc() + if str(filter_list.get("order_type"))[0] == "d" + else field.asc() + ) @classmethod def _format_record(cls, record: Any, schemas: List[str]) -> Dict[str, str]: """Format a database record into a dictionary. - + Args: record: Database record to format schemas: List of schema fields - + Returns: Formatted record dictionary """ @@ -216,14 +220,14 @@ class AddressSearchEventMethods(MethodToEvent): token_dict: Union[EmployeeTokenObject, OccupantTokenObject], ) -> JSONResponse: """Search for addresses using text search and filters. - + Args: data: Search parameters including text and filters token_dict: Authentication token - + Returns: JSON response with search results - + Raises: HTTPException: If search fails """ @@ -236,23 +240,23 @@ class AddressSearchEventMethods(MethodToEvent): if not search_result: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="No addresses found matching search criteria" + detail="No addresses found matching search criteria", ) query = search_result.get("query") schemas = search_result.get("schema") - + # Apply filters filter_list = data.list_options.dump() filter_table = AddressStreet - + # Build and apply order clause order = cls._build_order_clause(filter_list, schemas, filter_table) - + # Apply pagination page_size = int(filter_list.get("size")) offset = (int(filter_list.get("page")) - 1) * page_size - + # Execute query query = ( query.order_by(order) @@ -270,9 +274,7 @@ class AddressSearchEventMethods(MethodToEvent): print(f"Address search completed in {duration:.3f}s") return AlchemyJsonResponse( - completed=True, - message="Address search results", - result=results + completed=True, message="Address search results", result=results ) except HTTPException as e: @@ -283,7 +285,7 @@ class AddressSearchEventMethods(MethodToEvent): print(f"Address search error: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to search addresses" + detail="Failed to search addresses", ) from e @@ -321,7 +323,9 @@ class AddressUpdateEventMethods(MethodToEvent): updated_address = address.update(**data_dict) updated_address.save() return AlchemyJsonResponse( - completed=True, message="Address updated successfully", result=updated_address.get_dict() + completed=True, + message="Address updated successfully", + result=updated_address.get_dict(), ) elif isinstance(token_dict, OccupantTokenObject): raise HTTPException( @@ -368,7 +372,9 @@ class AddressPatchEventMethods(MethodToEvent): patched_address = address.patch(**data_dict) return AlchemyJsonResponse( - completed=True, message="Address patched successfully", result=patched_address.get_dict() + completed=True, + message="Address patched successfully", + result=patched_address.get_dict(), ) @@ -416,7 +422,9 @@ class AddressPostCodeCreateEventMethods(MethodToEvent): relation_table.update(is_confirmed=True) relation_table.save() return AlchemyJsonResponse( - completed=True, message="Post code created successfully", result=post_code.get_dict() + completed=True, + message="Post code created successfully", + result=post_code.get_dict(), ) @@ -457,7 +465,9 @@ class AddressPostCodeUpdateEventMethods(MethodToEvent): updated_post_code = post_code.update(**data_dict) updated_post_code.save() return AlchemyJsonResponse( - completed=True, message="Post code updated successfully", result=updated_post_code.get_dict() + completed=True, + message="Post code updated successfully", + result=updated_post_code.get_dict(), ) elif isinstance(token_dict, OccupantTokenObject): raise HTTPException( diff --git a/api_events/events/application/authentication.py b/api_events/events/application/authentication.py index a9f032e..b34d242 100644 --- a/api_events/events/application/authentication.py +++ b/api_events/events/application/authentication.py @@ -13,10 +13,21 @@ from api_configs import Auth, ApiStatic from api_events.events.abstract_class import MethodToEvent, ActionsSchema from databases import ( - Companies, Staff, Duties, Departments, Employees, - BuildLivingSpace, BuildParts, Build, Duty, Event2Occupant, - Event2Employee, Users, UsersTokens, OccupantTypes, - RelationshipEmployee2Build + Companies, + Staff, + Duties, + Departments, + Employees, + BuildLivingSpace, + BuildParts, + Build, + Duty, + Event2Occupant, + Event2Employee, + Users, + UsersTokens, + OccupantTypes, + RelationshipEmployee2Build, ) from api_services import ( @@ -28,17 +39,23 @@ from api_services import ( ) from api_validations.validations_request import ( - Login, Logout, ChangePassword, Remember, - Forgot, CreatePassword, OccupantSelection, + Login, + Logout, + ChangePassword, + Remember, + Forgot, + CreatePassword, + OccupantSelection, EmployeeSelection, ) from api_validations.validations_response import ( AuthenticationLoginResponse, AuthenticationRefreshResponse, - AuthenticationUserInfoResponse + AuthenticationUserInfoResponse, ) + class AuthenticationLoginEventMethods(MethodToEvent): event_type = "LOGIN" event_description = "Login via domain and access key : [email] | [phone]" @@ -56,19 +73,22 @@ class AuthenticationLoginEventMethods(MethodToEvent): try: access_dict = Users.login_user_with_credentials(data=data, request=request) found_user = access_dict.get("user") - + if not found_user: user_logger.log_login_attempt( - request, None, data.domain, data.access_key, - success=False, error="Invalid credentials" + request, + None, + data.domain, + data.access_key, + success=False, + error="Invalid credentials", ) return ResponseHandler.unauthorized("Invalid credentials") user_logger.log_login_attempt( - request, found_user.id, data.domain, data.access_key, - success=True + request, found_user.id, data.domain, data.access_key, success=True ) - + response_data = { "access_token": access_dict.get("access_token"), "refresh_token": access_dict.get("refresher_token"), @@ -78,17 +98,13 @@ class AuthenticationLoginEventMethods(MethodToEvent): return ResponseHandler.success( message="User logged in successfully", data=response_data, - response_model=AuthenticationLoginResponse + response_model=AuthenticationLoginResponse, ) except Exception as e: user_logger.log_login_attempt( - request, None, data.domain, data.access_key, - success=False, error=str(e) - ) - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=str(e) + request, None, data.domain, data.access_key, success=False, error=str(e) ) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) class AuthenticationSelectEventMethods(MethodToEvent): @@ -105,14 +121,13 @@ class AuthenticationSelectEventMethods(MethodToEvent): @classmethod def _handle_employee_selection( - cls, - data: EmployeeSelection, - token_dict: EmployeeTokenObject, - request: Request + cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request ): """Handle employee company selection""" if data.company_uu_id not in token_dict.companies_uu_id_list: - return ResponseHandler.unauthorized("Company not found in user's company list") + return ResponseHandler.unauthorized( + "Company not found in user's company list" + ) selected_company = Companies.filter_one( Companies.uu_id == data.company_uu_id @@ -122,29 +137,27 @@ class AuthenticationSelectEventMethods(MethodToEvent): # Get department IDs for the company department_ids = [ - dept.id for dept in Departments.filter_all( + dept.id + for dept in Departments.filter_all( Departments.company_id == selected_company.id ).data ] # Get duties IDs for the company duties_ids = [ - duty.id for duty in Duties.filter_all( - Duties.company_id == selected_company.id - ).data + duty.id + for duty in Duties.filter_all(Duties.company_id == selected_company.id).data ] # Get staff IDs staff_ids = [ - staff.id for staff in Staff.filter_all( - Staff.duties_id.in_(duties_ids) - ).data + staff.id for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids)).data ] # Get employee employee = Employees.filter_one( Employees.people_id == token_dict.person_id, - Employees.staff_id.in_(staff_ids) + Employees.staff_id.in_(staff_ids), ).data if not employee: @@ -158,16 +171,14 @@ class AuthenticationSelectEventMethods(MethodToEvent): # Get staff and duties staff = Staff.filter_one(Staff.id == employee.staff_id).data duties = Duties.filter_one(Duties.id == staff.duties_id).data - department = Departments.filter_one( - Departments.id == duties.department_id - ).data + department = Departments.filter_one(Departments.id == duties.department_id).data # Get bulk duty bulk_id = Duty.filter_by_one(system=True, duty_code="BULK").data bulk_duty_id = Duties.filter_by_one( company_id=selected_company.id, duties_id=bulk_id.id, - **Duties.valid_record_dict + **Duties.valid_record_dict, ).data # Create company token @@ -183,7 +194,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): staff_uu_id=staff.uu_id.__str__(), employee_id=employee.id, employee_uu_id=employee.uu_id.__str__(), - reachable_event_list_id=reachable_event_list_id + reachable_event_list_id=reachable_event_list_id, ) # Update Redis @@ -192,24 +203,19 @@ class AuthenticationSelectEventMethods(MethodToEvent): @classmethod def _handle_occupant_selection( - cls, - data: OccupantSelection, - token_dict: OccupantTokenObject, - request: Request + cls, data: OccupantSelection, token_dict: OccupantTokenObject, request: Request ): """Handle occupant type selection""" # Get occupant type occupant_type = OccupantTypes.filter_by_one( - system=True, - uu_id=data.occupant_uu_id + system=True, uu_id=data.occupant_uu_id ).data if not occupant_type: return ResponseHandler.not_found("Occupant Type not found") # Get build part build_part = BuildParts.filter_by_one( - system=True, - uu_id=data.build_part_uu_id + system=True, uu_id=data.build_part_uu_id ).data if not build_part: return ResponseHandler.not_found("Build Part not found") @@ -230,7 +236,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): selected_occupant_type = BuildLivingSpace.filter_one( BuildLivingSpace.occupant_type == occupant_type.id, BuildLivingSpace.person_id == token_dict.person_id, - BuildLivingSpace.build_parts_id == build_part.id + BuildLivingSpace.build_parts_id == build_part.id, ).data if not selected_occupant_type: return ResponseHandler.not_found("Selected occupant type not found") @@ -255,7 +261,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): responsible_employee_uuid=responsible_employee.uu_id.__str__(), responsible_company_id=company_related.id, responsible_company_uuid=company_related.uu_id.__str__(), - reachable_event_list_id=reachable_event_list_id + reachable_event_list_id=reachable_event_list_id, ) # Update Redis @@ -276,13 +282,11 @@ class AuthenticationSelectEventMethods(MethodToEvent): elif isinstance(token_dict, OccupantTokenObject): return cls._handle_occupant_selection(data, token_dict, request) return ResponseHandler.error( - "Invalid token type", - status_code=status.HTTP_400_BAD_REQUEST + "Invalid token type", status_code=status.HTTP_400_BAD_REQUEST ) except Exception as e: return ResponseHandler.error( - str(e), - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR + str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR ) @@ -331,9 +335,7 @@ class AuthenticationRefreshEventMethods(MethodToEvent): return ResponseHandler.unauthorized() # Get user and token info - found_user = Users.filter_one( - Users.uu_id == token_dict.user_uu_id - ).data + found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data if not found_user: return ResponseHandler.not_found("User not found") @@ -349,12 +351,12 @@ class AuthenticationRefreshEventMethods(MethodToEvent): response_data = { "access_token": access_token, "refresh_token": getattr(user_token, "token", None), - "user": found_user.get_dict() + "user": found_user.get_dict(), } return ResponseHandler.success( "User info refreshed successfully", data=response_data, - response_model=AuthenticationRefreshResponse + response_model=AuthenticationRefreshResponse, ) except Exception as e: return ResponseHandler.error(str(e)) @@ -381,7 +383,9 @@ class AuthenticationChangePasswordEventMethods(MethodToEvent): ): try: if not isinstance(token_dict, EmployeeTokenObject): - return ResponseHandler.unauthorized("Only employees can change password") + return ResponseHandler.unauthorized( + "Only employees can change password" + ) found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data if not found_user: @@ -389,22 +393,27 @@ class AuthenticationChangePasswordEventMethods(MethodToEvent): if not found_user.check_password(data.old_password): user_logger.log_password_change( - request, found_user.id, "change", - success=False, error="Invalid old password" + request, + found_user.id, + "change", + success=False, + error="Invalid old password", ) return ResponseHandler.unauthorized("Old password is incorrect") found_user.set_password(data.new_password) user_logger.log_password_change( - request, found_user.id, "change", - success=True + request, found_user.id, "change", success=True ) return ResponseHandler.success("Password changed successfully") except Exception as e: user_logger.log_password_change( - request, found_user.id if found_user else None, - "change", success=False, error=str(e) + request, + found_user.id if found_user else None, + "change", + success=False, + error=str(e), ) return ResponseHandler.error(str(e)) @@ -471,7 +480,9 @@ class AuthenticationDisconnectUserEventMethods(MethodToEvent): found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data if not found_user: return ResponseHandler.not_found("User not found") - if already_tokens := RedisActions.get_object_via_user_uu_id(user_id=str(found_user.uu_id)): + if already_tokens := RedisActions.get_object_via_user_uu_id( + user_id=str(found_user.uu_id) + ): for key, token_user in already_tokens.items(): RedisActions.delete_key(key) selected_user = Users.filter_one( @@ -566,7 +577,7 @@ class AuthenticationRefreshTokenEventMethods(MethodToEvent): return ResponseHandler.success( "User is logged in successfully via refresher token", data=response_data, - response_model=AuthenticationRefreshResponse + response_model=AuthenticationRefreshResponse, ) return ResponseHandler.not_found("Invalid data") @@ -697,7 +708,7 @@ class AuthenticationDownloadAvatarEventMethods(MethodToEvent): return ResponseHandler.success( "Avatar and profile is shared via user credentials", data=user_info, - response_model=AuthenticationUserInfoResponse + response_model=AuthenticationUserInfoResponse, ) return ResponseHandler.not_found("Invalid data") diff --git a/api_events/events/building/building_build.py b/api_events/events/building/building_build.py index 224c172..d461c7f 100644 --- a/api_events/events/building/building_build.py +++ b/api_events/events/building/building_build.py @@ -59,7 +59,7 @@ class BuildListEventMethods(MethodToEvent): result=records, cls_object=Build, filter_attributes=list_options, - response_model=BuildResponse + response_model=BuildResponse, ) @@ -213,7 +213,7 @@ class BuildUpdateEventMethods(MethodToEvent): completed=False, message="Building not found", result={}, - status_code="HTTP_404_NOT_FOUND" + status_code="HTTP_404_NOT_FOUND", ) build.update(**data.excluded_dump()) @@ -256,7 +256,7 @@ class BuildPatchEventMethods(MethodToEvent): completed=False, message="Building not found", result={}, - status_code="HTTP_404_NOT_FOUND" + status_code="HTTP_404_NOT_FOUND", ) build.update(**data.excluded_dump()) diff --git a/api_events/events/building/building_build_area.py b/api_events/events/building/building_build_area.py index d2f3db1..590501d 100644 --- a/api_events/events/building/building_build_area.py +++ b/api_events/events/building/building_build_area.py @@ -51,12 +51,12 @@ class BuildAreaListEventMethods(MethodToEvent): BuildArea.filter_attr = list_options records = BuildArea.filter_all() return AlchemyJsonResponse( - completed=True, - message="Building areas listed successfully", + completed=True, + message="Building areas listed successfully", result=records, cls_object=BuildArea, filter_attributes=list_options, - response_model=BuildResponse + response_model=BuildResponse, ) @@ -112,9 +112,7 @@ class BuildAreaCreateEventMethods(MethodToEvent): data_dict["build_uu_id"] = str(selected_build.uu_id) area = BuildArea.insert_one(data_dict).data return AlchemyJsonResponse( - completed=True, - message="Building area created successfully", - result=area + completed=True, message="Building area created successfully", result=area ) @@ -137,9 +135,7 @@ class BuildAreaUpdateEventMethods(MethodToEvent): ): area = BuildArea.update_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, - message="Building area updated successfully", - result=area + completed=True, message="Building area updated successfully", result=area ) @@ -162,9 +158,7 @@ class BuildAreaPatchEventMethods(MethodToEvent): ): area = BuildArea.patch_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, - message="Building area patched successfully", - result=area + completed=True, message="Building area patched successfully", result=area ) diff --git a/api_events/events/building/building_build_parts.py b/api_events/events/building/building_build_parts.py index 6fba3e9..53e3e0f 100644 --- a/api_events/events/building/building_build_parts.py +++ b/api_events/events/building/building_build_parts.py @@ -14,6 +14,7 @@ from databases import ( BuildParts, ) + class BuildingBuildPartsListEventMethods(MethodToEvent): event_type = "SELECT" diff --git a/api_events/events/building/building_build_sites.py b/api_events/events/building/building_build_sites.py index 5cd5466..857e512 100644 --- a/api_events/events/building/building_build_sites.py +++ b/api_events/events/building/building_build_sites.py @@ -59,12 +59,12 @@ class BuildSitesListEventMethods(MethodToEvent): BuildSites.filter_attr = list_options records = BuildSites.filter_all() return AlchemyJsonResponse( - completed=True, - message="Building sites listed successfully", + completed=True, + message="Building sites listed successfully", result=records, cls_object=BuildSites, filter_attributes=list_options, - response_model=BuildSitesResponse + response_model=BuildSitesResponse, ) @@ -110,9 +110,7 @@ class BuildSitesCreateEventMethods(MethodToEvent): data_dict = data.excluded_dump() site = BuildSites.insert_one(data_dict).data return AlchemyJsonResponse( - completed=True, - message="Building site created successfully", - result=site + completed=True, message="Building site created successfully", result=site ) @@ -135,9 +133,7 @@ class BuildSitesUpdateEventMethods(MethodToEvent): ): site = BuildSites.update_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, - message="Building site updated successfully", - result=site + completed=True, message="Building site updated successfully", result=site ) @@ -160,9 +156,7 @@ class BuildSitesPatchEventMethods(MethodToEvent): ): site = BuildSites.patch_one(build_uu_id, data).data return AlchemyJsonResponse( - completed=True, - message="Building site patched successfully", - result=site + completed=True, message="Building site patched successfully", result=site ) diff --git a/api_events/events/building/building_build_types.py b/api_events/events/building/building_build_types.py index 1df6328..b80e77d 100644 --- a/api_events/events/building/building_build_types.py +++ b/api_events/events/building/building_build_types.py @@ -16,9 +16,7 @@ class BuildTypesListEventMethods(MethodToEvent): __event_keys__ = { "5344d03c-fc47-43ec-8c44-6c2acd7e5d9f": "build_types_list", } - __event_validation__ = { - "5344d03c-fc47-43ec-8c44-6c2acd7e5d9f": BuildTypesResponse - } + __event_validation__ = {"5344d03c-fc47-43ec-8c44-6c2acd7e5d9f": BuildTypesResponse} @classmethod def build_types_list( @@ -37,7 +35,7 @@ class BuildTypesListEventMethods(MethodToEvent): result=results, cls_object=BuildTypes, filter_attributes=list_options, - response_model=BuildTypesListResponse + response_model=BuildTypesListResponse, ) elif isinstance(token_dict, OccupantTokenObject): return AlchemyJsonResponse( @@ -46,7 +44,7 @@ class BuildTypesListEventMethods(MethodToEvent): result=None, cls_object=BuildTypes, filter_attributes=list_options, - response_model=BuildTypesResponse + response_model=BuildTypesResponse, ) diff --git a/api_events/events/building/building_living_spaces.py b/api_events/events/building/building_living_spaces.py index bec8d14..8b7287c 100644 --- a/api_events/events/building/building_living_spaces.py +++ b/api_events/events/building/building_living_spaces.py @@ -73,7 +73,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent): return AlchemyJsonResponse( completed=True, message="Living spaces listed successfully", - result=records + result=records, ) elif isinstance(token_dict, EmployeeTokenObject): build_id_list_query = Build.select_action( @@ -217,7 +217,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent): return AlchemyJsonResponse( completed=True, message="Living space created successfully", - result=created_living_space + result=created_living_space, ) @@ -307,7 +307,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent): return AlchemyJsonResponse( completed=True, message="Living space updated successfully", - result=living_space + result=living_space, ) diff --git a/api_events/events/company/company_company.py b/api_events/events/company/company_company.py index dc062b6..70f641e 100644 --- a/api_events/events/company/company_company.py +++ b/api_events/events/company/company_company.py @@ -108,7 +108,7 @@ class CompanyUpdateEventMethods(MethodToEvent): completed=False, message="Company not found", result={}, - status_code="HTTP_404_NOT_FOUND" + status_code="HTTP_404_NOT_FOUND", ) company.save() return AlchemyJsonResponse( @@ -142,7 +142,7 @@ class CompanyPatchEventMethods(MethodToEvent): completed=False, message="Company not found", result={}, - status_code="HTTP_404_NOT_FOUND" + status_code="HTTP_404_NOT_FOUND", ) company.save() return AlchemyJsonResponse( diff --git a/api_events/events/identity/people.py b/api_events/events/identity/people.py index e93ee59..0bd714e 100644 --- a/api_events/events/identity/people.py +++ b/api_events/events/identity/people.py @@ -4,17 +4,16 @@ from fastapi import status from fastapi.responses import JSONResponse from api_validations.validations_response import PeopleListResponse +from api_validations.validations_request import InsertPerson, UpdateUsers +from api_events.events.abstract_class import MethodToEvent, ActionsSchema +from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject +from ApiServices.api_handlers import AlchemyJsonResponse from databases import ( People, Users, Companies, ) -from api_validations.validations_request import InsertPerson, UpdateUsers -from api_events.events.abstract_class import MethodToEvent, ActionsSchema -from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject -from ApiServices.api_handlers import AlchemyJsonResponse - class PeopleListEventMethods(MethodToEvent): diff --git a/api_library/date_time_actions/date_functions.py b/api_library/date_time_actions/date_functions.py index e8a8558..b30a03b 100644 --- a/api_library/date_time_actions/date_functions.py +++ b/api_library/date_time_actions/date_functions.py @@ -8,8 +8,10 @@ class DateTimeLocal: def __init__(self, timezone: str = None, is_client: bool = True): if timezone and timezone not in Config.SUPPORTED_TIMEZONES: - raise ValueError(f"Unsupported timezone: {timezone}. Must be one of {Config.SUPPORTED_TIMEZONES}") - + raise ValueError( + f"Unsupported timezone: {timezone}. Must be one of {Config.SUPPORTED_TIMEZONES}" + ) + self.timezone = Config.SYSTEM_TIMEZONE if is_client: self.timezone = (timezone or Config.DEFAULT_TIMEZONE).replace("-", "+") @@ -84,11 +86,11 @@ class DateTimeLocal: components = [str(base_key)] components.extend(str(arg) for arg in args) components.append(f"tz_{self.timezone}") - return ':'.join(components) + return ":".join(components) def format_for_db(self, date): """Format date for database storage""" - return self.get(date).format('YYYY-MM-DD HH:mm:ss.SSSZZ') + return self.get(date).format("YYYY-MM-DD HH:mm:ss.SSSZZ") def parse_from_db(self, date_str): """Parse date from database format""" @@ -99,16 +101,17 @@ class DateTimeLocal: def get_day_boundaries(self, date=None): """Get start and end of day in current timezone""" dt = self.get(date) if date else self.now() - start = dt.floor('day') - end = dt.ceil('day') + start = dt.floor("day") + end = dt.ceil("day") return start, end def get_month_boundaries(self, date=None): """Get start and end of month in current timezone""" dt = self.get(date) if date else self.now() - start = dt.floor('month') - end = dt.ceil('month') + start = dt.floor("month") + end = dt.ceil("month") return start, end + client_arrow = DateTimeLocal(is_client=True) system_arrow = DateTimeLocal(is_client=False) diff --git a/api_library/logger.py b/api_library/logger.py index 978e3ed..f328afe 100644 --- a/api_library/logger.py +++ b/api_library/logger.py @@ -4,18 +4,19 @@ from typing import Optional, Dict, Any from fastapi.requests import Request from api_library.date_time_actions.date_functions import system_arrow + class UserActivityLogger: def __init__(self): self.logger = logging.getLogger("user_activity") self.logger.setLevel(logging.INFO) - + # Add handlers if not already added if not self.logger.handlers: log_path = "/service_app/logs/user_activity.log" os.makedirs(os.path.dirname(log_path), exist_ok=True) handler = logging.FileHandler(log_path) formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) handler.setFormatter(formatter) self.logger.addHandler(handler) @@ -24,9 +25,10 @@ class UserActivityLogger: """Extract common metadata from request""" return { "agent": request.headers.get("User-Agent"), - "ip": getattr(request, "remote_addr", None) or request.headers.get("X-Forwarded-For"), + "ip": getattr(request, "remote_addr", None) + or request.headers.get("X-Forwarded-For"), "platform": request.headers.get("Origin"), - "timestamp": str(system_arrow.now()) + "timestamp": str(system_arrow.now()), } def log_login_attempt( @@ -36,7 +38,7 @@ class UserActivityLogger: domain: str, access_key: str, success: bool, - error: Optional[str] = None + error: Optional[str] = None, ): """Log login attempts""" metadata = self._get_request_metadata(request) @@ -47,9 +49,9 @@ class UserActivityLogger: "access_key": access_key, "success": success, "error": error, - **metadata + **metadata, } - + if success: self.logger.info("Login successful", extra=log_data) else: @@ -61,7 +63,7 @@ class UserActivityLogger: user_id: int, change_type: str, success: bool, - error: Optional[str] = None + error: Optional[str] = None, ): """Log password changes""" metadata = self._get_request_metadata(request) @@ -71,9 +73,9 @@ class UserActivityLogger: "change_type": change_type, "success": success, "error": error, - **metadata + **metadata, } - + if success: self.logger.info("Password change successful", extra=log_data) else: @@ -86,7 +88,7 @@ class UserActivityLogger: activity_type: str, domain: Optional[str] = None, success: bool = True, - error: Optional[str] = None + error: Optional[str] = None, ): """Log session activities (logout, disconnect, etc)""" metadata = self._get_request_metadata(request) @@ -97,13 +99,14 @@ class UserActivityLogger: "domain": domain, "success": success, "error": error, - **metadata + **metadata, } - + if success: self.logger.info(f"{activity_type} successful", extra=log_data) else: self.logger.warning(f"{activity_type} failed", extra=log_data) + # Global logger instance user_logger = UserActivityLogger() diff --git a/api_library/response_handlers.py b/api_library/response_handlers.py index 364478d..403f8e0 100644 --- a/api_library/response_handlers.py +++ b/api_library/response_handlers.py @@ -2,9 +2,12 @@ from typing import Any, Optional from fastapi import status from fastapi.responses import JSONResponse + class ResponseHandler: @staticmethod - def success(message: str, data: Optional[Any] = None, status_code: int = status.HTTP_200_OK) -> JSONResponse: + def success( + message: str, data: Optional[Any] = None, status_code: int = status.HTTP_200_OK + ) -> JSONResponse: """Create a success response""" return JSONResponse( content={ @@ -16,7 +19,11 @@ class ResponseHandler: ) @staticmethod - def error(message: str, data: Optional[Any] = None, status_code: int = status.HTTP_400_BAD_REQUEST) -> JSONResponse: + def error( + message: str, + data: Optional[Any] = None, + status_code: int = status.HTTP_400_BAD_REQUEST, + ) -> JSONResponse: """Create an error response""" return JSONResponse( content={ diff --git a/api_services/__init__.py b/api_services/__init__.py index 762e43f..5dce80c 100644 --- a/api_services/__init__.py +++ b/api_services/__init__.py @@ -8,4 +8,4 @@ from .templates.password_templates import ( change_your_password_template, ) -update_selected_to_redis = RedisActions.set_json \ No newline at end of file +update_selected_to_redis = RedisActions.set_json diff --git a/api_services/redis/functions.py b/api_services/redis/functions.py index 736d339..751a075 100644 --- a/api_services/redis/functions.py +++ b/api_services/redis/functions.py @@ -70,7 +70,9 @@ class RedisActions: already_tokens = redis_cli.scan_iter(match=str(value_regex)) already_tokens_list = {} for already_token in already_tokens: - already_tokens_list[already_token.decode()] = json.loads(redis_cli.get(already_token)) + already_tokens_list[already_token.decode()] = json.loads( + redis_cli.get(already_token) + ) return RedisResponse( status=True, message="Values are listed successfully.", @@ -187,21 +189,22 @@ class RedisActions: try: search_name = str(name) if isinstance(name, str) else name.decode() expiry_time = system_arrow.get_expiry_time(**expiry_kwargs) - seconds_until_expiry = int(expiry_time.timestamp() - system_arrow.now().timestamp()) - + seconds_until_expiry = int( + expiry_time.timestamp() - system_arrow.now().timestamp() + ) + redis_cli.setex( name=search_name, time=seconds_until_expiry, - value=json.dumps({ - 'value': value, - 'expires_at': expiry_time.timestamp() - }) + value=json.dumps( + {"value": value, "expires_at": expiry_time.timestamp()} + ), ) - + return RedisResponse( status=True, message="Value is set successfully with expiry.", - data={'value': value, 'expires_at': expiry_time.timestamp()}, + data={"value": value, "expires_at": expiry_time.timestamp()}, ) except Exception as e: return RedisResponse( @@ -216,25 +219,25 @@ class RedisActions: try: search_name = str(name) if isinstance(name, str) else name.decode() result = redis_cli.get(name=search_name) - + if not result: return RedisResponse( status=False, message="Key not found.", ) - + data = json.loads(result) - if system_arrow.is_expired(data.get('expires_at')): + if system_arrow.is_expired(data.get("expires_at")): redis_cli.delete(search_name) return RedisResponse( status=False, message="Cache expired.", ) - + return RedisResponse( status=True, message="Value retrieved successfully.", - data=data['value'], + data=data["value"], ) except Exception as e: return RedisResponse( @@ -272,24 +275,22 @@ class RedisActions: try: key = f"{access_token}:{model_object.user_uu_id}" expiry_time = system_arrow.get_expiry_time(minutes=expiry_minutes) - seconds_until_expiry = max(1, int(expiry_time.timestamp() - system_arrow.now().timestamp())) - + seconds_until_expiry = max( + 1, int(expiry_time.timestamp() - system_arrow.now().timestamp()) + ) + # Add expiry time to the model data model_data = json.loads(model_object.model_dump_json()) - model_data['expires_at'] = expiry_time.timestamp() - + model_data["expires_at"] = expiry_time.timestamp() + if redis_cli.setex( - name=key, - time=seconds_until_expiry, - value=json.dumps(model_data) + name=key, time=seconds_until_expiry, value=json.dumps(model_data) ): return access_token - + except Exception as e: - raise Exception( - f"Failed to save object to Redis. Error: {str(e)}" - ) - + raise Exception(f"Failed to save object to Redis. Error: {str(e)}") + raise Exception("Failed to save token to Redis") @classmethod @@ -304,21 +305,21 @@ class RedisActions: """ from api_configs.configs import Auth from api_library.date_time_actions.date_functions import system_arrow - + if not hasattr(request, "headers"): raise Exception("Headers not found in request") - + access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) if not access_token: raise Exception("Unauthorized user, please login") - + # Scan for matching tokens token_pattern = f"{access_token}:*" matching_tokens = list(redis_cli.scan_iter(match=token_pattern)) - + if not matching_tokens: raise Exception("Invalid credentials. Please login again") - + try: # Check if token has expired in Redis token_key = matching_tokens[0] @@ -326,23 +327,23 @@ class RedisActions: if ttl <= 0: redis_cli.delete(token_key) raise Exception("Token expired. Please login again") - + # Get the token data - token_data = json.loads(redis_cli.get(token_key) or '{}') - + token_data = json.loads(redis_cli.get(token_key) or "{}") + # Return appropriate token object based on user type if token_data.get("user_type") == 1: # Employee if not token_data.get("selected_company"): token_data["selected_company"] = None return EmployeeTokenObject(**token_data) - + elif token_data.get("user_type") == 2: # Occupant if not token_data.get("selected_occupant"): token_data["selected_occupant"] = None return OccupantTokenObject(**token_data) - + raise Exception("Invalid user type in token") - + except Exception as e: raise Exception(f"Failed to retrieve token: {str(e)}") @@ -356,24 +357,28 @@ class RedisActions: """ token_pattern = f"*:{str(user_id)}" matching_tokens = redis_cli.scan_iter(match=token_pattern) - + tokens_dict = {} for token_key in matching_tokens: - token_data = json.loads(redis_cli.get(token_key) or '{}') - + token_data = json.loads(redis_cli.get(token_key) or "{}") + # Skip expired tokens and clean them up - if system_arrow.is_expired(token_data.get('expires_at')): + if system_arrow.is_expired(token_data.get("expires_at")): redis_cli.delete(token_key) continue - + tokens_dict[token_key.decode()] = token_data - + return tokens_dict class RedisResponse: def __init__( - self, status: bool, message: str, data: typing.Union[dict | list] = None, error: str = None + self, + status: bool, + message: str, + data: typing.Union[dict | list] = None, + error: str = None, ): self.status = status self.message = message diff --git a/api_services/token_service.py b/api_services/token_service.py index 9730bc8..1a1a248 100644 --- a/api_services/token_service.py +++ b/api_services/token_service.py @@ -8,16 +8,18 @@ from api_configs import Auth from databases import Users, UsersTokens from api_library.date_time_actions.date_functions import system_arrow + class TokenService: @staticmethod - def validate_token(request: Request) -> Union[OccupantTokenObject, EmployeeTokenObject]: + def validate_token( + request: Request, + ) -> Union[OccupantTokenObject, EmployeeTokenObject]: """Validate and return token object from request""" try: return RedisActions.get_object_via_access_key(request) except Exception as e: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail={"message": str(e)} + status_code=status.HTTP_401_UNAUTHORIZED, detail={"message": str(e)} ) @staticmethod @@ -26,12 +28,12 @@ class TokenService: return RedisActions.get_object_via_user_uu_id(user_id) @staticmethod - def validate_refresh_token(domain: str, refresh_token: str) -> Optional[UsersTokens]: + def validate_refresh_token( + domain: str, refresh_token: str + ) -> Optional[UsersTokens]: """Validate refresh token and return token object""" return UsersTokens.filter_by_one( - token=refresh_token, - domain=domain, - **UsersTokens.valid_record_dict + token=refresh_token, domain=domain, **UsersTokens.valid_record_dict ).data @staticmethod @@ -39,10 +41,9 @@ class TokenService: """Update user metadata from request""" user.last_agent = request.headers.get("User-Agent") user.last_platform = request.headers.get("Origin") - user.last_remote_addr = ( - getattr(request, "remote_addr", None) or - request.headers.get("X-Forwarded-For") - ) + user.last_remote_addr = getattr( + request, "remote_addr", None + ) or request.headers.get("X-Forwarded-For") user.last_seen = str(system_arrow.now()) user.save() @@ -64,12 +65,12 @@ class TokenService: user = Users.filter_one(Users.password_token == token).data if not user: return None - + # Check if token is expired token_valid_until = system_arrow.get(str(user.password_token_is_valid)) if system_arrow.now() > token_valid_until: user.password_token = "" user.save() return None - + return user diff --git a/api_validations/validations_response/__init__.py b/api_validations/validations_response/__init__.py index 9133388..b208148 100644 --- a/api_validations/validations_response/__init__.py +++ b/api_validations/validations_response/__init__.py @@ -19,19 +19,19 @@ from .building_responses import ( BuildCompaniesProvidingResponse, BuildCompaniesProvidingCollection, BuildManagementResponse, - BuildManagementCollection + BuildManagementCollection, ) from .account_responses import ( AccountRecordResponse, AccountRecordCollection, AccountRecordExchangeResponse, - AccountRecordExchangeCollection + AccountRecordExchangeCollection, ) from .address_responses import ListAddressResponse, AddressPostCodeResponse from .auth_responses import ( AuthenticationLoginResponse, AuthenticationRefreshResponse, - AuthenticationUserInfoResponse + AuthenticationUserInfoResponse, ) from .base_responses import BaseResponse, CrudCollection from .budget_responses import ( @@ -42,13 +42,13 @@ from .budget_responses import ( DecisionBookBudgetMasterResponse, DecisionBookBudgetMasterCollection, DecisionBookBudgetsResponse, - DecisionBookBudgetsCollection + DecisionBookBudgetsCollection, ) from .company_responses import ( CompanyListResponse, CompanyDepartmentListResponse, CompanyDutyListResponse, - CompanyEmployeeListResponse + CompanyEmployeeListResponse, ) from .decision_book_responses import ( BuildDecisionBookResponse, @@ -66,7 +66,7 @@ from .decision_book_responses import ( BuildDecisionBookPaymentsResponse, BuildDecisionBookPaymentsCollection, BuildDecisionBookLegalResponse, - BuildDecisionBookLegalCollection + BuildDecisionBookLegalCollection, ) from .living_space_responses import LivingSpaceListResponse from .parts_responses import BuildPartsListResponse @@ -115,5 +115,5 @@ __all__ = [ "BuildDecisionBookLegalCollection", "LivingSpaceListResponse", "BuildPartsListResponse", - "PeopleListResponse" + "PeopleListResponse", ] diff --git a/api_validations/validations_response/account_responses.py b/api_validations/validations_response/account_responses.py index 8b23f4b..78767bb 100644 --- a/api_validations/validations_response/account_responses.py +++ b/api_validations/validations_response/account_responses.py @@ -8,6 +8,7 @@ from .base_responses import BaseResponse, CrudCollection class AccountBooksResponse(BaseResponse): """Response model for account books""" + country: str branch_type: int company_id: int @@ -18,11 +19,13 @@ class AccountBooksResponse(BaseResponse): class AccountBooksCollection(CrudCollection[AccountBooksResponse]): """Collection of account books""" + pass class AccountCodesResponse(BaseResponse): """Response model for account codes""" + account_code: str comment_line: str is_receive_or_debit: bool @@ -42,11 +45,13 @@ class AccountCodesResponse(BaseResponse): class AccountCodesCollection(CrudCollection[AccountCodesResponse]): """Collection of account codes""" + pass class AccountCodeParserResponse(BaseResponse): """Response model for account code parser""" + account_code_1: str account_code_2: str account_code_3: str @@ -59,11 +64,13 @@ class AccountCodeParserResponse(BaseResponse): class AccountCodeParserCollection(CrudCollection[AccountCodeParserResponse]): """Collection of account code parsers""" + pass class AccountMasterResponse(BaseResponse): """Response model for account master""" + doc_date: datetime plug_type: str plug_number: int @@ -106,11 +113,13 @@ class AccountMasterResponse(BaseResponse): class AccountMasterCollection(CrudCollection[AccountMasterResponse]): """Collection of account masters""" + pass class AccountDetailResponse(BaseResponse): """Response model for account detail""" + doc_date: datetime line_no: int receive_debit: str @@ -160,15 +169,16 @@ class AccountDetailResponse(BaseResponse): class AccountDetailCollection(CrudCollection[AccountDetailResponse]): """Collection of account details""" + pass class AccountRecordResponse(BaseResponse): """Response model for account financial records. - + This model represents a financial transaction record in the system, including bank transaction details, amounts, and related metadata. - + Attributes: iban (str): International Bank Account Number bank_date (datetime): Date when the transaction occurred at the bank @@ -206,6 +216,7 @@ class AccountRecordResponse(BaseResponse): build_parts_uu_id (Optional[str]): Related building part ID build_decision_book_uu_id (Optional[str]): Related decision book ID """ + iban: str bank_date: datetime currency_value: Decimal @@ -245,19 +256,20 @@ class AccountRecordResponse(BaseResponse): class AccountRecordCollection(CrudCollection[AccountRecordResponse]): """Collection of account financial records. - + This model represents a paginated list of account records with sorting and pagination information. """ + pass class AccountRecordExchangeResponse(BaseResponse): """Response model for currency exchange records. - + This model represents a currency exchange transaction, tracking exchange rates and converted amounts for financial records. - + Attributes: account_record_id (int): ID of the related account record account_record_uu_id (str): UUID of the related account record @@ -266,6 +278,7 @@ class AccountRecordExchangeResponse(BaseResponse): exchange_value (Decimal): Converted amount exchange_date (datetime): When the exchange was calculated """ + account_record_id: int account_record_uu_id: str exchange_rate: Decimal @@ -276,15 +289,17 @@ class AccountRecordExchangeResponse(BaseResponse): class AccountRecordExchangeCollection(CrudCollection[AccountRecordExchangeResponse]): """Collection of currency exchange records. - + This model represents a paginated list of currency exchange records with sorting and pagination information. """ + pass class AccountRecordsListResponse(BaseModel): """Response model for account records list endpoint""" + uu_id: UUID account_name: str account_code: str diff --git a/api_validations/validations_response/auth_responses.py b/api_validations/validations_response/auth_responses.py index 348e03f..7e03182 100644 --- a/api_validations/validations_response/auth_responses.py +++ b/api_validations/validations_response/auth_responses.py @@ -6,6 +6,7 @@ from uuid import UUID class AuthenticationLoginResponse(BaseModel): """Response model for authentication login endpoint""" + token: str refresh_token: str token_type: str @@ -15,6 +16,7 @@ class AuthenticationLoginResponse(BaseModel): class AuthenticationRefreshResponse(BaseModel): """Response model for authentication refresh endpoint""" + token: str refresh_token: str token_type: str @@ -23,6 +25,7 @@ class AuthenticationRefreshResponse(BaseModel): class AuthenticationUserInfoResponse(BaseModel): """Response model for authentication user info endpoint""" + user_id: int username: str email: str diff --git a/api_validations/validations_response/base_responses.py b/api_validations/validations_response/base_responses.py index 9b21458..06e6c02 100644 --- a/api_validations/validations_response/base_responses.py +++ b/api_validations/validations_response/base_responses.py @@ -3,15 +3,16 @@ from typing import Optional, TypeVar, Generic, List from datetime import datetime from uuid import UUID -T = TypeVar('T') +T = TypeVar("T") + class BaseResponse(BaseModel): """Base response model that all response models inherit from. - + This model provides common fields that are present in all database records, including tracking information (created/updated timestamps), user actions (created by, updated by, confirmed by), and record status (active, deleted). - + Attributes: uu_id (str): Unique identifier for the record, typically a UUID created_at (datetime): Timestamp when the record was created @@ -27,6 +28,7 @@ class BaseResponse(BaseModel): is_notification_send (Optional[bool]): Whether notifications have been sent for this record is_email_send (Optional[bool]): Whether emails have been sent for this record """ + uu_id: str created_at: datetime updated_at: Optional[datetime] @@ -43,23 +45,24 @@ class BaseResponse(BaseModel): class Config: """Pydantic configuration for the base response model. - + Attributes: from_attributes (bool): Enables ORM mode for SQLAlchemy integration """ + from_attributes = True class CrudCollection(BaseModel, Generic[T]): """Base collection model for paginated responses. - + This model is used to return collections of items with pagination information. It is generic over the type of items in the collection, allowing it to be used with any response model. - + Type Parameters: T: The type of items in the collection - + Attributes: page (int): Current page number, 1-based indexing size (int): Number of items per page @@ -67,13 +70,13 @@ class CrudCollection(BaseModel, Generic[T]): order_field (str): Field used for sorting the collection order_type (str): Sort direction ('asc' or 'desc') items (List[T]): List of items in the current page - + Example: ```python class UserResponse(BaseResponse): name: str email: str - + users = CrudCollection[UserResponse]( page=1, size=10, @@ -84,6 +87,7 @@ class CrudCollection(BaseModel, Generic[T]): ) ``` """ + page: int = 1 size: int = 10 total: int = 0 @@ -93,8 +97,9 @@ class CrudCollection(BaseModel, Generic[T]): class Config: """Pydantic configuration for the collection model. - + Attributes: from_attributes (bool): Enables ORM mode for SQLAlchemy integration """ + from_attributes = True diff --git a/api_validations/validations_response/budget_responses.py b/api_validations/validations_response/budget_responses.py index 2d3e94a..9ecd619 100644 --- a/api_validations/validations_response/budget_responses.py +++ b/api_validations/validations_response/budget_responses.py @@ -8,6 +8,7 @@ from .base_responses import BaseResponse, CrudCollection class DecisionBookBudgetBooksResponse(BaseResponse): """Response model for decision book budget books""" + country: str branch_type: int = 0 company_id: int @@ -18,13 +19,17 @@ class DecisionBookBudgetBooksResponse(BaseResponse): build_decision_book_uu_id: Optional[str] -class DecisionBookBudgetBooksCollection(CrudCollection[DecisionBookBudgetBooksResponse]): +class DecisionBookBudgetBooksCollection( + CrudCollection[DecisionBookBudgetBooksResponse] +): """Collection of decision book budget books""" + pass class DecisionBookBudgetCodesResponse(BaseResponse): """Response model for decision book budget codes""" + budget_code: str comment_line: str budget_type: str @@ -37,13 +42,17 @@ class DecisionBookBudgetCodesResponse(BaseResponse): customer_uu_id: str -class DecisionBookBudgetCodesCollection(CrudCollection[DecisionBookBudgetCodesResponse]): +class DecisionBookBudgetCodesCollection( + CrudCollection[DecisionBookBudgetCodesResponse] +): """Collection of decision book budget codes""" + pass class DecisionBookBudgetMasterResponse(BaseResponse): """Response model for decision book budget master""" + budget_type: str currency: str = "TRY" total_budget: Decimal @@ -55,13 +64,17 @@ class DecisionBookBudgetMasterResponse(BaseResponse): department_uu_id: Optional[str] -class DecisionBookBudgetMasterCollection(CrudCollection[DecisionBookBudgetMasterResponse]): +class DecisionBookBudgetMasterCollection( + CrudCollection[DecisionBookBudgetMasterResponse] +): """Collection of decision book budget masters""" + pass class DecisionBookBudgetsResponse(BaseResponse): """Response model for decision book budgets""" + process_date: datetime budget_codes_id: int total_budget: Decimal @@ -73,4 +86,5 @@ class DecisionBookBudgetsResponse(BaseResponse): class DecisionBookBudgetsCollection(CrudCollection[DecisionBookBudgetsResponse]): """Collection of decision book budgets""" + pass diff --git a/api_validations/validations_response/building_responses.py b/api_validations/validations_response/building_responses.py index db52405..dcbd9fd 100644 --- a/api_validations/validations_response/building_responses.py +++ b/api_validations/validations_response/building_responses.py @@ -8,6 +8,7 @@ from .base_responses import BaseResponse, CrudCollection class BuildAreaListResponse(BaseResponse): """Response model for building area list endpoint""" + uu_id: UUID build_id: int build_uu_id: str @@ -20,11 +21,13 @@ class BuildAreaListResponse(BaseResponse): class BuildAreaListCollection(CrudCollection[BuildAreaListResponse]): """Collection of building area list""" + pass class BuildSitesListResponse(BaseResponse): """Response model for building sites list endpoint""" + uu_id: UUID address_id: int site_name: str @@ -36,11 +39,13 @@ class BuildSitesListResponse(BaseResponse): class BuildSitesListCollection(CrudCollection[BuildSitesListResponse]): """Collection of building sites list""" + pass class BuildTypesListResponse(BaseResponse): """Response model for building types list endpoint""" + uu_id: UUID type_name: str type_value: str @@ -51,11 +56,13 @@ class BuildTypesListResponse(BaseResponse): class BuildTypesListCollection(CrudCollection[BuildTypesListResponse]): """Collection of building types list""" + pass class BuildTypesResponse(BaseResponse): """Response model for building types""" + function_code: str type_code: str lang: str = "TR" @@ -63,11 +70,13 @@ class BuildTypesResponse(BaseResponse): class BuildTypesCollection(CrudCollection[BuildTypesResponse]): """Collection of building types""" + pass class Part2EmployeeResponse(BaseResponse): """Response model for part to employee mapping""" + build_id: int part_id: int employee_id: int @@ -75,11 +84,13 @@ class Part2EmployeeResponse(BaseResponse): class Part2EmployeeCollection(CrudCollection[Part2EmployeeResponse]): """Collection of part to employee mappings""" + pass class RelationshipEmployee2BuildResponse(BaseResponse): """Response model for employee to build relationship""" + company_id: int employee_id: int member_id: int @@ -87,13 +98,17 @@ class RelationshipEmployee2BuildResponse(BaseResponse): show_only: bool = False -class RelationshipEmployee2BuildCollection(CrudCollection[RelationshipEmployee2BuildResponse]): +class RelationshipEmployee2BuildCollection( + CrudCollection[RelationshipEmployee2BuildResponse] +): """Collection of employee to build relationships""" + pass class BuildResponse(BaseResponse): """Response model for buildings""" + gov_address_code: str = "" build_name: str build_no: str @@ -120,11 +135,13 @@ class BuildResponse(BaseResponse): class BuildCollection(CrudCollection[BuildResponse]): """Collection of buildings""" + pass class BuildPartsResponse(BaseResponse): """Response model for building parts""" + address_gov_code: str part_no: int = 0 part_level: int = 0 @@ -144,11 +161,13 @@ class BuildPartsResponse(BaseResponse): class BuildPartsCollection(CrudCollection[BuildPartsResponse]): """Collection of building parts""" + pass class BuildLivingSpaceResponse(BaseResponse): """Response model for building living space""" + fix_value: Decimal = Decimal("0") fix_percent: Decimal = Decimal("0") agreement_no: str = "" @@ -164,11 +183,13 @@ class BuildLivingSpaceResponse(BaseResponse): class BuildLivingSpaceCollection(CrudCollection[BuildLivingSpaceResponse]): """Collection of building living spaces""" + pass class BuildManagementResponse(BaseResponse): """Response model for building management""" + discounted_percentage: Decimal = Decimal("0.00") discounted_price: Decimal = Decimal("0.00") calculated_price: Decimal = Decimal("0.00") @@ -182,11 +203,13 @@ class BuildManagementResponse(BaseResponse): class BuildManagementCollection(CrudCollection[BuildManagementResponse]): """Collection of building management records""" + pass class BuildAreaResponse(BaseResponse): """Response model for building area""" + area_name: str = "" area_code: str = "" area_type: str = "GREEN" @@ -203,11 +226,13 @@ class BuildAreaResponse(BaseResponse): class BuildAreaCollection(CrudCollection[BuildAreaResponse]): """Collection of building areas""" + pass class BuildSitesResponse(BaseResponse): """Response model for building sites""" + site_name: str site_no: str address_id: int @@ -216,11 +241,13 @@ class BuildSitesResponse(BaseResponse): class BuildSitesCollection(CrudCollection[BuildSitesResponse]): """Collection of building sites""" + pass class BuildCompaniesProvidingResponse(BaseResponse): """Response model for building companies providing services""" + build_id: int build_uu_id: Optional[str] company_id: int @@ -230,13 +257,17 @@ class BuildCompaniesProvidingResponse(BaseResponse): contract_id: Optional[int] -class BuildCompaniesProvidingCollection(CrudCollection[BuildCompaniesProvidingResponse]): +class BuildCompaniesProvidingCollection( + CrudCollection[BuildCompaniesProvidingResponse] +): """Collection of building companies providing services""" + pass class BuildPersonProvidingResponse(BaseResponse): """Response model for building person providing services""" + build_id: int build_uu_id: Optional[str] people_id: int @@ -248,4 +279,5 @@ class BuildPersonProvidingResponse(BaseResponse): class BuildPersonProvidingCollection(CrudCollection[BuildPersonProvidingResponse]): """Collection of building person providing services""" + pass diff --git a/api_validations/validations_response/company_responses.py b/api_validations/validations_response/company_responses.py index ce475a6..1c261c4 100644 --- a/api_validations/validations_response/company_responses.py +++ b/api_validations/validations_response/company_responses.py @@ -6,6 +6,7 @@ from uuid import UUID class CompanyListResponse(BaseModel): """Response model for company list endpoint""" + uu_id: UUID company_name: str company_code: str @@ -19,6 +20,7 @@ class CompanyListResponse(BaseModel): class CompanyDepartmentListResponse(BaseModel): """Response model for company department list endpoint""" + uu_id: UUID department_name: str department_code: str @@ -31,6 +33,7 @@ class CompanyDepartmentListResponse(BaseModel): class CompanyDutyListResponse(BaseModel): """Response model for company duty list endpoint""" + uu_id: UUID duty_name: str duty_code: str @@ -43,6 +46,7 @@ class CompanyDutyListResponse(BaseModel): class CompanyEmployeeListResponse(BaseModel): """Response model for company employee list endpoint""" + uu_id: UUID employee_id: int employee_uu_id: str diff --git a/api_validations/validations_response/decision_book_responses.py b/api_validations/validations_response/decision_book_responses.py index 588b2d9..53d504b 100644 --- a/api_validations/validations_response/decision_book_responses.py +++ b/api_validations/validations_response/decision_book_responses.py @@ -8,6 +8,7 @@ from .base_responses import BaseResponse, CrudCollection class BuildDecisionBookResponse(BaseResponse): """Response model for building decision book""" + decision_book_pdf_path: Optional[str] = "" resp_company_fix_wage: float = 0 contact_agreement_path: Optional[str] = "" @@ -18,11 +19,13 @@ class BuildDecisionBookResponse(BaseResponse): class BuildDecisionBookCollection(CrudCollection[BuildDecisionBookResponse]): """Collection of building decision books""" + pass class BuildDecisionBookInvitationsResponse(BaseResponse): """Response model for building decision book invitations""" + build_id: int build_uu_id: Optional[str] decision_book_id: int @@ -36,13 +39,17 @@ class BuildDecisionBookInvitationsResponse(BaseResponse): planned_date_expires: datetime -class BuildDecisionBookInvitationsCollection(CrudCollection[BuildDecisionBookInvitationsResponse]): +class BuildDecisionBookInvitationsCollection( + CrudCollection[BuildDecisionBookInvitationsResponse] +): """Collection of building decision book invitations""" + pass class BuildDecisionBookPersonResponse(BaseResponse): """Response model for building decision book person""" + dues_percent_discount: int = 0 dues_fix_discount: Decimal = Decimal("0") dues_discount_approval_date: datetime @@ -61,13 +68,17 @@ class BuildDecisionBookPersonResponse(BaseResponse): person_id: int -class BuildDecisionBookPersonCollection(CrudCollection[BuildDecisionBookPersonResponse]): +class BuildDecisionBookPersonCollection( + CrudCollection[BuildDecisionBookPersonResponse] +): """Collection of building decision book persons""" + pass class BuildDecisionBookPersonOccupantsResponse(BaseResponse): """Response model for building decision book person occupants""" + build_decision_book_person_id: int build_decision_book_person_uu_id: Optional[str] invite_id: Optional[int] @@ -76,13 +87,17 @@ class BuildDecisionBookPersonOccupantsResponse(BaseResponse): occupant_type_uu_id: Optional[str] -class BuildDecisionBookPersonOccupantsCollection(CrudCollection[BuildDecisionBookPersonOccupantsResponse]): +class BuildDecisionBookPersonOccupantsCollection( + CrudCollection[BuildDecisionBookPersonOccupantsResponse] +): """Collection of building decision book person occupants""" + pass class BuildDecisionBookItemsResponse(BaseResponse): """Response model for building decision book items""" + item_order: int item_comment: str item_objection: Optional[str] @@ -97,11 +112,13 @@ class BuildDecisionBookItemsResponse(BaseResponse): class BuildDecisionBookItemsCollection(CrudCollection[BuildDecisionBookItemsResponse]): """Collection of building decision book items""" + pass class BuildDecisionBookItemsUnapprovedResponse(BaseResponse): """Response model for building decision book items unapproved""" + item_objection: str item_order: int decision_book_item_id: int @@ -112,13 +129,17 @@ class BuildDecisionBookItemsUnapprovedResponse(BaseResponse): build_decision_book_item_uu_id: Optional[str] -class BuildDecisionBookItemsUnapprovedCollection(CrudCollection[BuildDecisionBookItemsUnapprovedResponse]): +class BuildDecisionBookItemsUnapprovedCollection( + CrudCollection[BuildDecisionBookItemsUnapprovedResponse] +): """Collection of building decision book items unapproved""" + pass class BuildDecisionBookPaymentsResponse(BaseResponse): """Response model for building decision book payments""" + payment_plan_time_periods: str process_date: datetime payment_amount: Decimal @@ -138,13 +159,17 @@ class BuildDecisionBookPaymentsResponse(BaseResponse): account_records_uu_id: Optional[str] -class BuildDecisionBookPaymentsCollection(CrudCollection[BuildDecisionBookPaymentsResponse]): +class BuildDecisionBookPaymentsCollection( + CrudCollection[BuildDecisionBookPaymentsResponse] +): """Collection of building decision book payments""" + pass class BuildDecisionBookLegalResponse(BaseResponse): """Response model for building decision book legal""" + period_start_date: datetime lawsuits_decision_number: str lawsuits_decision_date: datetime @@ -175,4 +200,5 @@ class BuildDecisionBookLegalResponse(BaseResponse): class BuildDecisionBookLegalCollection(CrudCollection[BuildDecisionBookLegalResponse]): """Collection of building decision book legal records""" + pass diff --git a/databases/language_models/identity/identity.py b/databases/language_models/identity/identity.py index 0220fed..162f3c4 100644 --- a/databases/language_models/identity/identity.py +++ b/databases/language_models/identity/identity.py @@ -70,7 +70,7 @@ RelationshipDutyPeopleLanguageModel = dict( "member_id": "Member ID", "relationship_type": "Relationship Type", "show_only": "Show Only", - } + }, ) PeopleLanguageModel = dict( @@ -105,7 +105,7 @@ PeopleLanguageModel = dict( "birth_place": "Birth Place", "birth_date": "Birth Date", "tax_no": "Tax No", - } + }, ) RelationshipEmployee2PostCodeLanguageModel = dict( @@ -124,7 +124,7 @@ RelationshipEmployee2PostCodeLanguageModel = dict( "member_id": "Member ID", "relationship_type": "Relationship Type", "show_only": "Show Only", - } + }, ) AddressPostcodeLanguageModel = dict( @@ -168,7 +168,7 @@ AddressesLanguageModel = dict( "longitude": "Longitude", "street_id": "Street ID", "street_uu_id": "Street UUID", - } + }, ) AddressGeographicLocationsLanguageModel = dict( @@ -195,7 +195,7 @@ AddressGeographicLocationsLanguageModel = dict( "geo_description": "Description", "geo_area_size": "Area", "geo_population": "Population", - } + }, ) AddressCountryLanguageModel = dict( diff --git a/databases/language_models/rules/rules.py b/databases/language_models/rules/rules.py index 39cb033..d0a4bb7 100644 --- a/databases/language_models/rules/rules.py +++ b/databases/language_models/rules/rules.py @@ -16,5 +16,5 @@ EndpointRestrictionLanguageModel = dict( "endpoint_method": "API Method", "endpoint_desc": "API Description", "endpoint_code": "API Code", - } + }, ) diff --git a/databases/no_sql_models/identity.py b/databases/no_sql_models/identity.py index 25f37c9..f63357e 100644 --- a/databases/no_sql_models/identity.py +++ b/databases/no_sql_models/identity.py @@ -13,8 +13,8 @@ def validate_timestamp(doc): """Validate and fix timestamp fields in MongoDB documents""" if not doc: return doc - - timestamp_fields = ['modified_at', 'created_at', 'accessed_at', 'timestamp'] + + timestamp_fields = ["modified_at", "created_at", "accessed_at", "timestamp"] for field in timestamp_fields: if field in doc and not isinstance(doc[field], (int, float)): # Convert to proper timestamp if it's not already diff --git a/databases/sql_models/building/build.py b/databases/sql_models/building/build.py index e7777b4..31d6e78 100644 --- a/databases/sql_models/building/build.py +++ b/databases/sql_models/building/build.py @@ -41,6 +41,7 @@ from databases.language_models.building.build import ( BuildPersonProvidingLanguageModel, ) + class BuildTypes(CrudCollection): """ BuildTypes class based on declarative_base and BaseMixin via session @@ -177,12 +178,8 @@ class Build(CrudCollection, SelectActionWithEmployee): heating_system: Mapped[bool] = mapped_column(Boolean, server_default="True") cooling_system: Mapped[bool] = mapped_column(Boolean, server_default="False") hot_water_system: Mapped[bool] = mapped_column(Boolean, server_default="False") - block_service_man_count: Mapped[int] = mapped_column( - Integer, server_default="0" - ) - security_service_man_count: Mapped[int] = mapped_column( - Integer, server_default="0" - ) + block_service_man_count: Mapped[int] = mapped_column(Integer, server_default="0") + security_service_man_count: Mapped[int] = mapped_column(Integer, server_default="0") garage_count: Mapped[int] = mapped_column( Integer, server_default="0", comment="Garage Count" ) diff --git a/databases/sql_models/company/company.py b/databases/sql_models/company/company.py index 46578fa..627124c 100644 --- a/databases/sql_models/company/company.py +++ b/databases/sql_models/company/company.py @@ -2,7 +2,16 @@ from fastapi.exceptions import HTTPException from databases.sql_models.core_mixin import CrudCollection -from sqlalchemy import String, Integer, Boolean, ForeignKey, Index, Identity, TIMESTAMP, func +from sqlalchemy import ( + String, + Integer, + Boolean, + ForeignKey, + Index, + Identity, + TIMESTAMP, + func, +) from sqlalchemy.orm import mapped_column, relationship, Mapped from api_configs import RelationAccess diff --git a/databases/sql_models/postgres_database.py b/databases/sql_models/postgres_database.py index 0eb7c0d..a711d5e 100644 --- a/databases/sql_models/postgres_database.py +++ b/databases/sql_models/postgres_database.py @@ -9,12 +9,14 @@ engine_config = { "pool_size": 20, "max_overflow": 10, "echo": True, - "echo_pool":True, + "echo_pool": True, "isolation_level": "READ COMMITTED", "pool_pre_ping": True, } -engine = create_engine(**engine_config, ) +engine = create_engine( + **engine_config, +) SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False) session = scoped_session(sessionmaker(bind=engine)) diff --git a/databases/sql_models/sql_operations.py b/databases/sql_models/sql_operations.py index 43739f4..328995b 100644 --- a/databases/sql_models/sql_operations.py +++ b/databases/sql_models/sql_operations.py @@ -52,9 +52,9 @@ class FilterAttributes: meta_data = getattr(cls, "meta_data", {}) meta_data_created = meta_data.get("created", False) if meta_data_created: - print('meta_data_created commit', meta_data_created) + print("meta_data_created commit", meta_data_created) cls.__session__.commit() - print('meta_data_created rollback', meta_data_created) + print("meta_data_created rollback", meta_data_created) cls.__session__.rollback() # cls.raise_http_exception( # status_code="HTTP_304_NOT_MODIFIED", @@ -82,6 +82,7 @@ class FilterAttributes: data={}, message=str(e.__context__).split("\n")[0], ) + @classmethod def rollback(cls): """Rollback the current session.""" diff --git a/service_app_init/runner.py b/service_app_init/runner.py index 8712495..6b66bee 100644 --- a/service_app_init/runner.py +++ b/service_app_init/runner.py @@ -16,6 +16,7 @@ def do_alembic(): generate_alembic_with_session(text=text) + # # def create_one_address(): # from databases import ( diff --git a/testers/test.py b/testers/test.py index 14c2e19..f974c92 100644 --- a/testers/test.py +++ b/testers/test.py @@ -36,10 +36,9 @@ CrudCollection.http_exception = HTTPException new_person_find_or_create = People.find_or_create(**person_dict) new_person_find_or_create.save_via_metadata() -print('meta_data', new_person_find_or_create.meta_data) -print('new_person_find_or_create', new_person_find_or_create) +print("meta_data", new_person_find_or_create.meta_data) +print("new_person_find_or_create", new_person_find_or_create) quit() new_user_find_or_create = Users.find_or_create(**user_dict) new_user_find_or_abort = Users.find_or_abort(**user_dict) - diff --git a/testers/test_helpers.py b/testers/test_helpers.py index 0e11d57..985530d 100644 --- a/testers/test_helpers.py +++ b/testers/test_helpers.py @@ -9,7 +9,7 @@ def create_test_occupant_token( person_id: int = 1, person_uu_id: str = "test-person", credentials: Optional[Dict[str, Any]] = None, - available_occupants: Optional[Dict[str, Any]] = None + available_occupants: Optional[Dict[str, Any]] = None, ) -> OccupantTokenObject: """Create a test occupant token object""" return OccupantTokenObject( @@ -32,7 +32,7 @@ def create_test_employee_token( person_id: int = 1, person_uu_id: str = "test-person", credentials: Optional[Dict[str, Any]] = None, - selected_company: Optional[Dict[str, Any]] = None + selected_company: Optional[Dict[str, Any]] = None, ) -> EmployeeTokenObject: """Create a test employee token object""" return EmployeeTokenObject( @@ -50,5 +50,6 @@ def create_test_employee_token( class MockRequest: """Mock request object for testing""" + def __init__(self, headers: Optional[Dict[str, str]] = None): self.headers = headers or {} diff --git a/testers/test_token_management.py b/testers/test_token_management.py index 60736bb..ca296fb 100644 --- a/testers/test_token_management.py +++ b/testers/test_token_management.py @@ -35,9 +35,7 @@ def test_save_object_to_redis_success(): # Test save result = AccessObjectActions.save_object_to_redis( - access_token=access_token, - model_object=model_object, - expiry_minutes=1 + access_token=access_token, model_object=model_object, expiry_minutes=1 ) assert result is True @@ -69,7 +67,7 @@ def test_save_object_to_redis_expiry(): AccessObjectActions.save_object_to_redis( access_token=access_token, model_object=model_object, - expiry_minutes=2/60 # 2 seconds + expiry_minutes=2 / 60, # 2 seconds ) # Verify token exists @@ -86,13 +84,15 @@ def test_save_object_to_redis_expiry(): RedisActions.get_object_via_access_key( MockRequest(headers={Auth.ACCESS_TOKEN_TAG: access_token}) ) - assert any(msg in str(exc_info.value) for msg in ["Token expired", "Invalid credentials"]) + assert any( + msg in str(exc_info.value) for msg in ["Token expired", "Invalid credentials"] + ) def test_get_object_via_user_uu_id(): """Test retrieving all tokens for a user""" user_id = "test-uuid-3" - + # Create multiple tokens for same user tokens = [] for i in range(3): @@ -114,7 +114,7 @@ def test_get_object_via_user_uu_id(): # Get all tokens user_tokens = AccessObjectActions.get_object_via_user_uu_id(user_id) assert len(user_tokens) == 3 - + # Verify each token for token in tokens: found = False @@ -167,7 +167,7 @@ def test_timezone_handling(): token_data = RedisActions.get_object_via_access_key( MockRequest(headers={Auth.ACCESS_TOKEN_TAG: access_token}) ) - + # Verify expiry time is in future assert system_arrow.from_timestamp(token_data.expires_at) > system_arrow.now()