diff --git a/lib/api.py b/lib/api.py index 789c9e8..595a131 100644 --- a/lib/api.py +++ b/lib/api.py @@ -1,12 +1,14 @@ """ This is the main API file for the RocketPy API. """ +import logging from typing import Any, Dict -from fastapi import FastAPI, status +from fastapi import FastAPI, Request, status +from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.openapi.utils import get_openapi -from fastapi.responses import RedirectResponse +from fastapi.responses import RedirectResponse, JSONResponse from lib.views.flight import FlightSummary, FlightCreated, FlightUpdated, FlightDeleted, FlightPickle from lib.views.environment import EnvSummary, EnvCreated, EnvUpdated, EnvDeleted, EnvPickle @@ -14,8 +16,8 @@ from lib.views.motor import MotorSummary, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle from lib.models.environment import Env from lib.models.flight import Flight -from lib.models.rocket import Rocket -from lib.models.motor import Motor +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import Motor, MotorKinds, MotorEngines from lib.controllers.flight import FlightController from lib.controllers.environment import EnvController from lib.controllers.rocket import RocketController @@ -70,140 +72,176 @@ async def main_page(): # Flight routes @app.post("/flights/", tags=["FLIGHT"]) -async def create_flight(flight: Flight) -> "FlightCreated": +async def create_flight(flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightCreated": """ - Creates a new flight. + Creates a new flight - Args: - Pydantic flight object as a JSON request according to API docs. + ## Args + + ``` Flight object as JSON ``` - Returns: + ## Returns + HTTP 200 { "message": "Flight created successfully.", id: flight_id_hash } - Raises: + ## Raises + HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to create flight in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await FlightController(flight).create_flight() + return await FlightController(flight, rocket_option, motor_kind).create_flight() @app.get("/flights/{flight_id}", tags=["FLIGHT"]) async def read_flight(flight_id: int) -> "Flight": """ - Reads a flight. + Reads a flight - Args: - flight_id: Flight ID hash. + ## Args + + ``` flight_id: Flight ID hash ``` + + ## Returns - Returns: - Pydantic flight object as JSON. + ``` Flight object as JSON ``` - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. """ return await FlightController.get_flight(flight_id) @app.get("/flights/rocketpy/{flight_id}", tags=["FLIGHT"]) async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": """ - Reads a rocketpy flight object. + Reads a rocketpy flight object - Args: - flight_id: Flight ID hash. + ## Args + + ``` flight_id: Flight ID hash. ``` - Returns: - RocketPy flight object encoded as a jsonpickle string. + ## Returns - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ``` RocketPy flight object as jsonpickle string ``` + + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. """ return await FlightController.get_rocketpy_flight(flight_id) @app.put("/flights/{flight_id}/env", tags=["FLIGHT"]) async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": """ - Updates flight environment. + Updates flight environment - Args: - flight_id: Flight ID hash. - env: Pydantic env object as JSON request according to API docs. + ## Args + + ``` + flight_id: Flight ID hash + env: env object as JSON + ``` + + ## Returns - Returns: HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. """ return await FlightController.update_env(flight_id, env) @app.put("/flights/{flight_id}/rocket", tags=["FLIGHT"]) -async def update_flight_rocket(flight_id: int, rocket: Rocket) -> "FlightUpdated": +async def update_flight_rocket(flight_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": """ Updates flight rocket. - Args: + ## Args + + ``` flight_id: Flight ID hash. - rocket: Pydantic rocket object as JSON request according to API docs. + rocket: Rocket object as JSON + ``` + + ## Returns - Returns: HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await FlightController.update_rocket(flight_id, rocket) + return await FlightController.update_rocket(flight_id, rocket, rocket_option, motor_kind) @app.put("/flights/{flight_id}", tags=["FLIGHT"]) -async def update_flight(flight_id: int, flight: Flight) -> "FlightUpdated": +async def update_flight(flight_id: int, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": """ - Updates whole flight. + Updates Flight object - Args: + ## Args + ``` flight_id: Flight ID hash. - flight: Pydantic flight object as JSON request according to API docs. + flight: Flight object as JSON + ``` + + ## Returns - Returns: HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await FlightController(flight).update_flight(flight_id) + return await FlightController(flight, rocket_option, motor_kind).update_flight(flight_id) @app.delete("/flights/{flight_id}", tags=["FLIGHT"]) async def delete_flight(flight_id: int) -> "FlightDeleted": """ - Deletes a flight. + Deletes a flight - Args: - flight_id: Flight ID hash. + ## Args + + ``` flight_id: Flight ID hash ``` + + ## Returns - Returns: HTTP 200 { "message": "Flight deleted successfully." } - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. """ return await FlightController.delete_flight(flight_id) @app.get("/flights/{flight_id}/simulate", tags=["FLIGHT"]) async def simulate_flight(flight_id: int) -> "FlightSummary": """ - Simulates a flight. + Simulates a flight - Args: - flight_id: Flight ID hash. + ## Args + + ``` flight_id: Flight ID hash ``` + + ## Returns - Returns: - HTTP 200 pydantic flight summary object as JSON. + ``` Flight summary as JSON ``` - Raises: - HTTP 404 Not Found: If flight_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If flight_id does not exist at database. """ return await FlightController.simulate(flight_id) @@ -211,16 +249,20 @@ async def simulate_flight(flight_id: int) -> "FlightSummary": @app.post("/environments/", tags=["ENVIRONMENT"]) async def create_env(env: Env) -> "EnvCreated": """ - Creates a new environment. + Creates a new environment + + ## Args - Args: - Pydantic env object as a JSON request according to API docs. + ``` Env object as a JSON ``` - Returns: + ## Returns + HTTP 200 { "message": "Environment created successfully.", id: env_id_hash } - Raises: + ## Raises + HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to create env in mongoDB or valid parameter type/structure provided but content is breaking the API. """ return await EnvController(env).create_env() @@ -228,34 +270,44 @@ async def create_env(env: Env) -> "EnvCreated": @app.get("/environments/{env_id}", tags=["ENVIRONMENT"]) async def read_env(env_id: int) -> "Env": """ - Reads an environment. + Reads an environment + + ## Args - Args: - env_id: Environment ID hash. + ``` env_id: Environment ID hash ``` - Returns: - Pydantic env object as JSON. + ## Returns - Raises: - HTTP 404 Not Found: If env_id does not exist in database. + ``` Env object as JSON. ``` + + ## Raises + + HTTP 404 Not Found: If env_id does not exist at database. """ return await EnvController.get_env(env_id) @app.put("/environments/{env_id}", tags=["ENVIRONMENT"]) async def update_env(env_id: int, env: Env) -> "EnvUpdated": """ - Updates an environment. + Updates an environment + + ## Args - Args: - env_id: Environment ID hash. - env: Pydantic env object as JSON request according to API docs. + ``` + env_id: Environment ID hash + env: Env object as JSON + ``` + + ## Returns - Returns: HTTP 200 { "message": "Environment updated successfully.", new_env_id: new_env_id_hash } - Raises: - HTTP 404 Not Found: If env_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If env_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update env in mongoDB or valid parameter type/structure provided but content is breaking the API. """ return await EnvController(env).update_env(env_id) @@ -263,253 +315,315 @@ async def update_env(env_id: int, env: Env) -> "EnvUpdated": @app.delete("/environments/{env_id}", tags=["ENVIRONMENT"]) async def delete_env(env_id: int) -> "EnvDeleted": """ - Deletes an environment. + Deletes an environment + + ## Args + + ``` env_id: Environment ID hash ``` - Args: - env_id: Environment ID hash. + ## Returns - Returns: HTTP 200 { "message": "Environment deleted successfully." } - Raises: - HTTP 404 Not Found: If env_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If env_id does not exist at database. """ return await EnvController.delete_env(env_id) @app.get("/environments/rocketpy/{env_id}", tags=["ENVIRONMENT"]) async def read_rocketpy_env(env_id: int) -> "EnvPickle": """ - Reads a rocketpy environment. + Reads a rocketpy environment + + ## Args + + ``` env_id: Environment ID hash ``` - Args: - env_id: Environment ID hash. + ## Returns - Returns: - Rocketpy Environment object encoded as JSONPickle string. + ``` Rocketpy Environment object as JSONPickle string. ``` - Raises: - HTTP 404 Not Found: If env_id does not exist in database. + ## Raises + HTTP 404 Not Found: If env_id does not exist at database. """ return await EnvController.get_rocketpy_env(env_id) @app.get("/environments/{env_id}/simulate", tags=["ENVIRONMENT"]) async def simulate_env(env_id: int) -> "EnvSummary": """ - Simulates an environment. + Simulates an environment + + ## Args + + ``` env_id: Env ID hash ``` + + ## Returns - Args: - env_id: Env ID hash. + ``` Env summary object containig simulation numbers and plots as JSON ``` - Returns: - HTTP 200 pydantic env summary object containig simulation numbers and plots as JSON. + ## Raises - Raises: - HTTP 404 Not Found: If env_id does not exist in database. + HTTP 404 Not Found: If env_id does not exist at database. """ return await EnvController.simulate(env_id) # Motor routes @app.post("/motors/", tags=["MOTOR"]) -async def create_motor(motor: Motor) -> "MotorCreated": +async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": """ - Creates a new motor. + Creates a new motor - Args: - Pydantic motor object as a JSON request according to API docs. + ## Args + + ``` Motor object as a JSON ``` - Returns: + ## Returns + HTTP 200 { "message": "Motor created successfully.", id: motor_id_hash } - Raises: + ## Raises + HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to create motor in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await MotorController(motor).create_motor() + return await MotorController(motor, motor_kind).create_motor() @app.get("/motors/{motor_id}", tags=["MOTOR"]) async def read_motor(motor_id: int) -> "Motor": """ - Reads a motor. + Reads a motor + + ## Args + + ``` motor_id: Motor ID hash ``` - Args: - motor_id: Motor ID hash. + ## Returns - Returns: - Pydantic motor object as JSON. + Motor object as JSON. - Raises: - HTTP 404 Not Found: If motor_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If motor_id does not exist at database. """ return await MotorController.get_motor(motor_id) @app.put("/motors/{motor_id}", tags=["MOTOR"]) -async def update_motor(motor_id: int, motor: Motor) -> "MotorUpdated": +async def update_motor(motor_id: int, motor: Motor, motor_kind: MotorKinds) -> "MotorUpdated": """ - Updates a motor. + Updates a motor + + ## Args + + ``` + motor_id: Motor ID hash + motor: Motor object as JSON + ``` - Args: - motor_id: Motor ID hash. - motor: Pydantic motor object as JSON request according to API docs. + ## Returns - Returns: HTTP 200 { "message": "Motor updated successfully.", new_motor_id: new_motor_id_hash } - Raises: - HTTP 404 Not Found: If motor_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If motor_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update motor in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await MotorController(motor).update_motor(motor_id) + return await MotorController(motor, motor_kind).update_motor(motor_id) @app.delete("/motors/{motor_id}", tags=["MOTOR"]) async def delete_motor(motor_id: int) -> "MotorDeleted": """ - Deletes a motor. + Deletes a motor - Args: - motor_id: Motor ID hash. + ## Args + + ``` motor_id: Motor ID hash ``` + + ## Returns - Returns: HTTP 200 { "message": "Motor deleted successfully." } - Raises: - HTTP 404 Not Found: If motor_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If motor_id does not exist at database. """ return await MotorController.delete_motor(motor_id) @app.get("/motors/rocketpy/{motor_id}", tags=["MOTOR"]) async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": """ - Reads a rocketpy motor. + Reads a rocketpy motor - Args: - motor_id: Motor ID hash. + ## Args - Returns: - Rocketpy Motor object encoded as JSONPickle string. + ``` motor_id: Motor ID hash ``` - Raises: - HTTP 404 Not Found: If motor_id does not exist in database. + ## Returns + + ``` Rocketpy Motor object as JSONPickle string ``` + + ## Raises + HTTP 404 Not Found: If motor_id does not exist at database. """ return await MotorController.get_rocketpy_motor(motor_id) @app.get("/motors/{motor_id}/simulate", tags=["MOTOR"]) async def simulate_motor(motor_id: int) -> "MotorSummary": """ - Simulates a motor. + Simulates a motor + + ## Args - Args: - motor_id: Motor ID hash. + ``` motor_id: Motor ID hash ``` - Returns: - HTTP 200 pydantic motor summary object containig simulation numbers and plots as JSON. + ## Returns - Raises: - HTTP 404 Not Found: If motor_id does not exist in database. + ``` Motor summary as JSON ``` + + ## Raises + + HTTP 404 Not Found: If motor_id does not exist at database. """ return await MotorController.simulate(motor_id) # Rocket routes @app.post("/rockets/", tags=["ROCKET"]) -async def create_rocket(rocket: Rocket) -> "RocketCreated": +async def create_rocket(rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketCreated": """ - Creates a new rocket. + Creates a new rocket + + ## Args - Args: - Pydantic rocket object as a JSON request according to API docs. + ``` Rocket object as a JSON ``` - Returns: + ## Returns + HTTP 200 { "message": "Rocket created successfully.", id: rocket_id_hash } - Raises: + ## Raises + HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to create rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await RocketController(rocket).create_rocket() + return await RocketController(rocket, rocket_option, motor_kind).create_rocket() @app.get("/rockets/{rocket_id}", tags=["ROCKET"]) async def read_rocket(rocket_id: int) -> Rocket: """ - Reads a rocket. + Reads a rocket - Args: - rocket_id: Rocket ID hash. + ## Args - Returns: - Pydantic rocket object as JSON. + ``` rocket_id: Rocket ID hash ``` - Raises: - HTTP 404 Not Found: If rocket_id does not exist in database. + ## Returns + + ``` Rocket object as JSON ``` + + ## Raises + + HTTP 404 Not Found: If rocket_id does not exist at database. """ return await RocketController.get_rocket(rocket_id) @app.put("/rockets/{rocket_id}", tags=["ROCKET"]) -async def update_rocket(rocket_id: int, rocket: Rocket) -> "RocketUpdated": +async def update_rocket(rocket_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketUpdated": """ - Updates a rocket. + Updates a rocket - Args: - rocket_id: Rocket ID hash. - rocket: Pydantic rocket object as JSON request according to API docs. + ## Args + + ``` + rocket_id: Rocket ID hash + rocket: Rocket object as JSON + ``` + + ## Returns - Returns: HTTP 200 { "message": "Rocket updated successfully.", new_rocket_id: new_rocket_id_hash } - Raises: - HTTP 404 Not Found: If rocket_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If rocket_id does not exist at database. + HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. + HTTP 500 Internal Server Error: If API is either unable to update rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. """ - return await RocketController(rocket).update_rocket(rocket_id) + return await RocketController(rocket, rocket_option, motor_kind).update_rocket(rocket_id) @app.delete("/rockets/{rocket_id}", tags=["ROCKET"]) async def delete_rocket(rocket_id: int) -> "RocketDeleted": """ - Deletes a rocket. + Deletes a rocket + + ## Args - Args: - rocket_id: Rocket ID hash. + ``` rocket_id: Rocket ID hash ``` + + ## Returns - Returns: HTTP 200 { "message": "Rocket deleted successfully." } - Raises: - HTTP 404 Not Found: If rocket_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If rocket_id does not exist at database. """ return await RocketController.delete_rocket(rocket_id) @app.get("/rockets/rocketpy/{rocket_id}", tags=["ROCKET"]) async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": """ - Reads a rocketpy rocket. + Reads a rocketpy rocket + + ## Args + + ``` rocket_id: Rocket ID hash ``` - Args: - rocket_id: Rocket ID hash. + ## Returns - Returns: - Rocketpy Rocket object encoded as JSONPickle string. + ``` Rocketpy Rocket object as JSONPickle string ``` - Raises: - HTTP 404 Not Found: If rocket_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If rocket_id does not exist at database. """ return await RocketController.get_rocketpy_rocket(rocket_id) @app.get("/rockets/{rocket_id}/simulate", tags=["ROCKET"]) async def simulate_rocket(rocket_id: int) -> "RocketSummary": """ - Simulates a rocket. + Simulates a rocket + + ## Args + + ``` rocket_id: Rocket ID hash ``` - Args: - rocket_id: Rocket ID hash. + ## Returns - Returns: HTTP 200 pydantic rocket summary object containig simulation numbers and plots as JSON. - Raises: - HTTP 404 Not Found: If rocket_id does not exist in database. + ## Raises + + HTTP 404 Not Found: If rocket_id does not exist at database. """ return await RocketController.simulate(rocket_id) +# Additional routes @app.get("/health", status_code=status.HTTP_200_OK, include_in_schema=False) async def __perform_healthcheck(): return {'health': 'Everything OK!'} + +# Errors +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request: Request, exc: RequestValidationError): + exc_str = f'{exc}'.replace('\n', ' ').replace(' ', ' ') + logging.error(f"{request}: {exc_str}") + content = {'status_code': 10422, 'message': exc_str, 'data': None} + return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) diff --git a/lib/controllers/flight.py b/lib/controllers/flight.py index 2786d8c..e31c36b 100644 --- a/lib/controllers/flight.py +++ b/lib/controllers/flight.py @@ -5,7 +5,8 @@ import jsonpickle -from lib.models.rocket import Rocket +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import Motor, MotorEngines, MotorKinds from lib.models.flight import Flight from lib.models.environment import Env from lib.views.flight import FlightSummary, SurfaceWindConditions, OutOfRailConditions, BurnoutConditions, ApogeeConditions, MaximumValues, InitialConditions, NumericalIntegrationSettings, ImpactConditions, EventsRegistered, LaunchRailConditions, FlightData, FlightCreated, FlightUpdated, FlightDeleted, FlightPickle @@ -31,17 +32,20 @@ class FlightController(): - Read a RocketpyFlight object from the database. """ - def __init__(self, flight: Flight): - rocketpy_rocket = RocketController(flight.rocket).rocketpy_rocket + def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds): + rocketpy_rocket = RocketController(flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind).rocketpy_rocket rocketpy_env = EnvController(flight.environment).rocketpy_env - rocketpy_flight=RocketpyFlight( - rocket=rocketpy_rocket, - inclination=flight.inclination, - heading=flight.heading, - environment=rocketpy_env, - rail_length=flight.rail_length, + rocketpy_flight = RocketpyFlight( + rocket=rocketpy_rocket, + inclination=flight.inclination, + heading=flight.heading, + environment=rocketpy_env, + rail_length=flight.rail_length ) + + self.rocket_option = rocket_option + self.motor_kind = motor_kind self.rocketpy_flight = rocketpy_flight self.flight = flight @@ -53,7 +57,12 @@ async def create_flight(self) -> "Union[FlightCreated, Response]": Dict[str, str]: Flight id. """ flight = FlightRepository(flight=self.flight) - successfully_created_flight = await flight.create_flight() + successfully_created_flight = \ + await flight.create_flight( + motor_kind = self.motor_kind, + rocket_option = self.rocket_option + ) + if successfully_created_flight: return FlightCreated(flight_id=str(flight.flight_id)) return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @@ -98,7 +107,9 @@ async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]" return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_read_rocketpy_flight = \ - FlightController(successfully_read_flight).rocketpy_flight + FlightController(flight = successfully_read_flight, + rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), + motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight return FlightPickle(jsonpickle_rocketpy_flight=jsonpickle.encode(successfully_read_rocketpy_flight)) @@ -121,7 +132,10 @@ async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response] return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_updated_flight = \ - await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight() + await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight( + rocket_option = self.rocket_option, + motor_kind = self.motor_kind + ) if successfully_updated_flight: return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @@ -158,7 +172,7 @@ async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @staticmethod - async def update_rocket(flight_id: int, rocket: Rocket) -> "Union[FlightUpdated, Response]": + async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kind) -> "Union[FlightUpdated, Response]": """ Update the rocket of a flight in the database. @@ -178,7 +192,11 @@ async def update_rocket(flight_id: int, rocket: Rocket) -> "Union[FlightUpdated, return Response(status_code=status.HTTP_404_NOT_FOUND) flight = successfully_read_flight.dict() - flight["rocket"] = rocket + updated_rocket = rocket.dict() + updated_rocket["rocket_option"] = rocket_option + updated_rocket["motor"]["motor_kind"] = motor_kind + flight["rocket"] = Rocket(**updated_rocket) + flight = Flight(**flight) successfully_updated_flight = \ await FlightRepository(flight=flight, flight_id=flight_id).update_flight() @@ -231,7 +249,9 @@ async def simulate(flight_id: int) -> "Union[FlightSummary, Response]": if not successfully_read_flight: return Response(status_code=status.HTTP_404_NOT_FOUND) - flight = FlightController(successfully_read_flight).rocketpy_flight + flight = FlightController(flight = successfully_read_flight, + rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), + motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight _initial_conditions = InitialConditions( initial_altitude = "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format(flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0)), diff --git a/lib/controllers/motor.py b/lib/controllers/motor.py index 0219713..b8afae4 100644 --- a/lib/controllers/motor.py +++ b/lib/controllers/motor.py @@ -1,9 +1,12 @@ from fastapi import Response, status from typing import Any, Dict, Union from rocketpy.motors.solid_motor import SolidMotor +from rocketpy.motors.liquid_motor import LiquidMotor +from rocketpy.motors.hybrid_motor import HybridMotor +from rocketpy import TankGeometry, Fluid import jsonpickle -from lib.models.motor import Motor +from lib.models.motor import Motor, MotorKinds from lib.repositories.motor import MotorRepository from lib.views.motor import MotorSummary, MotorData, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle @@ -17,26 +20,55 @@ class MotorController(): Enables: - Create a rocketpy.Motor object from a Motor model object. """ - def __init__(self, motor: Motor): - rocketpy_motor = SolidMotor( - burn_time=motor.burn_time, - grain_number=motor.grain_number, - grain_density=motor.grain_density, - grain_outer_radius=motor.grain_outer_radius, - grain_initial_inner_radius=motor.grain_initial_inner_radius, - grain_initial_height=motor.grain_initial_height, - grains_center_of_mass_position=-motor.grains_center_of_mass_position, - thrust_source=f"lib/data/motors/{motor.thrust_source.value}.eng", - grain_separation=motor.grain_separation, - nozzle_radius=motor.nozzle_radius, - dry_mass=motor.dry_mass, - center_of_dry_mass_position=motor.center_of_dry_mass_position, - dry_inertia=motor.dry_inertia, - throat_radius=motor.throat_radius, - interpolation_method=motor.interpolation_method - ) + def __init__(self, motor: Motor, motor_kind): + self.guard(motor, motor_kind) + motor_core = { + "thrust_source": f"lib/data/motors/{motor.thrust_source.value}.eng", + "burn_time": motor.burn_time, + "nozzle_radius": motor.nozzle_radius, + "dry_mass": motor.dry_mass, + "dry_inertia": motor.dry_inertia, + "center_of_dry_mass_position": motor.center_of_dry_mass_position + } + + match motor_kind: + case MotorKinds.liquid: + rocketpy_motor = LiquidMotor(**motor_core) + case MotorKinds.hybrid: + rocketpy_motor = HybridMotor(**motor_core, + throat_radius=motor.throat_radius, + grain_number=motor.grain_number, + grain_density=motor.grain_density, + grain_outer_radius=motor.grain_outer_radius, + grain_initial_inner_radius=motor.grain_initial_inner_radius, + grain_initial_height=motor.grain_initial_height, + grain_separation=motor.grain_separation, + grains_center_of_mass_position=motor.grains_center_of_mass_position + ) + case _: + rocketpy_motor = SolidMotor(**motor_core, + grain_number=motor.grain_number, + grain_density=motor.grain_density, + grain_outer_radius=motor.grain_outer_radius, + grain_initial_inner_radius=motor.grain_initial_inner_radius, + grain_initial_height=motor.grain_initial_height, + grains_center_of_mass_position=motor.grains_center_of_mass_position, + grain_separation=motor.grain_separation, + throat_radius=motor.throat_radius, + interpolation_method=motor.interpolation_method + ) + + if motor_kind != MotorKinds.solid: + for tank in motor.tanks: + rocketpy_motor.add_tank(tank.tank, tank.position) + + self.motor_kind = motor_kind #tracks motor kind state self.rocketpy_motor = rocketpy_motor self.motor = motor + + def guard(self, motor: Motor, motor_kind): + if motor_kind != MotorKinds.solid and motor.tanks is None: + return Response(status_code=status.HTTP_400_BAD_REQUEST, content="Tanks must be provided for liquid and hybrid motors.") async def create_motor(self) -> "Union[MotorCreated, Response]": """ @@ -46,7 +78,7 @@ async def create_motor(self) -> "Union[MotorCreated, Response]": Dict[str, str]: motor id. """ motor = MotorRepository(motor=self.motor) - successfully_created_motor = await motor.create_motor() + successfully_created_motor = await motor.create_motor(motor_kind = self.motor_kind) if successfully_created_motor: return MotorCreated(motor_id=str(motor.motor_id)) return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @@ -91,7 +123,8 @@ async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]": return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_read_rocketpy_motor = \ - MotorController( successfully_read_motor ).rocketpy_motor + MotorController(motor = successfully_read_motor, + motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor return MotorPickle(jsonpickle_rocketpy_motor=jsonpickle.encode(successfully_read_rocketpy_motor)) @@ -114,7 +147,7 @@ async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]": return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_updated_motor = \ - await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor() + await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor(motor_kind = self.motor_kind) if successfully_updated_motor: return MotorUpdated(new_motor_id=str(successfully_updated_motor)) @@ -164,7 +197,8 @@ async def simulate(motor_id: int) -> "Union[MotorSummary, Response]": if not successfully_read_motor: return Response(status_code=status.HTTP_404_NOT_FOUND) - motor = MotorController(successfully_read_motor).rocketpy_motor + motor = MotorController(motor=successfully_read_motor, + motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor motor_simulation_numbers = MotorData( total_burning_time="Total Burning Time: " + str(motor.burn_out_time) + " s", diff --git a/lib/controllers/rocket.py b/lib/controllers/rocket.py index 29f870d..04b502f 100644 --- a/lib/controllers/rocket.py +++ b/lib/controllers/rocket.py @@ -12,7 +12,8 @@ from rocketpy.rocket.aero_surface import Tail as RocketpyTail from lib.controllers.motor import MotorController -from lib.models.rocket import Rocket +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import MotorKinds from lib.models.aerosurfaces import NoseCone, TrapezoidalFins, Tail from lib.models.parachute import Parachute from lib.repositories.rocket import RocketRepository @@ -28,13 +29,13 @@ class RocketController(): Enables: create a RocketpyRocket object from a Rocket model object. """ - def __init__(self, rocket: Rocket): + def __init__(self, rocket: Rocket, rocket_option, motor_kind): rocketpy_rocket = RocketpyRocket( radius=rocket.radius, mass=rocket.mass, inertia=rocket.inertia, - power_off_drag=f"lib/data/{rocket.power_off_drag.value}/powerOffDragCurve.csv", - power_on_drag=f"lib/data/{rocket.power_on_drag.value}/powerOnDragCurve.csv", + power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv", + power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv", center_of_mass_without_motor=rocket.center_of_mass_without_motor, coordinate_system_orientation=rocket.coordinate_system_orientation ) @@ -43,7 +44,7 @@ def __init__(self, rocket: Rocket): rocketpy_rocket.set_rail_buttons(upper_button_position=rocket.rail_buttons.upper_button_position, lower_button_position=rocket.rail_buttons.lower_button_position, angular_position=rocket.rail_buttons.angular_position) - rocketpy_rocket.add_motor(MotorController(rocket.motor).rocketpy_motor, + rocketpy_rocket.add_motor(MotorController(rocket.motor, motor_kind).rocketpy_motor, rocket.motor_position) #NoseCone @@ -73,6 +74,7 @@ def __init__(self, rocket: Rocket): print("Parachute trigger not valid. Skipping parachute.") continue + self.rocket_option = rocket_option #tracks rocket option state self.rocketpy_rocket = rocketpy_rocket self.rocket = rocket @@ -231,7 +233,7 @@ async def create_rocket(self) -> "Union[RocketCreated, Response]": Dict[str, str]: Rocket id. """ rocket = RocketRepository(rocket=self.rocket) - successfully_created_rocket = await rocket.create_rocket() + successfully_created_rocket = await rocket.create_rocket(rocket_option = self.rocket_option) if successfully_created_rocket: return RocketCreated(rocket_id=str(rocket.rocket_id)) return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @@ -276,7 +278,9 @@ async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]" return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_read_rocketpy_rocket = \ - RocketController( successfully_read_rocket ).rocketpy_rocket + RocketController(rocket=successfully_read_rocket, + rocket_option = RocketOptions(successfully_read_rocket._rocket_option), + motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket return RocketPickle(jsonpickle_rocketpy_rocket=jsonpickle.encode(successfully_read_rocketpy_rocket)) @@ -299,7 +303,7 @@ async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response] return Response(status_code=status.HTTP_404_NOT_FOUND) successfully_updated_rocket = \ - await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket() + await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket(rocket_option = self.rocket_option) if successfully_updated_rocket: return RocketUpdated(new_rocket_id=str(successfully_updated_rocket)) @@ -349,7 +353,9 @@ async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]": if not successfully_read_rocket: return Response(status_code=status.HTTP_404_NOT_FOUND) - rocket = RocketController(successfully_read_rocket).rocketpy_rocket + rocket = RocketController(rocket=successfully_read_rocket, + rocket_option = RocketOptions(successfully_read_rocket._rocket_option), + motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket _inertia_details = InertiaDetails( rocket_mass_without_propellant = "Rocket Mass: {:.3f} kg (No Propellant)".format(rocket.mass), diff --git a/lib/models/environment.py b/lib/models/environment.py index ee86ece..4a1a5bc 100644 --- a/lib/models/environment.py +++ b/lib/models/environment.py @@ -6,6 +6,8 @@ class Env(BaseModel, frozen=True): latitude: float = 0 longitude: float = 0 elevation: Optional[int] = 1400 + + #Opional parameters atmospheric_model_type: Optional[str] = 'standard_atmosphere' atmospheric_model_file: Optional[str] = 'GFS' date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta(days=1) diff --git a/lib/models/motor.py b/lib/models/motor.py index 84e2217..fb57453 100644 --- a/lib/models/motor.py +++ b/lib/models/motor.py @@ -1,26 +1,115 @@ -from typing import Optional, Tuple +from rocketpy import LevelBasedTank, MassBasedTank, MassFlowRateBasedTank, UllageBasedTank, SolidMotor, LiquidMotor, HybridMotor, TankGeometry, Function +from typing import Optional, Tuple, List, Dict from enum import Enum -from pydantic import BaseModel +from pydantic import BaseModel, PrivateAttr -class MotorOptions(str, Enum): +class MotorKinds(str, Enum): + hybrid: str = "Hybrid" + solid: str = "Solid" + liquid: str = "Liquid" + +class MotorEngines(str, Enum): cesaroni: str = "Cesaroni_M1670" - custom: str = "Custom (Coming soon)" + custom: str = "Custom" + +class TankKinds(str, Enum): + level: str = "Level" + mass: str = "Mass" + mass_flow: str = "MassFlow" + ullage: str = "Ullage" + +class TankFluids(BaseModel, frozen=True): + name: str = "FluidName" + density: float = 100.0 + +class MotorTank(BaseModel, frozen=True): + #Required parameters + geometry: "List[Tuple[Tuple[float,float],float]]" = [((0, 5), 1), ((5, 10), 2)] + tank_kind: TankKinds = TankKinds.mass_flow + gas: TankFluids = TankFluids() + liquid: TankFluids = TankFluids() + name: str = "Tank" + flux_time: "List[float]" = [0, 8] + position: float = 1.0 + + #Optional parameters + discretize: Optional[int] = 100 + liquid_height: Optional[float] = 0.5 + liquid_mass: Optional[float] = 5.0 + gas_mass: Optional[float] = 0.1 + gas_mass_flow_rate_in: Optional[float] = 0.1 + gas_mass_flow_rate_out: Optional[float] = 0.1 + liquid_mass_flow_rate_in: Optional[float] = 0.1 + liquid_mass_flow_rate_out: Optional[float] = 0.1 + initial_liquid_mass: Optional[float] = 5.0 + initial_gas_mass: Optional[float] = 0.1 + ullage: Optional[float] = 0.1 + + def __init__(self, **kwargs): + super().__init__(**kwargs) + tank_core = { + "name": self.name, + "geometry": TankGeometry(geometry_dict = { t:f for t,f in self.geometry }), + "flux_time": self.flux_time, + "gas": self.gas, + "liquid": self.liquid, + "discretize": self.discretize + } + + match self.tank_kind: + case TankKinds.level: + tank = LevelBasedTank(**tank_core, liquid_height=self.liquid_height) + case TankKinds.mass: + tank = MassBasedTank(**tank_core, + liquid_mass=self.liquid_mass, gas_mass=self.gas_mass) + case TankKinds.mass_flow: + tank = MassFlowRateBasedTank(**tank_core, + gas_mass_flow_rate_in=self.gas_mass_flow_rate_in, + gas_mass_flow_rate_out=self.gas_mass_flow_rate_out, + liquid_mass_flow_rate_in=self.liquid_mass_flow_rate_in, + liquid_mass_flow_rate_out=self.liquid_mass_flow_rate_out, + initial_liquid_mass=self.initial_liquid_mass, + initial_gas_mass=self.initial_gas_mass + ) + case TankKinds.ullage: + tank = UllageBasedTank(**tank_core, ullage=self.ullage) + object.__setattr__(self, "tank", tank) + + def __hash__(self): + temp = vars(self) + temp = str(temp) + return hash(temp) class Motor(BaseModel, frozen=True): + #TBD: 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 burn_time: float = 3.9 + nozzle_radius: float = 0.033 dry_mass: float = 1.815 dry_inertia: "Tuple[float, float, float]" = (0.125, 0.125, 0.002) center_of_dry_mass_position: float = 0.317 - grain_number: int = 5 - grain_density: float = 1815 - grain_outer_radius: float = 0.033 - grain_initial_inner_radius: float = 0.015 - grain_initial_height: float = 0.12 - grains_center_of_mass_position: float = -0.85704 - #TBD: 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 - thrust_source: MotorOptions = "Cesaroni_M1670" - grain_separation: float = 0.005 - nozzle_radius: float = 0.033 - throat_radius: float = 0.011 - interpolation_method: str = "linear" - coordinate_system_orientation: str = "nozzle_to_combustion_chamber" + _motor_kind: MotorKinds = PrivateAttr() + + #Optional parameters + tanks: Optional["List[MotorTank]"] = [MotorTank()] + grain_number: Optional[int] = 5 + grain_density: Optional[float] = 1815 + grain_outer_radius: Optional[float] = 0.033 + grain_initial_inner_radius: Optional[float] = 0.015 + grain_initial_height: Optional[float] = 0.12 + grains_center_of_mass_position: Optional[float] = -0.85704 + grain_separation: Optional[float] = 0.005 + throat_radius: Optional[float] = 0.011 + interpolation_method: Optional[str] = "linear" + coordinate_system_orientation: Optional[str] = "nozzle_to_combustion_chamber" + + def __init__(self, motor_kind = MotorKinds.solid, **kwargs): + super().__init__(**kwargs) + self._motor_kind = motor_kind + + def __hash__(self): + temp = vars(self) + temp = str(temp) + return hash(temp) diff --git a/lib/models/parachute.py b/lib/models/parachute.py index 8b075fa..f1106a3 100644 --- a/lib/models/parachute.py +++ b/lib/models/parachute.py @@ -4,8 +4,8 @@ class Parachute(BaseModel, frozen=True): name: "List[str]" = ["Main","Drogue"] cd_s: "List[float]" = [10, 1] - sampling_rate: "List[int]" = [105, 105] lag: "List[float]" = [1.5, 1.5] + sampling_rate: "List[int]" = [105, 105] noise: "List[Tuple[float, float, float]]" = [(0, 8.3, 0.5), (0, 8.3, 0.5)] triggers: "List[str]" = ["lambda p, h, y: y[5] < 0 and h < 800", "lambda p, h, y: y[5] < 0"] diff --git a/lib/models/rocket.py b/lib/models/rocket.py index 632b5bf..c31abda 100644 --- a/lib/models/rocket.py +++ b/lib/models/rocket.py @@ -1,28 +1,48 @@ -from typing import Optional, Tuple -from pydantic import BaseModel +from typing import Optional, Tuple, List +from pydantic import BaseModel, PrivateAttr from enum import Enum from lib.models.motor import Motor from lib.models.aerosurfaces import Fins, NoseCone, Tail, RailButtons from lib.models.parachute import Parachute class RocketOptions(str, Enum): - calisto: str = "calisto" - custom: str = "Custom (Coming soon)" + calisto: str = "Calisto" + custom: str = "Custom" class Rocket(BaseModel, frozen=True): - radius: float = 0.0632 - mass: float = 16.235 - inertia: "Tuple[float, float, float]" = (6.321, 6.321, 0.0346) - #TBD: powerOffDrag an powerOnDrag need to be the id of previously uploaded .csv files and a list of "default" files must be provided in the api docs - power_off_drag: RocketOptions = "calisto" - power_on_drag: RocketOptions = "calisto" - center_of_mass_without_motor: int = 0 - #TBD: a list of possible tailToNose values must be provided in the api docs - coordinate_system_orientation: Optional[str] = "tail_to_nose" - motor_position: float = -1.255 + #Required parameters rail_buttons: RailButtons = RailButtons() motor: Motor = Motor() nose: NoseCone = NoseCone() fins: Fins = Fins() tail: Tail = Tail() parachutes: Parachute = Parachute() + inertia: "Tuple[float, float, float]" = (6.321, 6.321, 0.0346) + center_of_mass_without_motor: int = 0 + radius: float = 0.0632 + mass: float = 16.235 + motor_position: float = -1.255 + power_off_drag: "List[Tuple[float, float]]" = [ + (0.01,0.333865758), + (0.02,0.394981721), + (0.03,0.407756063) + ] + power_on_drag: "List[Tuple[float, float]]" = [ + (0.01,0.333865758), + (0.02,0.394981721), + (0.03,0.407756063) + ] + _rocket_option: RocketOptions = PrivateAttr() + + #Optional parameters + #TBD: a list of possible tailToNose values must be provided in the api docs + coordinate_system_orientation: Optional[str] = "tail_to_nose" + + def __init__(self, rocket_option = RocketOptions.calisto, **kwargs): + super().__init__(**kwargs) + self._rocket_option = rocket_option + + def __hash__(self): + temp = vars(self) + temp = str(temp) + return hash(temp) diff --git a/lib/repositories/environment.py b/lib/repositories/environment.py index 9a8cd27..ddc9dbc 100644 --- a/lib/repositories/environment.py +++ b/lib/repositories/environment.py @@ -76,7 +76,6 @@ async def get_env(self) -> "Union[Env, None]": try: environment = await self.collection.find_one({ "env_id": self.env_id }) if environment is not None: - del environment["_id"] return Env.parse_obj(environment) return None except: diff --git a/lib/repositories/flight.py b/lib/repositories/flight.py index 3eb6da6..eea93de 100644 --- a/lib/repositories/flight.py +++ b/lib/repositories/flight.py @@ -26,7 +26,7 @@ def __init__(self, flight: Flight = None, flight_id: str = None): def __del__(self): super().__del__() - async def create_flight(self) -> "InsertOneResult": + async def create_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "InsertOneResult": """ Creates a flight in the database @@ -40,12 +40,14 @@ async def create_flight(self) -> "InsertOneResult": 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 return await self.collection.insert_one(flight_to_dict) except: raise Exception("Error creating flight") return InsertOneResult( acknowledged=True, inserted_id=None ) - async def update_flight(self) -> "Union[int, None]": + async def update_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "Union[int, None]": """ Updates a flight in the database @@ -55,6 +57,8 @@ async def update_flight(self) -> "Union[int, None]": try: flight_to_dict = self.flight.dict() flight_to_dict["flight_id"] = self.flight.__hash__() + flight_to_dict["rocket"]["rocket_option"] = rocket_option + flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind updated_flight = await self.collection.update_one( { "flight_id": self.flight_id }, @@ -76,7 +80,6 @@ async def get_flight(self) -> "Union[Flight, None]": try: flight = await self.collection.find_one({ "flight_id": self.flight_id }) if flight is not None: - del flight["_id"] return Flight.parse_obj(flight) return None except: diff --git a/lib/repositories/motor.py b/lib/repositories/motor.py index 29410de..030bf3e 100644 --- a/lib/repositories/motor.py +++ b/lib/repositories/motor.py @@ -15,7 +15,7 @@ class MotorRepository(Repository): Enables CRUD operations on motor objects """ - def __init__(self, motor: Motor = None, motor_id: str = None): + def __init__(self, motor: Motor = None, motor_id: str = None) -> None: super().__init__("motors") self.motor = motor if motor_id: @@ -26,7 +26,7 @@ def __init__(self, motor: Motor = None, motor_id: str = None): def __del__(self): super().__del__() - async def create_motor(self) -> "InsertOneResult": + async def create_motor(self, motor_kind: str = "solid") -> "InsertOneResult": """ Creates a motor in the database @@ -40,12 +40,13 @@ async def create_motor(self) -> "InsertOneResult": try: motor_to_dict = self.motor.dict() motor_to_dict["motor_id"] = self.motor_id + motor_to_dict["motor_kind"] = motor_kind return await self.collection.insert_one(motor_to_dict) except: raise Exception("Error creating motor") return InsertOneResult( acknowledged=True, inserted_id=None ) - async def update_motor(self) -> "Union[int, None]": + async def update_motor(self, motor_kind: str = "solid") -> "Union[int, None]": """ Updates a motor in the database @@ -55,6 +56,7 @@ async def update_motor(self) -> "Union[int, None]": try: motor_to_dict = self.motor.dict() motor_to_dict["motor_id"] = self.motor.__hash__() + motor_to_dict["motor_kind"] = motor_kind updated_motor = await self.collection.update_one( { "motor_id": self.motor_id }, @@ -76,7 +78,6 @@ async def get_motor(self) -> "Union[motor, None]": try: motor = await self.collection.find_one({ "motor_id": self.motor_id }) if motor is not None: - del motor["_id"] return Motor.parse_obj(motor) return None except: diff --git a/lib/repositories/rocket.py b/lib/repositories/rocket.py index 0770948..ab03807 100644 --- a/lib/repositories/rocket.py +++ b/lib/repositories/rocket.py @@ -26,7 +26,7 @@ def __init__(self, rocket: Rocket = None, rocket_id: str = None): def __del__(self): super().__del__() - async def create_rocket(self) -> "InsertOneResult": + async def create_rocket(self, rocket_option: str = "Calisto") -> "InsertOneResult": """ Creates a rocket in the database @@ -40,12 +40,13 @@ async def create_rocket(self) -> "InsertOneResult": try: rocket_to_dict = self.rocket.dict() rocket_to_dict["rocket_id"] = self.rocket_id + rocket_to_dict["rocket_option"] = rocket_option return await self.collection.insert_one(rocket_to_dict) except: raise Exception("Error creating rocket") return InsertOneResult( acknowledged=True, inserted_id=None ) - async def update_rocket(self) -> "Union[int, None]": + async def update_rocket(self, rocket_option: str = "Calisto") -> "Union[int, None]": """ Updates a rocket in the database @@ -55,6 +56,7 @@ async def update_rocket(self) -> "Union[int, None]": try: rocket_to_dict = self.rocket.dict() rocket_to_dict["rocket_id"] = self.rocket.__hash__() + rocket_to_dict["rocket_option"] = rocket_option updated_rocket = await self.collection.update_one( { "rocket_id": self.rocket_id }, @@ -76,7 +78,6 @@ async def get_rocket(self) -> "Union[Rocket, None]": try: rocket = await self.collection.find_one({ "rocket_id": self.rocket_id }) if rocket is not None: - del rocket["_id"] return Rocket.parse_obj(rocket) return None except: