Skip to content

fix/refactor: minor code clean up and fix for annotations in peakmaps#98

Merged
singjc merged 6 commits intoOpenMS:mainfrom
singjc:main
Nov 2, 2025
Merged

fix/refactor: minor code clean up and fix for annotations in peakmaps#98
singjc merged 6 commits intoOpenMS:mainfrom
singjc:main

Conversation

@singjc
Copy link
Collaborator

@singjc singjc commented Nov 2, 2025

This pull request contains a series of code cleanups, refactorings, ruff formating, and some bug fixes. Some of the params for controlling annotation params were not added/removed with the refactoring of how configs are handled.

Enhancements to annotation configuration:

  • Added new fields to PeakMapConfig and its factory for annotation customization, including annotation_colormap, annotation_line_width, annotation_line_type, and annotation_legend_config, as well as additional fields for column names and color mapping. These changes allow for more flexible and detailed annotation rendering in plots. [1] [2]
  • Updated the __post_init__ method in PeakMapConfig to ensure that annotation_legend_config is always a LegendConfig instance, improving type safety and consistency.

Code organization and cleanup:

  • Reorganized and cleaned up imports in pyopenms_viz/_bokeh/core.py, pyopenms_viz/_matplotlib/core.py, and pyopenms_viz/_core.py for better readability and maintainability. This includes deduplication, alphabetization, and removal of unused imports. [1] [2] [3] [4]
  • Removed unnecessary blank lines and minor formatting inconsistencies throughout the codebase for improved clarity. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

API and doc improvements:

  • Improved docstrings and parameter documentation in configuration classes to reflect new annotation-related options.
  • Refactored plotting API functions in pyopenms_viz/_core.py to single-line return statements for conciseness and consistency. [1] [2] [3] [4] [5]

Bugfixes and logic improvements:

  • Improved logic in the _get_annotations method to clarify assignment of the annotation color column and ensure correct handling of annotation color defaults. [1] [2]
  • Minor improvements to warnings and error messages for clarity and consistency. [1] [2]

Miscellaneous:

  • Added a new ncol field to LegendConfig for specifying the number of columns in legends, improving legend layout control.

Summary by CodeRabbit

  • New Features

    • Added annotation configuration options: customizable colors, line styles, and legend settings
    • Enhanced legend customization with column count control
  • Bug Fixes

    • Added validation to prevent plotting empty datasets
  • Refactor

    • Improved marker generation efficiency with lazy initialization
    • Streamlined configuration management with helper methods

* pandas peakmap plots failed in matplotlib backend because MarkerShapeGenerator defaulted to an uninitialized cycle and next(self.marker) raised on None.
* Persist engine/n size hints and lazily bootstrap the cycle, so defaults auto-initialize and existing backends keep their behavior.
* Adds a defensive engine validator and preserves explicit shape lists, preventing regressions for plotly/bokeh marker cycling.
@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Walkthrough

This pull request refactors annotation and legend configuration across the visualization library. It introduces new configuration fields for annotation styling (colormap, line width/type) and legend display, adds helper methods to the base configuration class, and updates plotting backends to use the new annotation-based configuration properties instead of feature-based ones.

Changes

Cohort / File(s) Summary
Configuration Schema Extensions
pyopenms_viz/_config.py
Adds ncol field to LegendConfig; introduces BaseConfig helper methods (from_dict, update_none_fields, update) for configuration merging; extends PeakMapConfig and ChromatogramConfig with annotation styling fields (colormap, line_width, line_type, legend_config) and marginal bound tracking fields; normalizes annotation_legend_config to LegendConfig instances in post-init methods.
Core Plotting Logic
pyopenms_viz/_core.py
Adds explicit empty DataFrame validation in BasePlot initializer; reorganizes imports (numpy, pandas, re, warnings); adjusts public plotting wrappers to return direct .plot() calls; refactors annotation color column selection logic and adds debug output for annotation colors.
Import Reorganization & Compatibility
pyopenms_viz/_bokeh/core.py
Reorganizes imports with added typing support (Tuple); consolidates Bokeh model imports (BoxEditTool, GlyphRenderer, Label, Plasma256, linear_cmap); adjusts error message formatting; minor spacing adjustments around methods.
Matplotlib Backend Annotation Handling
pyopenms_viz/_matplotlib/core.py
Switches legend and color handling from feature_config.* to annotation_* properties (colormap, line_width, line_type); introduces legend item aggregation with legend_items and custom_lines_list; updates legend construction to use annotation_legend_config; adds get_manual_bounding_box_coords() method stub.
Plotly Backend Property Mapping
pyopenms_viz/_plotly/core.py
Replaces feature-based configuration references with annotation properties: annotation_colormap, annotation_line_width, annotation_line_type; updates line dash type mapping calls; minor import reorganization and error message formatting.
Lazy Initialization for Marker Shapes
pyopenms_viz/_misc.py
Implements lazy initialization pattern in MarkerShapeGenerator with deferred engine/n setup; adds internal state tracking (_engine, _n); introduces validation for invalid engine values with fallback logic; exposes initialization state via new is_initialized() method.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Configuration schema changes across multiple classes: Verify all new annotation-related fields (annotation_colormap, annotation_line_width, annotation_line_type, annotation_legend_config, marginal bounds) are correctly initialized and used throughout the codebase.
  • Backend-specific refactoring: The shift from feature_config.* to annotation_* properties spans three backend implementations (matplotlib, plotly, bokeh) — ensure consistency in property naming and access patterns across all three.
  • Configuration helper methods: Review BaseConfig.from_dict(), update_none_fields(), and update() for correctness in merging/updating logic, especially edge cases with nested dataclass fields.
  • Lazy initialization logic: Verify MarkerShapeGenerator lazy initialization doesn't introduce timing issues or unexpected state transitions, and that is_initialized() correctly reflects the object's state.
  • Empty DataFrame validation: Confirm the new empty DataFrame check in BasePlot doesn't break edge cases or mask valid use scenarios.

Possibly related PRs

Suggested reviewers

  • jcharkow

Poem

🐰 Behold! Configuration flowers bloom,
Annotations painted in their rightful room,
Legends dance in columns neat,
Lazy shapes await their beat,
Plotly, Matplotlib, in harmony sweet! 🎨✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "fix/refactor: minor code clean up and fix for annotations in peakmaps" is directly related to the main changes in the changeset. The title accurately captures two primary aspects of the PR: code cleanup (import reorganization, formatting, blank line removal) and annotation handling improvements (new annotation configuration fields, refactored annotation logic across backends). The PR objectives confirm the focus is "annotation handling for peak maps" along with code cleanup and refactoring. While the scope extends to configuration structures and other plot types, the title appropriately highlights the central theme without being misleading.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pyopenms_viz/_bokeh/core.py (1)

644-663: Fix annotation color handling in box boundaries

When an annotation color column is supplied, the rectangle still gets colored with next(color_gen), so user-defined colors are ignored. Even without a color column, the extra next(color_gen) call advances the iterator twice per feature and desynchronizes the palette. Please reuse the resolved color variable for the glyph stroke.

             if self.annotation_colors in feature:
-                color = feature[self.annotation_colors]
+                color = feature[self.annotation_colors]
             else:
                 color = next(color_gen)
