Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 52 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,27 +1,69 @@
VENV_BIN ?= ./infinity_env/bin
PYLINTHOME ?= .pylint.d

export PYLINTHOME

ifneq (,$(wildcard $(VENV_BIN)/black))
BLACK := $(VENV_BIN)/black
else
BLACK := black
endif

ifneq (,$(wildcard $(VENV_BIN)/ruff))
RUFF := $(VENV_BIN)/ruff
else
RUFF := ruff
endif

ifneq (,$(wildcard $(VENV_BIN)/flake8))
FLAKE8 := $(VENV_BIN)/flake8
else
FLAKE8 := flake8
endif

ifneq (,$(wildcard $(VENV_BIN)/pylint))
PYLINT := $(VENV_BIN)/pylint
else
PYLINT := pylint
endif

ifneq (,$(wildcard $(VENV_BIN)/pytest))
PYTEST := $(VENV_BIN)/pytest
else
PYTEST := python3 -m pytest
endif

ifneq (,$(wildcard $(VENV_BIN)/uvicorn))
UVICORN := $(VENV_BIN)/uvicorn
else
UVICORN := python3 -m uvicorn
endif

format: black ruff
lint: flake8 pylint

black:
black ./src || true
black ./tests || true
$(BLACK) ./src || true
$(BLACK) ./tests || true

flake8:
flake8 --ignore E501,E402,F401,W503,C0414 ./src || true
flake8 --ignore E501,E402,F401,W503,C0414 ./tests || true
$(FLAKE8) --ignore E501,E402,F401,W503,C0414 ./src || true
$(FLAKE8) --ignore E501,E402,F401,W503,C0414 ./tests || true

pylint:
pylint ./src || true
pylint ./tests || true
@mkdir -p $(PYLINTHOME)
$(PYLINT) ./src || true
$(PYLINT) ./tests || true

ruff:
ruff check --fix ./src || true
ruff check --fix ./tests || true
$(RUFF) check --fix ./src || true
$(RUFF) check --fix ./tests || true

test:
python3 -m pytest .
$(PYTEST) .

dev:
python3 -m uvicorn src:app --reload --port 3000 --loop uvloop
$(UVICORN) src:app --reload --port 3000 --loop uvloop

clean:
docker stop infinity-api
Expand Down
16 changes: 10 additions & 6 deletions src/models/sub/aerosurfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ class Fins(BaseModel):
sweep_length: Optional[float] = None
sweep_angle: Optional[float] = None

_base_keys = {"fins_kind", "name", "n", "root_chord", "span", "position"}

def get_additional_parameters(self):
return {
key: value
for key, value in self.dict().items()
if value is not None
and key
not in ["fins_kind", "name", "n", "root_chord", "span", "position"]
params = {
k: v
for k, v in self.dict().items()
if v is not None and k not in self._base_keys
}
if "sweep_angle" in params and "sweep_length" in params:
params.pop("sweep_length")

return params


# TODO: implement airbrakes
Expand Down
154 changes: 70 additions & 84 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ def for_flight(cls) -> 'DiscretizeConfig':


class InfinityEncoder(RocketPyEncoder):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def default(self, obj):
def default(self, o):
obj = o
if (
isinstance(obj, Function)
and not callable(obj.source)
Expand All @@ -75,7 +73,7 @@ def default(self, obj):
mutate_self=False,
)
if isinstance(obj, Flight):
obj._Flight__evaluate_post_process
obj._Flight__evaluate_post_process()
solution = np.array(obj.solution)
size = len(solution)
if size > 25:
Expand Down Expand Up @@ -117,92 +115,80 @@ def rocketpy_encoder(obj):


def collect_attributes(obj, attribute_classes=None):
"""
Collect attributes from various simulation classes and populate them from the flight object.
"""
if attribute_classes is None:
attribute_classes = []
"""Collect and serialize attributes from simulation classes."""
attribute_classes = attribute_classes or []

attributes = rocketpy_encoder(obj)

for attribute_class in attribute_classes:
if issubclass(attribute_class, FlightSimulation):
flight_attributes_list = [
attr
for attr in attribute_class.__annotations__.keys()
if attr not in ["message", "rocket", "env"]
]
try:
for key in flight_attributes_list:
if key not in attributes:
try:
value = getattr(obj, key)
attributes[key] = value
except Exception:
pass
except Exception:
pass

elif issubclass(attribute_class, RocketSimulation):
rocket_attributes_list = [
attr
for attr in attribute_class.__annotations__.keys()
if attr not in ["message", "motor"]
]
try:
for key in rocket_attributes_list:
if key not in attributes.get("rocket", {}):
try:
value = getattr(obj.rocket, key)
attributes.setdefault("rocket", {})[key] = value
except Exception:
pass
except Exception:
pass

elif issubclass(attribute_class, MotorSimulation):
motor_attributes_list = [
attr
for attr in attribute_class.__annotations__.keys()
if attr not in ["message"]
]
try:
for key in motor_attributes_list:
if key not in attributes.get("rocket", {}).get(
"motor", {}
):
try:
value = getattr(obj.rocket.motor, key)
attributes.setdefault("rocket", {}).setdefault(
"motor", {}
)[key] = value
except Exception:
pass
except Exception:
pass

elif issubclass(attribute_class, EnvironmentSimulation):
environment_attributes_list = [
attr
for attr in attribute_class.__annotations__.keys()
if attr not in ["message"]
]
try:
for key in environment_attributes_list:
if key not in attributes.get("env", {}):
try:
value = getattr(obj.env, key)
attributes.setdefault("env", {})[key] = value
except Exception:
pass
except Exception:
pass
else:
continue
_populate_simulation_attributes(obj, attribute_class, attributes)

return rocketpy_encoder(attributes)


def _populate_simulation_attributes(obj, attribute_class, attributes):
if not isinstance(attribute_class, type):
return

mappings = (
(FlightSimulation, (), (), {"message", "rocket", "env"}),
(RocketSimulation, ("rocket",), ("rocket",), {"message", "motor"}),
(
MotorSimulation,
("rocket", "motor"),
("rocket", "motor"),
{"message"},
),
(EnvironmentSimulation, ("env",), ("env",), {"message"}),
)

for klass, source_path, target_path, exclusions in mappings:
if not issubclass(attribute_class, klass):
continue

keys = _annotation_keys(attribute_class, exclusions)
if not keys:
return

source = _resolve_attribute_path(obj, source_path)
if source is None:
return

target = _resolve_attribute_target(attributes, target_path)
_copy_missing_attributes(source, target, keys)
return


def _annotation_keys(attribute_class, exclusions):
annotations = getattr(attribute_class, "__annotations__", {})
return [key for key in annotations if key not in exclusions]


def _resolve_attribute_path(root, path):
current = root
for attr in path:
if current is None:
return None
current = getattr(current, attr, None)
return current


def _resolve_attribute_target(attributes, path):
target = attributes
for key in path:
target = target.setdefault(key, {})
return target


def _copy_missing_attributes(source, target, keys):
for key in keys:
if key in target:
continue
try:
target[key] = getattr(source, key)
except AttributeError:
continue


def _fix_datetime_fields(data):
"""
Fix datetime fields that RocketPyEncoder converted to lists.
Expand Down