Programming

general_functions

import plotly.express as px
import plotly.graph_objects as go
import plotly.graph_objects as go
import numpy as np
import os 
import datetime
import pandas as pd

target_font = "Times New Roman, serif"

def read_recent_file():
    today = datetime.date.today()
    active_date = str(today)
    filepath = f"autotask/processed/ready_{active_date}.csv"
    is_exist = os.path.isfile(filepath)
    i = 1
    while is_exist == False and i < 200:
        active_date = str(today-datetime.timedelta(days = i))
        filepath = f"autotask/processed/ready_{active_date}.csv"
        is_exist = os.path.isfile(filepath)
        i = i+1
    df = pd.read_csv(filepath, low_memory = False)
    date_name = active_date
    return df, date_name



def format_number(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:,.1f}"

def figures(df, col_name, colorise):
    # Calculate percentages for hover
    df['yes_percent'] = (df['yes'] / df['total'] * 100).round(1)
    df['no_percent'] = (df['no'] / df['total'] * 100).round(1)

    # Format values for hover display
    df['yes_formatted'] = df['yes'].apply(format_number)
    df['no_formatted'] = df['no'].apply(format_number)
    df['total_formatted'] = df['total'].apply(format_number)

    fig = go.Figure()
    # Yes bar (Overdue, Red)
    fig.add_trace(go.Bar(
        y=df[col_name],
        x=df['yes'],
        orientation='h',
        name='NPL 90+',
        marker=dict(color='#ef4444', cornerradius=3),  # Red for yes
        opacity=0.9,
        hovertemplate=(
            '<b>%{y}</b><br>' +
            'Непросроченные: %{customdata[0]}<br>' +
            'Непроср. Доля: %{customdata[1]}%<br>' +
            'Просроченные: %{customdata[2]}<br>' +
            'Проср. Доля: %{customdata[3]}%<br>' +
            'Все: %{customdata[4]}<extra></extra>'
        ),
        hoverlabel=dict(
        bgcolor="white",  # Background color for visibility
        font=dict(size=12),
        namelength=-1  # Show full label
    ),
        customdata=np.column_stack((
            df['no_formatted'],
            df['no_percent'],
            df['yes_formatted'],
            df['yes_percent'],
            df['total_formatted'], 
        )),
        textfont=dict(size=16, color='#fff')  # Increased from 14 to 16
    ))
    # No bar (Non-overdue, matches card colors)
    color_map = {
        '01-Кредитный департамент': '#5E4D21',  # Unchanged, matches gradient-corporate
        '02-Розничный департамент': '#1e3655',                       # Updated to approximate gradient-medium
        '03-Малое кредитование': '#174d39',
         # Updated to approximate gradient-small
    }
    department = df['department'].iloc[0] if 'department' in df else 'All'
    fig.add_trace(go.Bar(
        y=df[col_name],
        x=df['no'],
        orientation='h',
        name='Непросроченные',
        marker=dict(color=color_map.get(department, colorise), cornerradius=3),
        opacity=0.9,
        text=df['total'].apply(lambda x: format_number(x)),
        textposition='auto',
        
        hovertemplate=(
            '<b>%{y}</b><br>' +
            'Непросроченные: %{customdata[0]}<br>' +
            'Непроср. Доля: %{customdata[1]}%<br>' +
            'Просроченные: %{customdata[2]}<br>' +
            'Проср. Доля: %{customdata[3]}%<br>' +
            'Все: %{customdata[4]}<extra></extra>'
        ),
        hoverlabel=dict(
        bgcolor="white",  # Background color for visibility
        font=dict(size=12),
        namelength=-1  # Show full label
    ),
        customdata=np.column_stack((
            df['no_formatted'],
            df['no_percent'],
            df['yes_formatted'],
            df['yes_percent'],
            df['total_formatted'],
        )),
        textfont=dict(size=16, color='#fff')  # Increased from 14 to 16
    ))

    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=450,
        font=dict(size=16,
        family=target_font),  # Increased from 14 to 16 for general font
        legend=dict(
            orientation='h',
            yanchor='bottom',
            y=1.02,
            xanchor='right',
            x=1,
            font=dict(size=14)  # Increased from default to 14 for legend
        )
    )
    fig.update_yaxes(tickfont=dict(size=12))  # Increased from 10 to 12
    plot_html = fig.to_html(
        full_html=False,
        include_plotlyjs=False,
        config={"modeBarButtonsToRemove": ['hoverClosestCartesian', 'pan2d', 'select2d', "zoom2d", "lasso2d", "zoomIn2d", "zoomOut2d", "autoScale2d","resetScale2d"],"displaylogo": False, }
    )
    return plot_html

def sunburst_for_all(df):
    # Define a vibrant yet professional color palette
    custom_colors = [
        '#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
        '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'
    ]

    fig_sun = px.sunburst(
        df,
        path=['Половая принадлежность', 'dep', 'quality_standart'],
        values='Ос. кр. на балансе(экв.брутто)',
        branchvalues='total',
        color_discrete_sequence=custom_colors,  # Apply custom color palette
    )

    # Update layout for aesthetics
    fig_sun.update_layout(
        margin=dict(t=20, l=20, r=20, b=20),  # Slightly larger margins for breathing room
        font=dict(family=target_font, size=14, color="#333"),  # Modern font
        
        height=750,  # Slightly larger for better visibility
        paper_bgcolor="#f4f7fb",  # Soft, modern background
        plot_bgcolor="#ffffff",   # Clean white plot area
        showlegend=False,          # Hide legend for simplicity
    )

    # Update traces for refined styling
    fig_sun.update_traces(
        hoverinfo='skip',         # Disable hover for cleaner interaction
        hovertemplate=None,
        textinfo='label+percent entry',  # Show labels and percentages
        textfont=dict(size=13, color="#ffffff", family=target_font),  # White text for contrast
        marker=dict(
            line=dict(color="#ffffff", width=1.5),  # Thicker white borders for clarity
        ),
    )

    # Export to HTML with customized config
    fig_sun_html = fig_sun.to_html(
        full_html=False,
        include_plotlyjs=False,
        config={
            "modeBarButtonsToRemove": [
                'hoverClosestCartesian', 'pan2d', 'select2d', 'zoom2d',
                'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'
            ],
            "displaylogo": False,
        }
    )
    return fig_sun_html

def pie_by_category(df, names_for):
    color_map = {
        'Стандартный': '#00CC96',        # Green for Standard
        'Субстандартный': '#FFCC00',     # Yellow for Substandard
        'Неудовлетворительный': '#FF9900', # Orange for Unsatisfactory
        'Сомнительный': '#FF6666',       # Light Red for Doubtful
        'Безнадежный': '#CC0000'         # Dark Red for Hopeless
    }
    df['total_formatted'] = df['total'].apply(format_number)
    
    fig_mk_category = px.pie(df,values='total',names=names_for,hole=0.4,color=names_for,color_discrete_map=color_map)
    fig_mk_category.update_traces(
        textinfo='percent',                    
        texttemplate='%{percent:.1%}',        
        hovertemplate=(
           
            '<b>%{label}</b><br>' +
            'Доля: %{percent:.1%}<br>' +
            'Сумма: %{customdata}<extra></extra>'),
            customdata = df['total_formatted'],
            marker=dict(line=dict(color='#171717', width=2))
            ),

           
           
    fig_mk_category.update_layout(
        showlegend=True,
        annotations=[dict(text="Категория",x=0.5,y=0.5,font_size=16,showarrow=False,font=dict(color='black', family=target_font))],
        margin=dict(t=50, b=50, l=50, r=50),    
        font=dict(family=target_font, size=14),    
         legend=dict(
        orientation='v',
        yanchor='middle',
        y=0.5,
        xanchor='right',
        x=1.1,
        font=dict(
            family=target_font,
            size=11,  # Adjust the size as needed
            color="black"
        )
    ),
        plot_bgcolor='rgba(0,0,0,0)',paper_bgcolor='rgba(0,0,0,0)')
    fig_mk_cat = fig_mk_category.to_html(
        full_html=False,include_plotlyjs=False,
        config={"modeBarButtonsToRemove": ['hoverClosestCartesian', 'pan2d', 'select2d','zoom2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'],
            "displaylogo": False})
    return fig_mk_cat
   


def no_extra(figure):
    return figure.update_layout(
    xaxis_visible=False,
    yaxis_visible=False,
    xaxis_showticklabels=False,
    yaxis_showticklabels=False,
    xaxis_title=None,
    yaxis_title=None,
    showlegend=False,
    plot_bgcolor="rgba(0,0,0,0)",  # Transparent background
    paper_bgcolor="rgba(0,0,0,0)",  # Transparent paper
    margin=dict(l=0, r=0, t=0, b=0),
    height = 50  # Remove margins
    )
 

def common_function(df, col_name):
    alfa = pd.pivot_table(df, index = col_name, columns = 'is_npl', values = 'Ос. кр. на балансе(экв.брутто)', aggfunc = 'sum').reset_index()
    alfa = alfa.fillna(0)
    alfa['total'] = alfa['yes']+alfa['no']
    alfa = alfa.sort_values('total', ascending = True)
    alfa['percent'] = alfa['yes']/alfa['total']*100
    return alfa

def find_avg_interest(df):
    # Filter local and global currency data
    local_currency = df[df['currency'] == 'local'].copy()
    global_currency = df[df['currency'] == 'global'].copy()

    # Initialize default return values
    local_interest_rate = 0.0
    global_interest_rate = 0.0

    # Process local currency
    if not local_currency.empty:
        local_total = local_currency['Ос. кр. на балансе(экв.брутто)'].sum()
        if local_total > 0:  # Avoid division by zero
            local_currency['weight'] = local_currency['Ос. кр. на балансе(экв.брутто)'] / local_total
            local_currency['interest_weight'] = local_currency['weight'] * local_currency['Йиллик фоиз ставкаси']
            local_interest_rate = round(local_currency['interest_weight'].sum(), 1)
        else:
            local_interest_rate = 0.0  # Return 0 if total balance is 0

    # Process global currency
    if not global_currency.empty:
        global_total = global_currency['Ос. кр. на балансе(экв.брутто)'].sum()
        if global_total > 0:  # Avoid division by zero
            global_currency['weight'] = global_currency['Ос. кр. на балансе(экв.брутто)'] / global_total
            global_currency['interest_weight'] = global_currency['weight'] * global_currency['Йиллик фоиз ставкаси']
            global_interest_rate = round(global_currency['interest_weight'].sum(), 1)
        else:
            global_interest_rate = 0.0  # Return 0 if total balance is 0

    return local_interest_rate, global_interest_rate
def find_currency(df_currency):
    # Initialize default values
    national_percent = 0.0
    national_lp = 0.0
    foreign_percent = 0.0
    foreign_lp = 0.0,
    foreign_npl = 0.0,
    national_npl = 0.0,

    national_data = df_currency.loc[df_currency['currency'] == 'local', ['percent', 'total', 'yes']].fillna(0)
    if not national_data.empty:
        national_percent = float(national_data['percent'].iloc[0])
        national_lp = float(national_data['total'].iloc[0])
        national_npl = float(national_data['yes'].iloc[0])

    foreign_data = df_currency.loc[df_currency['currency'] == 'global', ['percent', 'total', 'yes']].fillna(0)
    if not foreign_data.empty:
        foreign_percent = float(foreign_data['percent'].iloc[0])
        foreign_lp = float(foreign_data['total'].iloc[0])
        foreign_npl = float(foreign_data['yes'].iloc[0])
    return national_percent, national_lp, foreign_percent, foreign_lp, national_npl, foreign_npl  

def find_gender(df_gender):
    # Initialize default values
    female_percent = 0.0
    female_lp = 0.0
    male_percent = 0.0
    male_lp = 0.0
    female_npl = 0.0
    male_npl = 0.0
    # Check for female data
    female_data = df_gender.loc[df_gender['Половая принадлежность'] == 'Женщина', ['percent', 'total', 'yes']].fillna(0)
    if not female_data.empty:
        female_percent = float(female_data['percent'].iloc[0])
        female_lp = float(female_data['total'].iloc[0])
        female_npl = float(female_data['yes'].iloc[0])

    # Check for male data
    male_data = df_gender.loc[df_gender['Половая принадлежность'] == 'Мужчина', ['percent', 'total', 'yes']].fillna(0)
    if not male_data.empty:
        male_percent = float(male_data['percent'].iloc[0])
        male_lp = float(male_data['total'].iloc[0])
        male_npl = float(male_data['yes'].iloc[0])

    return female_percent, female_lp, male_percent, male_lp, female_npl, male_npl

def find_corporate(df_corp):
    # Initialize default values
    yuridik_percent = 0.0
    yuridik_lp = 0.0
    jismoniy_percent = 0.0
    jismoniy_lp = 0.0
    jismoniy_npl = 0.0
    yuridik_npl = 0.0

    # Check for Yuridik (corporate) data
    yuridik_data = df_corp.loc[df_corp['is_corporate'] == 'Yuridik', ['percent', 'total', 'yes']].fillna(0)
    if not yuridik_data.empty:
        yuridik_percent = float(yuridik_data['percent'].iloc[0])
        yuridik_lp = float(yuridik_data['total'].iloc[0])
        yuridik_npl = float(yuridik_data['yes'].iloc[0])

    # Check for Jismoniy (individual) data
    jismoniy_data = df_corp.loc[df_corp['is_corporate'] == 'Jismoniy', ['percent', 'total', 'yes']].fillna(0)
    if not jismoniy_data.empty:
        jismoniy_percent = float(jismoniy_data['percent'].iloc[0])
        jismoniy_lp = float(jismoniy_data['total'].iloc[0])
        jismoniy_npl = float(jismoniy_data['yes'].iloc[0])
        

    return yuridik_percent, yuridik_lp, jismoniy_percent, jismoniy_lp, yuridik_npl,jismoniy_npl 



def currency_home(request):
    # Get parameters from query string, default to 'local' and 'all'
    currency_type = request.GET.get('currency', 'local')  # 'local' or 'global'
    dep_name = request.GET.get('dep', 'all')  # 'all', 'rk', 'kk', 'mk'

    # Validate inputs to prevent invalid values
    valid_currencies = ['local', 'global']
    valid_deps = ['all', 'rk', 'kk', 'mk']
    if currency_type not in valid_currencies:
        currency_type = 'local'
    if dep_name not in valid_deps:
        dep_name = 'all'

    # Call the existing currency_together function
    context = currency_together(currency_type, dep_name)

    # Add current filter states to context for template rendering
    context.update({
        'currency_type': currency_type,
        'dep_name': dep_name,
    })

    #return render(request, 'general/currency_home.html', context)
Helpful? Dislike 0 Log in to react