序
项目是23年的一个练手小项目,因为环节很完好,自始至终思考了许多细节,完结后很有成就感。趁春节整理出来,尽量规范化的把整个流程整理出来,为往后开发培育习惯,希望各位多指教。
项目布景
打卡机生成固定格局的数据,每个月会保存为一个Excel文件,记载了职工打卡的时刻点。数据格局如下表:
考勤序号 | 名字 | 工号 | 所属部门 | 5-4 四 | 5-5 五 | 5-6 六 | 5-8 一 | … |
---|---|---|---|---|---|---|---|---|
1 | 张三 | 42 | A | 07:40 11:17 17:10 |
07:44 11:19 17:44 |
07:14 11:19 |
07:41 14:51 17:09 |
… |
2 | 李四 | 53 | B | 07:37 08:48 11:12 14:59 16:55 |
07:48 11:08 14:47 16:45 |
07:33 11:18 14:52 16:45 |
08:03 11:14 15:02 17:01 |
… |
需求方的打卡规矩如下:
打卡需在指定的四个时刻段内完结,同一时段打卡屡次也只记一次
1. 06:50 ~ 09:10
2. 11:10 ~ 12:30
3. 13:40 ~ 15:10
4. 16:29 ~ 18:01
要求规划程序,核算职工每月打卡次数。
需求剖析
功用性需求
- 数据读写
- Excel格局化读取,提取有用信息
- 时刻信息的存储格局及比较办法
- 打卡次数核算
- 每人每日数据核算
- 每人整月数据求和
非功用性需求
- 可用性需求
- 运行环境为Windows 7或Windows 10体系
- 面向无代码开发经验的运用者
- 操作需求
- 打卡核算时刻段可修正
- 时刻段个数可修正
规划思路
言语、环境挑选
最开端需求方计划自己配环境跑代码,鉴于功用需求比较简略,没有性能要求,挑选Python进行开发。后来对方提出Python环境装备有困难,不要求源码,所以终究用pyinstaller打包成exe交给。
相关库
- pandas:用于Excel读写、数据格局化存储,杀鸡用牛刀,但真香
- json:用于读取参数装备文件
参数读取
装备文件内容
clock_in_lists
:打卡规矩,要保证时刻段成对出现,不同时刻段不重合。每个时刻段的开端在左,结束在右,从小到大以此排列。
num_skip_cols
:跳过表格前面的列数。
in_file_name
和out_file_name
:输入数据文件名和输出数据文件名。
{
"clock_in_lists": [
"06:50",
"09:10",
"11:10",
"12:30",
"13:40",
"15:10",
"16:30",
"18:00"
],
"num_skip_cols": 4,
"in_file_name": "data.xlsx",
"out_file_name": "out.xlsx"
}
读取代码
import json as js
import pandas as pd
# 装备文件加载
f = open('config.json', 'r')
content = f.read()
root = js.loads(content)
CLOCK_IN_LISTS = root['clock_in_lists']
NUM_SKIP_COLS = root['num_skip_cols']
# 输入/输出文件名可以改,但不能包括中文
IN_FILE_NAME = root['in_file_name']
OUT_FILE_NAME = root['out_file_name']
数据存储
文件读取一行代码即可处理,需要思考的是如何处理每个Excel格子中的内容。原始数据输入后是一个字符串,时刻段之间用n间隔,即如下格局
07:40n11:56n15:06n16:38
考虑到后面有比较大小的要求,这儿首要运用n将时刻段相互隔开,再把时刻段转换为分钟数,用于后续比较,代码如下
# 时刻段分割
item = raw_data.loc[i][j]
if pd.isna(item):
continue
one_day_str = item.split('n')
# 时刻字符串 --> 分钟数
def time2nmin(time_str):
time_array = time_str.split(':')
result = int(time_array[0])*60 int(time_array[1])
return result
打卡规矩处理△
打卡规矩可以简略粗犷地用if-else
进行处理,但我猜写出来会很难看。我看到需求中说同一时段屡次打卡也只算一次
时,第一时刻想到的是桶排序的办法。四个时刻段相当于四个桶,每输入一个时刻就判别是不是在四个桶里,终究核算不为空的桶有几个。 而第二个问题是如何简化判别规矩,不用if-else
,而是将时刻的单调性和有序性充分运用。首要对四个时刻段的起止点编号,线段表述该段时刻内为合法打卡时刻段,其他空白位置如B、D、F均为不合法打卡时刻段。
合法段 | A | C | E | G |
---|---|---|---|---|
左端点编号 | 0 | 2 | 4 | 6 |
右端点编号 | 1 | 3 | 5 | 7 |
段编号 | 0 | 1 | 2 | 3 |
非法段 | B | D | F |
---|---|---|---|
左端点编号 | 1 | 3 | 5 |
右端点编号 | 2 | 4 | 6 |
观察上述表格,可以总结出两条规律 1. 合法段端点编号满意“左偶右奇”; 2. 合法段编号=左/右端点编号 // 2
至此,咱们可以规划如下判别流程:首要承认一个输入时刻地点的时刻段,然后判别该时刻段的左右端点为奇数或偶数,比方左端点是奇数,则非法;左端点是偶数,则判别该端点的段编号,给相应的“桶”增加记载。 编写代码如下
# 打卡判别
# input:
# clock_in_nmins: 打卡规矩
# tic_time: 刷卡时刻
# tags: 当日打卡标签
def check_tic(clock_in_nmins, tic_time, tags):
for i in range(0, len(clock_in_nmins)):
# 遍历打卡时刻点
if tic_time > clock_in_nmins[i]:
continue
else:
if i % 2 == 1:
tags[i // 2] = 1
break
调试进程
鸿沟检验
规划进程中的思路很清晰,可是实现进程中还要检验鸿沟条件,比方小于最小的时刻和大于最大时刻的情况能否处理?卡点06:50的打卡,是否核算在内?第一个问题通过验证发现当时逻辑即可处理,第二个问题则需要加以讨论。当时的代码逻辑是先遍历找到右端点,然后检验其他内容。找右端点时,在输入时刻≤某时刻点时才停下,这种逻辑本质上是把数轴分割成了一系列左开右闭的区间(解释思路源于代码随想录的一期视频)。可是需求方的打卡分割方式其实是四个合法段是闭区间,其他非法段是开区间,可以凭借下图了解。
处理的方案便是在加载打卡规矩时,将左端点的时刻-1,这样尽管仍是左开右闭的分割,但实现了合法段构成闭区间的要求。
clock_in_nmins = []
clock_in_list_len = len(clock_in_lists)
# 此处的减1与打卡核算算法鸿沟条件有关
for i in range(0,clock_in_list_len):
if i % 2 == 0:
clock_in_nmins.append(time2nmin(clock_in_lists[i]) - 1)
else:
clock_in_nmins.append(time2nmin(clock_in_lists[i]))
其他问题
调试中还遇到一个由于言语特性导致的问题,第一个版本的check_tic()
函数中的continue
是i = i 1
。这个写法假如见效,逻辑上其实也存在问题,可是这个写法在Python中实践并无作用。对
i值进行的修正只能在该轮循环中有用,下一循环i会被for句子指定的下标变化规矩重置。其实原因从语义上也好了解,for i in range(0,n)
便是让i遍历去取range中的值的,根本不存在 1的逻辑,也无从保留这种修正。
终究作用
运用pyinstaller打包成exe,和输入文件、装备文件放在同一文件夹下,双击运行即可。终究输出作用如图
交给内容也非常简略,exe和文档
.
|-- config.json
|-- data.xlsx
|-- exe运用说明.docx
|-- record.exe
`-- out.xlsx
复盘&改善
开发进程中应该在最开端对中心功用构建单元测试,可是仍是print调试法,往后有待改善。