diff --git a/lib/controllers/environment.py b/lib/controllers/environment.py index 17d0cb0..e9de748 100644 --- a/lib/controllers/environment.py +++ b/lib/controllers/environment.py @@ -44,7 +44,7 @@ def env(self, env: Env): self._env = env @staticmethod - async def get_rocketpy_env(env: Env) -> RocketPyEnvironment: + def get_rocketpy_env(env: Env) -> RocketPyEnvironment: """ Get the rocketpy env object. @@ -70,9 +70,7 @@ async def create_env(self) -> Union[EnvCreated, HTTPException]: views.EnvCreated """ try: - created_env = await EnvRepository( - environment=self.env - ).create_env() + await EnvRepository.fetch_env(self.env).create_env() except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.environment.create_env: {exc_str}") @@ -81,7 +79,7 @@ async def create_env(self) -> Union[EnvCreated, HTTPException]: detail=f"Failed to create environment: {exc_str}", ) from e else: - return EnvCreated(env_id=created_env.env_id) + return EnvCreated(env_id=self.env.env_id) finally: logger.info( f"Call to controllers.environment.create_env completed for Env {hash(self.env)}" @@ -141,7 +139,7 @@ async def get_rocketpy_env_as_jsonpickle( """ try: read_env = await cls.get_env_by_id(env_id) - rocketpy_env = await cls.get_rocketpy_env(read_env) + rocketpy_env = cls.get_rocketpy_env(read_env) except HTTPException as e: raise e from e except Exception as e: @@ -162,7 +160,7 @@ async def get_rocketpy_env_as_jsonpickle( f"Call to controllers.environment.get_rocketpy_env_as_jsonpickle completed for Env {env_id}" ) - async def update_env( + async def update_env_by_id( self, env_id: str ) -> Union[EnvUpdated, HTTPException]: """ @@ -178,12 +176,8 @@ async def update_env( HTTP 404 Not Found: If the env is not found in the database. """ try: - await EnvController.get_env_by_id(env_id) - updated_env = await EnvRepository( - environment=self.env - ).update_env_by_id(env_id) - except HTTPException as e: - raise e from e + env_repo = await EnvRepository.fetch_env(self.env).create_env() + await env_repo.delete_env_by_id(env_id) except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.environment.update_env: {exc_str}") @@ -192,14 +186,16 @@ async def update_env( detail=f"Failed to update environment: {exc_str}", ) from e else: - return EnvUpdated(new_env_id=updated_env.env_id) + return EnvUpdated(new_env_id=self.env.env_id) finally: logger.info( f"Call to controllers.environment.update_env completed for Env {env_id}; Env {hash(self.env)}" ) @staticmethod - async def delete_env(env_id: str) -> Union[EnvDeleted, HTTPException]: + async def delete_env_by_id( + env_id: str, + ) -> Union[EnvDeleted, HTTPException]: """ Delete a models.Env from the database. @@ -246,7 +242,7 @@ async def simulate_env( """ try: read_env = await cls.get_env_by_id(env_id) - rocketpy_env = await cls.get_rocketpy_env(read_env) + rocketpy_env = cls.get_rocketpy_env(read_env) env_simulation_numbers = EnvData.parse_obj( rocketpy_env.all_info_returned() ) diff --git a/lib/controllers/flight.py b/lib/controllers/flight.py index 966234c..3013e29 100644 --- a/lib/controllers/flight.py +++ b/lib/controllers/flight.py @@ -80,7 +80,7 @@ def flight(self, flight: Flight): self._flight = flight @staticmethod - async def get_rocketpy_flight(flight: Flight) -> RocketPyFlight: + def get_rocketpy_flight(flight: Flight) -> RocketPyFlight: """ Get the rocketpy flight object. @@ -106,9 +106,7 @@ async def create_flight(self) -> Union[FlightCreated, HTTPException]: views.FlightCreated """ try: - created_flight = await FlightRepository( - flight=self.flight - ).create_flight( + await FlightRepository.fetch_flight(self.flight).create_flight( motor_kind=self.motor_kind, rocket_option=self.rocket_option ) except Exception as e: @@ -116,10 +114,10 @@ async def create_flight(self) -> Union[FlightCreated, HTTPException]: logger.error(f"controllers.flight.create_flight: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to create flight: {exc_str}", + detail=f"Failed to create flight: {exc_str}", ) from e else: - return FlightCreated(flight_id=created_flight.flight_id) + return FlightCreated(flight_id=self.flight.flight_id) finally: logger.info( f"Call to controllers.flight.create_flight completed for Flight {hash(self.flight)}" @@ -146,7 +144,7 @@ async def get_flight_by_id(flight_id: str) -> Union[Flight, HTTPException]: logger.error(f"controllers.flight.get_flight_by_id: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to read flight: {exc_str}", + detail=f"Failed to read flight: {exc_str}", ) from e else: if read_flight: @@ -154,7 +152,7 @@ async def get_flight_by_id(flight_id: str) -> Union[Flight, HTTPException]: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found.", - ) from e + ) finally: logger.info( f"Call to controllers.flight.get_flight_by_id completed for Flight {flight_id}" @@ -179,7 +177,7 @@ async def get_rocketpy_flight_as_jsonpickle( """ try: read_flight = await cls.get_flight_by_id(flight_id) - rocketpy_flight = await cls.get_rocketpy_flight(read_flight) + rocketpy_flight = cls.get_rocketpy_flight(read_flight) except HTTPException as e: raise e from e except Exception as e: @@ -189,7 +187,7 @@ async def get_rocketpy_flight_as_jsonpickle( ) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to read flight: {exc_str}", + detail=f"Failed to read flight: {exc_str}", ) from e else: return FlightPickle( @@ -200,7 +198,7 @@ async def get_rocketpy_flight_as_jsonpickle( f"Call to controllers.flight.get_rocketpy_flight_as_jsonpickle completed for Flight {flight_id}" ) - async def update_flight( + async def update_flight_by_id( self, flight_id: str ) -> Union[FlightUpdated, HTTPException]: """ @@ -216,30 +214,28 @@ async def update_flight( HTTP 404 Not Found: If the flight is not found in the database. """ try: - await FlightController.get_flight_by_id(flight_id) - updated_flight = await FlightRepository( - flight=self.flight, - motor_kind=self.motor_kind, - rocket_option=self.rocket_option, - ).update_flight_by_id(flight_id=flight_id) - except HTTPException as e: - raise e from e + flight_repo = await FlightRepository.fetch_flight( + self.flight + ).create_flight( + motor_kind=self.motor_kind, rocket_option=self.rocket_option + ) + await flight_repo.delete_flight_by_id(flight_id) except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.flight.update_flight: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to update flight: {exc_str}", + detail=f"Failed to update flight: {exc_str}", ) from e else: - return FlightUpdated(new_flight_id=updated_flight.flight_id) + return FlightUpdated(new_flight_id=self.flight.flight_id) finally: logger.info( f"Call to controllers.flight.update_flight completed for Flight {flight_id}" ) @classmethod - async def update_env( + async def update_env_by_flight_id( cls, flight_id: str, env: Env ) -> Union[FlightUpdated, HTTPException]: """ @@ -260,9 +256,13 @@ async def update_env( new_flight = read_flight.dict() new_flight["environment"] = env new_flight = Flight(**new_flight) - updated_flight = await FlightRepository( - flight=new_flight, flight_id=flight_id - ).update_flight() + flight_repo = await FlightRepository.fetch_flight( + new_flight + ).create_flight( + motor_kind=read_flight.rocket.motor.motor_kind, + rocket_option=read_flight.rocket.rocket_option, + ) + await flight_repo.delete_flight_by_id(flight_id) except HTTPException as e: raise e from e except Exception as e: @@ -270,18 +270,18 @@ async def update_env( logger.error(f"controllers.flight.update_env: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to update environment: {exc_str}", + detail=f"Failed to update environment: {exc_str}", ) from e else: - return FlightUpdated(new_flight_id=updated_flight.flight_id) + return FlightUpdated(new_flight_id=new_flight.flight_id) finally: logger.info( f"Call to controllers.flight.update_env completed for Flight {flight_id}; Env {hash(env)}" ) - @staticmethod - async def update_rocket( - flight_id: str, rocket: Rocket, rocket_option, motor_kind + @classmethod + async def update_rocket_by_flight_id( + cls, flight_id: str, rocket: Rocket, rocket_option, motor_kind ) -> Union[FlightUpdated, HTTPException]: """ Update a models.Flight.rocket in the database. @@ -297,16 +297,17 @@ async def update_rocket( HTTP 404 Not Found: If the flight is not found in the database. """ try: - read_flight = await FlightController.get_flight_by_id(flight_id) + read_flight = await cls.get_flight_by_id(flight_id) updated_rocket = rocket.dict() updated_rocket["rocket_option"] = rocket_option updated_rocket["motor"]["motor_kind"] = motor_kind new_flight = read_flight.dict() new_flight["rocket"] = updated_rocket new_flight = Flight(**new_flight) - new_flight_id = await FlightRepository( - flight=new_flight, flight_id=flight_id - ).update_flight() + flight_repo = await FlightRepository.fetch_flight( + new_flight + ).create_flight(motor_kind=motor_kind, rocket_option=rocket_option) + await flight_repo.delete_flight_by_id(flight_id) except HTTPException as e: raise e from e except Exception as e: @@ -314,17 +315,17 @@ async def update_rocket( logger.error(f"controllers.flight.update_rocket: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to update rocket: {exc_str}", + detail=f"Failed to update rocket: {exc_str}", ) from e else: - return FlightUpdated(new_flight_id=new_flight_id) + return FlightUpdated(new_flight_id=new_flight.flight_id) finally: logger.info( f"Call to controllers.flight.update_rocket completed for Flight {flight_id}; Rocket {hash(rocket)}" ) @staticmethod - async def delete_flight( + async def delete_flight_by_id( flight_id: str, ) -> Union[FlightDeleted, HTTPException]: """ @@ -341,14 +342,12 @@ async def delete_flight( """ try: await FlightRepository().delete_flight_by_id(flight_id) - except HTTPException as e: - raise e from e except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.flight.delete_flight: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to delete flight: {exc_str}", + detail=f"Failed to delete flight: {exc_str}", ) from e else: return FlightDeleted(deleted_flight_id=flight_id) @@ -635,7 +634,7 @@ async def simulate_flight( logger.error(f"controllers.flight.simulate_flight: {exc_str}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Failed to simulate flight: {exc_str}", + detail=f"Failed to simulate flight: {exc_str}", ) from e else: return flight_summary diff --git a/lib/controllers/motor.py b/lib/controllers/motor.py index d155013..cda4426 100644 --- a/lib/controllers/motor.py +++ b/lib/controllers/motor.py @@ -118,7 +118,7 @@ async def create_motor(self) -> Union[MotorCreated, HTTPException]: views.MotorCreated """ try: - created_motor = MotorRepository(motor=self.motor).create_motor( + await MotorRepository.fetch_motor(self.motor).create_motor( motor_kind=self.motor_kind ) except Exception as e: @@ -129,7 +129,7 @@ async def create_motor(self) -> Union[MotorCreated, HTTPException]: detail=f"Failed to create motor: {exc_str}", ) from e else: - return MotorCreated(motor_id=created_motor.motor_id) + return MotorCreated(motor_id=self.motor.motor_id) finally: logger.info( f"Call to controllers.motor.create_motor completed for Motor {self.motor.motor_id}" @@ -210,7 +210,7 @@ async def get_rocketpy_motor_as_jsonpickle( f"Call to controllers.motor.get_rocketpy_motor_as_jsonpickle completed for Motor {motor_id}" ) - async def update_motor( + async def update_motor_by_id( self, motor_id: str ) -> Union[MotorUpdated, HTTPException]: """ @@ -226,12 +226,10 @@ async def update_motor( HTTP 404 Not Found: If the motor is not found in the database. """ try: - await MotorController.get_motor_by_id(motor_id) - updated_motor = await MotorRepository( - motor=self.motor - ).update_motor_by_id(motor_id=motor_id, motor_kind=self.motor_kind) - except HTTPException as e: - raise e from e + motor_repo = await MotorRepository.fetch_motor( + self.motor + ).create_motor(motor_kind=self.motor_kind) + await motor_repo.delete_motor_by_id(motor_id) except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.motor.update_motor: {exc_str}") @@ -240,14 +238,14 @@ async def update_motor( detail=f"Failed to update motor: {exc_str}", ) from e else: - return MotorUpdated(new_motor_id=updated_motor.motor_id) + return MotorUpdated(new_motor_id=self.motor.motor_id) finally: logger.info( f"Call to controllers.motor.update_motor completed for Motor {motor_id}" ) @staticmethod - async def delete_motor( + async def delete_motor_by_id( motor_id: str, ) -> Union[MotorDeleted, HTTPException]: """ @@ -296,7 +294,7 @@ async def simulate_motor( """ try: read_motor = await MotorRepository().get_motor_by_id(motor_id) - motor = await cls.get_rocketpy_motor(read_motor) + motor = cls.get_rocketpy_motor(read_motor) motor_simulation_numbers = MotorData( total_burning_time="Total Burning Time: " diff --git a/lib/controllers/rocket.py b/lib/controllers/rocket.py index a7b9545..88bcbd2 100644 --- a/lib/controllers/rocket.py +++ b/lib/controllers/rocket.py @@ -83,8 +83,8 @@ def get_rocketpy_rocket(cls, rocket: Rocket) -> RocketPyRocket: radius=rocket.radius, mass=rocket.mass, inertia=rocket.inertia, - power_off_drag=f"lib/data/{rocket.rocket_option.value.lower()}/powerOffDragCurve.csv", - power_on_drag=f"lib/data/{rocket.rocket_option.value.lower()}/powerOnDragCurve.csv", + power_off_drag=f"lib/data/{rocket.rocket_option.lower()}/powerOffDragCurve.csv", + power_on_drag=f"lib/data/{rocket.rocket_option.lower()}/powerOnDragCurve.csv", center_of_mass_without_motor=rocket.center_of_mass_without_motor, coordinate_system_orientation=rocket.coordinate_system_orientation, ) @@ -140,9 +140,7 @@ async def create_rocket(self) -> Union[RocketCreated, HTTPException]: views.RocketCreated """ try: - created_rocket = await RocketRepository( - rocket=self.rocket - ).create_rocket( + await RocketRepository.fetch_rocket(self.rocket).create_rocket( rocket_option=self.rocket_option, motor_kind=self.motor_kind ) except Exception as e: @@ -153,7 +151,7 @@ async def create_rocket(self) -> Union[RocketCreated, HTTPException]: detail=f"Failed to create rocket: {exc_str}", ) from e else: - return RocketCreated(rocket_id=created_rocket.rocket_id) + return RocketCreated(rocket_id=self.rocket.rocket_id) finally: logger.info( f"Call to controllers.rocket.create_rocket completed for Rocket {hash(self.rocket)}" @@ -235,7 +233,7 @@ async def get_rocketpy_rocket_as_jsonpickle( f"Call to controllers.rocket.get_rocketpy_rocket_as_jsonpickle completed for Rocket {rocket_id}" ) - async def update_rocket( + async def update_rocket_by_id( self, rocket_id: str ) -> Union[RocketUpdated, HTTPException]: """ @@ -251,14 +249,12 @@ async def update_rocket( HTTP 404 Not Found: If the rocket is not found in the database. """ try: - await RocketController.get_rocket_by_id(rocket_id) - updated_rocket = await RocketRepository( - rocket=self.rocket - ).update_rocket_by_id( - rocket_id=rocket_id, rocket_option=self.rocket_option + rocket_repo = await RocketRepository.fetch_rocket( + self.rocket + ).create_rocket( + rocket_option=self.rocket_option, motor_kind=self.motor_kind ) - except HTTPException as e: - raise e from e + await rocket_repo.delete_rocket_by_id(rocket_id) except Exception as e: exc_str = parse_error(e) logger.error(f"controllers.rocket.update_rocket: {exc_str}") @@ -267,14 +263,14 @@ async def update_rocket( detail=f"Failed to update rocket: {exc_str}", ) from e else: - return RocketUpdated(new_rocket_id=updated_rocket.rocket_id) + return RocketUpdated(new_rocket_id=self.rocket.rocket_id) finally: logger.info( f"Call to controllers.rocket.update_rocket completed for Rocket {rocket_id}" ) @staticmethod - async def delete_rocket( + async def delete_rocket_by_id( rocket_id: str, ) -> Union[RocketDeleted, HTTPException]: """ @@ -324,7 +320,7 @@ async def simulate_rocket( """ try: read_rocket = await cls.get_rocket_by_id(rocket_id) - rocket = await cls.get_rocketpy_rocket(read_rocket) + rocket = cls.get_rocketpy_rocket(read_rocket) _inertia_details = InertiaDetails( rocket_mass_without_propellant="Rocket Mass: {:.3f} kg (No Propellant)".format( @@ -413,7 +409,7 @@ async def simulate_rocket( aerodynamics_center_of_pressure=_aerodynamics_center_of_pressure, distance_cop_to_codm="Distance from Center of Pressure to Center of Dry Mass: " + "{:.3f}".format( - rocket.center_of_mass(0) - rocket.cp_position + rocket.center_of_mass(0) - rocket.cp_position(0) ) + " m", initial_static_margin="Initial Static Margin: " diff --git a/lib/models/aerosurfaces.py b/lib/models/aerosurfaces.py index 73284ea..0dff121 100644 --- a/lib/models/aerosurfaces.py +++ b/lib/models/aerosurfaces.py @@ -28,8 +28,7 @@ class Fins(BaseModel, frozen=True): class TrapezoidalFins(Fins, frozen=True): - def __init__(self): - super().__init__() + pass class Tail(BaseModel, frozen=True): diff --git a/lib/models/motor.py b/lib/models/motor.py index 22a9af2..1598b49 100644 --- a/lib/models/motor.py +++ b/lib/models/motor.py @@ -17,7 +17,7 @@ class MotorKinds(str, Enum): class MotorEngines(str, Enum): - CESARONI: str = "CESARONI_M1670" + CESARONI_M1670: str = "CESARONI_M1670" CUSTOM: str = "CUSTOM" @@ -71,11 +71,11 @@ def __init__(self, **kwargs): } match self.tank_kind: - case TankKinds.level: + case TankKinds.LEVEL: tank = LevelBasedTank( **tank_core, liquid_height=self.liquid_height ) - case TankKinds.mass: + case TankKinds.MASS: tank = MassBasedTank( **tank_core, liquid_mass=self.liquid_mass, @@ -105,7 +105,7 @@ class Motor(BaseModel, frozen=True): # TODO: thrust_source must be the id of a previously uploaded .eng file and a list of "default" files must be provided in the api docs # Required parameters - thrust_source: MotorEngines = MotorEngines.cesaroni + thrust_source: MotorEngines = MotorEngines.CESARONI_M1670 burn_time: float = 3.9 nozzle_radius: float = 0.033 dry_mass: float = 1.815 @@ -128,7 +128,7 @@ class Motor(BaseModel, frozen=True): "nozzle_to_combustion_chamber" ) - def __init__(self, motor_kind=MotorKinds.solid, **kwargs): + def __init__(self, motor_kind=MotorKinds.SOLID, **kwargs): super().__init__(**kwargs) self._motor_kind = motor_kind diff --git a/lib/repositories/environment.py b/lib/repositories/environment.py index 6e81eca..55db695 100644 --- a/lib/repositories/environment.py +++ b/lib/repositories/environment.py @@ -16,14 +16,15 @@ class EnvRepository(Repository): """ - def __init__(self, environment: Env | None = None): + def __init__(self): super().__init__("environments") - self._env = environment if environment else None - self._env_id = environment.env_id if environment else None - def __del__(self): - self.connection.close() - super().__del__() + @classmethod + def fetch_env(cls, environment: Env): + instance = cls() + instance.env = environment + instance.env_id = environment.env_id + return instance @property def env(self) -> Env: @@ -63,30 +64,6 @@ async def create_env(self): f"Call to repositories.environment.create_env completed for Env {self.env_id}" ) - async def update_env_by_id(self, env_id: str): - """ - Updates a models.Env in the database - - Returns: - self - """ - try: - environment_to_dict = self.env.dict() - environment_to_dict["env_id"] = self.env_id - await self.collection.update_one( - {"env_id": env_id}, {"$set": environment_to_dict} - ) - except Exception as e: - exc_str = parse_error(e) - logger.error(f"repositories.environment.update_env: {exc_str}") - raise Exception(f"Error updating environment: {exc_str}") from e - else: - return self - finally: - logger.info( - f"Call to repositories.environment.update_env completed for Env {self.env_id}" - ) - async def get_env_by_id(self, env_id: str) -> Union[Env, None]: """ Gets a models.Env from the database diff --git a/lib/repositories/flight.py b/lib/repositories/flight.py index e2c6dbc..1076e76 100644 --- a/lib/repositories/flight.py +++ b/lib/repositories/flight.py @@ -16,14 +16,15 @@ class FlightRepository(Repository): """ - def __init__(self, flight: Flight | None = None): + def __init__(self): super().__init__("flights") - self._flight = flight if flight else None - self._flight_id = flight.flight_id if flight else None - def __del__(self): - self.connection.close() - super().__del__() + @classmethod + def fetch_flight(cls, flight: Flight): + instance = cls() + instance.flight = flight + instance.flight_id = flight.flight_id + return instance @property def flight(self) -> Flight: @@ -42,7 +43,7 @@ def flight_id(self, flight_id: "str"): self._flight_id = flight_id async def create_flight( - self, motor_kind: str = "SOLID", rocket_option: str = "CALISTO" + self, *, motor_kind: str = "SOLID", rocket_option: str = "CALISTO" ): """ Creates a non-existing models.Flight in the database @@ -71,36 +72,6 @@ async def create_flight( f"Call to repositories.flight.create_flight completed for Flight {self.flight_id}" ) - async def update_flight_by_id( - self, - *, - flight_id: str, - motor_kind: str = "SOLID", - rocket_option: str = "CALISTO", - ): - """ - Updates a models.Flight in the database - - Returns: - self - """ - try: - flight_to_dict = self.flight.dict() - flight_to_dict["flight_id"] = self.flight_id - flight_to_dict["rocket"]["rocket_option"] = rocket_option - flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind - await self.collection.update_one( - {"flight_id": flight_id}, {"$set": flight_to_dict} - ) - except Exception as e: - exc_str = parse_error(e) - logger.error(f"repositories.flight.update_flight: {exc_str}") - raise Exception(f"Error updating flight: {exc_str}") from e - finally: - logger.info( - f"Call to repositories.flight.update_flight completed for Flight {self.flight_id}" - ) - async def get_flight_by_id(self, flight_id: str) -> Union[Flight, None]: """ Gets a models.Flight from the database diff --git a/lib/repositories/motor.py b/lib/repositories/motor.py index 006e2c6..6db9683 100644 --- a/lib/repositories/motor.py +++ b/lib/repositories/motor.py @@ -16,14 +16,15 @@ class MotorRepository(Repository): """ - def __init__(self, motor: Motor | None = None): + def __init__(self): super().__init__("motors") - self._motor = motor if motor else None - self._motor_id = motor.motor_id if motor else None - def __del__(self): - self.connection.close() - super().__del__() + @classmethod + def fetch_motor(cls, motor: Motor): + instance = cls() + instance.motor = motor + instance.motor_id = motor.motor_id + return instance @property def motor(self) -> Motor: @@ -67,33 +68,6 @@ async def create_motor(self, motor_kind: str = "SOLID"): f"Call to repositories.motor.create_motor completed for Motor {self.motor_id}" ) - async def update_motor_by_id( - self, *, motor_id: str, motor_kind: str = "SOLID" - ): - """ - Updates a models.Motor in the database - - Returns: - self - """ - try: - motor_to_dict = self.motor.dict() - motor_to_dict["motor_id"] = self.motor_id - motor_to_dict["motor_kind"] = motor_kind - await self.collection.update_one( - {"motor_id": motor_id}, {"$set": motor_to_dict} - ) - except Exception as e: - exc_str = parse_error(e) - logger.error(f"repositories.motor.update_motor: {exc_str}") - raise Exception(f"Error updating motor: {exc_str}") from e - else: - return self - finally: - logger.info( - f"Call to repositories.motor.update_motor completed for Motor {self.motor_id}" - ) - async def get_motor_by_id(self, motor_id: str) -> Union[motor, None]: """ Gets a models.Motor from the database diff --git a/lib/repositories/repo.py b/lib/repositories/repo.py index f76dfea..39afffd 100644 --- a/lib/repositories/repo.py +++ b/lib/repositories/repo.py @@ -1,6 +1,7 @@ +import threading +from lib.secrets import Secrets from motor.motor_asyncio import AsyncIOMotorClient from pymongo.server_api import ServerApi -from lib.secrets import secrets_instance class Repository: @@ -8,26 +9,54 @@ class Repository: Base class for all repositories (singleton) """ - _self = None + _instances = {} + _lock = threading.Lock() - def __new__(cls, *args, **kwargs): - if cls._self is None: - cls._self = super().__new__(cls) - return cls._self + def __new__(cls): + with cls._lock: + if cls not in cls._instances: + cls._instances[cls] = super(Repository, cls).__new__(cls) + cls._instances[cls]._initialized = False # Initialize here + return cls._instances[cls] def __init__(self, collection: str): - self.connection_string = secrets_instance.get_secret( - "MONGODB_CONNECTION_STRING" - ) - self.client = AsyncIOMotorClient( - self.connection_string, - server_api=ServerApi("1"), - maxIdleTimeMS=5000, - connectTimeoutMS=5000, - serverSelectionTimeoutMS=30000, - ) - self.db = self.client.rocketpy - self.collection = self.db[collection] + if not self._initialized: + self._connection_string = Secrets.get_secret( + "MONGODB_CONNECTION_STRING" + ) + self._client = AsyncIOMotorClient( + self.connection_string, + server_api=ServerApi("1"), + maxIdleTimeMS=5000, + connectTimeoutMS=5000, + serverSelectionTimeoutMS=30000, + ) + self._collection = self.client.rocketpy[collection] + self._initialized = True # Mark as initialized + + @property + def connection_string(self): + return self._connection_string + + @connection_string.setter + def connection_string(self, value): + self._connection_string = value + + @property + def client(self): + return self._client + + @client.setter + def client(self, value): + self._client = value + + @property + def collection(self): + return self._collection + + @collection.setter + def collection(self, value): + self._collection = value async def close_connection(self) -> None: self.client.close() diff --git a/lib/repositories/rocket.py b/lib/repositories/rocket.py index 632b193..0e76a08 100644 --- a/lib/repositories/rocket.py +++ b/lib/repositories/rocket.py @@ -16,14 +16,15 @@ class RocketRepository(Repository): """ - def __init__(self, rocket: Rocket | None = None): + def __init__(self): super().__init__("rockets") - self._rocket = rocket if rocket else None - self._rocket_id = rocket.rocket_id if rocket else None - def __del__(self): - self.connection.close() - super().__del__() + @classmethod + def fetch_rocket(cls, rocket: Rocket): + instance = cls() + instance.rocket = rocket + instance.rocket_id = rocket.rocket_id + return instance @property def rocket(self) -> Rocket: @@ -42,7 +43,7 @@ def rocket_id(self, rocket_id: "str"): self._rocket_id = rocket_id async def create_rocket( - self, rocket_option: str = "CALISTO", motor_kind: str = "SOLID" + self, *, rocket_option: str = "CALISTO", motor_kind: str = "SOLID" ): """ Creates a non-existing models.Rocket in the database @@ -71,38 +72,6 @@ async def create_rocket( f"Call to repositories.rocket.create_rocket completed for Rocket {self.rocket_id}" ) - async def update_rocket_by_id( - self, - *, - rocket_id: str, - rocket_option: str = "CALISTO", - motor_kind: str = "SOLID", - ): - """ - Updates a models.Rocket in the database - - Returns: - self - """ - try: - rocket_to_dict = self.rocket.dict() - rocket_to_dict["rocket_id"] = self.rocket_id - rocket_to_dict["rocket_option"] = rocket_option - rocket_to_dict["motor"]["motor_kind"] = motor_kind - await self.collection.update_one( - {"rocket_id": rocket_id}, {"$set": rocket_to_dict} - ) - except Exception as e: - exc_str = parse_error(e) - logger.error(f"repositories.rocket.update_rocket: {exc_str}") - raise Exception(f"Error updating rocket: {exc_str}") from e - else: - return self - finally: - logger.info( - f"Call to repositories.rocket.update_rocket completed for Rocket {self.rocket_id}" - ) - async def get_rocket_by_id(self, rocket_id: str) -> Union[Rocket, None]: """ Gets a models.Rocket from the database diff --git a/lib/routes/environment.py b/lib/routes/environment.py index 91d70ef..6647073 100644 --- a/lib/routes/environment.py +++ b/lib/routes/environment.py @@ -26,7 +26,7 @@ @router.post("/") -async def create_env(env: Env) -> "EnvCreated": +async def create_env(env: Env) -> EnvCreated: """ Creates a new environment @@ -37,7 +37,7 @@ async def create_env(env: Env) -> "EnvCreated": @router.get("/{env_id}") -async def read_env(env_id: str) -> "Env": +async def read_env(env_id: str) -> Env: """ Reads an environment @@ -48,7 +48,7 @@ async def read_env(env_id: str) -> "Env": @router.put("/{env_id}") -async def update_env(env_id: str, env: Env) -> "EnvUpdated": +async def update_env(env_id: str, env: Env) -> EnvUpdated: """ Updates an environment @@ -58,22 +58,22 @@ async def update_env(env_id: str, env: Env) -> "EnvUpdated": env: models.Env JSON ``` """ - return await EnvController(env).update_env(env_id) + return await EnvController(env).update_env_by_id(env_id) @router.delete("/{env_id}") -async def delete_env(env_id: str) -> "EnvDeleted": +async def delete_env(env_id: str) -> EnvDeleted: """ Deletes an environment ## Args ``` env_id: Environment ID hash ``` """ - return await EnvController.delete_env(env_id) + return await EnvController.delete_env_by_id(env_id) @router.get("/rocketpy/{env_id}") -async def read_rocketpy_env(env_id: str) -> "EnvPickle": +async def read_rocketpy_env(env_id: str) -> EnvPickle: """ Loads rocketpy.environment as jsonpickle string @@ -84,7 +84,7 @@ async def read_rocketpy_env(env_id: str) -> "EnvPickle": @router.get("/{env_id}/simulate") -async def simulate_env(env_id: str) -> "EnvSummary": +async def simulate_env(env_id: str) -> EnvSummary: """ Loads rocketpy.environment simulation diff --git a/lib/routes/flight.py b/lib/routes/flight.py index e920041..a4e4e53 100644 --- a/lib/routes/flight.py +++ b/lib/routes/flight.py @@ -31,7 +31,7 @@ @router.post("/") async def create_flight( flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds -) -> "FlightCreated": +) -> FlightCreated: """ Creates a new flight @@ -44,18 +44,18 @@ async def create_flight( @router.get("/{flight_id}") -async def read_flight(flight_id: int) -> "Flight": +async def read_flight(flight_id: str) -> Flight: """ Reads a flight ## Args ``` flight_id: Flight ID hash ``` """ - return await FlightController.get_flight(flight_id) + return await FlightController.get_flight_by_id(flight_id) @router.get("/rocketpy/{flight_id}") -async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": +async def read_rocketpy_flight(flight_id: str) -> FlightPickle: """ Reads a rocketpy flight object @@ -66,7 +66,7 @@ async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": @router.put("/{flight_id}/env") -async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": +async def update_flight_env(flight_id: str, env: Env) -> FlightUpdated: """ Updates flight environment @@ -76,16 +76,16 @@ async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": env: env object as JSON ``` """ - return await FlightController.update_env(flight_id, env) + return await FlightController.update_env_by_flight_id(flight_id, env) @router.put("/{flight_id}/rocket") async def update_flight_rocket( - flight_id: int, + flight_id: str, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds, -) -> "FlightUpdated": +) -> FlightUpdated: """ Updates flight rocket. @@ -95,18 +95,18 @@ async def update_flight_rocket( rocket: Rocket object as JSON ``` """ - return await FlightController.update_rocket( + return await FlightController.update_rocket_by_flight_id( flight_id, rocket, rocket_option, motor_kind ) @router.put("/{flight_id}") async def update_flight( - flight_id: int, + flight_id: str, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds, -) -> "FlightUpdated": +) -> FlightUpdated: """ Updates Flight object @@ -118,22 +118,22 @@ async def update_flight( """ return await FlightController( flight, rocket_option, motor_kind - ).update_flight(flight_id) + ).update_flight_by_id(flight_id) @router.delete("/{flight_id}") -async def delete_flight(flight_id: int) -> "FlightDeleted": +async def delete_flight(flight_id: str) -> FlightDeleted: """ Deletes a flight ## Args ``` flight_id: Flight ID hash ``` """ - return await FlightController.delete_flight(flight_id) + return await FlightController.delete_flight_by_id(flight_id) @router.get("/{flight_id}/simulate") -async def simulate_flight(flight_id: int) -> "FlightSummary": +async def simulate_flight(flight_id: str) -> FlightSummary: """ Simulates a flight diff --git a/lib/routes/motor.py b/lib/routes/motor.py index 5f0ef26..49fa753 100644 --- a/lib/routes/motor.py +++ b/lib/routes/motor.py @@ -26,7 +26,7 @@ @router.post("/") -async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": +async def create_motor(motor: Motor, motor_kind: MotorKinds) -> MotorCreated: """ Creates a new motor @@ -37,20 +37,20 @@ async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": @router.get("/{motor_id}") -async def read_motor(motor_id: int) -> "Motor": +async def read_motor(motor_id: str) -> Motor: """ Reads a motor ## Args ``` motor_id: Motor ID hash ``` """ - return await MotorController.get_motor(motor_id) + return await MotorController.get_motor_by_id(motor_id) @router.put("/{motor_id}") async def update_motor( - motor_id: int, motor: Motor, motor_kind: MotorKinds -) -> "MotorUpdated": + motor_id: str, motor: Motor, motor_kind: MotorKinds +) -> MotorUpdated: """ Updates a motor @@ -60,22 +60,24 @@ async def update_motor( motor: Motor object as JSON ``` """ - return await MotorController(motor, motor_kind).update_motor(motor_id) + return await MotorController(motor, motor_kind).update_motor_by_id( + motor_id + ) @router.delete("/{motor_id}") -async def delete_motor(motor_id: int) -> "MotorDeleted": +async def delete_motor(motor_id: str) -> MotorDeleted: """ Deletes a motor ## Args ``` motor_id: Motor ID hash ``` """ - return await MotorController.delete_motor(motor_id) + return await MotorController.delete_motor_by_id(motor_id) @router.get("/rocketpy/{motor_id}") -async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": +async def read_rocketpy_motor(motor_id: str) -> MotorPickle: """ Reads a rocketpy motor @@ -86,7 +88,7 @@ async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": @router.get("/{motor_id}/simulate") -async def simulate_motor(motor_id: int) -> "MotorSummary": +async def simulate_motor(motor_id: str) -> MotorSummary: """ Simulates a motor diff --git a/lib/routes/rocket.py b/lib/routes/rocket.py index 40789eb..fd04820 100644 --- a/lib/routes/rocket.py +++ b/lib/routes/rocket.py @@ -29,7 +29,7 @@ @router.post("/") async def create_rocket( rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds -) -> "RocketCreated": +) -> RocketCreated: """ Creates a new rocket @@ -42,7 +42,7 @@ async def create_rocket( @router.get("/{rocket_id}") -async def read_rocket(rocket_id: int) -> Rocket: +async def read_rocket(rocket_id: str) -> Rocket: """ Reads a rocket @@ -54,11 +54,11 @@ async def read_rocket(rocket_id: int) -> Rocket: @router.put("/{rocket_id}") async def update_rocket( - rocket_id: int, + rocket_id: str, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds, -) -> "RocketUpdated": +) -> RocketUpdated: """ Updates a rocket @@ -70,22 +70,22 @@ async def update_rocket( """ return await RocketController( rocket, rocket_option, motor_kind - ).update_rocket(rocket_id) + ).update_rocket_by_id(rocket_id) @router.delete("/{rocket_id}") -async def delete_rocket(rocket_id: int) -> "RocketDeleted": +async def delete_rocket(rocket_id: str) -> RocketDeleted: """ Deletes a rocket ## Args ``` rocket_id: Rocket ID hash ``` """ - return await RocketController.delete_rocket(rocket_id) + return await RocketController.delete_rocket_by_id(rocket_id) @router.get("/rocketpy/{rocket_id}") -async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": +async def read_rocketpy_rocket(rocket_id: str) -> RocketPickle: """ Reads a rocketpy rocket @@ -96,7 +96,7 @@ async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": @router.get("/{rocket_id}/simulate") -async def simulate_rocket(rocket_id: int) -> "RocketSummary": +async def simulate_rocket(rocket_id: str) -> RocketSummary: """ Simulates a rocket diff --git a/lib/secrets.py b/lib/secrets.py index 422a491..42ddf94 100644 --- a/lib/secrets.py +++ b/lib/secrets.py @@ -1,25 +1,21 @@ import os from dotenv import dotenv_values -from pydantic import BaseModel -class Secrets(BaseModel): +class Secrets: """ Secrets class to load secrets from .env file """ - secrets: dict = dotenv_values(".env") + secrets = dotenv_values(".env") @staticmethod def get_os_secret(key): return os.environ.get(key) - def get_secret(self, key): - dotenv_secret = self.secrets.get(key) + @classmethod + def get_secret(cls, key): + dotenv_secret = cls.secrets.get(key) if not dotenv_secret: - return self.get_os_secret(key) + return cls.get_os_secret(key) return dotenv_secret - - -# global instance -secrets_instance = Secrets()