@@
-            box_boundary_lines = self.fig.rect(
+            box_boundary_lines = self.fig.rect(
                 x=center_x,
                 y=center_y,
                 width=width,
                 height=height,
-                color=next(color_gen),
+                line_color=color,
                 line_dash=self.annotation_line_type,
                 line_width=self.annotation_line_width,
                 fill_alpha=0,
             )
pyopenms_viz/_matplotlib/core.py (1)

567-568: Critical: Method defined in wrong scope.

The get_manual_bounding_box_coords method is incorrectly defined inside the _add_peak_boundaries method body. It should be defined at the class level as an instance method of MATPLOTLIBChromatogramPlot.

Apply this diff to fix the indentation:

-        # since matplotlib is not interactive cannot implement the following methods
-        def get_manual_bounding_box_coords(self):
-            pass
+    # since matplotlib is not interactive cannot implement the following methods
+    def get_manual_bounding_box_coords(self):
+        pass

Note: This should be dedented by 4 spaces to align with other class methods.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24b82d9 and f10cebc.

📒 Files selected for processing (6)
  • pyopenms_viz/_bokeh/core.py (2 hunks)
  • pyopenms_viz/_config.py (5 hunks)
  • pyopenms_viz/_core.py (14 hunks)
  • pyopenms_viz/_matplotlib/core.py (5 hunks)
  • pyopenms_viz/_misc.py (5 hunks)
  • pyopenms_viz/_plotly/core.py (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
pyopenms_viz/_plotly/core.py (2)
pyopenms_viz/_config.py (1)
  • bokeh_line_dash_mapper (524-571)
pyopenms_viz/_core.py (9)
  • BaseMSPlot (493-544)
  • BasePlot (115-431)
  • ChromatogramPlot (547-631)
  • LinePlot (434-441)
  • MobilogramPlot (634-653)
  • PeakMapPlot (1117-1348)
  • ScatterPlot (473-490)
  • SpectrumPlot (656-1114)
  • VLinePlot (444-470)
pyopenms_viz/_bokeh/core.py (1)
pyopenms_viz/_core.py (8)
  • BaseMSPlot (493-544)
  • BasePlot (115-431)
  • ChromatogramPlot (547-631)
  • LinePlot (434-441)
  • PeakMapPlot (1117-1348)
  • ScatterPlot (473-490)
  • SpectrumPlot (656-1114)
  • show (401-407)
pyopenms_viz/_core.py (2)
pyopenms_viz/_config.py (1)
  • BasePlotConfig (134-238)
pyopenms_viz/__init__.py (1)
  • _get_plot_backend (193-202)
pyopenms_viz/_config.py (1)
pyopenms_viz/_misc.py (2)
  • ColorGenerator (11-128)
  • MarkerShapeGenerator (131-279)
pyopenms_viz/_matplotlib/core.py (3)
pyopenms_viz/_config.py (2)
  • LegendConfig (62-130)
  • _matplotlibLegendLocationMapper (94-110)
pyopenms_viz/_core.py (3)
  • BaseMSPlot (493-544)
  • SpectrumPlot (656-1114)
  • show (401-407)
pyopenms_viz/_misc.py (1)
  • ColorGenerator (11-128)
🪛 Ruff (0.14.2)
pyopenms_viz/_misc.py

251-251: Avoid specifying long messages outside the exception class

(TRY003)

pyopenms_viz/_core.py

131-131: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: test (windows-latest, 3.12)
🔇 Additional comments (5)
pyopenms_viz/_matplotlib/core.py (5)

5-27: LGTM!

The import reorganization improves code maintainability by alphabetizing imports and adding necessary imports (Rectangle) for the box boundary drawing functionality.


60-60: LGTM!

Appropriate change from f-string to plain string since there are no variables to interpolate.


502-564: LGTM!

The refactoring to use annotation-based configuration properties (annotation_colormap, annotation_line_width, annotation_line_type, annotation_legend_config) is consistent with the PR objectives and improves the clarity of the annotation rendering logic.


728-790: LGTM!

The refactoring consistently applies annotation-based configuration throughout the method. The variable naming is clear (legend_items for labels, custom_lines_list for Rectangle handles), and the legend construction properly uses the new ncol field from annotation_legend_config to control column layout.


793-794: LGTM!

The method stub is correctly defined at the class level with proper indentation, unlike the one in MATPLOTLIBChromatogramPlot.

Comment on lines +996 to +1003
"annotation_color"
if self.annotation_color is None
else self.annotation_color
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Drop stray stdout debug print

print(f"Annotation color: ...") will fire on every spectrum plot and spam stdout. Please remove this debug statement (or replace it with proper logging if needed).

         if self.annotation_color is None:
             data["annotation_color"] = "black"
-        print(f"Annotation color: {self.annotation_color}")
         annotation_color_column = (
             "annotation_color"
             if self.annotation_color is None
🤖 Prompt for AI Agents
In pyopenms_viz/_core.py around lines 996 to 1003 there is a stray debug print
that prints the annotation color to stdout on every spectrum plot; remove the
print(f"Annotation color: {self.annotation_color}") line (or replace it with a
proper logger.debug(...) call using the module logger) and ensure behavior
otherwise remains the same (keep the annotation_color defaulting and
column-selection logic intact).

@singjc singjc merged commit debdc73 into OpenMS:main Nov 2, 2025
7 of 8 checks passed
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.

1 participant