Skip to content

Refactor graph creators #219

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

Merged
merged 5 commits into from
May 25, 2025
Merged

Refactor graph creators #219

merged 5 commits into from
May 25, 2025

Conversation

Frix-x
Copy link
Owner

@Frix-x Frix-x commented May 25, 2025

Summary by Sourcery

Refactor graph creators to adopt a composition-based architecture by separating computation and plotting into dedicated modules, simplify the GraphCreator API, and add command-line interface support and documentation.

New Features:

  • Add a Shake&Tune CLI mode that can generate graphs from existing data without Klipper.
  • Support CSV loading in MeasurementsManager when running in CLI mode.

Enhancements:

  • Decouple computation and plotting logic into computations/ and plotters/ packages and introduce shared base models and utilities.
  • Unify GraphCreator implementation to accept computation and plotter strategy classes and streamline its create_graph workflow.
  • Reorganize imports and module exports for graph creators, including new plotting_utils and base_models.
  • Remove in-class computation implementations and replace with dedicated computation classes for each graph type.

Documentation:

  • Add CLI usage documentation in docs/cli_usage.md and update README to reference CLI.
  • Update graph creator docs to reflect new architecture and available modules.

Frix-x added 2 commits May 25, 2025 17:08
Better separation of concerns between computations and plotting logic,
and switch to composition instead of inheritance for the graph creators.
Also added better type safety using Protocols, better abstract base classes
and data transfer objects.
Copy link
Contributor

sourcery-ai bot commented May 25, 2025

Reviewer's Guide

This PR refactors the GraphCreator framework into a composition-based architecture, moving computation and plotting logic into dedicated modules, introducing base models and helpers for uniform interfaces, and simplifying the GraphCreator API and package structure.

Sequence Diagram: GraphCreator.create_graph Core Interaction Flow

sequenceDiagram
    participant Client
    participant GCreator as ConcreteGraphCreator
    participant GComputation as ConcreteComputation
    participant GPlotter as ConcretePlotter

    Client->>GCreator: create_graph(measurements_manager)
    activate GCreator
    GCreator->>GCreator: _create_computation(measurements_manager)
    activate GCreator
    GCreator-->>GComputation: new(measurements, params)
    deactivate GCreator
    GCreator->>GComputation: compute()
    activate GComputation
    GComputation-->>GCreator: computation_result
    deactivate GComputation
    GCreator->>GPlotter: plot(computation_result)
    activate GPlotter
    GPlotter-->>GCreator: figure
    deactivate GPlotter
    GCreator->>GCreator: _save_figure(figure)
    deactivate GCreator
Loading

Class Diagram: GraphCreator Compositional Architecture

classDiagram
    class GraphCreator {
        <<Abstract>>
        #_config: ShakeTuneConfig
        #_version: str
        #_type: str
        #_folder: Path
        #_output_target: Optional~Path~
        #_computation_class: Type~Computation~
        #_plotter: PlotterStrategy
        +register(graph_type: str)* decorator
        +__init__(config: ShakeTuneConfig, computation_class: Type~Computation~, plotter_class: Type~PlotterStrategy~)
        +configure(**kwargs)* void
        #_create_computation(measurements_manager: MeasurementsManager)* Computation
        +create_graph(measurements_manager: MeasurementsManager) void
        #_save_figure(fig: Figure) void
    }

    class Computation {
        <<Interface>>
        +compute()* ComputationResult
    }

    class PlotterStrategy {
        <<Abstract>>
        +KLIPPAIN_COLORS: dict
        #_logo_image: any
        +__init__()
        +plot(data: ComputationResult)* Figure
        +add_logo(fig: Figure, position: List~float~) void
        +add_version_text(fig: Figure, version: str, position: tuple) void
        +add_title(fig: Figure, title_lines: List~dict~) void
    }

    class ComputationResult {
        <<Abstract>>
        +metadata: GraphMetadata
        +measurements: List~Measurement~
        +get_plot_data()* Dict~str, any~
    }

    GraphCreator *-- "1" PlotterStrategy : _plotter (instance)
    GraphCreator --> "1" Computation : _computation_class (Type)
    GraphCreator ..> Computation : creates instance via _create_computation()

    class VibrationsGraphCreator {
        -_kinematics: Optional~str~
        -_accel: Optional~float~
        -_motors: Optional~List~any~~
        +__init__(config: ShakeTuneConfig)
        +configure(kinematics: str, accel: Optional~float~, motors: Optional~List~any~~) void
        #_create_computation(measurements_manager: MeasurementsManager) VibrationsComputation
    }
    VibrationsGraphCreator --|> GraphCreator

    class VibrationsComputation {
        +__init__(measurements, kinematics, accel, max_freq, motors, st_version)
        +compute() VibrationsResult
    }
    VibrationsComputation ..|> Computation

    class VibrationsPlotter {
        +plot(result: VibrationsResult) Figure
    }
    VibrationsPlotter --|> PlotterStrategy

    class VibrationsResult {
         +get_plot_data() Dict~str, any~
    }
    VibrationsResult --|> ComputationResult

    VibrationsGraphCreator ..> VibrationsComputation : creates
    VibrationsComputation ..> VibrationsResult : creates & returns
    VibrationsPlotter ..> VibrationsResult : uses in plot()
Loading

Class Diagram: GraphCreatorFactory Update

classDiagram
    class GraphCreatorFactory {
        <<Factory>>
        +create_graph_creator(graph_type: str, config: ShakeTuneConfig)* GraphCreator
    }

    class GraphCreator {
        <<Abstract>>
        registry: dict~str, Type[GraphCreator]~
        graph_type: str
        +register(graph_type: str)* decorator
    }
    GraphCreatorFactory ..> GraphCreator : uses registry
Loading

Class Diagram: New Data Models (GraphMetadata and its use in ComputationResult)

classDiagram
    class GraphMetadata {
        +title: str
        +subtitle: Optional~str~
        +version: str
        +timestamp: Optional~str~
        +additional_info: Dict~str, Any~
    }

    class ComputationResult {
        <<Abstract>>
        +metadata: GraphMetadata
        +measurements: List~Measurement~
    }
    ComputationResult o-- "1" GraphMetadata : contains
Loading

Class Diagram: New PlottingUtils Components

classDiagram
  namespace shaketune.graph_creators.plotting_utils {
    class AxesConfiguration {
      +configure_axes(ax, title, xlabel, ylabel, legend, grid, sci_axes) FontProperties
    }
    class PeakAnnotator {
      +annotate_peaks(ax, freqs, data, peaks, threshold, color, label_prefix) void
    }
    class PlottingConstants {
       +KLIPPAIN_COLORS : dict
    }
    class SpectrogramHelper {
      +plot_spectrogram(ax, t, bins, pdata, max_freq, title) void
    }
    class TableHelper {
      +add_table(ax, cell_text, row_labels, col_labels, title) void
    }
  }
  class PlotterStrategy {
    #_plotter: PlotterStrategy
  }
  PlotterStrategy --o plotting_utils.PlottingConstants : uses
  PlotterStrategy --o plotting_utils.AxesConfiguration : uses
  PlotterStrategy --o plotting_utils.SpectrogramHelper : uses
  PlotterStrategy --o plotting_utils.TableHelper : uses
  PlotterStrategy --o plotting_utils.PeakAnnotator : uses
Loading

Class Diagram: MeasurementsManager Method Updates

classDiagram
    class MeasurementsManager {
        +measurements: List~Measurement~
        +load_from_stdata(filename: Path) List~Measurement~
        +load_from_csvs(klipper_CSVs: List~Path~) List~Measurement~
        +add_measurement(name: str, samples: List~tuple~) void
    }
    note for MeasurementsManager "load_from_stdata now returns List[Measurement].
load_from_csvs updated for CLI support."
Loading

File-Level Changes

Change Details Files
Introduce composition-based GraphCreator API
  • Change GraphCreator to accept separate computation and plotter classes
  • Unify create_graph flow to call computation.compute() then plotter.plot()
  • Simplify GraphCreatorFactory and registry decorators
shaketune/graph_creators/graph_creator.py
shaketune/graph_creators/graph_creator_factory.py
shaketune/graph_creators/__init__.py
Extract computation logic into dedicated modules
  • Move each graph type’s compute methods into graph_creators/computations
  • Rename old inline computation classes to Computation implementations
  • Adjust configure hooks to return Computation instances
shaketune/graph_creators/computations/*.py
shaketune/graph_creators/*_graph_creator.py
Extract plotting logic into dedicated plotter modules
  • Move all plotting functions into graph_creators/plotters strategy classes
  • Implement PlotterStrategy protocol for uniform plotting interface
  • Update GraphCreator to instantiate and use those plotters
shaketune/graph_creators/plotters/*.py
shaketune/graph_creators/*_graph_creator.py
Add base models and structured result types
  • Introduce base_models with ComputationResult, GraphMetadata, and protocols
  • Create computation_results data classes for each graph type
  • Use these models to standardize get_plot_data()
shaketune/graph_creators/base_models.py
shaketune/graph_creators/computation_results.py
Add shared plotting utilities
  • Implement plotting_utils with axes configuration, spectrogram helper, tables, and peak annotation
  • Replace repeated helper code in plotters with these utilities
  • Import and expose utilities in graph_creators package
shaketune/graph_creators/plotting_utils.py
shaketune/graph_creators/__init__.py
Enhance MeasurementsManager CLI support
  • Return measurements from load_from_stdata
  • Add load_from_csvs branch for CLI mode without temp files
  • Append samples directly when SHAKETUNE_IN_CLI=1
shaketune/helpers/accelerometer.py
Update documentation with CLI guide
  • Add docs/cli_usage.md with usage examples for all graph types
  • Reference CLI in docs/README.md and main README
  • Align docs to reflect new package structure
docs/cli_usage.md
docs/README.md
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Frix-x Frix-x force-pushed the refact-creators branch from 7bf0ca5 to 991b119 Compare May 25, 2025 20:34
@Frix-x Frix-x added the enhancement New feature or request label May 25, 2025
@Frix-x Frix-x mentioned this pull request May 25, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @Frix-x - I've reviewed your changes - here's some feedback:

  • The reliance on the SHAKETUNE_IN_CLI environment variable in MeasurementsManager.load_from_csvs is brittle—consider passing an explicit CLI-mode flag or strategy into the loader rather than branching on os.environ directly.
  • You still call get_shaper_calibrate_module() in every computation class—consider injecting the calibrator (e.g. via the GraphCreator constructor or a shared service) to avoid duplication and make the modules easier to test.
Here's what I looked at during the review
  • 🟡 General issues: 5 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

this will allow to manually specify a specific chip for the measurement
and override the default behavior where S&T usually try to find the best
suited chip based on what is found in `[resonance_tester]` config section
@Frix-x Frix-x changed the base branch from main to develop May 25, 2025 21:33
@Frix-x Frix-x merged commit 06358be into develop May 25, 2025
11 checks passed
@Frix-x Frix-x deleted the refact-creators branch May 25, 2025 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant