目录
需求分析
数据模型
打卡功能
数据维护
考勤报表
系统设计
环境配置
功能结构
业务流程
项目结构
文件系统设计
数据实体模块设计
工具模块设计
公共工具模块
IO流模块
摄像头工具模块
服务模块设计
人事服务模块
人脸识别服务模块
程序入口设计
具体功能可以解释为员工的增改删查4种操作,删除员工信息前需要验证权限,确认删除员工信息后同时要删除员工的打卡记录和文件照片。
考勤标准采用上班打卡和下班打卡进行判断,未在正常时间段内打卡属于打卡异常,包括迟到、早退和缺席,具体情况定义为:
全勤:上班时间前打卡且下班时间后打卡
迟到:上班时间前未打卡,在上班时间后至中午12点前打卡
早退:下班时间后未打卡,在中午12点后至下班时间前打卡
缺席:员工当天无打卡记录
python=3.10,opencv=4.7.0.72,numpy=1.21.5,pycharm=2021.3.2,windows10
文件路径 | 文件名 | 说明 |
---|---|---|
/data/ | 保存所有员工信息的文件 | |
/data/ | 保存所有员工打卡记录的文件 | |
/data/ | 保存上下班时间 | |
/data/ | 保存管理员账号和密码 | |
/data/face/ | *.png | 员工照片(包括默认图像) |
员工数据保存格式如下:
以字符串的形式保存内容:
{'张三': ['2021-03-01 08:33:17'], '李四': ['2021-03-01 08:28:49', '2021-03-01 17:17:46'], '王五': ['2021-03-01 08:44:40', '2021-03-01 17:38:19', '2021-03-02 08:14:33'], '赵六': ['2021-03-01 08:54:12']}
以字符串的形式保存数据,格式如下:
以字符串的形式保存数据:
/data/face/文件夹下保存的是所有员工的照片,格式为png,照片大小为,每名员工保存3张照片,以及2个默认的图像文件,文件名为1000000000.png和2000000000.png。
class Employee:def __init__(self, id, name, code):self.name = name # 员工编号self.id = id # 员工姓名de = code # 员工的特征码
LOCK_RECORD = dict() # 打卡记录字典,格式为{姓名:[时间1,时间2]}
EMPLOYEES = list() # 全体员工列表
MAX_ID = 0 # 目前可用的最大ID
CODE_LEN = 6 # 特征码的默认长度
WORK_TIME = "" # 上班时间
CLOSING_TIME = "" # 工下班时间
USERS = dict() # 管理员账号密码
# 添加新员工
def add(e: Employee):EMPLOYEES.append(e)# 删除指定ID的员工记录
def remove(id):for emp in EMPLOYEES:if str(id) == str(emp.id):ve(emp) # 员工列表中删除员工if emp.name in LOCK_RECORD.keys(): # 如果存在该员工的打卡记录del LOCK_RECORD[emp.name] # 删除该员工的打卡记录break
# 获取新员工的ID
def get_new_id():global MAX_ID # 调用全局变量MAX_ID += 1 # 当前最大的ID + 1return MAX_ID
工具模块一共包含3个文件:public_tools.py(公共工具模块),io_tools.py(IO流模块),camera.py(摄像头工具模块)。
公共工具模块主要包含以下功能:
import random
import datetime
from entity import organizations as o
# 随机生成长度为len的数字
def randomNumber(len):first = str(random.randint(1, 9)) # 第一位取非0数last = "".join(random.sample("1234567890", len - 1)) # 后几位随机拼接任意数字return first + last# 随机生成与特征码长度相等的数字
def randomCode():return randomNumber(o.CODE_LEN) # 特征码的长度
# 校验时间格式
def valid_time(str):try:datetime.datetime.strptime(str, "%H:%M:%S")return Trueexcept:return False# 校验年月格式
def valid_year_month(str):try:datetime.datetime.strptime(str, "%Y-%m")return Trueexcept:return False# 校验日期格式
def valid_date(date):try:datetime.datetime.strptime(date, "%Y-%m-%d")return Trueexcept:return False
IO流模块主要包含以下功能:
from service import hr_service as hr
from entity import organizations as o
from service import recognize_service as rs
import os
import cv2
import numpy as np
PATH = os.getcwd() + "\data\" # 数据文件夹根目录
PIC_PATH = PATH + "faces\" # 照片文件夹
DATA_FILE = PATH + "" # 员工信息文件
WORK_TIME = PATH + "" # 上下班时间配置文件
USER_PASSWORD = PATH + "" # 管理员账号密码文件
RECORD_FILE = PATH + "" # 打卡记录文件
IMG_WIDTH = 640 # 图像的统一宽度
IMG_HEIGHT = 480 # 图像的统一高度
# 自检,检查默认文件缺失
def checking_data_files():if not ists(PATH):os.mkdir(PATH)print("数据文件夹丢失,已重新创建:" + PATH)if not ists(PIC_PATH):os.mkdir(PIC_PATH)print("照片文件夹丢失,已重新创建:" + PIC_PATH)sample1 = PIC_PATH + "1000000000.png" # 样本1文件路径if not ists(sample1):sample_img_1 = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), np.uint8) # 创建一个空内容图像sample_img_1[:, :, 0] = 255 # 改为纯蓝图像cv2.imwrite(sample1, sample_img_1) # 保存此图像print("默认样本1已补充")sample2 = PIC_PATH + "2000000000.png" # 样本2文件路径if not ists(sample2):sample_img_2 = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), np.uint8) # 创建一个空内容图像sample_img_2[:, :, 1] = 255 # 改为纯蓝图像cv2.imwrite(sample2, sample_img_2) # 保存此图像print("默认样本2已补充")if not ists(DATA_FILE):open(DATA_FILE, "a+") # 附加读写方式打开文件,达到创建空文件目的print("员工信息文件丢失,已重新创建:" + DATA_FILE)if not ists(RECORD_FILE):open(RECORD_FILE, "a+") # 附加读写方式打开文件,达到创建空文件目的print("打卡记录文件丢失,已重新创建:" + RECORD_FILE)if not ists(USER_PASSWORD):file = open(USER_PASSWORD, "a+", encoding="utf-8") # 附加读写方式打开文件,达到创建空文件目的user = dict()user["mr"] = "mrsoft"file.write(str(user)) # 将默认管理员账号密码写入到文件中file.close() # 关闭文件print("管理员账号密码文件丢失,已重新创建:" + RECORD_FILE)if not ists(WORK_TIME):file = open(WORK_TIME, "a+", encoding="utf-8") # 附加读写方式打开文件,达到创建空文件目的file.write("09:00:00/17:00:00") # 将默认时间写入到文件中file.close() # 关闭文件print("上下班时间配置文件丢失,已重新创建:" + RECORD_FILE)
# 加载全部员工信息
def load_employee_info():max_id = 1 # 最大员工IDfile = open(DATA_FILE, "r", encoding="utf-8") # 打开文件,只读for line adlines(): # 遍历文件中的行内容id, name, code = line.rstrip().split(",") # 去除换行符,并分割字符串信息o.add(o.Employee(id, name, code)) # 组织结构中添加员工信息if int(id) > max_id: # 如果发现某员工的id更大max_id = int(id) # 修改最大IDo.MAX_ID = max_id # 记录最大IDfile.close() # 关闭文件# 加载所有打卡记录
def load_lock_record():file = open(RECORD_FILE, "r", encoding="utf-8") # 打开打卡记录文件,只读text = ad() # 读取所有文本if len(text) > 0: # 如果存在文本o.LOCK_RECORD = eval(text) # 将文本转换成打卡记录字典file.close() # 关闭文件# 加载员工图像
def load_employee_pic():photos = list() # 样本图像列表lables = list() # 标签列表pics = os.listdir(PIC_PATH) # 读取所有照片if len(pics) != 0: # 如果照片文件不是空的for file_name in pics: # 遍历所有图像文件code = file_name[0:o.CODE_LEN] # 截取文件名开头的特征码photos.append(cv2.imread(PIC_PATH + file_name, 0)) # 以灰度图像的方式读取样本lables.append(int(code)) # 样本的特征码作为训练标签rs.train(photos, lables) # 识别器训练样本else: # 不存在任何照片print("Error >> 员工照片文件丢失,请重新启动程序并录入员工信息!")# 加载上下班时间数据
def load_work_time_config():file = open(WORK_TIME, "r", encoding="utf-8") # 打开打卡记录文件,只读text = ad().rstrip() # 读取所有文本times = text.split("/") # 分割字符串o.WORK_TIME = times[0] # 第一个值是上班时间o.CLOSING_TIME = times[1] # 第二个值是下班时间file.close() # 关闭文件# 加载管理员账号密码
def load_users():file = open(USER_PASSWORD, "r", encoding="utf-8") # 打开打卡记录文件,只读text = ad() # 读取所有文本if len(text) > 0: # 如果存在文本o.USERS = eval(text) # 将文本转换成打卡记录字典file.close() # 关闭文件
# 将员工信息持久化
def save_employee_all():file = open(DATA_FILE, "w", encoding="utf-8") # 打开员工信息文件,只写,覆盖info = "" # 待写入的字符串for emp in o.EMPLOYEES: # 遍历所有员工信息# 拼接员工信息info += str(emp.id) + "," + str(emp.name) + "," + de) + "n"file.write(info) # 将这些员工信息写入到文件中file.close() # 关闭文件# 将打卡记录持久化
def save_lock_record():file = open(RECORD_FILE, "w", encoding="utf-8") # 打开打卡记录文件,只写,覆盖info = str(o.LOCK_RECORD) # 将打卡记录字典转换成字符串file.write(info) # 将字符串内容写入到文件中file.close() # 关闭文件# 将上下班时间写到文件中
def save_work_time_config():file = open(WORK_TIME, "w", encoding="utf-8") # 打开打卡记录文件,只写,覆盖times = str(o.WORK_TIME) + "/" + str(o.CLOSING_TIME)file.write(times) # 将字符串内容写入到文件中file.close() # 关闭文件
# 删除指定员工的所有照片
def remove_pics(id):pics = os.listdir(PIC_PATH) # 读取所有照片文件code = _code_with_id(id)) # 获取该员工的特征码for file_name in pics: # 遍历文件if file_name.startswith(code): # 如果文件名以特征码开头os.remove(PIC_PATH + file_name) # 删除此文件print("删除照片:" + file_name)
# 生成csv文件,采用Windows默认的gbk编码
def create_CSV(file_name, text):file = open(PATH + file_name + ".csv", "w", encoding="gbk") # 打开文件,只写,覆盖file.write(text) # 将文本写入文件中file.close() # 关闭文件print("已生成文件,请注意查看:" + PATH + file_name + ".csv")
摄像头工具模块主要包含以下功能:
import cv2
from util import public_tools as tool
from util import io_tools as io
from service import recognize_service as rs
from service import hr_service as hr
ESC_KEY = 27 # Esc键的ASCII码
ENTER_KEY = 13 # Enter键的ASCII码
# 打开摄像头进行登记
def register(code):cameraCapture = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 获得默认摄像头success, frame = ad() # 读取一帧shooting_time = 0 # 拍摄次数while success: # 如果读到有效帧数cv2.imshow("register", frame) # 展示当前画面success, frame = ad() # 再读一帧key = cv2.waitKey(1) # 记录当前用户敲下的按键if key == ESC_KEY: # 如果直接按ESC键break # 停止循环if key == ENTER_KEY: # 如果按Enter键# 将当前帧缩放成统一大小photo = size(frame, (io.IMG_WIDTH, io.IMG_HEIGHT))# 拼接照片名:照片文件夹+特征码+随机数字+图片后缀img_name = io.PIC_PATH + str(code) + str(tool.randomNumber(8)) + ".png"cv2.imwrite(img_name, photo) # 保存将图像shooting_time += 1 # 拍摄次数递增if shooting_time == 3: # 如果拍完三张照片break # 停止循环cv2.destroyAllWindows() # 释放所有窗体lease() # 释放摄像头io.load_employee_pic() # 让人脸识别服务重新载入员工照片
# 打开摄像头打卡
def clock_in():cameraCapture = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 获得默认摄像头success, frame = ad() # 读取一帧while success and cv2.waitKey(1) == -1: # 如果读到有效帧数cv2.imshow("check in", frame) # 展示当前画面gary = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 将彩色图片转为灰度图片if rs.found_face(gary): # 如果屏幕中出现正面人脸gary = size(gary, (io.IMG_WIDTH, io.IMG_HEIGHT)) # 将当前帧缩放成统一大小code = rs.recognise_face(gary) # 识别图像if code != -1: # 如果识别成功name = hr.get_name_with_code(code) # 获取此特征码对应的员工if name != None: # 如果返回的结果不是空的cv2.destroyAllWindows() # 释放所有窗体lease() # 释放摄像头return name # 返回打卡成功者的姓名success, frame = ad() # 再读一帧cv2.destroyAllWindows() # 释放所有窗体lease() # 释放摄像头
服务模块包含以下两个文件:hr_service.py(人事服务模块)和recognize_servise.py(人脸识别服务模块)。
人事服务模块主要包含以下功能:
from entity import organizations as o
from util import public_tools as tool
from util import io_tools as io
import datetime
import calendar
# 加载数据
def load_emp_data():io.checking_data_files() # 文件自检io.load_users() # 载入管理员账号io.load_lock_record() # 载入打卡记录io.load_employee_info() # 载入员工信息io.load_employee_pic() # 载入员工照片
# 添加新员工
def add_new_employee(name):code = tool.randomCode() # 生成随机特征码newEmp = o._new_id(), name, code) # 创建员工对象o.add(newEmp) # 组织结构中添加新员工io.save_employee_all() # 保存最新的员工信息return code # 新员工的特征码
# 删除某个员工
def remove_employee(id):io.remove_pics(id) # 删除该员工所有图片o.remove(id) # 从组织结构中删除io.save_employee_all() # 保存最新的员工信息io.save_lock_record() # 保存最新的打卡记录
# 为指定员工添加打卡记录
def add_lock_record(name):record = o.LOCK_RECORD # 所有打卡记录now_time = w().strftime("%Y-%m-%d %H:%M:%S") # 当前时间if name in record.keys(): # 如果这个人有打卡记录r_list = record[name] # 去除他的记录if len(r_list) == 0: # 如果记录为空r_list = list() # 创建新列表r_list.append(now_time) # 记录当前时间else: # 如果这个人从未打过卡r_list = list() # 创建新列表r_list.append(now_time) # 记录当前时间record[name] = r_list # 将记录保存在字典中io.save_lock_record() # 保存所有打卡记录
# 所有员工信息报表
def get_employee_report():# report = list() # 员工信息列表report = "###########################################n"report += "员工名单如下:n"i = 0 # 换行计数器for emp in o.EMPLOYEES: # 遍历所有员工report += "(" + str(emp.id) + ")" + emp.name + "t"i += 1 # 计数器自增if i == 4: # 每四个员工换一行report += "n"i = 0 # 计数器归零report = report.strip() # 清除报表结尾可能出现的换行符report += "n###########################################"return report# 检查id是否存在
def check_id(id):for emp in o.EMPLOYEES:if str(id) == str(emp.id):return Truereturn False# 通过特征码获取员工姓名
def get_name_with_code(code):for emp in o.EMPLOYEES:if str(code) == de):return emp.name# 通过id获取员工特征码
def get_code_with_id(id):for emp in o.EMPLOYEES:if str(id) == str(emp.id):de
# 获取所有员工的打卡记录信息
def get_record_all():record = o.LOCK_RECORD # 获得打卡记录字典report = "" # 报表内容for name in record.keys(): # 遍历所有名字report += "-----------------------------------n"report += name + " 打卡记录如下:n"for date in record[name]: # 遍历所有时间字符串report += date + "n"return report
# 验证管理员账号和密码
def valid_user(username, password):if username in o.USERS.keys(): # 如果有这个账号if (username) == password: # 如果账号和密码匹配return True # 验证成功return False # 验证失败
# 保存上下班时间
def save_work_time(work_time, close_time):o.WORK_TIME = work_timeo.CLOSING_TIME = close_timeio.save_work_time_config() # 上下班时间保存到文件中
def get_day_report(date):io.load_work_time_config() # 读取上下班时间earliest_time = datetime.datetime.strptime(date + " 00:00:00", "%Y-%m-%d %H:%M:%S") # 今天0点noon_time = datetime.datetime.strptime(date + " 12:00:00", "%Y-%m-%d %H:%M:%S") # 今天中午12点latest_time = datetime.datetime.strptime(date + " 23:59:59", "%Y-%m-%d %H:%M:%S") # 今晚0点之前work_time = datetime.datetime.strptime(date + " " + o.WORK_TIME, "%Y-%m-%d %H:%M:%S") # 上班时间closing_time = datetime.datetime.strptime(date + " " + o.CLOSING_TIME, "%Y-%m-%d %H:%M:%S") # 下班时间late_list = [] # 迟到名单left_early = [] # 早退名单absent_list = [] # 缺席名单for emp in o.EMPLOYEES: # 遍历所有员工if emp.name in o.LOCK_RECORD.keys(): # 如果该员工有打卡记录emp_lock_list = o.(emp.name) # 获取该员工所有的打卡记录is_absent = True # 缺席状态for lock_time_str in emp_lock_list: # 遍历所有打卡记录lock_time = datetime.datetime.strptime(lock_time_str, "%Y-%m-%d %H:%M:%S") # 打卡记录转为日期格式if earliest_time < lock_time < latest_time: # 如果当天有打卡记录is_absent = False # 不缺席if work_time < lock_time <= noon_time: # 上班时间后、中午之前打卡late_list.append(emp.name) # 加入迟到名单if noon_time < lock_time < closing_time: # 中午之后、下班之前打卡left_early.append(emp.name) # 加入早退名单if is_absent: # 如果仍然是缺席状态absent_list.append(emp.name) # 加入缺席名单else: # 该员工没有打卡记录absent_list.append(emp.name) # 加入缺席名单emp_count = len(o.EMPLOYEES) # 员工总人数print("--------" + date + "--------")print("应到人数:" + str(emp_count))print("缺席人数:" + str(len(absent_list)))absent_name = "" # 缺席名单if len(absent_list) == 0: # 如果没有缺席的absent_name = "(空)"else: # 有缺席的for name in absent_list: # 遍历缺席列表absent_name += name + " " # 拼接名字print("缺席名单:" + absent_name)print("迟到人数:" + str(len(late_list)))late_name = "" # 迟到名单if len(late_list) == 0: # 如果没有迟到的late_name = "(空)"else: # 有迟到的for name in late_list: # 遍历迟到列表late_name += name + " " # 拼接名字print("迟到名单:" + str(late_name))print("早退人数:" + str(len(left_early)))early_name = "" # 早退名单if len(left_early) == 0: # 如果没有早退的early_name = "(空)"else: # 有早退的for name in left_early: # 遍历早退列表early_name += name + " " # 拼接名字print("早退名单:" + early_name)# 打印今天的打卡日报
def get_today_report():date = w().strftime("%Y-%m-%d") # 今天的日期get_day_report(str(date)) # 打印今天的日报
def get_month_report(month):io.load_work_time_config() # 读取上下班时间date = datetime.datetime.strptime(month, "%Y-%m") # 月份转为时间对象monthRange = ar, h)[1] # 该月最后一天的天数month_first_day = datetime.ar, h, 1) # 该月的第一天month_last_day = datetime.ar, h, monthRange) # 该月的最后一天clock_in = "I" # 正常上班打卡标志clock_out = "O" # 正常下班打卡标志late = "L" # 迟到标志left_early = "E" # 早退标志absent = "A" # 缺席标志lock_report = dict() # 键为员工名,值为员工打卡情况列表for emp in o.EMPLOYEES:emp_lock_data = [] # 员工打卡情况列表if emp.name in o.LOCK_RECORD.keys(): # 如果员工有打卡记录emp_lock_list = o.(emp.name) # 从打卡记录中获取该员工的记录index_day = month_first_day # 遍历日期,从该月第一天开始while index_day <= month_last_day:is_absent = True # 缺席状态earliest_time = datetime.datetime.strptime(str(index_day) + " 00:00:00", "%Y-%m-%d %H:%M:%S") # 当天0点noon_time = datetime.datetime.strptime(str(index_day) + " 12:00:00", "%Y-%m-%d %H:%M:%S") # 当天中午12点latest_time = datetime.datetime.strptime(str(index_day) + " 23:59:59", "%Y-%m-%d %H:%M:%S") # 当天0点之前work_time = datetime.datetime.strptime(str(index_day) + " " + o.WORK_TIME,"%Y-%m-%d %H:%M:%S") # 当天上班时间closing_time = datetime.datetime.strptime(str(index_day) + " "+ o.CLOSING_TIME, "%Y-%m-%d %H:%M:%S") # 当天下班时间emp_today_data = "" # 员工打卡标记汇总for lock_time_str in emp_lock_list: # 遍历所有打卡记录lock_time = datetime.datetime.strptime(lock_time_str, "%Y-%m-%d %H:%M:%S") # 打卡记录转为日期格式if earliest_time < lock_time < latest_time: # 如果当前日期有打卡记录is_absent = False # 不缺席if lock_time <= work_time: # 上班时间前打卡emp_today_data += clock_in # 追加正常上班打卡标志elif lock_time >= closing_time: # 下班时间后打卡emp_today_data += clock_out # 追加正常下班打卡标志elif work_time < lock_time <= noon_time: # 上班时间后、中午之前打卡emp_today_data += late # 追加迟到标志elif noon_time < lock_time < closing_time: # 中午之后、下班之前打卡emp_today_data += left_early # 追加早退标志if is_absent: # 如果缺席emp_today_data = absent # 直接赋予缺席标志emp_lock_data.append(emp_today_data) # 员工打卡标记添加到打卡情况列表中index_day = index_day + datetime.timedelta(days=1) # 遍历天数递增else: # 没有打卡记录的员工index_day = month_first_day # 从该月第一天开始while index_day <= month_last_day: # 遍历整月emp_lock_data.append(absent) # 每天都缺席index_day = index_day + datetime.timedelta(days=1) # 日期递增lock_report[emp.name] = emp_lock_data # 将打卡情况列表保存到该员工之下report = ""姓名/日期"" # cvs文件的文本内容,第一行第一列index_day = month_first_day # 从该月第一天开始while index_day <= month_last_day: # 遍历整月report += ","" + str(index_day) + """ # 添加每一天的日期index_day = index_day + datetime.timedelta(days=1) # 日期递增report += "n"for emp in lock_report.keys(): # 遍历报表中的所有员工report += """ + emp + """ # 第一列为员工名data_list = (emp) # 取出员工的打卡情况列表for data in data_list: # 取出每一天的打卡情况text = "" # CSV中显示的内容if absent == data: # 如果是缺席text = "【缺席】"elif clock_in in data and clock_out in data: # 如果是全勤,不考虑迟到和早退text = "" # 显示空白else: # 如果不是全勤if late in data and clock_in not in data: # 有迟到记录且无上班打卡text += "【迟到】"if left_early in data and clock_out not in data: # 有早退记录且无下班打卡text += "【早退】"if clock_out not in data and left_early not in data: # 无下班打卡和早退记录text += "【下班未打卡】"if clock_in not in data and late not in data: # 有无上班打卡和迟到记录text += "【上班未打卡】"report += ","" + text + """report += "n"title_date = month_first_day.strftime("%Y{y}%m{m}").format(y="年", m="月") # csv文件标题日期file_name = title_date + "考勤月报" # CSV的文件名io.create_CSV(file_name, report) # 生成csv文件# 创建上个月打卡记录月报
def get_pre_month_report():today = day() # 得到今天的日期pre_month_first_day = datetime.ar, h - 1, 1) # 获得上个月的第一天的日期pre_month = pre_month_first_day.strftime("%Y-%m") # 转成年月格式字符串get_month_report(pre_month) # 生成上个月的月报
该模块主要包含以下功能:
import cv2
import numpy as np
import os
RECOGNIZER = cv2.face.LBPHFaceRecognizer_create() # LBPH识别器
# RECOGNIZER = cv2.face.EigenFaceRecognizer_create()#特征脸识别器
# RECOGNIZER = cv2.face.FisherFaceRecognizer_create() # LBPH识别器
PASS_CONF = 45 # 最高评分,LBPH最高建议用45,特征脸最高建议用22000
FACE_CASCADE = cv2.wd() + "\cascades\haarcascade_l") # 加载人脸识别级联分类器
# 训练识别器
def train(photos, lables):ain(photos, np.array(lables)) # 识别器开始训练
# 判断图像中是否有正面人脸
def found_face(gary_img):faces = FACE_CASCADE.detectMultiScale(gary_img, 1.15, 4) # 找出图像中所有的人脸return len(faces) > 0 # 返回人脸数量大于0的结果
# 识别器识别图像中的人脸
def recognise_face(photo):label, confidence = RECOGNIZER.predict(photo) # 识别器开始分析人脸图像if confidence > PASS_CONF: # 忽略评分大于最高评分的结果return -1return label
程序入口文件(main.py)主要负责在控制台中打印菜单界面,用户通过指令可以使用系统中的全部功能,包括打卡、员工管理等。
from util import camera
from util import public_tools as tool
from service import hr_service as hr
ADMIN_LOGIN = False # 管理员登录状态# 管理员登录
def login():while True:username = input("请输入管理员账号(输入0取消操作):")if username == "0": # 如果只输入0return # 结束方法passowrd = input("请输入管理员密码:")if hr.valid_user(username.strip(), passowrd.strip()): # 校验账号密码global ADMIN_LOGIN # 读取全局变量ADMIN_LOGIN = True # 设置为管理员已登录状态print(username + "登录成功!请选择重新选择功能菜单")breakelse:print("账号或密码错误,请重新输入!")print("---------------------------")
def start():finish = False # 程序结束标志menu = """
+--------------------------------------------------+
| 主功能菜单 |
+--------------------------------------------------+①打卡 ②查看记录 ③员工管理 ④考勤报表 ⑤退出
---------------------------------------------------"""while not finish:print(menu) # 打印菜单option = input("请输入菜单序号:")if option == "1": # 如果选择“打卡”face_clock() # 启动人脸打卡elif option == "2": # 如果选择“查看记录”if ADMIN_LOGIN: # 如果管理员已登录check_record() # 进入查看记录方法else:login() # 先让管理员登录elif option == "3": # 如果选择“员工管理”if ADMIN_LOGIN:employee_management() # 进入员工管理方法else:login()elif option == "4": # 如果选择“考勤报表”if ADMIN_LOGIN:check_report() # 进入考勤报表方法else:login()elif option == "5": # 如果选择“退出”finish = True # 确认结束,循环停止else:print("输入的指令有误,请重新输入!")print("Bye Bye !")
# 人脸打卡
def face_clock():print("请正面对准摄像头进行打卡")name = camera.clock_in() # 开启摄像头,返回打卡员工名称if name is not None: # 如果员工名称有效hr.add_lock_record(name) # 保存打卡记录print(name + " 打卡成功!")
def employee_management():menu = """+-------------------------------------------------+
| 员工管理功能菜单 |
+-------------------------------------------------+①录入新员工 ②删除员工 ③返回上级菜单
---------------------------------------------------"""while True:print(menu) # 打印菜单option = input("请输入菜单序号:")if option == "1": # 如果选择“录入新员工”name = str(input("请输入新员工姓名(输入0取消操作):")).strip()if name != "0": # 只要输入的不是0code = hr.add_new_employee(name) # 人事服务添加新员工,并获得该员工的特征码print("请面对摄像头,敲击三次回车键完成拍照!")ister(code) # 打开摄像头为员工照相print("录入成功!")# return # 退出员工管理功能菜单elif option == "2": # 如果选择“删除员工”# show_employee_all() # 展示员工列表_employee_report()) # 打印员工信息报表id = int(input("请输入要删除的员工编号(输入0取消操作):"))if id > 0: # 只要输入的不是0if hr.check_id(id): # 若此编号有对应员工verification = tool.randomNumber(4) # 生成随机4位验证码inputVer = input("[" + str(verification) + "] 请输入验证码:") # 让用户输入验证码if str(verification) == str(inputVer).strip(): # 如果验证码正确hr.remove_employee(id) # 人事服务删除该员工print(str(id) + "号员工已删除!")else: # 无效编号print("验证码有误,操作取消")else:print("无此员工,操作取消")elif option == "3": # 如果选择“返回上级菜单”return # 退出员工管理功能菜单else:print("输入的指令有误,请重新输入!")
# 查看记录
def check_record():menu = """+-------------------------------------------------+
| 查看记录功能菜单 |
+-------------------------------------------------+①查看员工列表 ②查看打卡记录 ③返回上级菜单
---------------------------------------------------"""while True:print(menu) # 打印菜单option = input("请输入菜单序号:")if option == "1": # 如果选择“查看员工列表”_employee_report()) # 打印员工信息报表elif option == "2": # 如果选择“查看打卡记录”report = hr.get_record_all()print(report)elif option == "3": # 如果选择“返回上级菜单”return # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
# 考勤报表
def check_report():menu = """+-------------------------------------------------+
| 考勤报表功能菜单 |
+-------------------------------------------------+①日报 ②月报 ③报表设置 ④返回上级菜单
---------------------------------------------------"""while True:print(menu) # 打印菜单option = input("请输入菜单序号:")if option == "1": # 如果选择“日报”while True:date = input("输入查询日期,格式为(2008-08-08),输入0则查询今天:")if date == "0": # 如果只输入_today_report() # 打印今天的日报break # 打印完之后结束循环elif tool.valid_date(date): # 如果输入的日期格式有效hr.get_day_report(date) # 打印指定日期的日报break # 打印完之后结束循环else: # 如果输入的日期格式无效print("日期格式有误,请重新输入!")elif option == "2": # 如果选择“月报”while True:date = input("输入查询月份,格式为(2008-08),输入0则查询上个月:")if date == "0": # 如果只输入_pre_month_report() # 生成上个月的月报break # 生成完毕之后结束循环elif tool.valid_year_month(date): # 如果输入的月份格式有效hr.get_month_report(date) # 生成指定月份的月报break # 生成完毕之后结束循环else:print("日期格式有误,请重新输入!")elif option == "3": # 如果选择“报表设置”report_config() # 进入“报表设置”菜单elif option == "4": # 如果选择“返回上级菜单”return # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
# 报表设置
def report_config():menu = """+-------------------------------------------------+
| 报表设置功能菜单 |
+-------------------------------------------------+
①作息时间设置 ②返回上级菜单
---------------------------------------------------"""while True:print(menu) # 打印菜单option = input("请输入菜单序号:")if option == "1": # 如果选择“作息时间设置”while True:work_time = input("请设置上班时间,格式为(08:00:00):")if tool.valid_time(work_time): # 如果时间格式正确break # 结束循环else: # 如果时间格式不对print("上班时间格式错误,请重新输入")while True:close_time = input("请设置下班时间,格式为(23:59:59):")if tool.valid_time(close_time): # 如果时间格式正确breakelse: # 如果时间格式不对print("下班时间格式错误,请重新输入")hr.save_work_time(work_time, close_time) # 保存用户设置的上班时间和下班时间print("设置完成,上班时间:" + work_time + ",下班时间为:" + close_time)elif option == "2": # 如果选择“返回上级菜单”return # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
hr.load_emp_data() # 数据初始化
tital = """
***************************************************
* MR智能视频打卡系统 *
***************************************************"""
print(tital) # 打印标题
start() # 启动程序
本文发布于:2024-02-01 10:08:59,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170675333935888.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |