From 36d8bc4d888a8e5aeffd28b273bdbad4ec563dc0 Mon Sep 17 00:00:00 2001 From: Luigi Maiorano Date: Thu, 29 Jan 2026 10:43:05 +0100 Subject: [PATCH] fixed saving png issue --- plots.py | 68 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/plots.py b/plots.py index 07c7269..fd6861a 100644 --- a/plots.py +++ b/plots.py @@ -8,6 +8,7 @@ import pandas as pd import polars as pl from theme import ColorPalette +import hashlib class JPMCPlotsMixin: """Mixin class for plotting functions in JPMCSurvey.""" @@ -56,8 +57,13 @@ class JPMCPlotsMixin: continue if len(value) > 3: - # If more than 3 options selected, use count to keep slug short - val_str = f"{len(value)}_grps" + # If more than 3 options selected, create a hash of the sorted values + # This ensures uniqueness properly while keeping the slug short + sorted_vals = sorted([str(v) for v in value]) + vals_str = "".join(sorted_vals) + # Create short 6-char hash + val_hash = hashlib.md5(vals_str.encode()).hexdigest()[:6] + val_str = f"{len(value)}_grps_{val_hash}" else: # Join values with '+' clean_values = [] @@ -196,9 +202,25 @@ class JPMCPlotsMixin: path.mkdir(parents=True, exist_ok=True) filename = f"{self._sanitize_filename(title)}.png" + filepath = path / filename - # Save using vl-convert backend - chart.save(str(path / filename), format='png', scale_factor=2.0) + # Use vl_convert directly with theme config for consistent rendering + import vl_convert as vlc + from theme import jpmc_altair_theme + + # Get chart spec and theme config + chart_spec = chart.to_dict() + theme_config = jpmc_altair_theme()['config'] + + png_data = vlc.vegalite_to_png( + vl_spec=chart_spec, + scale=2.0, + ppi=72, + config=theme_config + ) + + with open(filepath, 'wb') as f: + f.write(png_data) return chart @@ -282,7 +304,7 @@ class JPMCPlotsMixin: # Combine layers chart = (bars + text).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -339,7 +361,7 @@ class JPMCPlotsMixin: ] ).add_params(selection).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -399,7 +421,7 @@ class JPMCPlotsMixin: ] ).add_params(selection).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -452,7 +474,7 @@ class JPMCPlotsMixin: ] ).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -493,7 +515,7 @@ class JPMCPlotsMixin: chart = (bars + text).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -550,7 +572,7 @@ class JPMCPlotsMixin: ] ).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -606,7 +628,7 @@ class JPMCPlotsMixin: ] ).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) ) @@ -663,9 +685,10 @@ class JPMCPlotsMixin: else: trait_description = "" - # Horizontal bar chart + # Horizontal bar chart - use x2 to explicitly start bars at x=1 bars = alt.Chart(stats).mark_bar(color=ColorPalette.PRIMARY).encode( x=alt.X('mean_score:Q', title='Average Score (1-5)', scale=alt.Scale(domain=[1, 5])), + x2=alt.datum(1), # Bars start at x=1 (left edge of domain) y=alt.Y('Voice:N', title='Voice', sort='-x'), tooltip=[ alt.Tooltip('Voice:N'), @@ -674,24 +697,29 @@ class JPMCPlotsMixin: ] ) - # Count text inside bars - text = bars.mark_text( - align='center', + # Count text at end of bars (right-aligned inside bar) + text = alt.Chart(stats).mark_text( + align='right', baseline='middle', color='white', - fontSize=16 + fontSize=12, + dx=-5 # Slight padding from bar end ).encode( + x='mean_score:Q', + y=alt.Y('Voice:N', sort='-x'), text='count:Q' ) - # Combine + # Combine layers chart = (bars + text).properties( title={ "text": title, "subtitle": [trait_description, "(Numbers on bars indicate respondent count)"] }, - width=width if width else 'container', + width=width or 800, height=height or getattr(self, 'plot_height', 400) + ).configure_view( + strokeWidth=0 # Remove frame which might obscure labels ) chart = self._save_plot(chart, title) @@ -749,7 +777,7 @@ class JPMCPlotsMixin: ] ).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or 350 ) @@ -805,7 +833,7 @@ class JPMCPlotsMixin: ] ).properties( title=title, - width=width if width else 'container', + width=width or 800, height=height or 350 )