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)
PO
powerty
Author
· Staff
July 27, 2026
July 27, 2026
7
Views
0
Likes
6m
Read