钉钉考勤报表--工时统计小程序

本文介绍了如何使用Python处理钉钉考勤报表,统计每个员工的工时。通过读取和分析钉钉导出的原始Excel表格,结合公司的不同班次规则,实现了自动计算工时并生成新的Excel报表。最后,将Python脚本打包成EXE,方便Windows用户使用。

# 起因:

老婆大人:今天弄一天的公司的考勤报表,看的我眼睛都花了。这工作太难了,老公,我不想干了。。。

我:说说

老婆大人:公司用的钉钉,一个月会导出一份excel报表,让我统计每个人投入工时。你是不知道,我们公司是灵活上下班,而且有的不同的班次,更残忍的是大家打卡次数可以是多次,,,我现在挨个员工去掰着手指去算:是不是迟到、迟到几分钟、是不是早退、早退几分钟、加班多久。。。弄的我眼睛都花了。。。

我:要不我看看

。。。 a few minite later

我:你想要什么样的

老婆大人:这个是我辛辛苦苦整理的,要这样的

我:好的

# 需求分析:

目标:客户要统计每个员工当月的投入工时。

## 背景:

1.公司是灵活上下班

    09:00-18:00

    10:00-19:00

2.特殊班次

     夜班:16:00-次日上班前

     保洁:06:00-08:00(8点前打扫完即可走)

3.钉钉可打卡多次,每次都会记录到导出的表格

## 资料

   原始表格

   从钉钉导出的原始报表,命名格式如:XXXX公司_考勤报表_20200801-20200831.xlsx

  目标表格

# 技术选型

 由于是简单的表格处理,属于工具型,因此选择python实现。同时考虑到易用性和用户使用场景是在windows电脑端,最后将python工具转换成EXE。

# 实施

。。。

# 测试验收

用户非常满意

# 交付

https://download.csdn.net/download/xdstuhq/12852539

2023-3-20 补充

好久不发文章了,偶然点开自己的旧文章,竟然发现“我这样一个白嫖党”却要求别人用积分下载。。。顿感羞愧!!!

随补充源码,欢迎技术交流,批评指正,先致谢!

注:运行源码需要“python 操作excel”相关依赖,GitHub上就有,请提前就绪

# -*- coding: utf-8 -*-

import os
import threading
import time
import tkinter as tk
import xlrd
import xlwt
from datetime import datetime
from tkinter import Tk, Label, StringVar, Entry, Button, mainloop, W,filedialog,ttk,LEFT

date_pre = r'2016-12-21 '
date_pre_next = r'2016-12-22 '
date_suf = r':00'
chi_dao = r'迟到'
zao_tui = r'早退'
da_ka_yi_chang = r'打卡异常'
work_time_1 = r'09:00'
work_time_2 = r'10:00'
off_work_time_1 = r'18:00'
off_work_time_2 = r'19:00'
#界面相关
IN_FILE = ''
FILE_NAME = ''
FILE_BASE_DIR = ''
PROGRESS_VALUE = 5
root = Tk()

# monitoring thread
class MyThread(threading.Thread):
    def __init__(self, value):
        super(MyThread, self).__init__()
        self.value = value

    def run(self):
        root = Tk()
        Label(root, text='进度:').grid(row=1, column=0)
        p1 = ttk.Progressbar(root, length=200, mode="determinate",
                             maximum=200,orient=tk.HORIZONTAL)
        p1.grid(row=1,column=1)
        while(True):
            global PROGRESS_VALUE
            p1["value"] = PROGRESS_VALUE
            root.update()
            if PROGRESS_VALUE >= 200:
                break
            time.sleep(0.5)

        root.mainloop()


def showProgress():
    #start a new thread for monitoring
    t = MyThread(PROGRESS_VALUE)
    t.start()
# check
def checkFileAndInit(inFile):
    if not os.path.exists(inFile) or not os.path.isfile(inFile):
        print("文件不存在")
        exit(-1)
    global FILE_NAME, FILE_BASE_DIR
    FILE_NAME = os.path.basename(os.path.abspath(inFile))
    FILE_BASE_DIR = os.path.dirname(os.path.abspath(inFile))

def getFirstTime(value):
    if not value or len(value)<1:
        return chi_dao
    time_list = value.strip(' ').split('\n', 1)
    new_value = time_list[0]
    new_value = new_value.strip(' ').strip(r'外勤')
    # 特殊处理保洁
    if new_value <= work_time_1 and getLastTime(value) > work_time_2:
        return work_time_1
    elif new_value <= work_time_2 < getLastTime(value):
        return work_time_2
    else:
        return new_value


def getLastTime(value):
    if not value or len(value)<1:
        return zao_tui
    time_list = value.strip(' ').rsplit('\n', 1)
    if not time_list or len(time_list) < 2:
        return zao_tui
    new_value = time_list[1]
    print(new_value.strip(' ').strip(r'外勤'))

    return new_value.strip(' ').strip(r'外勤')


def getWorkTime(first_time):
    return date_pre + first_time + date_suf


def getOffWorkTime(first_tiem, last_time):
    # 特殊处理保洁
    if first_tiem > r'15:00' and r'00:00' < last_time < r'10:00':
        return date_pre_next + last_time + date_suf
    return date_pre + last_time + date_suf


