Skip to content

Change simulation encoding workaround to official rocketpy encoders #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

GabrielBarberini
Copy link
Collaborator

@GabrielBarberini GabrielBarberini commented Jun 1, 2025

Pull Request Overview

This PR shifts the simulation encoding approach to utilize the official RocketPy encoders and a new DiscretizeConfig for improved dynamic attribute support across simulation models. Key changes include:

  • Replacing usage of get_instance_attributes with the new rocketpy_encoder and DiscretizeConfig.
  • Updating simulation view classes (RocketSimulation, MotorSimulation, FlightSimulation, EnvironmentSimulation) to use pydantic’s ConfigDict.
  • Refactoring services to integrate the updated encoding logic for Rocket, Motor, Flight, and Environment simulations.

Summary by CodeRabbit

  • Refactor

    • Switched to a configurable encoder for environment/flight/motor/rocket data, enabling more flexible, discretized and dynamic simulation outputs.
    • Simulation view models now accept extra/dynamic attributes and have clearer, reorganized core fields for more robust decoding.
  • Chores

    • JSON serialization now omits null fields across models and repository payloads, producing cleaner API responses and storage payloads.
  • Documentation

    • Added and expanded docstrings to clarify simulation models and dynamic encoding behavior.

@GabrielBarberini GabrielBarberini requested review from Copilot and removed request for aasitvora99 June 1, 2025 03:02
Copy link
Contributor

coderabbitai bot commented Jun 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces generic attribute extraction with a configurable encoder for RocketPy objects, introduces DiscretizeConfig and rocketpy_encoder, updates services to use the encoder, adapts view models to accept dynamic encoded attributes, and configures serialization to exclude None fields for API models and views.

Changes

Cohort / File(s) Change Summary
Services
src/services/environment.py, src/services/flight.py, src/services/motor.py, src/services/rocket.py
Replaced get_instance_attributes(...) with rocketpy_encoder(obj, DiscretizeConfig.for_...) to produce encoded dicts passed into Simulation constructors. Adjusted imports accordingly.
Encoder utils
src/utils.py
Added DiscretizeConfig class and rocketpy_encoder function; removed to_python_primitive and related alias; added logging and fallback for encoding failures.
Views — simulations
src/views/environment.py, src/views/flight.py, src/views/motor.py, src/views/rocket.py
Made simulation models accept dynamic attributes: added model_config = ConfigDict(extra='allow', arbitrary_types_allowed=True), broadened function attribute types to Any, reorganized/trimmed explicit fields, added docstrings, and adjusted base classes where noted.
API models / views config
src/models/interface.py, src/views/interface.py
Added ser_json_exclude_none=True to model_config for ApiBaseModel and ApiBaseView so JSON serialization omits None-valued fields.
Repositories
src/repositories/flight.py, src/repositories/motor.py, src/repositories/rocket.py
Changed create/update payload serialization to use .model_dump(exclude_none=True) to omit None fields from DB insert/update payloads.

Sequence Diagram(s)

sequenceDiagram
    participant Service as Service (e.g., RocketService)
    participant Obj as RocketPy Object
    participant Config as DiscretizeConfig
    participant Encoder as rocketpy_encoder
    participant View as SimulationView

    Service->>Obj: read instance (self.<obj>)
    Service->>Config: DiscretizeConfig.for_<type>()
    Service->>Encoder: rocketpy_encoder(Obj, Config)
    Encoder->>Obj: discretize callable attributes, serialize
    Encoder-->>Service: encoded dict
    Service->>View: SimulationView(**encoded dict)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Implements motor route tests #38 — Related changes to Pydantic model configuration and JSON encoding for view models; overlaps with model_config/serialization changes.

Suggested reviewers

  • luimot
  • phmbressan
  • Gui-FernandesBR
  • aasitvora99

Poem

"I nibble bytes and hop through code,
Swapped extractors for an encoder mode.
Discretize the functions, pack them neat,
Views stretch wide on flexible feet.
🐰🚀 — a rabbit’s patchwork, tidy and sweet."

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rocketpy_encoders_refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copilot

