thanhpham0704/streamlit_python
0
1import streamlit as st2import requests3import plotly.express as px4import pandas as pd5from datetime import datetime, timedelta6from pathlib import Path7import pickle8import streamlit_authenticator as stauth9import numpy as np10import calendar11 12# names = ["Phạm Tấn Thành", "Phạm Minh Tâm", "Vận hành"]13# usernames = ["thanhpham", "tampham", "vietopvanhanh"]14# passwords = ['thanhpham0704', 'tampham1234', 'vanhanh2023']15 16# hashed_passwords = stauth.Hasher(passwords).generate()17 18# file_path = Path(__file__).parent / "hashed_pw.pkl"19# with file_path.open("wb") as file:20# pickle.dump(hashed_passwords, file)21 22page_title = "Lương và thực thu"23page_icon = ":chart_with_upwards_trend:"24layout = "wide"25st.set_page_config(page_title=page_title, page_icon=page_icon, layout=layout)26 27# ----------------------------------------28names = ["Phạm Tấn Thành", "Phạm Minh Tâm", "Vận hành"]29usernames = ["thanhpham", "tampham", "vietopvanhanh"]30 31# Load hashed password32file_path = Path(__file__).parent / 'hashed_pw.pkl'33with file_path.open("rb") as file:34 hashed_passwords = pickle.load(file)35 36authenticator = stauth.Authenticate(names, usernames, hashed_passwords,37 "sales_dashboard", "abcdef", cookie_expiry_days=1)38 39name, authentication_status, username = authenticator.login("Login", "main")40 41if authentication_status == False:42 st.error("Username/password is incorrect")43 44if authentication_status == None:45 st.warning("Please enter your username and password")46 47if authentication_status:48 authenticator.logout("logout", "main")49 50 # Add CSS styling to position the button on the top right corner of the page51 st.markdown(52 """53 <style>54 .stButton button {55 position: absolute;56 top: 0px;57 right: 0px;58 }59 </style>60 """,61 unsafe_allow_html=True62 )63 st.title(page_title + " " + page_icon)64 #----------------------#65 # Filter66 now = datetime.now()67 DEFAULT_START_DATE = datetime(now.year, now.month, 1)68 DEFAULT_END_DATE = datetime(now.year, now.month, 1) + timedelta(days=32)69 DEFAULT_END_DATE = DEFAULT_END_DATE.replace(day=1) - timedelta(days=1)70 71 # Create a form to get the date range filters72 with st.form(key='date_filter_form'):73 col1, col2 = st.columns(2)74 ketoan_start_time = col1.date_input(75 "Select start date", value=DEFAULT_START_DATE)76 ketoan_end_time = col2.date_input(77 "Select end date", value=DEFAULT_END_DATE)78 submit_button = st.form_submit_button(79 label='Filter')80 81 # the duration between 2 dates exclude Sunday82 duration = sum(1 for i in range((ketoan_end_time - ketoan_start_time).days)83 if (ketoan_start_time + timedelta(i)).weekday() != 6)84 # the number of days in a month exclude Sunday85 days_in_month = calendar.monthrange(86 ketoan_start_time.year, ketoan_start_time.month)[1]87 sundays_in_month = sum(1 for day in range(1, days_in_month + 1) if datetime(88 ketoan_start_time.year, ketoan_start_time.month, day).weekday() == 6)89 days_excluding_sundays = days_in_month - sundays_in_month90 #----------------------#91 92 @st.cache(ttl = timedelta(days = 1))93 def collect_data(link):94 return(pd.DataFrame((requests.get(link).json())))95 96 @st.cache97 def rename_lop(dataframe, column_name):98 dataframe[column_name] = dataframe[column_name].replace(99 {1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"})100 return dataframe101 102 @st.cache103 def grand_total(dataframe, column):104 # create a new row with the sum of each numerical column105 totals = dataframe.select_dtypes(include=[float, int]).sum()106 totals[column] = "Grand total"107 # append the new row to the dataframe108 dataframe = dataframe.append(totals, ignore_index=True)109 return dataframe110 111 # "---------------" Thông tin lương giáo viên112 users = collect_data('https://vietop.tech/api/get_data/users')113 # Thong tin luong114 import gspread115 sa = gspread.service_account(116 filename='taichinh-380507-b8f84e9ee681.json')117 sh = sa.open("Nhân sự")118 worksheet = sh.worksheet("Giáo viên")119 salary = pd.DataFrame(worksheet.get_all_records())120 salary = salary.replace("", np.nan).dropna(subset='Mã giáo viên')121 salary = salary.replace('\.', '', regex=True)122 salary = salary.astype({'Lương theo hợp đông': 'float', 'Thâm niên': 'float',123 'Chức danh': "float", 'Tổng lương': 'float', 'date_affected': 'datetime64[ns]'})124 # salary.info()125 # salary = salary.sort_values("date_affected", ascending=False)\126 # .drop_duplicates(subset='id_gg')127 salary = salary.sort_values("date_affected", ascending=False)\128 .query("date_affected <= @ketoan_end_time")\129 .drop_duplicates("id_gg")130 salary.fillna(0, inplace=True)131 # Thong tin luong132 salary['salary_ngay_cong'] = round(133 salary['Tổng lương'] * duration/days_excluding_sundays, 0)134 salary.fillna(0, inplace=True)135 salary.drop(['STT', 'Mã giáo viên', 'Tổng lương'], axis=1, inplace=True)136 salary.rename(columns={"Lương theo hợp đông": "hopdong", "Thâm niên": "thamnien",137 'Chức danh': 'chucdanh', 'Ngày': 'ngay', 'Tối': 'toi', 'Cuối tuần': 'cuoituan',138 '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',139 'Tổng ngày công thực tế': 'ngaycong_real_total'}, inplace=True)140 141 # "------------------"142 gv_diemdanh = collect_data('https://vietop.tech/api/get_data/diemdanh')143 gv_diemdanh['date_created'] = pd.to_datetime(gv_diemdanh['date_created'])144 gv_diemdanh = gv_diemdanh.query(145 "date_created >= @ketoan_start_time and date_created <= @ketoan_end_time")146 # Ca hoc table147 cahoc = {'cahoc': ['ca1', 'ca2', 'ca3', 'ca4', 'ca5', 'ca6'],148 'start_time': ['08:30:00', '10:30:00', '13:30:00', '15:30:00', '18:00:00', '19:45:00'],149 'end_time': ['10:30:00', '12:30:00', '15:30:00', '17:30:00', '19:45:00', '21:30:00']}150 cahoc = pd.DataFrame(cahoc)151 # --------------- GET DATA FROM API152 153 sh = sa.open("Nhân sự")154 worksheet = sh.worksheet("Overtime")155 offline_overtime = pd.DataFrame(worksheet.get_all_records())156 offline_overtime['date_affected'] = pd.to_datetime(157 offline_overtime['date_affected'])158 offline_overtime = offline_overtime.sort_values("date_affected", ascending=False)\159 .drop_duplicates(subset='Họ và tên')160 offline_overtime.drop("date_affected", axis=1, inplace=True)161 162 # Define the start and end times of the day163 start_time = pd.to_datetime('2000-01-01 06:00:00').time()164 end_time = pd.to_datetime('2000-01-01 18:00:00').time()165 ca1 = pd.to_datetime('2000-01-01 06:30:00').time()166 ca2 = pd.to_datetime('2000-01-01 10:30:00').time()167 ca3 = pd.to_datetime('2000-01-01 13:30:00').time()168 ca4 = pd.to_datetime('2000-01-01 15:30:00').time()169 ca5 = pd.to_datetime('2000-01-01 18:00:00').time()170 # tren office khong cos 19:45171 ca6 = pd.to_datetime('2000-01-01 19:30:00').time()172 # Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range173 174 @st.cache175 def time_of_day(time):176 if (time >= start_time) & (time < end_time):177 return "Sáng"178 else:179 return "Tối"180 # Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range181 182 @st.cache183 def day_of_week(day):184 if (day == 5) | (day == 6):185 return "weekend"186 else:187 return "weekdays"188 # Create a function that takes a time and returns "Morning" or "Evening" depending on whether the time falls within the specified range189 190 @st.cache191 def cahoc_converter(ca):192 if (ca >= ca1) & (ca < ca2):193 return 1194 elif (ca >= ca2) & (ca < ca3):195 return 2196 elif (ca >= ca3) & (ca < ca4):197 return 3198 elif (ca >= ca4) & (ca < ca5):199 return 4200 elif (ca >= ca5) & (ca < ca6):201 return 5202 elif (ca >= ca6):203 return 6204 205 # "---------------" Bảng lương giáo viên206 207 overtime_melt = offline_overtime.melt(208 id_vars=['id_gg', 'Họ và tên', 'WORKING_STATUS'], var_name='Column Name', value_name='overtime_status')209 overtime_melt['day_of_week'], overtime_melt['cahoc'] = overtime_melt['Column Name'].str.split(210 'Ca ', 1).str211 overtime_melt['day_of_week'].replace(212 {'T2': 0, 'T3': 1, 'T4': 2, 'T5': 3, 'T6': 4, 'T7': 5, 'T8': 6}, inplace=True)213 overtime_melt['overtime_status'].replace(214 {0: 'in', 'a': 'out'}, inplace=True)215 # Convert 5 and 6 into "Cuối tuần"216 overtime_melt['weekend_or_not'] = overtime_melt['day_of_week'].apply(217 day_of_week)218 # Convert ca into Sáng or Tối219 overtime_melt['time_of_day'] = ['Sáng' if i in [220 '1', '2', '3', '4'] else 'Tối' for i in overtime_melt['cahoc']]221 # Drop unnecessary columns222 overtime_melt.drop(223 ['Column Name'], axis=1, inplace=True)224 overtime_melt['cahoc'] = overtime_melt['cahoc'].astype(int)225 overtime_melt.rename(226 columns={'WORKING_STATUS': 'working_status'}, inplace=True)227 228 # "---------------" Lương overtime của giáo viên229 lophoc = collect_data('https://vietop.tech/api/get_data/lophoc')230 diemdanh = gv_diemdanh.merge(users[['fullname', 'id']], left_on='giaovien', right_on='id', how='inner')\231 .sort_values("created_at", ascending=False)232 # 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)233 diemdanh = diemdanh[['lop_id', 'sogio', 'cahoc', 'phanloai',234 'date_created', 'created_by', 'id',235 'updated_by', 'created_at', 'fullname']]236 # Convert the date column to datetime237 diemdanh['created_at'] = pd.to_datetime(diemdanh['created_at'])238 # Extract the time component of each datetime value239 diemdanh['created_at_time'] = diemdanh['created_at'].dt.time240 # Create a new column that indicates the day of the week241 diemdanh['day_of_week'] = diemdanh['created_at'].dt.dayofweek242 # Create a boolean mask that indicates whether each time is within the specified time frame243 diemdanh["time_of_day"] = diemdanh['created_at_time'].apply(time_of_day)244 # Convert 5 and 6 into "Cuối tuần"245 diemdanh['weekend_or_not'] = diemdanh["day_of_week"].apply(day_of_week)246 diemdanh = diemdanh[['id', 'fullname', 'sogio', 'cahoc', 'phanloai',247 'created_at', 'day_of_week', 'created_at_time', 'time_of_day', 'weekend_or_not', 'date_created',248 'lop_id']]249 diemdanh['cahoc'] = diemdanh['created_at_time'].apply(cahoc_converter)250 # Sum giohoc according to fullname, time of day and weekend or not251 diemdanh_sum_giohoc = diemdanh.groupby(252 ["id", "fullname", "cahoc", 'phanloai', 'day_of_week', "time_of_day", "weekend_or_not", 'lop_id'], as_index=False)['sogio'].sum()253 # Get lopcn from lophoc254 diemdanh_lop_cn = diemdanh_sum_giohoc.merge(255 lophoc[['lop_id', 'lop_cn']], on='lop_id')256 257 # "---------------"258 sal_diem = diemdanh_lop_cn.merge(259 salary, left_on='id', right_on='id_gg')260 # Merge diemdanh and overtime261 sal_diem_over = sal_diem\262 .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')263 # Drop duplicates264 sal_diem_over.drop_duplicates(inplace=True)265 # Fill na266 sal_diem_over.fillna(0, inplace=True)267 # Calculate luong ngay cong268 empty = []269 for index, value in sal_diem_over.iterrows():270 if value['weekend_or_not'] == 'weekend' and value['overtime_status'] == 'out':271 empty.append(value['cuoituan'] * value['sogio'])272 elif value['time_of_day'] == 'Tối' and value['overtime_status'] == 'out':273 empty.append(value['toi'] * value['sogio'])274 elif value['time_of_day'] == 'Sáng' and value['overtime_status'] == 'out':275 empty.append(value['ngay'] * value['sogio'])276 elif value['phanloai'] == 0:277 empty.append(value['trogiang'] * value['sogio'])278 else:279 empty.append(0)280 sal_diem_over['salary_gio_cong'] = empty281 282 # Slicing columns283 sal_diem_over = sal_diem_over.loc[:, ['lop_cn', 'id_gg', 'Họ và tên', 'working_status_x', 'cahoc', 'phanloai', 'overtime_status',284 'time_of_day', 'day_of_week', 'weekend_or_not', 'sogio',285 'ngay', 'toi', 'cuoituan', 'trogiang', 'salary_gio_cong', 'salary_ngay_cong']]286 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()\287 .query("overtime_status == 'out'")288 # Sum luong_gio_cong according to fullname289 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()\290 .query("overtime_status == 'out'").sort_values("salary_gio_cong", ascending=False)291 sal_diem_over_details = sal_diem_over.drop('salary_ngay_cong', axis=1)\292 .query("overtime_status == 'out'")\293 294 # ----------------------# Phân phối lương cứng theo chi nhánh295 df = sal_diem_over.groupby(['lop_cn', 'id_gg', 'Họ và tên', 'working_status_x', 'overtime_status',296 'salary_ngay_cong'], as_index=False)['sogio', 'salary_gio_cong'].sum()297 df = df.query('overtime_status == "in"')298 df_sum = df.groupby('Họ và tên', as_index=False)['sogio'].sum()299 df_proportion = df_sum.merge(df, on='Họ và tên')300 df_proportion['proportion_sogio'] = df_proportion['sogio_y'] / \301 df_proportion['sogio_x']302 df_proportion['salary_ngay_cong_divided'] = df_proportion['proportion_sogio'] * \303 df_proportion['salary_ngay_cong']304 # Giáo viên ngoại trừ phòng đào tạo305 df_proportion_nodaotao = df_proportion[~df_proportion['Họ và tên'].isin(306 ['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'])]307 # Subset308 df_proportion_nodaotao = df_proportion_nodaotao[[309 'id_gg', 'Họ và tên', 'lop_cn', 'salary_ngay_cong_divided']]310 # Riêng phòng đào tạo311 df_proportion_daotao = salary[salary['Họ và tên'].isin(312 ['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'])]313 df_proportion_daotao['1'] = 0.26 * df_proportion_daotao['salary_ngay_cong']314 df_proportion_daotao['2'] = 0.26 * df_proportion_daotao['salary_ngay_cong']315 df_proportion_daotao['3'] = 0.18 * df_proportion_daotao['salary_ngay_cong']316 df_proportion_daotao['5'] = 0.30 * df_proportion_daotao['salary_ngay_cong']317 # Subset318 df_proportion_daotao = df_proportion_daotao.loc[:, [319 'id_gg', 'Họ và tên', '1', '2', '3', '5']]320 # Lương đào tạo sau khi phân phối321 df_proportion_daotao = pd.melt(df_proportion_daotao, id_vars=[322 'id_gg', 'Họ và tên'], var_name='lop_cn', value_name='salary_ngay_cong_divided')323 df_proportion_daotao['lop_cn'] = df_proportion_daotao['lop_cn'].astype(324 'int64')325 # Concat luong đào tạo and lương giáo viên326 salary_gv_dt = pd.concat([df_proportion_daotao, df_proportion_nodaotao])327 salary_gv_dt['salary_ngay_cong_divided'] = round(328 salary_gv_dt['salary_ngay_cong_divided'], 2)329 salary_gv_dt = salary_gv_dt.sort_values(330 "salary_ngay_cong_divided", ascending=False)331 # ----------------------# Thực thu332 333 orders = collect_data(334 'https://vietop.tech/api/get_data/orders').query("deleted_at.isnull()")335 lophoc = collect_data('https://vietop.tech/api/get_data/lophoc')336 hocvien = collect_data(337 'https://vietop.tech/api/get_data/hocvien').query("hv_id != 737 and deleted_at.isnull()")338 # hv đang họccd Au339 hocvien_danghoc = hocvien.merge(orders, on='hv_id')\340 .query("ketoan_active == 1")\341 .groupby('ketoan_coso', as_index=False).size().rename(columns={"size": "total_students"})342 hocvien_danghoc = rename_lop(hocvien_danghoc, 'ketoan_coso')343 344 @st.cache345 def plotly_chart(df, yvalue, xvalue, text, title, y_title, x_title, color=None, discrete_sequence=None, map=None):346 fig = px.bar(df, y=yvalue,347 x=xvalue, text=text, color=color, color_discrete_sequence=discrete_sequence, color_discrete_map=map)348 fig.update_layout(349 title=title,350 yaxis_title=y_title,351 xaxis_title=x_title,352 )353 fig.update_traces(textposition='auto')354 return fig355 356 fig5 = plotly_chart(hocvien_danghoc, 'ketoan_coso', 'total_students', 'total_students',357 'Tổng học viên đang học theo chi nhánh', 'Chi nhánh', 'Học viên')358 359 # Lop dang hoc360 lop_danghoc = lophoc.query(361 "(lop_status == 2 or lop_status == 4) and deleted_at.isnull()")\362 .groupby('lop_cn', as_index=False).size().rename(columns={"size": "total_classes"})363 lop_danghoc.lop_cn = lop_danghoc.lop_cn.replace(364 {1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"})365 366 fig6 = plotly_chart(lop_danghoc, 'lop_cn', 'total_classes', 'total_classes',367 "Tổng lớp đang học theo chi nhánh", 'Chi nhánh', 'Lớp học')368 ""369 # "------------------"370 371 # Define a function372 @st.cache373 def csv_reader(file):374 df = pd.read_csv(file)375 df = df.query("phanloai == 1") # Filter lop chính376 df['date_created'] = pd.to_datetime(df['date_created'])377 return df378 379 @st.cache380 def collect_filtered_data(table, date_column='', start_time='', end_time=''):381 link = f"https://vietop.tech/api/get_data/{table}?column={date_column}&date_start={start_time}&date_end={end_time}"382 df = pd.DataFrame((requests.get(link).json()))383 df[date_column] = pd.to_datetime(df[date_column])384 return df385 386 df = csv_reader("diemdanh_details.csv")387 df1 = collect_filtered_data(table='diemdanh_details', date_column='date_created',388 start_time='2023-01-01', end_time='2025-01-01')389 diemdanh_details = pd.concat([df, df1])390 391 thucthu = diemdanh_details.query(392 'date_created >= @ketoan_start_time and date_created <= @ketoan_end_time')\393 .groupby(['ketoan_id', 'lop_id', 'gv_id', 'date_created'], as_index=False)['giohoc'].sum()\394 .merge(orders, on='ketoan_id')\395 .merge(lophoc, on='lop_id')\396 .merge(users[['fullname', 'id']], left_on='gv_id', right_on='id')397 thucthu_all = diemdanh_details.query("date_created > '2023-01-01'")\398 .groupby(['ketoan_id', 'lop_id', 'gv_id', 'date_created'], as_index=False)['giohoc'].sum()\399 .merge(orders, on='ketoan_id')\400 .merge(lophoc, on='lop_id')\401 .merge(users[['fullname', 'id']], left_on='gv_id', right_on='id')402 403 thucthu['thucthu'] = thucthu['giohoc'] * thucthu['ketoan_tientrengio']404 thucthu['date_created_month'] = thucthu['date_created'].dt.month_name()405 thucthu_all['thucthu'] = thucthu_all['giohoc'] * \406 thucthu_all['ketoan_tientrengio']407 thucthu_all['date_created_month'] = thucthu_all['date_created'].dt.month_name()408 409 new_order = ['January', 'February', 'March', 'April', 'May', 'June',410 'July', 'August', 'September', 'October', 'November', 'December']411 # Reorder months412 thucthu_all['date_created_month'] = pd.Categorical(413 thucthu_all['date_created_month'], categories=new_order, ordered=True)414 # Groupby giaovien415 thucthu_gv = thucthu.groupby(['id', 'fullname'], as_index=False)[416 'thucthu'].sum()417 # Groupby cn418 thucthu_cn = thucthu.groupby(['lop_cn'], as_index=False)['thucthu'].sum()419 thucthu_cn_rename = thucthu.groupby(420 ['lop_cn'], as_index=False)['thucthu'].sum()421 thucthu_cn_rename = rename_lop(thucthu_cn_rename, 'lop_cn')422 423 # Thực thu theo giáo viên và chi nhánh424 thucthu_details = thucthu.groupby(425 ['id', 'fullname', 'lop_cn'], as_index=False)['thucthu'].sum()426 # "_______________"427 428 @st.cache429 def thucthu_time(dataframe, column):430 df = dataframe.groupby(['lop_cn', column], as_index=False)[431 'thucthu'].sum()432 df.lop_cn = df.lop_cn.replace(433 {1: "Hoa Cúc", 2: "Gò Dầu", 3: "Lê Quang Định", 5: "Lê Hồng Phong"})434 return df435 thucthu_diemdanh_ngay = thucthu_time(thucthu, 'date_created')436 thucthu_diemdanh_ngay = thucthu_diemdanh_ngay.pivot(437 index='date_created', columns='lop_cn', values='thucthu')438 thucthu_diemdanh_month = thucthu_time(thucthu_all, 'date_created_month')439 # Thực thu điểm danh theo ngày và tháng440 fig9 = px.bar(thucthu_diemdanh_ngay, x=thucthu_diemdanh_ngay.index, y=thucthu_diemdanh_ngay.columns, barmode='stack',441 color_discrete_sequence=['#07a203', '#ffc107', '#e700aa', '#2196f3'])442 443 fig10 = px.bar(thucthu_diemdanh_month, x="date_created_month",444 y="thucthu", color="lop_cn", barmode="group", color_discrete_sequence=['#ffc107', '#07a203', '#2196f3', '#e700aa'], text="thucthu")445 # update the chart layout446 fig9.update_layout(title='Thực thu điểm danh theo ngày',447 xaxis_title='Ngày', yaxis_title='Thực thu điểm danh')448 fig10.update_layout(title='Thực thu điểm danh theo tháng trong năm 2023',449 xaxis_title='Tháng', yaxis_title='Thực thu', showlegend=True)450 fig10.update_traces(451 hovertemplate="Thực thu điểm danh: %{y:,.0f}<extra></extra>")452 # "_______________"453 fig1 = plotly_chart(thucthu_cn_rename, 'lop_cn', 'thucthu', thucthu_cn_rename['thucthu'].apply(lambda x: format(x, ',')),454 "Thực thu theo chi nhánh", 'Chi nhánh', 'Thực thu')455 456 # "_______________" Tính tổng lương457 458 fixed_salary_cn = salary_gv_dt.groupby("lop_cn", as_index=False)[459 'salary_ngay_cong_divided'].sum()460 overtime_salary_cn = sal_diem_over_group_lop.groupby("lop_cn", as_index=False)[461 'salary_gio_cong'].sum()462 overtime_fixed_salary_cn = fixed_salary_cn.merge(463 overtime_salary_cn, on='lop_cn')464 465 overtime_fixed_salary_cn['fixed_overtime'] = overtime_fixed_salary_cn['salary_ngay_cong_divided'] + \466 overtime_fixed_salary_cn['salary_gio_cong']467 468 salary_thucthu = overtime_fixed_salary_cn.merge(thucthu_cn, on='lop_cn')469 470 # Create grand total471 salary_thucthu_grand_total = grand_total(salary_thucthu, 'lop_cn')472 # Create percent473 salary_thucthu_grand_total['percent'] = salary_thucthu_grand_total.fixed_overtime / \474 salary_thucthu_grand_total.thucthu * 100475 salary_thucthu_grand_total['percent'] = round(476 salary_thucthu_grand_total['percent'], 2)477 salary_thucthu_grand_total = rename_lop(478 salary_thucthu_grand_total, 'lop_cn')479 480 salary_thucthu_grand_total.columns = ['Chi nhánh', 'Tổng lương ngày công',481 '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']482 # "_______________"483 # Create a barplot for Tỷ lệ tổng lương / thực thu theo chi nhánh484 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(485 lambda x: '{:.2%}'.format(x/100)),486 "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={487 'Hoa Cúc': '#ffc107',488 'Gò Dầu': '#07a203',489 'Lê Quang Định': '#2196f3',490 'Lê Hồng Phong': '#e700aa',491 'Grand total': 'white'492 })493 fig2.update_layout(font=dict(size=17), xaxis={494 'categoryorder': 'total descending'})495 496 # "_______________"497 thucthu_hocvien_lop = thucthu_cn_rename.merge(498 hocvien_danghoc, left_on='lop_cn', right_on='ketoan_coso')\499 .merge(lop_danghoc, on='lop_cn')500 thucthu_hocvien_lop['thucthu_div_hocvien'] = round(501 thucthu_hocvien_lop['thucthu'] / thucthu_hocvien_lop['total_students'], 0)502 thucthu_hocvien_lop['thucthu_div_lophoc'] = round(503 thucthu_hocvien_lop['thucthu'] / thucthu_hocvien_lop['total_classes'], 0)504 505 # fig7 = plotly_chart(thucthu_hocvien_lop, 'lop_cn', 'thucthu_div_hocvien', thucthu_hocvien_lop['thucthu_div_hocvien'].apply(lambda x: format(x, ',')),506 # "Trung bình thực thu 1 học viên", 'Chi nhánh', 'Thực thu / học viên')507 508 # fig8 = plotly_chart(thucthu_hocvien_lop, 'lop_cn', 'thucthu_div_lophoc', thucthu_hocvien_lop['thucthu_div_lophoc'].apply(lambda x: format(x, ',')),509 # "Trung bình thực thu 1 lớp học", 'Chi nhánh', 'Thực thu / lớp học')510 511 # "_______________"512 overtime_salary_cn_gv = sal_diem_over_group_lop.groupby(513 ['id_gg', 'Họ và tên', "lop_cn", "working_status_x"], as_index=False)['salary_gio_cong'].sum()514 df = overtime_salary_cn_gv.merge(515 salary_gv_dt, on=['lop_cn', 'Họ và tên', 'id_gg'], how='outer')516 df.fillna(0, inplace=True)517 df['fixed_overtime'] = df['salary_gio_cong'] + \518 df['salary_ngay_cong_divided']519 gv_thucthu_cs = df.merge(thucthu_cn, on='lop_cn')520 gv_thucthu_cs['percent'] = round(gv_thucthu_cs['fixed_overtime'] /521 gv_thucthu_cs['thucthu'] * 100, 2)522 # "_______________"523 # salary_merge = st.session_state['salary_merge']524 gv_thucthu_gv = gv_thucthu_cs.groupby(['id_gg', 'Họ và tên'], as_index=False)['fixed_overtime'].sum()\525 .merge(thucthu_gv, left_on='id_gg', right_on='id', how='left')526 gv_thucthu_gv['percent'] = round(gv_thucthu_gv['fixed_overtime'] /527 gv_thucthu_gv['thucthu'] * 100, 2)528 529 gv_thucthu_gv = gv_thucthu_gv.merge(salary, left_on='id', right_on='id_gg')530 531 # Fulltime532 df = gv_thucthu_gv533 df1 = df[df["working_status"] == "Fulltime"].sort_values(534 by="percent", ascending=True)535 df1['fullname'] = df1['fullname'] + " (" + df['level'] + ")"536 537 # Parttime538 df2 = df[df["working_status"] == "Partime"].sort_values(539 by="percent", ascending=True)540 df2['fullname'] = df2['fullname'] + " (" + df['level'] + ")"541 # Plotly graphs542 543 fig3 = plotly_chart(df1.sort_values(544 "percent", ascending=True), 'fullname', 'percent', df1["percent"].apply(545 lambda x: '{:.2%}'.format(x/100)),546 "Fulltime - Tỷ lệ tổng lương / thực thu", '', 'Tỷ lệ')547 fig3.update_layout(548 height=1000, # set the height of the plot to 600 pixels549 width=800)550 551 552# Plotly graphs553 fig3_1 = plotly_chart(df2.sort_values(554 "percent", ascending=True), 'fullname', 'percent', df2["percent"].apply(555 lambda x: '{:.2%}'.format(x/100)),556 "Parttime - Tỷ lệ tổng lương / thực thu", '', 'Tỷ lệ')557 fig3_1.update_layout(558 height=1000, # set the height of the plot to 600 pixels559 width=800)560 561 # "_______________" thực thu chuyển phí562 hv_status = collect_data('https://vietop.tech/api/get_data/hv_status')563 # Filter orders564 orders_chuyenphi = orders.query("ketoan_active == 5")[["ketoan_id", "hv_id", "ketoan_details", "ketoan_coso", "ketoan_sogio", "ketoan_price",565 "ketoan_tientrengio", "remaining_time", "kh_id"]]566 # Filter hv_status567 hv_status_chuyenphi = hv_status[['ketoan_id', 'status', 'lop_id', 'note', 'is_price', 'created_at']]\568 .query("status ==7")569 # Filter hocvien570 hocvien_chuyenphi = hocvien[['hv_id', 'hv_fullname']]571 # Merge hv_status and orders572 chuyenphi = hv_status_chuyenphi.merge(573 orders_chuyenphi, on='ketoan_id', how='left')574 # Merge hocvien_chuyephi575 chuyenphi = chuyenphi.merge(hocvien_chuyenphi, on='hv_id', how='left')576 chuyenphi = chuyenphi[['created_at', 'hv_fullname',577 'ketoan_coso', 'note', 'is_price']]578 # Add column Phi Chuyen579 chuyenphi['phí chuyển'] = chuyenphi.is_price * 0.1580 # Add column Tien con lai sau phi581 chuyenphi['thực thu chuyển phí'] = chuyenphi.is_price - \582 chuyenphi['phí chuyển']583 # Change data type584 chuyenphi = chuyenphi.astype(585 {'created_at': 'datetime64[ns]'})586 # Sort587 chuyenphi = chuyenphi.sort_values(by='created_at', ascending=False)588 chuyenphi = chuyenphi.query(589 "created_at >= @ketoan_start_time and created_at <= @ketoan_end_time")590 # Rename columns591 # 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í"]]592 chuyenphi = chuyenphi.groupby('ketoan_coso', as_index=False)[593 'thực thu chuyển phí'].sum()594 595 chuyenphi = rename_lop(chuyenphi, 'ketoan_coso')596 597 # "_______________" Thực thu kết thúc598 # Filter ketthuc599 orders_ketthuc = orders.query("ketoan_active == 5 and deleted_at.isnull()")600 # Merge orders_ketthuc and diemdanh_details601 df = diemdanh_details[['ketoan_id', 'giohoc']]\602 .merge(orders_ketthuc, on='ketoan_id', how='right').groupby(603 ['ketoan_id', 'ketoan_coso', 'remaining_time', 'ketoan_tientrengio', 'date_end'], as_index=False).giohoc.sum()604 # Add 2 more columns605 df['gio_con_lai'] = df.remaining_time - df.giohoc606 df['thực thu kết thúc'] = df.gio_con_lai * df.ketoan_tientrengio607 # Convert to datetime608 df['date_end'] = pd.to_datetime(df['date_end'])609 # Filter gioconlai > 0 and time610 thucthu_ketthuc = df.query("gio_con_lai > 0")\611 .query("date_end >= @ketoan_start_time and date_end <= @ketoan_end_time")612 thucthu_ketthuc = thucthu_ketthuc.merge(613 orders[['hv_id', 'ketoan_id']], on='ketoan_id')614 thucthu_ketthuc = thucthu_ketthuc.groupby("ketoan_coso", as_index=False)[615 'thực thu kết thúc'].sum()616 thucthu_ketthuc = rename_lop(thucthu_ketthuc, 'ketoan_coso')617 618 # "_______________"619 620 overtime_salary = sal_diem_over_group.query('working_status_x != "Ngoài giờ"')\621 .merge(salary[['Họ và tên', 'working_status']], left_on='Họ và tên', right_on='Họ và tên')622 overtime_salary['out_div_total'] = overtime_salary['salary_gio_cong'] / \623 (overtime_salary['salary_ngay_cong'] +624 overtime_salary['salary_gio_cong'])625 overtime_salary = overtime_salary[[626 'Họ và tên', 'salary_ngay_cong', 'salary_gio_cong', 'out_div_total']]627 overtime_salary['out_div_total'] = round(628 overtime_salary['out_div_total'] * 100, 2)629 overtime_salary_fulltime = overtime_salary.query("salary_ngay_cong != 0")630 631 # "_______________" thucthu điểm danh and chuyen phi632 thucthu_hocvien_lop = thucthu_hocvien_lop.merge(633 chuyenphi, left_on='ketoan_coso', right_on='ketoan_coso', how='left')\634 .merge(thucthu_ketthuc, left_on='ketoan_coso', right_on='ketoan_coso')635 # Renam thucthu => thuc thu diem danh636 thucthu_hocvien_lop = thucthu_hocvien_lop.rename(637 columns={'thucthu': "thực thu điểm danh"})638 # Fillna with 0639 thucthu_hocvien_lop.fillna(0, inplace=True)640 # Add all thucthu641 thucthu_hocvien_lop['tổng thực thu'] = thucthu_hocvien_lop['thực thu chuyển phí'] + \642 thucthu_hocvien_lop['thực thu kết thúc'] + \643 thucthu_hocvien_lop['thực thu điểm danh']644 # Add ty lệ thực thu645 thucthu_hocvien_lop['tỷ trọng tổng thực thu'] = thucthu_hocvien_lop['tổng thực thu'].apply(646 lambda x: round(x/thucthu_hocvien_lop['tổng thực thu'].sum()*100, 2))647 # create a new row with the sum of each numerical column648 totals = thucthu_hocvien_lop.select_dtypes(include=[float, int]).sum()649 totals["lop_cn"] = "Grand total"650 # append the new row to the dataframe651 thucthu_hocvien_lop = thucthu_hocvien_lop.append(totals, ignore_index=True)652 # Add % in tỷ trọng tổng thực thu653 thucthu_hocvien_lop["tỷ trọng tổng thực thu"] = thucthu_hocvien_lop["tỷ trọng tổng thực thu"].apply(654 lambda x: '{:.2%}'.format(x/100))655 salary_thucthu_grand_total['Tổng lương / thực thu'] = salary_thucthu_grand_total['Tổng lương / thực thu'].apply(656 lambda x: '{:.2%}'.format(x/100))657 658 # define a function659 @ st.cache660 def thousands_divider(df, col):661 df[col] = df[col].apply(662 lambda x: '{:,.0f}'.format(x))663 return df664 thucthu_hocvien_lop = thousands_divider(665 thucthu_hocvien_lop, 'tổng thực thu')666 thucthu_hocvien_lop = thousands_divider(667 thucthu_hocvien_lop, 'thực thu điểm danh')668 thucthu_hocvien_lop = thousands_divider(669 thucthu_hocvien_lop, 'thực thu chuyển phí')670 thucthu_hocvien_lop = thousands_divider(671 thucthu_hocvien_lop, 'thực thu kết thúc')672 thucthu_hocvien_lop = thucthu_hocvien_lop.set_index("lop_cn")673 674 thucthu_hocvien_lop.index.names = ['Chi nhánh']675 # Show tables676 st.plotly_chart(fig2, use_container_width=True)677 st.subheader("Thưc thu theo chi nhánh")678 st.dataframe(thucthu_hocvien_lop.drop(["ketoan_coso", "total_students", "total_classes", "thucthu_div_hocvien", "thucthu_div_lophoc"],679 axis=1).style.background_gradient().set_precision(0), use_container_width=True)680 # Show Chi nhanh by 2 columns681 st.plotly_chart(fig10, use_container_width=True)682 st.plotly_chart(fig9, use_container_width=True)683 684 # left_column, right_column = st.columns([1, 2])685 # left_column.plotly_chart(fig10, use_container_width=True)686 # right_column.plotly_chart(fig9, use_container_width=True)687 # left_column, right_column = st.columns(2)688 # left_column.plotly_chart(fig7, use_container_width=True)689 # right_column.plotly_chart(fig8, use_container_width=True)690 691 # left_column, right_column = st.columns(2)692 # left_column.plotly_chart(fig5, use_container_width=True)693 # right_column.plotly_chart(fig6, use_container_width=True)694 695 # left_column, right_column = st.columns(2)696 # left_column.plotly_chart(fig1, use_container_width=True)697 # right_column.plotly_chart(fig2, use_container_width=True)698 699 left_column, right_column = st.columns(2)700 left_column.plotly_chart(fig3, use_container_width=True)701 right_column.plotly_chart(fig3_1, use_container_width=True)702 703 ""704 705 fig4 = plotly_chart(overtime_salary_fulltime[['Họ và tên', 'out_div_total']].sort_values(706 "out_div_total", ascending=True), "Họ và tên", 'out_div_total', 'out_div_total',707 "Tỷ lệ lương ngoài giờ / trong giờ của giáo viên fulltime", '', 'Tỷ lệ')708 fig4.update_layout(height=800, width=800)709 st.plotly_chart(fig4)710 