def getHourFromSecond(second):
    return int(second*10)/10
    pass


def read_excel(file_name):
    # 打开文件
    workbook = xlrd.open_workbook(file_name)
    # 获取所有sheet
    print(workbook.sheet_names()) # [u'sheet1', u'sheet2']
    #新建excel
    data=xlwt.Workbook()
    #新建表
    des_table=data.add_sheet(r'打卡统计', cell_overwrite_ok=True)
    # 根据sheet索引或者名称获取sheet内容
    src_sheet = workbook.sheet_by_name('打卡时间')

    for row_num in range(src_sheet.nrows):
        for col_num in range(src_sheet.ncols):
            global PROGRESS_VALUE
            PROGRESS_VALUE = int((row_num + 1) * 100 / src_sheet.nrows) * 2

            if row_num < 3 or col_num < 6:
                des_table.write(row_num, col_num, src_sheet.cell(row_num, col_num).value)
                continue
            else:
                print(src_sheet.cell(row_num, col_num).value)
                if src_sheet.cell(row_num, col_num).value:
                    value = src_sheet.cell(row_num, col_num).value
                    first_time = getFirstTime(value)
                    last_time = getLastTime(value)
                    print(row_num, col_num, first_time, last_time)
                    if first_time == chi_dao and src_sheet.cell(2, col_num).value != r'六' and src_sheet.cell(2, col_num).value != r'日' :
                        des_table.write(row_num, col_num, src_sheet.cell(row_num, col_num).value + '\n' + chi_dao)
                        continue
                    if last_time == zao_tui and src_sheet.cell(2, col_num).value != r'六' and src_sheet.cell(2, col_num).value != r'日':
                        des_table.write(row_num, col_num, src_sheet.cell(row_num, col_num).value + '\n' + zao_tui)
                        continue

                    if first_time == chi_dao or last_time == zao_tui:
                        # des_table.write(row_num, col_num, da_ka_yi_chang)
                        continue

                    if row_num==37:
                        print(first_time, last_time)
                    work_time = getWorkTime(first_time)
                    off_work_time = getOffWorkTime(first_time, last_time)
                    t1 = datetime.strptime(work_time, "%Y-%m-%d %H:%M:%S")
                    t2 = datetime.strptime(off_work_time, "%Y-%m-%d %H:%M:%S")
                    print((t2 - t1).seconds/3600)
                    des_table.write(row_num, col_num, getHourFromSecond((t2 - t1).seconds/3600))
                # elif src_sheet.cell(2, col_num).value != r'六' and src_sheet.cell(2, col_num).value != r'日':
                    # des_table.write(row_num, col_num, da_ka_yi_chang)

    saveExcel(data)


def createNewExcel(sheet_name):
    #新建excel
    data=xlwt.Workbook()
    #新建表
    table=data.add_sheet(sheet_name, cell_overwrite_ok=True)
    return table

def saveExcel(data):
    data.save(FILE_BASE_DIR + '/工时统计.xls')

#gui
def inputParams():
    global root
    Label(root, text='Excel路径:').grid(row=0, column=0)  # 对Label内容进行 表格式 布局

    v1 = StringVar()  # 设置变量 .

    e1 = Entry(root, textvariable=v1)  # 用于储存 输入的内容

    e1.grid(row=0, column=1, padx=10, pady=5)  # 进行表格式布局 .


    def selectPath(path):
        def wrapper(path=path):
            #选择文件path_接收文件地址
            path_select = tk.filedialog.askopenfilename()
            
            #通过replace函数替换绝对文件地址中的/来使文件可被程序读取 
            #注意:\\转义后为\,所以\\\\转义后为\\
            path_=path_select.replace("/","\\\\")
            #path设置path_的值
            path.set(path_select)
        return wrapper

    def initInputParams():
        global IN_FILE
        IN_FILE = e1.get()
        print("图片路径: %s" % e1.get())  # get 变量内容
        global root
        root.destroy()
        start_time = int(time.time())
        justDoIt()
        end_time = int(time.time())
        print('总耗时:%s秒' % str(end_time - start_time))
        exit(0)

    #Button中command不能直接传参,需对函数进行特殊处理。详见selectPath(path)函数实现
    Button(root, text = "路径选择", width=10, command = selectPath(v1)).grid(row = 0, column = 2, sticky=W, padx=15, pady=5)
   
    Button(root, text='芝麻开门', width=10, command=initInputParams).grid(row=4, column=1, sticky=W, padx=15, pady=5)  # 设置 button 指定 宽度 , 并且 关联 函数 , 使用表格式布局 .
    # Button(root, text='退出', width=10, command=root.quit).grid(row=3, column=1, sticky=E, padx=10, pady=5)

    
    mainloop()

def justDoIt():
    # 参数校验和初始化
    checkFileAndInit(IN_FILE)
    # 显示进度
    showProgress()
    # 执行操作
    read_excel(IN_FILE)

def getAbsPath(*arg):
    path = ""
    for str in arg:
        path = os.path.abspath(os.path.join(path, str))
    return path

if __name__ == '__main__':
    inputParams()

个人公众号:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值