设置单元格颜色
cell_obj = sheet[pos]
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFEE0808', end_color='FFEE0808',
fill_type='solid')
设置单元格批注
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
详细代码如下
import openpyxl
import pandas as pd
import re
from datetime import datetime
from datetime import datetime, timedelta
from openpyxl.styles import colors, Font
import os
def pre_daka_addr(excel_daka):
"""
对打卡地点数据做预处理
:param excel_daka:
:return:
"""
df = pd.read_excel(excel_daka, skiprows=1)
df = pd.DataFrame(df, columns=["Unnamed: 0", "日期", "上班 1 打卡地点", "下班 1 打卡地点"])
df = df.rename(columns={"Unnamed: 0": "姓名"})
df["是否出差"] = ""
df["是否出差"] = df.apply(lambda x: compute_weather_trip(x["上班 1 打卡地点"], x["下班 1 打卡地点"]), axis=1)
return df
def compute_weather_trip(a, b):
"""
根据打卡地点判断是否在出差
:param a: 上班打卡地点
:param b: 下班打卡地点
:return:
"""
addr_a = a[0:6]
addr_b = b[0:6]
if addr_a == "-" or addr_b == "-":
return "其他"
elif (addr_a not in ["菁蓉国际广场", "四川省成都市"]) and (addr_b not in ["菁蓉国际广场", "四川省成都市"]):
return "是"
else:
return "否"
def add_chuchai_comment(df, excel_output_path):
"""
核对出差数据
:param df: 打卡地点预处理后的数据
:param excel_output_path: 汇总统计表
:return:
"""
df = pre_daka_addr(excel_daka)
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
if row_index == 1:
continue
for cell in row:
cell_index += 1
if cell_index == 1:
continue
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
row_title = sheet["A" + str(row_index)].value
column_title = sheet[char + "1"].value
nickname = row_title
dates = column_title.split(" ")[0]
if (df.query("`姓名`==@nickname and `日期`==@dates").shape[0] == 1) and (
df.query("`姓名`==@nickname and `日期`==@dates").values[0][4] == "是"):
if cell_obj.value != "出差" and cell_obj.value != "休":
cell_obj.value = "出差"
workbook.save(excel_output_path)
workbook.close()
def add_daily_comment(excel_ribao,excel_output_path):
"""
添加日报批注
:param df: 日报预处理后的数据
:param excel_output_path: 汇总统计表
:return:
"""
df = pd.read_excel(excel_ribao)
df = pd.DataFrame(df, columns=["姓名", "提交时间"])
df["提交时间"] = pd.to_datetime(df["提交时间"])
df["提交时间"] = df["提交时间"].apply(compute_daily_time)
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
if row_index==1:
continue
for cell in row:
cell_index += 1
if cell_index==1:
continue
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
row_title = sheet["A" + str(row_index)].value
column_title = sheet[char + "1"].value
nickname=row_title
dates=column_title.split(" ")[0]
weekend=column_title.split(" ")[1]
if weekend=="星期六" or weekend=="星期日":
continue
if cell_obj.value=="休" or cell_obj.value=="年假" or cell_obj.value=="调休假" or cell_obj.value=="病假" or cell_obj.value=="事假" or cell_obj.value=="婚假" or cell_obj.value=="产假" or cell_obj.value=="陪产假" or cell_obj.value=="丧假" or cell_obj.value=="哺乳假":
continue
if df.query("`姓名`==@nickname and `提交时间`==@dates").shape[0] ==0:
old_value=cell_obj.value
cell_obj.value=old_value+"\n"+"未提交"
cell_obj.font=Font(color="FF0000")
workbook.save(excel_output_path)
workbook.close()
def compute_daily_time(now_time):
"""
计算日报实际日期
:param now_time: 下载的日报填写日期
:return:
"""
date = str(now_time).split(" ")[0]
first_time = date + " " + "17:00:00"
second_time = add_one_day(date, 1) + " " + "10:00:00"
first_time_strp = datetime.strptime(first_time, "%Y-%m-%d %H:%M:%S")
sencond_time_strp = datetime.strptime(second_time, "%Y-%m-%d %H:%M:%S")
yes_date = add_one_day(date, -1)
yes_start_time = yes_date + " " + "17:00:00"
yes_end_time = date + " " + "10:00:00"
yes_start_time_strp = datetime.strptime(yes_start_time, "%Y-%m-%d %H:%M:%S")
yes_end_time_strp = datetime.strptime(yes_end_time, "%Y-%m-%d %H:%M:%S")
daily_time = ""
if first_time_strp < now_time < sencond_time_strp:
daily_time = date
return daily_time
elif yes_start_time_strp < now_time < yes_end_time_strp:
daily_time = yes_date
return daily_time
return "不是上月日报"
def add_one_day(date_str, num):
date = datetime.strptime(date_str, "%Y-%m-%d")
next_day = date + timedelta(days=num)
next_day_str = next_day.strftime("%Y-%m-%d")
return next_day_str
def double_time_reduce(start, end):
"""
两个时间相减
:param start:
:param end:
:return:
"""
time_format = "%H:%M"
dt1 = datetime.strptime(start, time_format)
dt2 = datetime.strptime(end, time_format)
time_difference = dt2 - dt1
hours_difference = time_difference.total_seconds() / 3600
return hours_difference
def change_excel(excel_download_path):
"""
统计研发部工作日出勤数据---判断是否加班
:param excel_download_path:
:return:
"""
data_records = pd.read_excel(excel_download_path, skiprows=1)
data_records = data_records[data_records["部门"] == "技术一部"]
data_records = data_records[data_records["Unnamed: 0"] != "阿灿"]
data_records = data_records.to_dict(orient="records")
new_data_list = []
for row_data in data_records:
new_row_data = {"姓名": row_data["Unnamed: 0"]}
pattern = r'^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$'
for key, value in row_data.items():
if bool(re.match(pattern, key[:10])):
new_row_data[key] = value
new_data_list.append(new_row_data)
for row_data in new_data_list:
for key, value in row_data.items():
if key == "姓名":
continue
time_pattern = r'\b(\d{2}:\d{2})\b'
arr = re.findall(time_pattern, value)
print("date", arr)
if len(arr) != 2:
row_data[key] = "无"
else:
start = arr[0]
end = arr[1]
result = double_time_reduce(start, end) - 10
row_data[key] = "加班一次" if result >= 0 else "无"
df = pd.DataFrame.from_records(new_data_list)
results = []
for index, row in df.iterrows():
for col in df.columns[1:]:
if row[col] == "加班一次":
results.append([row["姓名"], col])
return results
def weekday_overtime(excel_download_path, excel_output_path):
"""
统计研发部工作日加班情况
:param excel_download_path: 导出的考勤表
:param excel_output_path: 输出的汇总表
:return:
"""
dt = change_excel(excel_download_path)
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
for arr in dt:
nickname = arr[0]
dates = arr[1]
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
for cell in row:
cell_index += 1
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
row_title = sheet["A" + str(row_index)].value
index_title = sheet[char + "1"].value
if row_title == nickname and index_title == dates:
print(cell_obj.value)
if (cell_obj.fill.start_color != "FF2d529f"):
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FF2d529f', end_color='FF2d529f',
fill_type='solid')
if "加班" not in cell_obj.value:
cell_obj.value = "加班"
if not cell_obj.comment:
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if cell_obj.comment:
current_comment = cell_obj.comment.text
content = "加班一次"
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
workbook.save(excel_output_path)
workbook.close()
def add_jiaban_comment(excel_overtime, excel_output_path):
"""
添加非工作日加班批注
:param excel_overtime:
:param excel_output_path:
:return:
"""
df = pd.read_excel(excel_overtime, skiprows=1)
df = df[df["申请状态"] == "已同意"]
df = df[df["发起人部门"] == "研发中心/技术一部"]
df=df[df["发起人姓名"] !="阿灿"]
df_cus = pd.DataFrame(df, columns=["发起人姓名", "申请状态", "开始时间", "结束时间", "时长", "发起人部门"])
df_cus['开始时间'] = df_cus['开始时间'].str.split(' ').str[0]
df_cus['结束时间'] = df_cus['结束时间'].str.split(' ').str[0]
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
dt = df_cus.values
for arr in dt:
nickname = arr[0]
dates = reverseDate(arr[2])
overtime=str(arr[4])+"小时"
start_date=arr[2]
end_date=arr[3]
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
for cell in row:
cell_index += 1
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
row_title = sheet["A" + str(row_index)].value
index_title = sheet[char + "1"].value
if row_title == nickname and index_title == dates:
print(cell_obj.value)
if(cell_obj.fill.start_color != "FF2d529f"):
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FF2d529f', end_color='FF2d529f',
fill_type='solid')
if start_date !=end_date:
char_new = dec_to_alphanumeric(cell_index+1)
pos_new = f"{char_new}{row_index}"
cell_obj_new = sheet[pos_new]
if (cell_obj_new.fill.start_color != "FF2d529f"):
cell_obj_new.fill = openpyxl.styles.PatternFill(start_color='FF2d529f', end_color='FF2d529f',
fill_type='solid')
if "加班" not in cell_obj_new.value:
cell_obj_new.value = "加班"
if "加班" not in cell_obj.value :
cell_obj.value = "加班"
if not cell_obj.comment:
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if cell_obj.comment:
current_comment = cell_obj.comment.text
content=overtime
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
workbook.save(excel_output_path)
workbook.close()
def add_holidays_comment(excel):
df = pd.read_excel(excel, skiprows=1)
df = df[df["假期类型"] == "事假"]
df_cus = pd.DataFrame(df, columns=["发起人姓名", "假期类型", "开始时间", "结束时间", "时长"])
df_cus['开始时间'] = df_cus['开始时间'].str.split(' ').str[0]
return df_cus
def add_buka_comment(excel_buka, excel):
"""
添加补卡批注
:param excel_buka:
:param excel: 汇总表
:return:
"""
df_buka = pd.read_excel(excel_buka, skiprows=1, index_col=0)
df_buka_cus = pd.DataFrame(df_buka, columns=["发起人姓名", "异常日期", "异常记录"])
df_buka_cus['异常记录'] = df_buka_cus['异常记录'].str.extract(r'(应上班|应下班)', expand=False)
print(df_buka_cus)
workbook = openpyxl.load_workbook(excel)
sheet = workbook.active
dt = df_buka_cus.values
for arr in dt:
print(arr)
nickname = arr[0]
dates = reverseDate(arr[1])
if type(arr[2])==float:
continue
work_type = arr[2].replace("应", "")
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
for cell in row:
cell_index += 1
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
row_title = sheet["A" + str(row_index)].value
index_title = sheet[char + "1"].value
if row_title == nickname and index_title == dates:
print(cell_obj.value)
if (cell_obj.fill.start_color != "FFCCFFFF"):
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFFFFF00', end_color='FFFFFF00',
fill_type='solid')
print(cell_obj.value)
if ("上班缺卡" in cell_obj.value) or ("下班缺卡" in cell_obj.value):
if (work_type + "缺卡") not in cell_obj.value:
cell_obj.value = cell_obj.value + work_type + "缺卡"
else:
cell_obj.value = ""
cell_obj.value = work_type + "缺卡"
if not cell_obj.comment:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFFFFF00', end_color='FFFFFF00',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if cell_obj.comment:
current_comment = cell_obj.comment.text
content = work_type + "已补卡"
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
workbook.save(excel_output_path)
workbook.close()
def converDate(date_str):
"""
转化日期格式
2024-06-10 星期一 =====>2024年06月10日
:return:
"""
date_part = re.findall(r'\d{4}-\d{2}-\d{2}', date_str)[0]
date_obj = datetime.strptime(date_part, '%Y-%m-%d')
formatted_date = date_obj.strftime('%Y年%m月%d日')
return formatted_date
def reverseDate(date_str):
"""
转化日期格式
2024年06月10日=====>2024-06-10 星期一
:param date_str:
:return:
"""
date_obj = datetime.strptime(date_str, '%Y年%m月%d日')
formatted_date = date_obj.strftime('%Y-%m-%d')
week_day = date_obj.strftime('%A')
week_days = {
'Monday': '星期一',
'Tuesday': '星期二',
'Wednesday': '星期三',
'Thursday': '星期四',
'Friday': '星期五',
'Saturday': '星期六',
'Sunday': '星期日'
}
week_day_chinese = week_days[week_day]
formatted_date_with_weekday = f"{formatted_date} {week_day_chinese}"
return formatted_date_with_weekday
def dec_to_alphanumeric(number):
"""
数字转字母:
1 - A
:param number: 需要被转换的数字
:return: 转换后的字母
"""
base26 = []
while number > 0:
number, remainder = divmod(number, 26)
if remainder == 0:
base26.append('Z')
number -= 1
else:
base26.append(chr(remainder - 1 + 65))
base26 = base26[::-1]
return ''.join(base26)
def add_comment(excel_download_path,excel_output_path,excel_buka):
"""
添加迟到 缺卡 事假批注
:param excel_download_path: 下载的考勤表
:param excel_output_path: 生成的模板表
:return:
"""
df_buka = pd.read_excel(excel_buka, skiprows=1, index_col=0)
df_buka_cus = pd.DataFrame(df_buka, columns=["发起人姓名", "异常日期"])
df_chidao = pd.read_excel(excel_download_path, skiprows=1, index_col=0)
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
for cell in row:
cell_index += 1
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
if "迟到" in cell:
nickname = sheet["A" + str(row_index)].value
dates = sheet[char + str(1)].value
cell_value = df_chidao.loc[nickname, dates]
matches = re.findall(r'\d+分钟', cell_value)
result = matches[0]
matches = re.findall(r"\d+", result)
result = int(matches[0]) - 30
content = "迟到" + str(result) + "分钟"
if cell_obj.comment:
current_comment = cell_obj.comment.text
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
if "缺卡" in cell:
nickname = sheet["A" + str(row_index)].value
dates = sheet[char + str(1)].value
dates = converDate(dates)
arr = df_buka_cus.query("`发起人姓名`==@nickname")["异常日期"].values
if dates in arr:
if not cell_obj.comment:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFFFFF00', end_color='FFFFFF00',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if cell_obj.comment:
current_comment = cell_obj.comment.text
content = "已补卡"
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
if "事假" in cell:
nickname = sheet["A" + str(row_index)].value
dates = sheet[char + str(1)].value
dates = converDate(dates)
df_holiday = add_holidays_comment(excel_holiday)
df_holiday_cus = df_holiday.query("`发起人姓名`==@nickname and `开始时间`==@dates")
duration = df_holiday_cus["时长"].values[0]
if duration:
if int(duration <= 4):
content = "事假半天"
if cell_obj.comment:
current_comment = cell_obj.comment.text
if content not in current_comment:
additional_text = "\n" + content
new_comment_text = current_comment + additional_text
cell_obj.comment.text = new_comment_text
workbook.save(excel_output_path)
workbook.close()
def generate_basic_excel(excel_download_path, excel_output_path):
data_records = pd.read_excel(excel_download_path, skiprows=1).to_dict(orient="records")
new_data_list = []
for row_data in data_records:
new_row_data = {"姓名": row_data["Unnamed: 0"]}
pattern = r'^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$'
for key, value in row_data.items():
if bool(re.match(pattern, key[:10])):
new_row_data[key] = value
new_data_list.append(new_row_data)
for row_data in new_data_list:
for key, value in row_data.items():
if key == "姓名":
continue
part_1 = value.split(";")[0]
morning = part_1.split(",")[0]
evening = part_1.split(",")[1] if len(part_1.split(",")) == 2 else ""
part_2 = value.split(";")[1] if len(value.split(";")) == 2 else ""
if "休息" in value:
row_data[key] = "休"
continue
elif "-" == value:
row_data[key] = ""
continue
elif "病假" in part_2:
row_data[key] = "病假"
continue
elif "事假" in part_2:
row_data[key] = "事假"
continue
elif "年假" in part_2:
row_data[key] = "年假"
continue
elif "调休假" in part_2:
row_data[key] = "调休假"
continue
elif "缺卡" in part_1 or "迟到" in part_1 or "早退" in part_1:
row_data[key] = ""
if "迟到" in morning:
row_data[key] += "迟到,"
if "缺卡" in morning:
row_data[key] += "上班缺卡,"
if "缺卡" in evening:
row_data[key] += "下班缺卡,"
if "早退" in evening:
row_data[key] += "早退,"
continue
elif "出差" in part_2:
row_data[key] = "出差"
continue
elif "正常" or "外勤" or "入职日" in morning and "正常" or "外勤" or "入职日" in evening:
row_data[key] = "打卡"
continue
pd.DataFrame.from_records(new_data_list).to_excel(excel_output_path, index=False)
workbook = openpyxl.load_workbook(excel_output_path)
sheet = workbook.active
row_index = 0
for row in sheet.iter_rows(values_only=True):
row_index += 1
cell_index = 0
for cell in row:
cell_index += 1
if not cell:
continue
char = dec_to_alphanumeric(cell_index)
pos = f"{char}{row_index}"
cell_obj = sheet[pos]
if "迟到" in cell:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFEE0808', end_color='FFEE0808',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if "缺卡" in cell:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFFFFF00', end_color='FFFFFF00',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if "早退" in cell:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFFFFF00', end_color='FFFFFF00',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if "假" in cell:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFF4AF85', end_color='FFF4AF85',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
if "出差" in cell:
cell_obj.fill = openpyxl.styles.PatternFill(start_color='FFCCFFFF', end_color='FFCCFFFF',
fill_type='solid')
cell_obj.comment = openpyxl.comments.Comment("批注", "Comment Author")
workbook.save(excel_output_path)
workbook.close()
if __name__ == "__main__":
excel_download_path=r"D:\飞书下载表格\导出考勤表2024-07-01.xlsx"
excel_output_path = r"D:\飞书下载表格\汇总统计.xlsx"
excel_buka = r"D:\飞书下载表格\补卡2024-07-01.xlsx"
excel_holiday = r"D:\飞书下载表格\请假2024-07-01.xlsx"
excel_overtime = r"D:\飞书下载表格\加班申请2024-07-01.xlsx"
excel_ribao = r"D:\飞书下载表格\日报2024-07-01.xlsx"
excel_daka = r"D:\飞书下载表格\打卡地点2024-07-01.xlsx"
generate_basic_excel(excel_download_path,excel_output_path)
if os.path.exists(excel_daka):
df=pre_daka_addr(excel_daka)
add_chuchai_comment(df, excel_output_path)
if os.path.exists(excel_buka):
add_comment(excel_download_path, excel_output_path,excel_buka)
add_buka_comment(excel_buka, excel_output_path)