This comment was marked as duplicate.

coderabbitai[bot]

This comment was marked as outdated.

@GabrielBarberini GabrielBarberini requested a review from luimot June 1, 2025 03:11
@GabrielBarberini GabrielBarberini requested a review from Copilot June 1, 2025 03:13
Copilot

This comment was marked as duplicate.

GabrielBarberini and others added 3 commits June 1, 2025 00:14
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@GabrielBarberini GabrielBarberini requested a review from Copilot June 1, 2025 03:23
Copilot

This comment was marked as outdated.

@GabrielBarberini GabrielBarberini requested a review from Copilot June 1, 2025 03:32
Copilot

This comment was marked as outdated.

@GabrielBarberini
Copy link
Collaborator Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jun 1, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR modernizes the simulation encoding approach by replacing the deprecated get_instance_attributes utility with official RocketPy encoders and introducing a new DiscretizeConfig class for better dynamic attribute handling. The changes enable more flexible and extensible simulation data serialization across all RocketPy object types.

Key Changes:

  • Replaced get_instance_attributes with new rocketpy_encoder function using official RocketPy RocketPyEncoder
  • Added DiscretizeConfig class for configurable function discretization parameters
  • Updated all simulation view classes to use Pydantic's ConfigDict with extra='allow' for dynamic attributes

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils.py Added DiscretizeConfig class and rocketpy_encoder function replacing deprecated encoding approach
src/views/*.py Updated simulation view classes to use ConfigDict and replaced specific attribute types with Any
src/services/*.py Replaced get_instance_attributes calls with rocketpy_encoder using appropriate configs
src/repositories/*.py Added exclude_none=True to model_dump() calls for cleaner serialization
src/models/interface.py Added ser_json_exclude_none=True to base model configuration


if callable(attr_value) and isinstance(attr_value, Function):
try:
discretized_func = Function(attr_value.source)
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new Function from attr_value.source may not preserve all properties of the original Function object. Consider using copy.deepcopy(attr_value) or a proper Function copy method if available.

Suggested change
discretized_func = Function(attr_value.source)
discretized_func = copy.deepcopy(attr_value)

Copilot uses AI. Check for mistakes.



class MotorSimulation(BaseModel):
class MotorSimulation(ApiBaseView):
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the base class from BaseModel to ApiBaseView is a breaking change that could affect existing API consumers who depend on the original inheritance structure.

Suggested change
class MotorSimulation(ApiBaseView):
from pydantic import BaseModel
class MotorSimulation(BaseModel):

Copilot uses AI. Check for mistakes.


# Core Rocket attributes (always present)
radius: Optional[float] = None
mass: Optional[float] = None
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed attribute area is not present in the new core attributes list. If this attribute is still needed, it should be included or documented why it was removed.

Copilot uses AI. Check for mistakes.

time: Optional[Any] = None
solution: Optional[Any] = None

# Function attributes (discretized by rocketpy_encoder, serialized by RocketPyEncoder)
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment appears to be a duplicate of the comment on line 33. The distinction between 'Key Flight Function attributes' and 'Function attributes' is unclear.

Suggested change
# Function attributes (discretized by rocketpy_encoder, serialized by RocketPyEncoder)
# Additional Flight Function attributes: Supplementary outputs that may be useful for advanced analysis or diagnostics (also discretized by rocketpy_encoder, serialized by RocketPyEncoder)

Copilot uses AI. Check for mistakes.

include_outputs=True,
include_function_data=True,
)
return json.loads(json_str)
Copy link
Preview

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The encoding process involves creating a JSON string and then immediately parsing it back to a dictionary. Consider using the encoder directly to return a dictionary without the JSON round-trip if possible.

Suggested change
return json.loads(json_str)
encoder = RocketPyEncoder(
include_outputs=True,
include_function_data=True,
)
return encoder.default(obj_copy)

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8450082 and d1f9a97.

📒 Files selected for processing (5)
  • src/models/interface.py (1 hunks)
  • src/repositories/flight.py (1 hunks)
  • src/repositories/motor.py (1 hunks)
  • src/repositories/rocket.py (1 hunks)
  • src/views/interface.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-02-22T21:53:21.139Z
Learnt from: GabrielBarberini
PR: RocketPy-Team/Infinity-API#44
File: src/models/interface.py:12-17
Timestamp: 2025-02-22T21:53:21.139Z
Learning: In Pydantic v2, both bare dict and ConfigDict can be used for model_config, but ConfigDict is recommended for better type hints and IDE support.

Applied to files:

  • src/views/interface.py
📚 Learning: 2025-02-22T21:53:21.139Z
Learnt from: GabrielBarberini
PR: RocketPy-Team/Infinity-API#44
File: src/models/interface.py:12-17
Timestamp: 2025-02-22T21:53:21.139Z
Learning: Pydantic v2 requires using ConfigDict for model configuration. Plain dictionaries lack type safety, proper validation, and full feature support.

Applied to files:

  • src/views/interface.py
🧬 Code Graph Analysis (3)
src/repositories/rocket.py (4)
src/repositories/interface.py (2)
  • insert (225-232)
  • update_by_id (235-239)
src/models/rocket.py (1)
  • RocketModel (13-63)
src/routes/rocket.py (1)
  • update_rocket (56-68)
tests/unit/test_routes/test_rockets_route.py (1)
  • test_update_rocket (357-365)
src/repositories/flight.py (2)
src/repositories/interface.py (2)
  • insert (225-232)
  • update_by_id (235-239)
src/models/flight.py (2)
  • FlightModel (7-71)
  • DELETED (53-54)
src/repositories/motor.py (5)
src/repositories/interface.py (3)
  • insert (225-232)
  • find_by_id (242-249)
  • update_by_id (235-239)
src/models/motor.py (2)
  • MotorModel (16-95)
  • UPDATED (73-74)
tests/unit/test_routes/test_motors_route.py (2)
  • test_update_motor_invalid_input (301-305)
  • test_update_motor (291-298)
src/routes/motor.py (1)
  • update_motor (56-68)
tests/unit/test_routes/conftest.py (1)
  • stub_motor_dump (19-30)
🔇 Additional comments (6)
src/repositories/rocket.py (2)

22-22: Insert payload: LGTM

Using model_dump(exclude_none=True) on create avoids persisting nulls for optional fields and aligns with the new global serialization strategy.


30-32: Re-verify null-field clearing behavior in update_by_id

I ran the suggested search for any tests sending None/null in update payloads and found no matches. However, absence of tests isn’t evidence that no clients rely on clearing fields via null. Please confirm whether any API consumers or tests expect setting a field to null to clear it. If so, address one of the following:

  • Keep exclude_none=True and update your API docs/tests to note that null can no longer clear fields.
  • Restore clearing behavior by splitting the payload into $set and $unset (example below).

File: src/repositories/rocket.py
Lines: 30–32

-        await self.update_by_id(
-            rocket.model_dump(exclude_none=True), data_id=rocket_id
-        )
+        raw = rocket.model_dump()
+        to_unset = {k: "" for k, v in raw.items() if v is None}
+        to_set   = {k: v  for k, v in raw.items() if v is not None}
+        await self.update_by_id(
+            {"$set": to_set, "$unset": to_unset}, data_id=rocket_id
+        )
src/repositories/flight.py (1)

22-22: Insert payload: consistent and correct

exclude_none=True on create is consistent with the new model/view serialization. Good.

src/repositories/motor.py (1)

22-22: Create flow: LGTM

exclude_none=True reduces stored document size and matches the PR-wide policy.

src/views/interface.py (1)

1-1: No JSON null expectations in view responses — approving use of ConfigDict

The only None assertion found (tests/unit/test_repositories/test_repository_interface.py:235) pertains to the repository stub’s return value, not to JSON payloads. There are no tests checking for ": null" in view output, so omitting null fields via ser_json_exclude_none=True is safe.

src/models/interface.py (1)

24-24: BaseModel JSON now omits None globally

The ser_json_exclude_none=True flag on your BaseModel (src/models/interface.py:24) means that any None‐valued fields will be dropped from the JSON output (model_dump_json()/.json()), matching patterns in repos/views.

  • I ran a search for any code or tests that inspect explicit null/None values in JSON serialization and found none.
  • Existing calls using exclude_none=True on .dict() remain unaffected.

Action required:

  • Update your API or developer documentation to note that fields with None values will no longer appear in serialized JSON (clients expecting explicit null entries should be informed).

fix_required

Comment on lines +30 to +32
await self.update_by_id(
flight.model_dump(exclude_none=True), data_id=flight_id
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Update semantics changed: None no longer clears fields

Same concern as in RocketRepository: exclude_none=True will ignore fields explicitly set to None, preventing clearing. Decide whether:

  • This is intended and document it, or
  • Implement $unset handling for None fields.

Happy to draft an interface-level change to support $unset cleanly if desired.


🏁 Script executed:

#!/bin/bash
# Sanity check: ensure all repositories applied the same exclude_none=True policy
rg -n "model_dump\(exclude_none=True\)" src/repositories

Length of output: 650


Support clearing fields set to None via $unset

Currently, all repository update calls use model_dump(exclude_none=True), so any fields explicitly set to None are omitted and won’t be cleared in the database. Please decide on one of the following:

• If omitting None is the intended behavior, add clear documentation (both inline and in our API docs) stating that fields cannot be cleared via None.
• Otherwise, extend update_by_id to handle $unset:
– Build a $set payload from non-None values
– Build an $unset payload from keys with None values
– Send both in a single update operation

Apply the chosen approach consistently in:

  • src/repositories/rocket.py (update_by_id call at line 31)
  • src/repositories/motor.py (update_by_id call at line 31)
  • src/repositories/flight.py (update_by_id call at line 31)

Let me know if you’d like a draft for the interface-level refactor to support $unset.

Comment on lines +30 to +32
await self.update_by_id(
motor.model_dump(exclude_none=True), data_id=motor_id
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

PUT behavior change: explicit nulls ignored on update

Applying exclude_none=True here prevents clients from nulling fields. If unsetting is a supported use case, consider the $set/$unset approach mentioned for Rocket/Flight repositories; otherwise, update API documentation to make this explicit.


🏁 Script executed:

#!/bin/bash
# Look for tests asserting null-able fields after update
rg -n "update_.*motor|client\.put\('/motors" -A 8 tests | rg -n "None|null"

Length of output: 179


Explicit nulls are ignored on updates; choose to implement unset logic or update docs accordingly

  • Location: src/repositories/motor.py, around the update_by_id( motor.model_dump(exclude_none=True), … ) call.
  • exclude_none=True drops any fields clients explicitly set to null, so you can’t use this endpoint to unset a value.
  • If unsetting fields is a required use case:
    • Adopt a $set/$unset pattern (see Rocket/Flight repos) to translate None into an $unset operation.
  • Otherwise, update the API documentation to clearly state that null values are ignored and cannot be used to clear existing fields.
🤖 Prompt for AI Agents
In src/repositories/motor.py around lines 30 to 32, the call to update_by_id
uses motor.model_dump(exclude_none=True) which strips fields explicitly set to
None so clients cannot unset values; either implement unset logic or document
behavior. To fix, implement a $set/$unset translation: serialize the model
without excluding None, iterate keys and build an update payload that places
non-None fields into a $set dict and None-valued fields into a $unset dict (or
omit $unset if unset not supported), then call update_by_id with the composed
Mongo-style update object; alternatively, if unsetting is intentionally
unsupported, update the API docs and client contract to state that nulls are
ignored and cannot clear fields.

PlainSerializer(to_python_primitive),
]
try:
json_str = json.dumps(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phmbressan @GabrielBarberini not sure who this is for but when i run the env class simulation I get an incorrect date format here

"date": [2025, 6, 9, 23]

get_instance_attributes parses it as a natural datetime object. Is this a encoder issue? Observed this behavior while printing this json_str variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants