Spaces:
Runtime error
Runtime error
import streamlit as st | |
import requests | |
import plotly.express as px | |
import pandas as pd | |
from datetime import datetime, timedelta | |
from pathlib import Path | |
import pickle | |
import streamlit_authenticator as stauth | |
import numpy as np | |
import calendar | |
# names = ["Phạm Tấn Thành", "Phạm Minh Tâm", "Vận hành"] | |
# usernames = ["thanhpham", "tampham", "vietopvanhanh"] | |
# passwords = ['thanhpham0704', 'tampham1234', 'vanhanh2023'] | |
# hashed_passwords = stauth.Hasher(passwords).generate() | |
# file_path = Path(__file__).parent / "hashed_pw.pkl" | |
# with file_path.open("wb") as file: | |
# pickle.dump(hashed_passwords, file) | |
page_title = "Lương và thực thu" | |
page_icon = ":chart_with_upwards_trend:" | |
layout = "wide" | |
st.set_page_config(page_title=page_title, page_icon=page_icon, layout=layout) | |
# ---------------------------------------- | |
names = ["Phạm Tấn Thành", "Phạm Minh Tâm", "Vận hành"] | |
usernames = ["thanhpham", "tampham", "vietopvanhanh"] | |
# Load hashed password | |
file_path = Path(__file__).parent / 'hashed_pw.pkl' | |
with file_path.open("rb") as file: | |
hashed_passwords = pickle.load(file) | |
authenticator = stauth.Authenticate(names, usernames, hashed_passwords, | |
"sales_dashboard", "abcdef", cookie_expiry_days=1) | |
name, authentication_status, username = authenticator.login("Login", "main") | |
if authentication_status == False: | |
st.error("Username/password is incorrect") | |
if authentication_status == None: | |
st.warning("Please enter your username and password") | |
if authentication_status: | |
authenticator.logout("logout", "main") | |
# Add CSS styling to position the button on the top right corner of the page | |
st.markdown( | |
""" | |
<style> | |
.stButton button { | |
position: absolute; | |
top: 0px; | |
right: 0px; | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
st.title(page_title + " " + page_icon) | |
#----------------------# | |
# Filter | |
now = datetime.now() | |
DEFAULT_START_DATE = datetime(now.year, now.month, 1) | |
DEFAULT_END_DATE = datetime(now.year, now.month, 1) + timedelta(days=32) | |
DEFAULT_END_DATE = DEFAULT_END_DATE.replace(day=1) - timedelta(days=1) | |
# Create a form to get the date range filters | |
with st.form(key='date_filter_form'): | |
col1, col2 = st.columns(2) | |
ketoan_start_time = col1.date_input( | |
"Select start date", value=DEFAULT_START_DATE) | |
ketoan_end_time = col2.date_input( | |
"Select end date", value=DEFAULT_END_DATE) | |
submit_button = st.form_submit_button( | |
label='Filter') | |
# the duration between 2 dates exclude Sunday | |
duration = sum(1 for i in range((ketoan_end_time - ketoan_start_time).days) | |
if (ketoan_start_time + timedelta(i)).weekday() != 6) | |
# the number of days in a month exclude Sunday | |
days_in_month = calendar.monthrange( | |
ketoan_start_time.year, ketoan_start_time.month)[1] | |
sundays_in_month = sum(1 for day in range(1, days_in_month + 1) if datetime( | |
ketoan_start_time.year, ketoan_start_time.month, day).weekday() == 6) | |
days_excluding_sundays = days_in_month - sundays_in_month | |
#----------------------# | |
def collect_data(link): | |
return(pd.DataFrame((requests.get(link).json()))) | |
def rename_lop(dataframe, column_name): | |
dataframe[column_name] = dataframe[column_name].replace( | |
{1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"}) | |
return dataframe | |
def grand_total(dataframe, column): | |
# create a new row with the sum of each numerical column | |
totals = dataframe.select_dtypes(include=[float, int]).sum() | |
totals[column] = "Grand total" | |
# append the new row to the dataframe | |
dataframe = dataframe.append(totals, ignore_index=True) | |
return dataframe | |
# "---------------" Thông tin lương giáo viên | |
users = collect_data('https://vietop.tech/api/get_data/users') | |
# Thong tin luong | |
import gspread | |
sa = gspread.service_account( | |
filename='taichinh-380507-b8f84e9ee681.json') | |
sh = sa.open("Nhân sự") | |
worksheet = sh.worksheet("Giáo viên") | |
salary = pd.DataFrame(worksheet.get_all_records()) | |
salary = salary.replace("", np.nan).dropna(subset='Mã giáo viên') | |
salary = salary.replace('\.', '', regex=True) | |
salary = salary.astype({'Lương theo hợp đông': 'float', 'Thâm niên': 'float', | |
'Chức danh': "float", 'Tổng lương': 'float', 'date_affected': 'datetime64[ns]'}) | |
# salary.info() | |
# salary = salary.sort_values("date_affected", ascending=False)\ | |
# .drop_duplicates(subset='id_gg') | |
salary = salary.sort_values("date_affected", ascending=False)\ | |
.query("date_affected <= @ketoan_end_time")\ | |
.drop_duplicates("id_gg") | |
salary.fillna(0, inplace=True) | |
# Thong tin luong | |
salary['salary_ngay_cong'] = round( | |
salary['Tổng lương'] * duration/days_excluding_sundays, 0) | |
salary.fillna(0, inplace=True) | |
salary.drop(['STT', 'Mã giáo viên', 'Tổng lương'], axis=1, inplace=True) | |
salary.rename(columns={"Lương theo hợp đông": "hopdong", "Thâm niên": "thamnien", | |
'Chức danh': 'chucdanh', 'Ngày': 'ngay', 'Tối': 'toi', 'Cuối tuần': 'cuoituan', | |
'Trợ giảng': 'trogiang', 'BHXH': 'bhxh', 'Chế độ': 'working_status', 'Bậc giáo viên': 'level', 'Tổng ngày nghỉ phép': 'ngaynghi_total', | |
'Tổng ngày công thực tế': 'ngaycong_real_total'}, inplace=True) | |
# "------------------" | |
gv_diemdanh = collect_data('https://vietop.tech/api/get_data/diemdanh') | |
gv_diemdanh['date_created'] = pd.to_datetime(gv_diemdanh['date_created']) | |
gv_diemdanh = gv_diemdanh.query( | |
"date_created >= @ketoan_start_time and date_created <= @ketoan_end_time") | |
# Ca hoc table | |
cahoc = {'cahoc': ['ca1', 'ca2', 'ca3', 'ca4', 'ca5', 'ca6'], | |
'start_time': ['08:30:00', '10:30:00', '13:30:00', '15:30:00', '18:00:00', '19:45:00'], | |
'end_time': ['10:30:00', '12:30:00', '15:30:00', '17:30:00', '19:45:00', '21:30:00']} | |
cahoc = pd.DataFrame(cahoc) | |
# --------------- GET DATA FROM API | |
sh = sa.open("Nhân sự") | |
worksheet = sh.worksheet("Overtime") | |
offline_overtime = pd.DataFrame(worksheet.get_all_records()) | |
offline_overtime['date_affected'] = pd.to_datetime( | |
offline_overtime['date_affected']) | |
offline_overtime = offline_overtime.sort_values("date_affected", ascending=False)\ | |
.drop_duplicates(subset='Họ và tên') | |
offline_overtime.drop("date_affected", axis=1, inplace=True) | |
# Define the start and end times of the day | |
start_time = pd.to_datetime('2000-01-01 06:00:00').time() | |
end_time = pd.to_datetime('2000-01-01 18:00:00').time() | |
ca1 = pd.to_datetime('2000-01-01 06:30:00').time() | |
ca2 = pd.to_datetime('2000-01-01 10:30:00').time() | |
ca3 = pd.to_datetime('2000-01-01 13:30:00').time() | |
ca4 = pd.to_datetime('2000-01-01 15:30:00').time() | |
ca5 = pd.to_datetime('2000-01-01 18:00:00').time() | |
# tren office khong cos 19:45 | |
ca6 = pd.to_datetime('2000-01-01 19:30:00').time() | |
# Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range | |
def time_of_day(time): | |
if (time >= start_time) & (time < end_time): | |
return "Sáng" | |
else: | |
return "Tối" | |
# Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range | |
def day_of_week(day): | |
if (day == 5) | (day == 6): | |
return "weekend" | |
else: | |
return "weekdays" | |
# Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range | |
def cahoc_converter(ca): | |
if (ca >= ca1) & (ca < ca2): | |
return 1 | |
elif (ca >= ca2) & (ca < ca3): | |
return 2 | |
elif (ca >= ca3) & (ca < ca4): | |
return 3 | |
elif (ca >= ca4) & (ca < ca5): | |
return 4 | |
elif (ca >= ca5) & (ca < ca6): | |
return 5 | |
elif (ca >= ca6): | |
return 6 | |
# "---------------" Bảng lương giáo viên | |
overtime_melt = offline_overtime.melt( | |
id_vars=['id_gg', 'Họ và tên', 'WORKING_STATUS'], var_name='Column Name', value_name='overtime_status') | |
overtime_melt['day_of_week'], overtime_melt['cahoc'] = overtime_melt['Column Name'].str.split( | |
'Ca ', 1).str | |
overtime_melt['day_of_week'].replace( | |
{'T2': 0, 'T3': 1, 'T4': 2, 'T5': 3, 'T6': 4, 'T7': 5, 'T8': 6}, inplace=True) | |
overtime_melt['overtime_status'].replace( | |
{0: 'in', 'a': 'out'}, inplace=True) | |
# Convert 5 and 6 into "Cuối tuần" | |
overtime_melt['weekend_or_not'] = overtime_melt['day_of_week'].apply( | |
day_of_week) | |
# Convert ca into Sáng or Tối | |
overtime_melt['time_of_day'] = ['Sáng' if i in [ | |
'1', '2', '3', '4'] else 'Tối' for i in overtime_melt['cahoc']] | |
# Drop unnecessary columns | |
overtime_melt.drop( | |
['Column Name'], axis=1, inplace=True) | |
overtime_melt['cahoc'] = overtime_melt['cahoc'].astype(int) | |
overtime_melt.rename( | |
columns={'WORKING_STATUS': 'working_status'}, inplace=True) | |
# "---------------" Lương overtime của giáo viên | |
lophoc = collect_data('https://vietop.tech/api/get_data/lophoc') | |
diemdanh = gv_diemdanh.merge(users[['fullname', 'id']], left_on='giaovien', right_on='id', how='inner')\ | |
.sort_values("created_at", ascending=False) | |
# diemdanh['cahoc'].replace({0: 'không học', 1: 'ca1', 2: 'ca2', 3: 'ca3', 4:'ca4', 5:'ca5', 6:'ca6', 7:'ca 1.5 giờ', 8: 'ca 2.5 giờ', 9: 'ca 3.0 giờ', 10: 'ca 1 giờ', 11: 'ca 1.75 giờ', 12: 'ca 2 giờ'}, inplace = True) | |
diemdanh = diemdanh[['lop_id', 'sogio', 'cahoc', 'phanloai', | |
'date_created', 'created_by', 'id', | |
'updated_by', 'created_at', 'fullname']] | |
# Convert the date column to datetime | |
diemdanh['created_at'] = pd.to_datetime(diemdanh['created_at']) | |
# Extract the time component of each datetime value | |
diemdanh['created_at_time'] = diemdanh['created_at'].dt.time | |
# Create a new column that indicates the day of the week | |
diemdanh['day_of_week'] = diemdanh['created_at'].dt.dayofweek | |
# Create a boolean mask that indicates whether each time is within the specified time frame | |
diemdanh["time_of_day"] = diemdanh['created_at_time'].apply(time_of_day) | |
# Convert 5 and 6 into "Cuối tuần" | |
diemdanh['weekend_or_not'] = diemdanh["day_of_week"].apply(day_of_week) | |
diemdanh = diemdanh[['id', 'fullname', 'sogio', 'cahoc', 'phanloai', | |
'created_at', 'day_of_week', 'created_at_time', 'time_of_day', 'weekend_or_not', 'date_created', | |
'lop_id']] | |
diemdanh['cahoc'] = diemdanh['created_at_time'].apply(cahoc_converter) | |
# Sum giohoc according to fullname, time of day and weekend or not | |
diemdanh_sum_giohoc = diemdanh.groupby( | |
["id", "fullname", "cahoc", 'phanloai', 'day_of_week', "time_of_day", "weekend_or_not", 'lop_id'], as_index=False)['sogio'].sum() | |
# Get lopcn from lophoc | |
diemdanh_lop_cn = diemdanh_sum_giohoc.merge( | |
lophoc[['lop_id', 'lop_cn']], on='lop_id') | |
# "---------------" | |
sal_diem = diemdanh_lop_cn.merge( | |
salary, left_on='id', right_on='id_gg') | |
# Merge diemdanh and overtime | |
sal_diem_over = sal_diem\ | |
.merge(overtime_melt, on=['id_gg', 'Họ và tên', 'cahoc', 'day_of_week', 'time_of_day', 'weekend_or_not'], how='inner', validate='many_to_many') | |
# Drop duplicates | |
sal_diem_over.drop_duplicates(inplace=True) | |
# Fill na | |
sal_diem_over.fillna(0, inplace=True) | |
# Calculate luong ngay cong | |
empty = [] | |
for index, value in sal_diem_over.iterrows(): | |
if value['weekend_or_not'] == 'weekend' and value['overtime_status'] == 'out': | |
empty.append(value['cuoituan'] * value['sogio']) | |
elif value['time_of_day'] == 'Tối' and value['overtime_status'] == 'out': | |
empty.append(value['toi'] * value['sogio']) | |
elif value['time_of_day'] == 'Sáng' and value['overtime_status'] == 'out': | |
empty.append(value['ngay'] * value['sogio']) | |
elif value['phanloai'] == 0: | |
empty.append(value['trogiang'] * value['sogio']) | |
else: | |
empty.append(0) | |
sal_diem_over['salary_gio_cong'] = empty | |
# Slicing columns | |
sal_diem_over = sal_diem_over.loc[:, ['lop_cn', 'id_gg', 'Họ và tên', 'working_status_x', 'cahoc', 'phanloai', 'overtime_status', | |
'time_of_day', 'day_of_week', 'weekend_or_not', 'sogio', | |
'ngay', 'toi', 'cuoituan', 'trogiang', 'salary_gio_cong', 'salary_ngay_cong']] | |
sal_diem_over_group_lop = sal_diem_over.groupby(['id_gg', 'Họ và tên', 'lop_cn', 'working_status_x', 'overtime_status', 'salary_ngay_cong'], as_index=False)['sogio', 'salary_gio_cong'].sum()\ | |
.query("overtime_status == 'out'") | |
# Sum luong_gio_cong according to fullname | |
sal_diem_over_group = sal_diem_over.groupby(['id_gg', 'Họ và tên', 'working_status_x', 'overtime_status', 'salary_ngay_cong'], as_index=False)['sogio', 'salary_gio_cong'].sum()\ | |
.query("overtime_status == 'out'").sort_values("salary_gio_cong", ascending=False) | |
sal_diem_over_details = sal_diem_over.drop('salary_ngay_cong', axis=1)\ | |
.query("overtime_status == 'out'")\ | |
# ----------------------# Phân phối lương cứng theo chi nhánh | |
df = sal_diem_over.groupby(['lop_cn', 'id_gg', 'Họ và tên', 'working_status_x', 'overtime_status', | |
'salary_ngay_cong'], as_index=False)['sogio', 'salary_gio_cong'].sum() | |
df = df.query('overtime_status == "in"') | |
df_sum = df.groupby('Họ và tên', as_index=False)['sogio'].sum() | |
df_proportion = df_sum.merge(df, on='Họ và tên') | |
df_proportion['proportion_sogio'] = df_proportion['sogio_y'] / \ | |
df_proportion['sogio_x'] | |
df_proportion['salary_ngay_cong_divided'] = df_proportion['proportion_sogio'] * \ | |
df_proportion['salary_ngay_cong'] | |
# Giáo viên ngoại trừ phòng đào tạo | |
df_proportion_nodaotao = df_proportion[~df_proportion['Họ và tên'].isin( | |
['Mai Minh Trung', 'Trần Thị Thanh Nga', 'Nguyễn Thị Thu Hà', 'Huỳnh Trương Hồng Châu Long', 'Nguyễn Huy Hoàng', 'Đỗ Nguyễn Đăng Khoa'])] | |
# Subset | |
df_proportion_nodaotao = df_proportion_nodaotao[[ | |
'id_gg', 'Họ và tên', 'lop_cn', 'salary_ngay_cong_divided']] | |
# Riêng phòng đào tạo | |
df_proportion_daotao = salary[salary['Họ và tên'].isin( | |
['Phạm Tấn Thành', 'Mai Minh Trung', 'Trần Thị Thanh Nga', 'Nguyễn Thị Thu Hà', 'Huỳnh Trương Hồng Châu Long', 'Nguyễn Huy Hoàng', 'Đỗ Nguyễn Đăng Khoa'])] | |
df_proportion_daotao['1'] = 0.26 * df_proportion_daotao['salary_ngay_cong'] | |
df_proportion_daotao['2'] = 0.26 * df_proportion_daotao['salary_ngay_cong'] | |
df_proportion_daotao['3'] = 0.18 * df_proportion_daotao['salary_ngay_cong'] | |
df_proportion_daotao['5'] = 0.30 * df_proportion_daotao['salary_ngay_cong'] | |
# Subset | |
df_proportion_daotao = df_proportion_daotao.loc[:, [ | |
'id_gg', 'Họ và tên', '1', '2', '3', '5']] | |
# Lương đào tạo sau khi phân phối | |
df_proportion_daotao = pd.melt(df_proportion_daotao, id_vars=[ | |
'id_gg', 'Họ và tên'], var_name='lop_cn', value_name='salary_ngay_cong_divided') | |
df_proportion_daotao['lop_cn'] = df_proportion_daotao['lop_cn'].astype( | |
'int64') | |
# Concat luong đào tạo and lương giáo viên | |
salary_gv_dt = pd.concat([df_proportion_daotao, df_proportion_nodaotao]) | |
salary_gv_dt['salary_ngay_cong_divided'] = round( | |
salary_gv_dt['salary_ngay_cong_divided'], 2) | |
salary_gv_dt = salary_gv_dt.sort_values( | |
"salary_ngay_cong_divided", ascending=False) | |
# ----------------------# Thực thu | |
orders = collect_data( | |
'https://vietop.tech/api/get_data/orders').query("deleted_at.isnull()") | |
lophoc = collect_data('https://vietop.tech/api/get_data/lophoc') | |
hocvien = collect_data( | |
'https://vietop.tech/api/get_data/hocvien').query("hv_id != 737 and deleted_at.isnull()") | |
# hv đang họccd Au | |
hocvien_danghoc = hocvien.merge(orders, on='hv_id')\ | |
.query("ketoan_active == 1")\ | |
.groupby('ketoan_coso', as_index=False).size().rename(columns={"size": "total_students"}) | |
hocvien_danghoc = rename_lop(hocvien_danghoc, 'ketoan_coso') | |
def plotly_chart(df, yvalue, xvalue, text, title, y_title, x_title, color=None, discrete_sequence=None, map=None): | |
fig = px.bar(df, y=yvalue, | |
x=xvalue, text=text, color=color, color_discrete_sequence=discrete_sequence, color_discrete_map=map) | |
fig.update_layout( | |
title=title, | |
yaxis_title=y_title, | |
xaxis_title=x_title, | |
) | |
fig.update_traces(textposition='auto') | |
return fig | |
fig5 = plotly_chart(hocvien_danghoc, 'ketoan_coso', 'total_students', 'total_students', | |
'Tổng học viên đang học theo chi nhánh', 'Chi nhánh', 'Học viên') | |
# Lop dang hoc | |
lop_danghoc = lophoc.query( | |
"(lop_status == 2 or lop_status == 4) and deleted_at.isnull()")\ | |
.groupby('lop_cn', as_index=False).size().rename(columns={"size": "total_classes"}) | |
lop_danghoc.lop_cn = lop_danghoc.lop_cn.replace( | |
{1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"}) | |
fig6 = plotly_chart(lop_danghoc, 'lop_cn', 'total_classes', 'total_classes', | |
"Tổng lớp đang học theo chi nhánh", 'Chi nhánh', 'Lớp học') | |
"" | |
# "------------------" | |
# Define a function | |
def csv_reader(file): | |
df = pd.read_csv(file) | |
df = df.query("phanloai == 1") # Filter lop chính | |
df['date_created'] = pd.to_datetime(df['date_created']) | |
return df | |
def collect_filtered_data(table, date_column='', start_time='', end_time=''): | |
link = f"https://vietop.tech/api/get_data/{table}?column={date_column}&date_start={start_time}&date_end={end_time}" | |
df = pd.DataFrame((requests.get(link).json())) | |
df[date_column] = pd.to_datetime(df[date_column]) | |
return df | |
df = csv_reader("diemdanh_details.csv") | |
df1 = collect_filtered_data(table='diemdanh_details', date_column='date_created', | |
start_time='2023-01-01', end_time='2025-01-01') | |
diemdanh_details = pd.concat([df, df1]) | |
thucthu = diemdanh_details.query( | |
'date_created >= @ketoan_start_time and date_created <= @ketoan_end_time')\ | |
.groupby(['ketoan_id', 'lop_id', 'gv_id', 'date_created'], as_index=False)['giohoc'].sum()\ | |
.merge(orders, on='ketoan_id')\ | |
.merge(lophoc, on='lop_id')\ | |
.merge(users[['fullname', 'id']], left_on='gv_id', right_on='id') | |
thucthu_all = diemdanh_details.query("date_created > '2023-01-01'")\ | |
.groupby(['ketoan_id', 'lop_id', 'gv_id', 'date_created'], as_index=False)['giohoc'].sum()\ | |
.merge(orders, on='ketoan_id')\ | |
.merge(lophoc, on='lop_id')\ | |
.merge(users[['fullname', 'id']], left_on='gv_id', right_on='id') | |
thucthu['thucthu'] = thucthu['giohoc'] * thucthu['ketoan_tientrengio'] | |
thucthu['date_created_month'] = thucthu['date_created'].dt.month_name() | |
thucthu_all['thucthu'] = thucthu_all['giohoc'] * \ | |
thucthu_all['ketoan_tientrengio'] | |
thucthu_all['date_created_month'] = thucthu_all['date_created'].dt.month_name() | |
new_order = ['January', 'February', 'March', 'April', 'May', 'June', | |
'July', 'August', 'September', 'October', 'November', 'December'] | |
# Reorder months | |
thucthu_all['date_created_month'] = pd.Categorical( | |
thucthu_all['date_created_month'], categories=new_order, ordered=True) | |
# Groupby giaovien | |
thucthu_gv = thucthu.groupby(['id', 'fullname'], as_index=False)[ | |
'thucthu'].sum() | |
# Groupby cn | |
thucthu_cn = thucthu.groupby(['lop_cn'], as_index=False)['thucthu'].sum() | |
thucthu_cn_rename = thucthu.groupby( | |
['lop_cn'], as_index=False)['thucthu'].sum() | |
thucthu_cn_rename = rename_lop(thucthu_cn_rename, 'lop_cn') | |
# Thực thu theo giáo viên và chi nhánh | |
thucthu_details = thucthu.groupby( | |
['id', 'fullname', 'lop_cn'], as_index=False)['thucthu'].sum() | |
# "_______________" | |
def thucthu_time(dataframe, column): | |
df = dataframe.groupby(['lop_cn', column], as_index=False)[ | |
'thucthu'].sum() | |
df.lop_cn = df.lop_cn.replace( | |
{1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"}) | |
return df | |
thucthu_diemdanh_ngay = thucthu_time(thucthu, 'date_created') | |
thucthu_diemdanh_ngay = thucthu_diemdanh_ngay.pivot( | |
index='date_created', columns='lop_cn', values='thucthu') | |
thucthu_diemdanh_month = thucthu_time(thucthu_all, 'date_created_month') | |
# Thực thu điểm danh theo ngày và tháng | |
fig9 = px.bar(thucthu_diemdanh_ngay, x=thucthu_diemdanh_ngay.index, y=thucthu_diemdanh_ngay.columns, barmode='stack', | |
color_discrete_sequence=['#07a203', '#ffc107', '#e700aa', '#2196f3']) | |
fig10 = px.bar(thucthu_diemdanh_month, x="date_created_month", | |
y="thucthu", color="lop_cn", barmode="group", color_discrete_sequence=['#ffc107', '#07a203', '#2196f3', '#e700aa'], text="thucthu") | |
# update the chart layout | |
fig9.update_layout(title='Thực thu điểm danh theo ngày', | |
xaxis_title='Ngày', yaxis_title='Thực thu điểm danh') | |
fig10.update_layout(title='Thực thu điểm danh theo tháng trong năm 2023', | |
xaxis_title='Tháng', yaxis_title='Thực thu', showlegend=True) | |
fig10.update_traces( | |
hovertemplate="Thực thu điểm danh: %{y:,.0f}<extra></extra>") | |
# "_______________" | |
fig1 = plotly_chart(thucthu_cn_rename, 'lop_cn', 'thucthu', thucthu_cn_rename['thucthu'].apply(lambda x: format(x, ',')), | |
"Thực thu theo chi nhánh", 'Chi nhánh', 'Thực thu') | |
# "_______________" Tính tổng lương | |
fixed_salary_cn = salary_gv_dt.groupby("lop_cn", as_index=False)[ | |
'salary_ngay_cong_divided'].sum() | |
overtime_salary_cn = sal_diem_over_group_lop.groupby("lop_cn", as_index=False)[ | |
'salary_gio_cong'].sum() | |
overtime_fixed_salary_cn = fixed_salary_cn.merge( | |
overtime_salary_cn, on='lop_cn') | |
overtime_fixed_salary_cn['fixed_overtime'] = overtime_fixed_salary_cn['salary_ngay_cong_divided'] + \ | |
overtime_fixed_salary_cn['salary_gio_cong'] | |
salary_thucthu = overtime_fixed_salary_cn.merge(thucthu_cn, on='lop_cn') | |
# Create grand total | |
salary_thucthu_grand_total = grand_total(salary_thucthu, 'lop_cn') | |
# Create percent | |
salary_thucthu_grand_total['percent'] = salary_thucthu_grand_total.fixed_overtime / \ | |
salary_thucthu_grand_total.thucthu * 100 | |
salary_thucthu_grand_total['percent'] = round( | |
salary_thucthu_grand_total['percent'], 2) | |
salary_thucthu_grand_total = rename_lop( | |
salary_thucthu_grand_total, 'lop_cn') | |
salary_thucthu_grand_total.columns = ['Chi nhánh', 'Tổng lương ngày công', | |
'Tổng lương giờ công', 'Tổng lương giáo viên', 'Thực thu điểm danh', 'Tổng lương / thực thu'] | |
# "_______________" | |
# Create a barplot for Tỷ lệ tổng lương / thực thu theo chi nhánh | |
fig2 = plotly_chart(salary_thucthu_grand_total, 'Tổng lương / thực thu', 'Chi nhánh', salary_thucthu_grand_total["Tổng lương / thực thu"].apply( | |
lambda x: '{:.2%}'.format(x/100)), | |
"Tỷ lệ tổng lương / thực thu theo chi nhánh", 'Chi nhánh', 'Tổng lương / thực thu', color='Chi nhánh', map={ | |
'Hoa Cúc': '#ffc107', | |
'Gò Dầu': '#07a203', | |
'Lê Quang Định': '#2196f3', | |
'Lê Hồng Phong': '#e700aa', | |
'Grand total': 'white' | |
}) | |
fig2.update_layout(font=dict(size=17), xaxis={ | |
'categoryorder': 'total descending'}) | |
# "_______________" | |
thucthu_hocvien_lop = thucthu_cn_rename.merge( | |
hocvien_danghoc, left_on='lop_cn', right_on='ketoan_coso')\ | |
.merge(lop_danghoc, on='lop_cn') | |
thucthu_hocvien_lop['thucthu_div_hocvien'] = round( | |
thucthu_hocvien_lop['thucthu'] / thucthu_hocvien_lop['total_students'], 0) | |
thucthu_hocvien_lop['thucthu_div_lophoc'] = round( | |
thucthu_hocvien_lop['thucthu'] / thucthu_hocvien_lop['total_classes'], 0) | |
# fig7 = plotly_chart(thucthu_hocvien_lop, 'lop_cn', 'thucthu_div_hocvien', thucthu_hocvien_lop['thucthu_div_hocvien'].apply(lambda x: format(x, ',')), | |
# "Trung bình thực thu 1 học viên", 'Chi nhánh', 'Thực thu / học viên') | |
# fig8 = plotly_chart(thucthu_hocvien_lop, 'lop_cn', 'thucthu_div_lophoc', thucthu_hocvien_lop['thucthu_div_lophoc'].apply(lambda x: format(x, ',')), | |
# "Trung bình thực thu 1 lớp học", 'Chi nhánh', 'Thực thu / lớp học') | |
# "_______________" | |
overtime_salary_cn_gv = sal_diem_over_group_lop.groupby( | |
['id_gg', 'Họ và tên', "lop_cn", "working_status_x"], as_index=False)['salary_gio_cong'].sum() | |
df = overtime_salary_cn_gv.merge( | |
salary_gv_dt, on=['lop_cn', 'Họ và tên', 'id_gg'], how='outer') | |
df.fillna(0, inplace=True) | |
df['fixed_overtime'] = df['salary_gio_cong'] + \ | |
df['salary_ngay_cong_divided'] | |
gv_thucthu_cs = df.merge(thucthu_cn, on='lop_cn') | |
gv_thucthu_cs['percent'] = round(gv_thucthu_cs['fixed_overtime'] / | |
gv_thucthu_cs['thucthu'] * 100, 2) | |
# "_______________" | |
# salary_merge = st.session_state['salary_merge'] | |
gv_thucthu_gv = gv_thucthu_cs.groupby(['id_gg', 'Họ và tên'], as_index=False)['fixed_overtime'].sum()\ | |
.merge(thucthu_gv, left_on='id_gg', right_on='id', how='left') | |
gv_thucthu_gv['percent'] = round(gv_thucthu_gv['fixed_overtime'] / | |
gv_thucthu_gv['thucthu'] * 100, 2) | |
gv_thucthu_gv = gv_thucthu_gv.merge(salary, left_on='id', right_on='id_gg') | |
# Fulltime | |
df = gv_thucthu_gv | |
df1 = df[df["working_status"] == "Fulltime"].sort_values( | |
by="percent", ascending=True) | |
df1['fullname'] = df1['fullname'] + " (" + df['level'] + ")" | |
# Parttime | |
df2 = df[df["working_status"] == "Partime"].sort_values( | |
by="percent", ascending=True) | |
df2['fullname'] = df2['fullname'] + " (" + df['level'] + ")" | |
# Plotly graphs | |
fig3 = plotly_chart(df1.sort_values( | |
"percent", ascending=True), 'fullname', 'percent', df1["percent"].apply( | |
lambda x: '{:.2%}'.format(x/100)), | |
"Fulltime - Tỷ lệ tổng lương / thực thu", '', 'Tỷ lệ') | |
fig3.update_layout( | |
height=1000, # set the height of the plot to 600 pixels | |
width=800) | |
# Plotly graphs | |
fig3_1 = plotly_chart(df2.sort_values( | |
"percent", ascending=True), 'fullname', 'percent', df2["percent"].apply( | |
lambda x: '{:.2%}'.format(x/100)), | |
"Parttime - Tỷ lệ tổng lương / thực thu", '', 'Tỷ lệ') | |
fig3_1.update_layout( | |
height=1000, # set the height of the plot to 600 pixels | |
width=800) | |
# "_______________" thực thu chuyển phí | |
hv_status = collect_data('https://vietop.tech/api/get_data/hv_status') | |
# Filter orders | |
orders_chuyenphi = orders.query("ketoan_active == 5")[["ketoan_id", "hv_id", "ketoan_details", "ketoan_coso", "ketoan_sogio", "ketoan_price", | |
"ketoan_tientrengio", "remaining_time", "kh_id"]] | |
# Filter hv_status | |
hv_status_chuyenphi = hv_status[['ketoan_id', 'status', 'lop_id', 'note', 'is_price', 'created_at']]\ | |
.query("status ==7") | |
# Filter hocvien | |
hocvien_chuyenphi = hocvien[['hv_id', 'hv_fullname']] | |
# Merge hv_status and orders | |
chuyenphi = hv_status_chuyenphi.merge( | |
orders_chuyenphi, on='ketoan_id', how='left') | |
# Merge hocvien_chuyephi | |
chuyenphi = chuyenphi.merge(hocvien_chuyenphi, on='hv_id', how='left') | |
chuyenphi = chuyenphi[['created_at', 'hv_fullname', | |
'ketoan_coso', 'note', 'is_price']] | |
# Add column Phi Chuyen | |
chuyenphi['phí chuyển'] = chuyenphi.is_price * 0.1 | |
# Add column Tien con lai sau phi | |
chuyenphi['thực thu chuyển phí'] = chuyenphi.is_price - \ | |
chuyenphi['phí chuyển'] | |
# Change data type | |
chuyenphi = chuyenphi.astype( | |
{'created_at': 'datetime64[ns]'}) | |
# Sort | |
chuyenphi = chuyenphi.sort_values(by='created_at', ascending=False) | |
chuyenphi = chuyenphi.query( | |
"created_at >= @ketoan_start_time and created_at <= @ketoan_end_time") | |
# Rename columns | |
# chuyenphi.columns = [["created_at", "Họ tên", "ketoan_coso", "Ghi chú", "Học phí chuyển", "Phí chuyển", "Còn lại sau phí"]] | |
chuyenphi = chuyenphi.groupby('ketoan_coso', as_index=False)[ | |
'thực thu chuyển phí'].sum() | |
chuyenphi = rename_lop(chuyenphi, 'ketoan_coso') | |
# "_______________" Thực thu kết thúc | |
# Filter ketthuc | |
orders_ketthuc = orders.query("ketoan_active == 5 and deleted_at.isnull()") | |
# Merge orders_ketthuc and diemdanh_details | |
df = diemdanh_details[['ketoan_id', 'giohoc']]\ | |
.merge(orders_ketthuc, on='ketoan_id', how='right').groupby( | |
['ketoan_id', 'ketoan_coso', 'remaining_time', 'ketoan_tientrengio', 'date_end'], as_index=False).giohoc.sum() | |
# Add 2 more columns | |
df['gio_con_lai'] = df.remaining_time - df.giohoc | |
df['thực thu kết thúc'] = df.gio_con_lai * df.ketoan_tientrengio | |
# Convert to datetime | |
df['date_end'] = pd.to_datetime(df['date_end']) | |
# Filter gioconlai > 0 and time | |
thucthu_ketthuc = df.query("gio_con_lai > 0")\ | |
.query("date_end >= @ketoan_start_time and date_end <= @ketoan_end_time") | |
thucthu_ketthuc = thucthu_ketthuc.merge( | |
orders[['hv_id', 'ketoan_id']], on='ketoan_id') | |
thucthu_ketthuc = thucthu_ketthuc.groupby("ketoan_coso", as_index=False)[ | |
'thực thu kết thúc'].sum() | |
thucthu_ketthuc = rename_lop(thucthu_ketthuc, 'ketoan_coso') | |
# "_______________" | |
overtime_salary = sal_diem_over_group.query('working_status_x != "Ngoài giờ"')\ | |
.merge(salary[['Họ và tên', 'working_status']], left_on='Họ và tên', right_on='Họ và tên') | |
overtime_salary['out_div_total'] = overtime_salary['salary_gio_cong'] / \ | |
(overtime_salary['salary_ngay_cong'] + | |
overtime_salary['salary_gio_cong']) | |
overtime_salary = overtime_salary[[ | |
'Họ và tên', 'salary_ngay_cong', 'salary_gio_cong', 'out_div_total']] | |
overtime_salary['out_div_total'] = round( | |
overtime_salary['out_div_total'] * 100, 2) | |
overtime_salary_fulltime = overtime_salary.query("salary_ngay_cong != 0") | |
# "_______________" thucthu điểm danh and chuyen phi | |
thucthu_hocvien_lop = thucthu_hocvien_lop.merge( | |
chuyenphi, left_on='ketoan_coso', right_on='ketoan_coso', how='left')\ | |
.merge(thucthu_ketthuc, left_on='ketoan_coso', right_on='ketoan_coso') | |
# Renam thucthu => thuc thu diem danh | |
thucthu_hocvien_lop = thucthu_hocvien_lop.rename( | |
columns={'thucthu': "thực thu điểm danh"}) | |
# Fillna with 0 | |
thucthu_hocvien_lop.fillna(0, inplace=True) | |
# Add all thucthu | |
thucthu_hocvien_lop['tổng thực thu'] = thucthu_hocvien_lop['thực thu chuyển phí'] + \ | |
thucthu_hocvien_lop['thực thu kết thúc'] + \ | |
thucthu_hocvien_lop['thực thu điểm danh'] | |
# Add ty lệ thực thu | |
thucthu_hocvien_lop['tỷ trọng tổng thực thu'] = thucthu_hocvien_lop['tổng thực thu'].apply( | |
lambda x: round(x/thucthu_hocvien_lop['tổng thực thu'].sum()*100, 2)) | |
# create a new row with the sum of each numerical column | |
totals = thucthu_hocvien_lop.select_dtypes(include=[float, int]).sum() | |
totals["lop_cn"] = "Grand total" | |
# append the new row to the dataframe | |
thucthu_hocvien_lop = thucthu_hocvien_lop.append(totals, ignore_index=True) | |
# Add % in tỷ trọng tổng thực thu | |
thucthu_hocvien_lop["tỷ trọng tổng thực thu"] = thucthu_hocvien_lop["tỷ trọng tổng thực thu"].apply( | |
lambda x: '{:.2%}'.format(x/100)) | |
salary_thucthu_grand_total['Tổng lương / thực thu'] = salary_thucthu_grand_total['Tổng lương / thực thu'].apply( | |
lambda x: '{:.2%}'.format(x/100)) | |
# define a function | |
def thousands_divider(df, col): | |
df[col] = df[col].apply( | |
lambda x: '{:,.0f}'.format(x)) | |
return df | |
thucthu_hocvien_lop = thousands_divider( | |
thucthu_hocvien_lop, 'tổng thực thu') | |
thucthu_hocvien_lop = thousands_divider( | |
thucthu_hocvien_lop, 'thực thu điểm danh') | |
thucthu_hocvien_lop = thousands_divider( | |
thucthu_hocvien_lop, 'thực thu chuyển phí') | |
thucthu_hocvien_lop = thousands_divider( | |
thucthu_hocvien_lop, 'thực thu kết thúc') | |
thucthu_hocvien_lop = thucthu_hocvien_lop.set_index("lop_cn") | |
thucthu_hocvien_lop.index.names = ['Chi nhánh'] | |
# Show tables | |
st.plotly_chart(fig2, use_container_width=True) | |
st.subheader("Thưc thu theo chi nhánh") | |
st.dataframe(thucthu_hocvien_lop.drop(["ketoan_coso", "total_students", "total_classes", "thucthu_div_hocvien", "thucthu_div_lophoc"], | |
axis=1).style.background_gradient().set_precision(0), use_container_width=True) | |
# Show Chi nhanh by 2 columns | |
st.plotly_chart(fig10, use_container_width=True) | |
st.plotly_chart(fig9, use_container_width=True) | |
# left_column, right_column = st.columns([1, 2]) | |
# left_column.plotly_chart(fig10, use_container_width=True) | |
# right_column.plotly_chart(fig9, use_container_width=True) | |
# left_column, right_column = st.columns(2) | |
# left_column.plotly_chart(fig7, use_container_width=True) | |
# right_column.plotly_chart(fig8, use_container_width=True) | |
# left_column, right_column = st.columns(2) | |
# left_column.plotly_chart(fig5, use_container_width=True) | |
# right_column.plotly_chart(fig6, use_container_width=True) | |
# left_column, right_column = st.columns(2) | |
# left_column.plotly_chart(fig1, use_container_width=True) | |
# right_column.plotly_chart(fig2, use_container_width=True) | |
left_column, right_column = st.columns(2) | |
left_column.plotly_chart(fig3, use_container_width=True) | |
right_column.plotly_chart(fig3_1, use_container_width=True) | |
"" | |
fig4 = plotly_chart(overtime_salary_fulltime[['Họ và tên', 'out_div_total']].sort_values( | |
"out_div_total", ascending=True), "Họ và tên", 'out_div_total', 'out_div_total', | |
"Tỷ lệ lương ngoài giờ / trong giờ của giáo viên fulltime", '', 'Tỷ lệ') | |
fig4.update_layout(height=800, width=800) | |
st.plotly_chart(fig4) | |