from pandas import DataFrame from plotly.subplots import make_subplots from plotly.graph_objects import Figure, Pie, Bar, Scatter, Scatterpolar def draw_report_figure( df: DataFrame, threshold: tuple[float] = [15_000, 5_000, 15_000] ) -> Figure: figure_specs = [ [{"type": "xy"}, {"type": "domain"}], [{"type": "xy"}, {"type": "xy"}], ] fig = make_subplots( rows=2, cols=2, specs=figure_specs, subplot_titles=( "Carbon Emission by Category", "Emission Distribution", ), ) # Pie chart settings pie_pull = [0.15 if x == min(df["Value"]) else 0.0 for x in df["Value"]] fig.add_trace( Pie( values=df["Value"], labels=df["Category"], hole=0.3, pull=pie_pull, name="Emission Distribution", marker={"colors": ["#6DA34D", "#81C3D7", "#FFC857"]}, ), row=1, col=2, ) # Bar chart for emissions by category fig.add_trace( Bar( x=df["Category"], y=df["Value"], name="Carbon Emission (kgCO2)", marker_color=["#6DA34D", "#81C3D7", "#FFC857"], ), row=1, col=1, ) # Annotation for highest emission fig.add_annotation( x=df["Category"][df["Value"].idxmax()], y=df["Value"].max(), text="Highest Emission", showarrow=True, arrowhead=1, ax=0, ay=-40, row=1, col=1, ) # Update layout for axes and overall layout fig.update_layout( title_text=f"Carbon Footprint of {df['Name'][0]}", plot_bgcolor="white", legend_title_text="Breakdown", xaxis_title="Emission Category", yaxis_title="Carbon Emission (kgCO2)", yaxis=dict( linecolor="black", showline=True, ticks="outside", mirror=True, gridcolor="lightgrey", ), legend=dict( x=1, y=0, xanchor="right", yanchor="bottom", orientation="h", ), ) fig.update_xaxes( linecolor="black", ticks="outside", showline=True, mirror=True, ) fig.update_yaxes( linecolor="black", showline=True, ticks="outside", mirror=True, gridcolor="lightgrey", ) # Add a single general recommendation text box e, w, b = df["Value"] threshold_values = [e >= threshold[0], w >= threshold[1], b >= threshold[2]] recommendations = [] texts = [ "- Reduce energy usage by adopting energy-efficient practices.\n", "- Minimize waste by recycling and using sustainable materials.\n", "- Limit business travel and opt for virtual meetings where possible." ] if any(threshold_values): fig.add_annotation( text=("Recommendations to reduce carbon footprint:\n"), xref="paper", yref="paper", x=0, y=0.2, # Positioning inside the plot area, just below center showarrow=False, font=dict(size=12), align="center", bordercolor="black", borderwidth=1, borderpad=10, bgcolor="lightyellow", ) if threshold_values[0]: recommendations.append(texts[0]) if threshold_values[1]: recommendations.append(texts[1]) if threshold_values[2]: recommendations.append(texts[2]) for i, text in enumerate(recommendations): fig.add_annotation( text=(text), xref="paper", yref="paper", x=0, y=(i + 1) * 0.05, # Positioning inside the plot area, just below center showarrow=False, font=dict(size=12), align="center", bordercolor="black", borderwidth=1, borderpad=10, bgcolor="lightyellow", ) return fig def draw_historic_figure(df: DataFrame) -> Figure: # Create subplots with 2 rows and 1 column fig = make_subplots( rows=2, cols=1, specs=[[{"type": "xy"}], [{"type": "polar"}]], subplot_titles=( "Total Carbon Footprint by Company", "Company Metrics Radar Chart", ), row_heights=[0.6, 0.4], ) # Calculate each company's total carbon footprint as the sum of the three metrics df["Carbon Footprint"] = ( df["Energy Usage"] + df["Waste Generated"] + df["Business Travel"] ) # Add gradient-filled area trace for the Carbon Footprint fig.add_trace( Scatter( x=df["Name"], y=df["Carbon Footprint"], mode="lines", fill="tozeroy", line=dict(color="blue"), fillcolor="rgba(31, 119, 180, 0.5)", # Gradient fill for blue name="Carbon Footprint", ), row=1, col=1, ) # Prepare data for radar chart (normalizing values for better comparability) categories = ["Energy Usage", "Waste Generated", "Business Travel"] for _, company in df.iterrows(): fig.add_trace( Scatterpolar( r=[ company["Energy Usage"], company["Waste Generated"], company["Business Travel"], company["Energy Usage"], # Closing the loop ], theta=categories + [categories[0]], # Circular radar plot fill="toself", name=company["Name"], ), row=2, col=1, ) # Update layout for the figure fig.update_layout( title="Company Metrics Visualization", template="plotly_white", height=800, width=1000, showlegend=True, legend=dict(x=1.05, y=1), # Adjust legend position ) # Update x and y-axis titles for the first plot fig.update_xaxes(title_text="Company", row=1, col=1) fig.update_yaxes(title_text="Carbon Footprint (total)", row=1, col=1) # Customize polar (radar) chart layout fig.update_polars( radialaxis=dict( visible=True, range=[ df[categories].values.min(), df[categories].values.max(), ], ) ) return fig def make_dataframe( company_name: str, avg_electric_bill: float, avg_gas_bill: float, avg_transport_bill: float, monthly_waste_generated: float, recycled_waste_percent: float, annual_travel_kms: float, fuel_efficiency: float, ) -> DataFrame: energy_usage = ( (avg_electric_bill * 12 * 5e-4) + (avg_gas_bill * 12 * 5.3e-3) + (avg_transport_bill * 12 * 2.32) ) waste_generated = monthly_waste_generated * 12 * 0.57 - recycled_waste_percent business_travel = annual_travel_kms * 1 / fuel_efficiency * 2.31 return DataFrame( { "Name": company_name, "Category": ["Energy Usage", "Waste Generated", "Business Travel"], "Value": [energy_usage, waste_generated, business_travel], } ) def dataframe_to_dict(df: DataFrame) -> dict: return { "Name": df["Name"][0], "Energy Usage": df["Value"][0], "Waste Generated": df["Value"][1], "Business Travel": df["Value"][2], }