This content originally appeared on DEV Community and was authored by Armaan Khan
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
def pie_chart_single_row(df, columns=None, title="Data Distribution"):
"""
Creates a pie chart from a single row DataFrame with equal slice sizes.
Parameters:
df (pd.DataFrame): DataFrame with typically one row
columns (list, optional): List of column names to include. If None, uses all columns
title (str): Title for the pie chart
Returns:
plotly.graph_objects.Figure: Pie chart figure
"""
# Use first row of data
row_data = df.iloc[0]
# Select columns
if columns is None:
columns = df.columns.tolist()
# Filter data for selected columns
filtered_data = row_data[columns]
# Create labels and values (equal sizes)
labels = [f"{col}: {val}" for col, val in filtered_data.items()]
values = [1] * len(filtered_data) # Equal slice sizes
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=0.3, # Donut style
textinfo='label+percent',
textposition='outside',
marker=dict(
colors=px.colors.qualitative.Set3[:len(labels)],
line=dict(color='#FFFFFF', width=2)
)
)])
fig.update_layout(
title={
'text': title,
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#2E4057'}
},
font=dict(size=12),
showlegend=True,
legend=dict(
orientation="v",
yanchor="middle",
y=0.5,
xanchor="left",
x=1.01
),
margin=dict(t=80, b=20, l=20, r=150),
paper_bgcolor='white',
plot_bgcolor='white'
)
return fig
def pie_chart_column_values(df, column_name, title=None):
"""
Creates a pie chart based on unique values and their counts in a specific column.
Parameters:
df (pd.DataFrame): Input DataFrame
column_name (str): Name of the column to analyze
title (str, optional): Title for the pie chart
Returns:
plotly.graph_objects.Figure: Pie chart figure
"""
if column_name not in df.columns:
raise ValueError(f"Column '{column_name}' not found in DataFrame")
# Get value counts
value_counts = df[column_name].value_counts()
if title is None:
title = f"Distribution of {column_name}"
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=value_counts.index,
values=value_counts.values,
hole=0.4, # Donut style
textinfo='label+percent+value',
textposition='auto',
marker=dict(
colors=px.colors.qualitative.Pastel[:len(value_counts)],
line=dict(color='#FFFFFF', width=2)
),
hovertemplate='<b>%{label}</b><br>Count: %{value}<br>Percentage: %{percent}<extra></extra>'
)])
fig.update_layout(
title={
'text': title,
'x': 0.5,
'xanchor': 'center',
'font': {'size': 22, 'color': '#2E4057', 'family': 'Arial Black'}
},
font=dict(size=14, family='Arial'),
showlegend=True,
legend=dict(
orientation="v",
yanchor="middle",
y=0.5,
xanchor="left",
x=1.02,
font=dict(size=12)
),
margin=dict(t=100, b=50, l=50, r=200),
paper_bgcolor='#F8F9FA',
plot_bgcolor='white',
height=600
)
return fig
def create_kpi_cards(df, columns=None, cards_per_row=4, title="KPI Dashboard"):
"""
Creates beautiful KPI/metric cards from the first row of DataFrame.
Parameters:
df (pd.DataFrame): Input DataFrame
columns (list, optional): List of column names to include. If None, uses all columns
cards_per_row (int): Number of cards per row (default: 4)
title (str): Title for the dashboard
Returns:
plotly.graph_objects.Figure: KPI cards figure
"""
# Use first row of data
row_data = df.iloc[0]
# Select columns
if columns is None:
columns = df.columns.tolist()
# Filter data for selected columns
filtered_data = row_data[columns]
# Calculate subplot dimensions
n_cards = len(filtered_data)
n_rows = (n_cards + cards_per_row - 1) // cards_per_row
# Create color palette
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']
# Create subplots
fig = make_subplots(
rows=n_rows,
cols=cards_per_row,
subplot_titles=[f"<b>{col}</b>" for col in filtered_data.index],
specs=[[{'type': 'indicator'}] * cards_per_row for _ in range(n_rows)],
vertical_spacing=0.15,
horizontal_spacing=0.1
)
# Add KPI cards
for i, (col, val) in enumerate(filtered_data.items()):
row = i // cards_per_row + 1
col_pos = i % cards_per_row + 1
color = colors[i % len(colors)]
# Format value based on type
if isinstance(val, (int, float)):
if val >= 1000000:
display_val = f"{val/1000000:.1f}M"
elif val >= 1000:
display_val = f"{val/1000:.1f}K"
else:
display_val = f"{val:.2f}" if isinstance(val, float) else str(val)
else:
display_val = str(val)
fig.add_trace(
go.Indicator(
mode="number",
value=val if isinstance(val, (int, float)) else 0,
number={
'font': {'size': 40, 'color': color, 'family': 'Arial Black'},
'suffix': '' if isinstance(val, (int, float)) else '',
},
title={
'text': f"<span style='font-size:16px; color:#2E4057; font-weight:bold'>{col}</span>",
'font': {'size': 16}
},
domain={'x': [0, 1], 'y': [0, 1]}
),
row=row, col=col_pos
)
# Add custom text for non-numeric values
if not isinstance(val, (int, float)):
fig.add_annotation(
text=f"<b style='color:{color}; font-size:32px'>{display_val}</b>",
x=0.5, y=0.4,
xref=f"x{i+1}", yref=f"y{i+1}",
showarrow=False,
font=dict(size=32, color=color, family='Arial Black')
)
# Update layout
fig.update_layout(
title={
'text': f"<b>{title}</b>",
'x': 0.5,
'xanchor': 'center',
'font': {'size': 28, 'color': '#2E4057', 'family': 'Arial Black'}
},
paper_bgcolor='#F8F9FA',
plot_bgcolor='white',
height=200 * n_rows + 100,
margin=dict(t=100, b=50, l=50, r=50),
font=dict(family='Arial')
)
return fig
# Example usage functions
def demo_functions():
"""
Demonstrates how to use all three functions with sample data.
"""
# Sample data for testing
sample_data = {
'Sales': [25000],
'Customers': [150],
'Revenue': [75000],
'Products': [45],
'Region': ['North'],
'Status': ['Active']
}
df_sample = pd.DataFrame(sample_data)
# Sample data for column-based pie chart
category_data = {
'Category': ['Electronics', 'Clothing', 'Electronics', 'Books', 'Clothing', 'Electronics', 'Books'],
'Sales': [100, 200, 150, 80, 120, 180, 90]
}
df_categories = pd.DataFrame(category_data)
print("Sample DataFrame for single row pie chart:")
print(df_sample)
print("\nSample DataFrame for column-based pie chart:")
print(df_categories)
# Function 1: Single row pie chart
fig1 = pie_chart_single_row(df_sample, title="Business Metrics Overview")
fig1.show()
# Function 2: Column values pie chart
fig2 = pie_chart_column_values(df_categories, 'Category', title="Sales by Category")
fig2.show()
# Function 3: KPI cards
fig3 = create_kpi_cards(df_sample, cards_per_row=3, title="Business Dashboard")
fig3.show()
# Uncomment the line below to run the demo
# demo_functions()
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
def pie_chart_single_row(df, columns=None, title="Data Distribution"):
"""
Creates a pie chart from a single row DataFrame with equal slice sizes.
Parameters:
df (pd.DataFrame): DataFrame with typically one row
columns (list, optional): List of column names to include. If None, uses all columns
title (str): Title for the pie chart
Returns:
plotly.graph_objects.Figure: Pie chart figure
"""
# Use first row of data
row_data = df.iloc[0]
# Select columns
if columns is None:
columns = df.columns.tolist()
# Filter data for selected columns
filtered_data = row_data[columns]
# Create labels and values (equal sizes)
labels = [f"{col}: {val}" for col, val in filtered_data.items()]
values = [1] * len(filtered_data) # Equal slice sizes
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=0.3, # Donut style
textinfo='label+percent',
textposition='outside',
marker=dict(
colors=px.colors.qualitative.Set3[:len(labels)],
line=dict(color='#FFFFFF', width=2)
)
)])
fig.update_layout(
title={
'text': title,
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#2E4057'}
},
font=dict(size=12),
showlegend=True,
legend=dict(
orientation="v",
yanchor="middle",
y=0.5,
xanchor="left",
x=1.01
),
margin=dict(t=80, b=20, l=20, r=150),
paper_bgcolor='white',
plot_bgcolor='white'
)
return fig
def pie_chart_column_values(df, column_name, title=None):
"""
Creates a pie chart based on unique values and their counts in a specific column.
Parameters:
df (pd.DataFrame): Input DataFrame
column_name (str): Name of the column to analyze
title (str, optional): Title for the pie chart
Returns:
plotly.graph_objects.Figure: Pie chart figure
"""
if column_name not in df.columns:
raise ValueError(f"Column '{column_name}' not found in DataFrame")
# Get value counts
value_counts = df[column_name].value_counts()
if title is None:
title = f"Distribution of {column_name}"
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=value_counts.index,
values=value_counts.values,
hole=0.4, # Donut style
textinfo='label+percent+value',
textposition='auto',
marker=dict(
colors=px.colors.qualitative.Pastel[:len(value_counts)],
line=dict(color='#FFFFFF', width=2)
),
hovertemplate='<b>%{label}</b><br>Count: %{value}<br>Percentage: %{percent}<extra></extra>'
)])
fig.update_layout(
title={
'text': title,
'x': 0.5,
'xanchor': 'center',
'font': {'size': 22, 'color': '#2E4057', 'family': 'Arial Black'}
},
font=dict(size=14, family='Arial'),
showlegend=True,
legend=dict(
orientation="v",
yanchor="middle",
y=0.5,
xanchor="left",
x=1.02,
font=dict(size=12)
),
margin=dict(t=100, b=50, l=50, r=200),
paper_bgcolor='#F8F9FA',
plot_bgcolor='white',
height=600
)
return fig
def create_kpi_cards(df, columns=None, cards_per_row=4, title="KPI Dashboard"):
"""
Creates beautiful KPI/metric cards from the first row of DataFrame.
Parameters:
df (pd.DataFrame): Input DataFrame
columns (list, optional): List of column names to include. If None, uses all columns
cards_per_row (int): Number of cards per row (default: 4)
title (str): Title for the dashboard
Returns:
plotly.graph_objects.Figure: KPI cards figure
"""
# Use first row of data
row_data = df.iloc[0]
# Select columns
if columns is None:
columns = df.columns.tolist()
# Filter data for selected columns
filtered_data = row_data[columns]
# Calculate subplot dimensions
n_cards = len(filtered_data)
n_rows = (n_cards + cards_per_row - 1) // cards_per_row
# Create color palette
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']
# Create subplots
fig = make_subplots(
rows=n_rows,
cols=cards_per_row,
subplot_titles=[f"<b>{col}</b>" for col in filtered_data.index],
specs=[[{'type': 'indicator'}] * cards_per_row for _ in range(n_rows)],
vertical_spacing=0.15,
horizontal_spacing=0.1
)
# Add KPI cards
for i, (col, val) in enumerate(filtered_data.items()):
row = i // cards_per_row + 1
col_pos = i % cards_per_row + 1
color = colors[i % len(colors)]
# Format value based on type
if isinstance(val, (int, float)):
if val >= 1000000:
display_val = f"{val/1000000:.1f}M"
elif val >= 1000:
display_val = f"{val/1000:.1f}K"
else:
display_val = f"{val:.2f}" if isinstance(val, float) else str(val)
else:
display_val = str(val)
fig.add_trace(
go.Indicator(
mode="number",
value=val if isinstance(val, (int, float)) else 0,
number={
'font': {'size': 40, 'color': color, 'family': 'Arial Black'},
'suffix': '' if isinstance(val, (int, float)) else '',
},
title={
'text': f"<span style='font-size:16px; color:#2E4057; font-weight:bold'>{col}</span>",
'font': {'size': 16}
},
domain={'x': [0, 1], 'y': [0, 1]}
),
row=row, col=col_pos
)
# Add custom text for non-numeric values
if not isinstance(val, (int, float)):
fig.add_annotation(
text=f"<b style='color:{color}; font-size:32px'>{display_val}</b>",
x=0.5, y=0.4,
xref=f"x{i+1}", yref=f"y{i+1}",
showarrow=False,
font=dict(size=32, color=color, family='Arial Black')
)
# Update layout
fig.update_layout(
title={
'text': f"<b>{title}</b>",
'x': 0.5,
'xanchor': 'center',
'font': {'size': 28, 'color': '#2E4057', 'family': 'Arial Black'}
},
paper_bgcolor='#F8F9FA',
plot_bgcolor='white',
height=200 * n_rows + 100,
margin=dict(t=100, b=50, l=50, r=50),
font=dict(family='Arial')
)
return fig
# Example usage functions
def demo_functions():
"""
Demonstrates how to use all three functions with sample data.
"""
# Sample data for testing
sample_data = {
'Sales': [25000],
'Customers': [150],
'Revenue': [75000],
'Products': [45],
'Region': ['North'],
'Status': ['Active']
}
df_sample = pd.DataFrame(sample_data)
# Sample data for column-based pie chart
category_data = {
'Category': ['Electronics', 'Clothing', 'Electronics', 'Books', 'Clothing', 'Electronics', 'Books'],
'Sales': [100, 200, 150, 80, 120, 180, 90]
}
df_categories = pd.DataFrame(category_data)
print("Sample DataFrame for single row pie chart:")
print(df_sample)
print("\nSample DataFrame for column-based pie chart:")
print(df_categories)
# Function 1: Single row pie chart
fig1 = pie_chart_single_row(df_sample, title="Business Metrics Overview")
fig1.show()
# Function 2: Column values pie chart
fig2 = pie_chart_column_values(df_categories, 'Category', title="Sales by Category")
fig2.show()
# Function 3: KPI cards
fig3 = create_kpi_cards(df_sample, cards_per_row=3, title="Business Dashboard")
fig3.show()
def status_classification_chart(df, x_col, status_col, title=None, chart_type='bar'):
"""
Creates a beautiful classification chart showing status distribution across categories.
Parameters:
df (pd.DataFrame): Input DataFrame
x_col (str): Column name for X-axis (categories)
status_col (str): Column name for status/classification
title (str, optional): Title for the chart
chart_type (str): Type of chart - 'bar', 'stacked_bar', or 'grouped_bar'
Returns:
plotly.graph_objects.Figure: Classification chart figure
"""
if x_col not in df.columns or status_col not in df.columns:
raise ValueError(f"Columns '{x_col}' or '{status_col}' not found in DataFrame")
if title is None:
title = f"{status_col} Distribution by {x_col}"
# Get unique statuses and assign colors
unique_statuses = df[status_col].unique()
colors = px.colors.qualitative.Set2[:len(unique_statuses)]
color_map = dict(zip(unique_statuses, colors))
# Create cross-tabulation
crosstab = pd.crosstab(df[x_col], df[status_col])
fig = go.Figure()
if chart_type == 'stacked_bar':
# Stacked bar chart
for status in unique_statuses:
fig.add_trace(go.Bar(
name=status,
x=crosstab.index,
y=crosstab[status] if status in crosstab.columns else [0] * len(crosstab.index),
marker_color=color_map[status],
text=crosstab[status] if status in crosstab.columns else [0] * len(crosstab.index),
textposition='inside',
textfont=dict(color='white', size=12, family='Arial Bold'),
hovertemplate=f'<b>{x_col}</b>: %{{x}}<br><b>{status}</b>: %{{y}}<br><extra></extra>'
))
fig.update_layout(barmode='stack')
elif chart_type == 'grouped_bar':
# Grouped bar chart
for status in unique_statuses:
fig.add_trace(go.Bar(
name=status,
x=crosstab.index,
y=crosstab[status] if status in crosstab.columns else [0] * len(crosstab.index),
marker_color=color_map[status],
text=crosstab[status] if status in crosstab.columns else [0] * len(crosstab.index),
textposition='outside',
textfont=dict(color=color_map[status], size=11, family='Arial Bold'),
hovertemplate=f'<b>{x_col}</b>: %{{x}}<br><b>{status}</b>: %{{y}}<br><extra></extra>'
))
fig.update_layout(barmode='group')
else: # Default bar chart with color coding
# Create a single bar chart with colors based on dominant status
category_counts = df.groupby([x_col, status_col]).size().unstack(fill_value=0)
dominant_status = category_counts.idxmax(axis=1)
total_counts = category_counts.sum(axis=1)
bar_colors = [color_map[status] for status in dominant_status]
fig.add_trace(go.Bar(
x=total_counts.index,
y=total_counts.values,
marker_color=bar_colors,
text=total_counts.values,
textposition='outside',
textfont=dict(color='#2E4057', size=12, family='Arial Bold'),
hovertemplate='<b>%{x}</b><br>Total Count: %{y}<br><extra></extra>',
showlegend=False
))
# Add custom legend
for status, color in color_map.items():
fig.add_trace(go.Scatter(
x=[None], y=[None],
mode='markers',
marker=dict(size=15, color=color),
name=f'{status}',
showlegend=True
))
# Update layout
fig.update_layout(
title={
'text': f"<b>{title}</b>",
'x': 0.5,
'xanchor': 'center',
'font': {'size': 24, 'color': '#2E4057', 'family': 'Arial Black'}
},
xaxis_title=f"<b>{x_col}</b>",
yaxis_title="<b>Count</b>",
xaxis=dict(
titlefont=dict(size=16, color='#2E4057', family='Arial Bold'),
tickfont=dict(size=12, color='#2E4057'),
gridcolor='#E5E5E5'
),
yaxis=dict(
titlefont=dict(size=16, color='#2E4057', family='Arial Bold'),
tickfont=dict(size=12, color='#2E4057'),
gridcolor='#E5E5E5'
),
plot_bgcolor='white',
paper_bgcolor='#F8F9FA',
font=dict(family='Arial'),
legend=dict(
orientation="v",
yanchor="top",
y=1,
xanchor="left",
x=1.02,
bgcolor="rgba(255,255,255,0.8)",
bordercolor="#CCCCCC",
borderwidth=1,
font=dict(size=12)
),
margin=dict(t=80, b=60, l=60, r=150),
height=500
)
return fig
def cause_solution_analysis(df, cause_col, solution_col, title=None):
"""
Creates a beautiful cause-solution analysis visualization using Sankey diagram.
Parameters:
df (pd.DataFrame): Input DataFrame
cause_col (str): Column name for causes
solution_col (str): Column name for solutions
title (str, optional): Title for the chart
Returns:
plotly.graph_objects.Figure: Sankey diagram figure
"""
if cause_col not in df.columns or solution_col not in df.columns:
raise ValueError(f"Columns '{cause_col}' or '{solution_col}' not found in DataFrame")
if title is None:
title = f"Cause-Solution Analysis: {cause_col} → {solution_col}"
# Create cause-solution pairs and count frequencies
cause_solution_counts = df.groupby([cause_col, solution_col]).size().reset_index(name='count')
# Get unique causes and solutions
unique_causes = df[cause_col].unique()
unique_solutions = df[solution_col].unique()
# Create node labels and colors
all_nodes = list(unique_causes) + list(unique_solutions)
node_colors = (px.colors.qualitative.Set3[:len(unique_causes)] +
px.colors.qualitative.Pastel[:len(unique_solutions)])
# Create source, target, and value lists for Sankey
source = []
target = []
value = []
for _, row in cause_solution_counts.iterrows():
cause_idx = list(unique_causes).index(row[cause_col])
solution_idx = len(unique_causes) + list(unique_solutions).index(row[solution_col])
source.append(cause_idx)
target.append(solution_idx)
value.append(row['count'])
# Create Sankey diagram
fig = go.Figure(data=[go.Sankey(
node=dict(
pad=20,
thickness=25,
line=dict(color="black", width=1),
label=all_nodes,
color=node_colors,
hovertemplate='<b>%{label}</b><br>Total Flow: %{value}<extra></extra>'
),
link=dict(
source=source,
target=target,
value=value,
color=[node_colors[i] + '40' for i in source], # Semi-transparent links
hovertemplate='<b>%{source.label}</b> → <b>%{target.label}</b><br>Count: %{value}<extra></extra>'
)
)])
fig.update_layout(
title={
'text': f"<b>{title}</b>",
'x': 0.5,
'xanchor': 'center',
'font': {'size': 24, 'color': '#2E4057', 'family': 'Arial Black'}
},
font=dict(size=14, family='Arial'),
paper_bgcolor='#F8F9FA',
plot_bgcolor='white',
margin=dict(t=80, b=50, l=50, r=50),
height=600,
annotations=[
dict(
text="<b>Causes</b>",
x=0.15, y=1.05,
xref="paper", yref="paper",
showarrow=False,
font=dict(size=18, color='#2E4057', family='Arial Bold')
),
dict(
text="<b>Solutions</b>",
x=0.85, y=1.05,
xref="paper", yref="paper",
showarrow=False,
font=dict(size=18, color='#2E4057', family='Arial Bold')
)
]
)
return fig
# Enhanced demo function with new visualizations
def demo_all_functions():
"""
Demonstrates all five functions with comprehensive sample data.
"""
# Sample data for status classification
status_data = {
'Department': ['Sales', 'Marketing', 'IT', 'Sales', 'HR', 'IT', 'Marketing', 'Sales', 'HR', 'IT'],
'Status': ['Active', 'Pending', 'Active', 'Completed', 'Active', 'Pending', 'Completed', 'Active', 'Pending', 'Active'],
'Priority': ['High', 'Medium', 'Low', 'High', 'Medium', 'High', 'Low', 'Medium', 'High', 'Low']
}
df_status = pd.DataFrame(status_data)
# Sample data for cause-solution analysis
cause_solution_data = {
'Problem_Cause': ['Network Issue', 'User Error', 'Hardware Failure', 'Network Issue', 'Software Bug',
'User Error', 'Hardware Failure', 'Software Bug', 'Network Issue', 'User Error'],
'Solution_Applied': ['Restart Router', 'User Training', 'Replace Hardware', 'Update Firmware', 'Patch Software',
'Create Manual', 'Replace Hardware', 'Bug Fix', 'Restart Router', 'User Training']
}
df_cause_solution = pd.DataFrame(cause_solution_data)
print("Sample DataFrame for status classification:")
print(df_status)
print("\nSample DataFrame for cause-solution analysis:")
print(df_cause_solution)
# Function 4: Status classification chart
fig4 = status_classification_chart(df_status, 'Department', 'Status',
title="Department Status Distribution",
chart_type='grouped_bar')
fig4.show()
# Function 5: Cause-solution analysis
fig5 = cause_solution_analysis(df_cause_solution, 'Problem_Cause', 'Solution_Applied')
fig5.show()
# Uncomment the line below to run the comprehensive demo
# demo_all_functions()
This content originally appeared on DEV Community and was authored by Armaan Khan
Print
Share
Comment
Cite
Upload
Translate
Updates
There are no updates yet.
Click the Upload button above to add an update.

APA
MLA
Armaan Khan | Sciencx (2025-07-23T15:26:47+00:00) new fuct. Retrieved from https://www.scien.cx/2025/07/23/new-fuct/
" » new fuct." Armaan Khan | Sciencx - Wednesday July 23, 2025, https://www.scien.cx/2025/07/23/new-fuct/
HARVARDArmaan Khan | Sciencx Wednesday July 23, 2025 » new fuct., viewed ,<https://www.scien.cx/2025/07/23/new-fuct/>
VANCOUVERArmaan Khan | Sciencx - » new fuct. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/07/23/new-fuct/
CHICAGO" » new fuct." Armaan Khan | Sciencx - Accessed . https://www.scien.cx/2025/07/23/new-fuct/
IEEE" » new fuct." Armaan Khan | Sciencx [Online]. Available: https://www.scien.cx/2025/07/23/new-fuct/. [Accessed: ]
rf:citation » new fuct | Armaan Khan | Sciencx | https://www.scien.cx/2025/07/23/new-fuct/ |
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.