Programming

functions_inner

import pandas as pd
import numpy as np

import numpy as np
import plotly.graph_objects as go

def format_number(value):
    try:
        value = float(value)
        """Format large numbers to B (billions) or T (trillions)."""
        if abs(value) >= 1_000_000_000_000:
            return f"{value / 1_000_000_000_000:.1f} Трлн."
        elif abs(value) >= 1_000_000_000:
            return f"{value / 1_000_000_000:.1f} Млрд."
        elif abs(value) >= 1_000_000:
            return f"{value / 1_000_000:.1f} Млн."
        return f"{value:,.0f} Сум"
    except (ValueError, TypeError):
        return value
        

def prepare_df(df, column, dept=None):
    try:
        df.loc[df['department'] == 'sk', 'deprtament'] = 'rk'
        df = df[df['brutto_95_amount']>0]
        if dept:
            df = df[df['department']==dept]
        df['is_bad_npl'] = df['npl_category'] > 2 
        df['brutto_bad'] = df['brutto_amount'].where(df['is_bad_npl'], 0)
        df['brutto_good'] = df['brutto_amount'].where(~df['is_bad_npl'], 0)
        print("1. Grouping")
        # 3. Group by whatever dimension you need dynamically (e.g. department)
        grouped = df.groupby(column).agg(
            brutto_bad=('brutto_bad', 'sum'),
            brutto_good=('brutto_good', 'sum')
        ).reset_index()


        print("2. Calculations")
        # 4. Calculate calculated totals & relative risk shares
        grouped['brutto_total'] = grouped['brutto_bad'] + grouped['brutto_good']
        grouped['bad_share'] = (grouped['brutto_bad'] / grouped['brutto_total'] * 100).fillna(0)
        grouped['good_share'] = (grouped['brutto_good'] / grouped['brutto_total'] * 100).fillna(0)
        grouped = grouped.sort_values('brutto_total', ascending = True)
        # 5. Format using your local layout number strings
        grouped['brutto_bad_formatted'] = grouped['brutto_bad'].apply(format_number)
        grouped['brutto_good_formatted'] = grouped['brutto_good'].apply(format_number)
        grouped['brutto_total_formatted'] = grouped['brutto_total'].apply(format_number)
        return grouped
    except Exception as e:
        print(f'{e} Error happened')

def generate_risk_chart(df, group_col, target_font="Arial", custom_color=None):
    COLOR_MAP = {
        'kk': '#5E4D21',
        'rk': '#1e3655',
        'mk': '#174d39',
    }
    active_dept = df['department'].iloc[0] if 'department' in df else 'All'
    good_color = COLOR_MAP.get(active_dept, custom_color or '#22e3a3')
    
    # --- SCROLL LOGIC SYSTEM ---
    ROW_LIMIT = 8            # Max bars to show without scrolling
    ROW_HEIGHT = 45          # Pixels allocated per bar
    BASE_PADDING = 100        # Pixels needed for margins and legend
    num_rows = len(df)
    #should set like > but I am finding this better
    if num_rows < ROW_LIMIT:
        # Calculate extended height if over the limit
        calculated_height = (num_rows * ROW_HEIGHT) + BASE_PADDING
    else:
        # Use your default static height if under the limit
        calculated_height = 450 
    
    hover_matrix = np.column_stack((
        df['brutto_good_formatted'], df['good_share'].round(1).astype(str) + '%',
        df['brutto_bad_formatted'], df['bad_share'].round(1).astype(str) + '%',
        df['brutto_total_formatted']
    ))
    
    hover_template = (
        '<b>%{y}</b><br>'
        'Стандарт: %{customdata[0]} (%{customdata[1]})<br>'
        'NPL (90+): %{customdata[2]} (%{customdata[3]})<br>'
        'Всего Портфел: %{customdata[4]}<extra></extra>'
    )
    
    hover_style = dict(bgcolor="white", font=dict(size=12), namelength=-1)
    traces_config = [
        {'x': df['brutto_bad'], 'name': 'NPL 90+', 'color': '#ef4444', 'text': None},
        {'x': df['brutto_good'], 'name': 'Стандарт', 'color': good_color, 'text': df['brutto_total_formatted']}
    ]

    fig = go.Figure()
    for t in traces_config:
        fig.add_trace(go.Bar(
            y=df[group_col],
            x=t['x'],
            orientation='h',
            name=t['name'],
            marker=dict(color=t['color'], cornerradius=3),
            opacity=0.9,
            text=t['text'],
            textposition='auto' if t['text'] is not None else 'none',
            hovertemplate=hover_template,
            hoverlabel=hover_style,
            customdata=hover_matrix,
            textfont=dict(size=16, color='#fff')
        ))

    fig.update_layout(
        barmode='stack',
        showlegend=True,
        plot_bgcolor='rgba(0,0,0,0)',
        paper_bgcolor='rgba(0,0,0,0)',
        margin=dict(l=5, r=5, t=5, b=5),
        height=calculated_height,  # Dynamic height injected here
        font=dict(size=16, family=target_font),
        legend=dict(
            orientation='h', yanchor='bottom', y=1.02, 
            xanchor='right', x=1, font=dict(size=14)
        )
    )
    fig.update_yaxes(tickfont=dict(size=12))
    
    buttons_to_kill = [
        'hoverClosestCartesian', 'pan2d', 'select2d', 'zoom2d', 
        'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'
    ]
    
    chart_html = fig.to_html(
        full_html=False,
        include_plotlyjs=False,
        config={"modeBarButtonsToRemove": buttons_to_kill, "displaylogo": False}
    )
    
    # --- HTML WRAPPER SYSTEM ---
    # Wraps the raw Plotly div in a scrollable CSS container 
    max_container_height = 450
    wrapper_html = f"""
    <div style="max-height: {max_container_height}px; overflow-y: auto; overflow-x: hidden;">
        {chart_html}
    </div>
    """
    
    return wrapper_html

Helpful? Dislike 0 Log in to react