def add_leave(self, start, end): try: work_time = Configs.get_instance().get_config("work_time") start_date = Functions.string2datetime(start) end_date = Functions.string2datetime(end) while start_date < end_date: lunch_break_start, lunch_break_end = CommonDatas.get_instance( ).create_lunch_break(start_date.day) tempend_date = CommonDatas.get_instance().create_work_time( start_date.day)[1] if end_date < tempend_date: tempend_date = end_date if start_date < lunch_break_start and lunch_break_end < tempend_date: #午休的时间拆开 self._add_leave(start_date, lunch_break_start) self._add_leave(lunch_break_end, tempend_date) else: self._add_leave(start_date, tempend_date) if start_date.day == CommonDatas.get_instance().days_number: break else: start_date = CommonDatas.get_instance().create_work_time( start_date.day + 1)[0] except Exception as e: print(e) return False return True
def get_attendance(self): ret_attendance = -round(self.leave_durations.total_seconds() / 3600, 1) for day in range(1, CommonDatas.get_instance().days_number + 1): if not self.in_company(day): ret_attendance -= round( CommonDatas.get_instance().get_work_day_duration(). total_seconds() / 3600, 1) return ret_attendance
def calc_all_data(self): #缺卡 self.lack_checkin_count = 0 #迟到早退 self.late_or_early_count = 0 self.late_or_early_duration = 0 self.late_or_early_fine_number = 0 self.late_early_time_dict = {} #工作天数 self.work_days = 0 #放假天数 self.holidays = 0 #请假时长 self.day_leave_durations = [] self.leave_durations = timedelta() #重新加载配置 self.load_leaves_data() self.load_added_checkins() self.load_config_data() for index in range(1, CommonDatas.get_instance().days_number + 1): if not self.in_company(index): #没入职 pass elif not Configs.get_instance().get_is_work_time(index): #休息日 self.holidays += 1 else: self.work_days += 1 duration, late, early = self.calc_late_and_early_time(index) self.late_early_time_dict[index] = [duration, late, early] if 0 < duration: self.late_or_early_count += 1 self.late_or_early_duration += duration self.late_or_early_fine_number += Configs.get_instance( ).get_fine_number(duration) if 30 <= duration: self.lack_checkin_count += 1 #请假时长 leave_duration_am = timedelta() leave_duration_pm = timedelta() leave_times = self.get_leave_time_range(index) lunch_break_start, lunch_break_end = CommonDatas.get_instance( ).create_lunch_break(index) for leave_time in leave_times: if leave_time[0] < lunch_break_start: leave_duration_am += leave_time[1] - leave_time[0] else: leave_duration_pm += leave_time[1] - leave_time[0] self.day_leave_durations.append( (leave_duration_am, leave_duration_pm)) self.leave_durations += (leave_duration_am + leave_duration_pm) if 5 < self.late_or_early_count: self.late_or_early_fine_number *= 2
def open(self): super().open() self.widget.show() self.calendar.setMinimumDate(CommonDatas.get_instance().start_date) self.calendar.setMaximumDate(CommonDatas.get_instance().end_date) work_time = Configs.get_instance().get_config("work_time") self.timeEditOut.setTime(QTime(work_time[1][0], work_time[1][1])) self.timeEditIn.setTime(QTime(work_time[0][0], work_time[0][1])) lunch_break = Configs.get_instance().get_config("lunch_break") self.lunchBreakEditOut.setTime(QTime(lunch_break[1][0], lunch_break[1][1])) self.lunchBreakEditIn.setTime(QTime(lunch_break[0][0], lunch_break[0][1])) self.work_time_table = Configs.get_instance().get_temp_config("work_time_table").copy() self.on_time_change() self.refresh_UI()
def load_added_checkins(self): start_date = CommonDatas.get_instance().start_date self.added_checkin_list = [] added_checkins_config = Configs.get_instance( ).get_member_checkin_temp_config(self.name) for day in range(1, CommonDatas.get_instance().days_number + 1): checkins = [] day_checkins = [] if str(day) in added_checkins_config: checkins = added_checkins_config[str(day)] for checkin in checkins: day_checkins.append(CommonDatas.get_instance().create_datetime( day=day, hour=checkin[0], minute=checkin[1])) self.added_checkin_list.append(day_checkins)
def refresh_UI(self): #成员列表 list_model = QStringListModel() listString = [] show_out_member = self.showOutMember.isChecked() for key in CommonDatas.get_instance().get_all_member_dict( show_out_member): listString.append(key) list_model.setStringList(listString) self.memberList.setModel(list_model) self.calendar.setMinimumDate(CommonDatas.get_instance().start_date) self.calendar.setMaximumDate(CommonDatas.get_instance().end_date) self.leaveMember.addItems(listString) self.saveResultLabel.setText("") self.refresh_select_member()
def copy_template(template_path, out_dir, out_name_template): start_date = CommonDatas.get_instance().start_date out_name = out_name_template.format(start_date.month, os.path.splitext(template_path)[1]) out_path = os.path.join(out_dir, out_name) shutil.copyfile(template_path, out_path) return out_path
def in_company(self, day): today = CommonDatas.get_instance().create_datetime(day=day) if self.in_date <= today or Functions.is_same_day(today, self.in_date): if today <= self.out_date or Functions.is_same_day( today, self.out_date): return True return False
def click_leave(self): member_name = self.leaveMember.currentText() self.edit_leave_member = CommonDatas.get_instance().get_member_by_name( member_name) self.leaveEdit.setText("") for leaveText in self.edit_leave_member.get_leave_text_list(): self.leaveEdit.append(leaveText)
def get_year_month_config(self): cur_temp_config = None from Scripts.Data.CommonDatas import CommonDatas start_date = CommonDatas.get_instance().start_date year_str = str(start_date.year) month_str = str(start_date.month) if year_str not in self.temp_content: self.temp_content[year_str] = {} year_config = self.temp_content[year_str] if month_str not in year_config: year_config[month_str] = DEFAULT_TEMP_CONFIG for day in range(1, CommonDatas.get_instance().days_number + 1): cur_date = CommonDatas.get_instance().create_datetime(day=day) year_config[month_str]["work_time_table"].append( cur_date.weekday() <= 4) self.save_temp_json() cur_temp_config = year_config[month_str] return cur_temp_config
def refresh_UI(self): cur_date = CommonDatas.get_instance().start_date for index, value in enumerate(self.work_time_table): brush = QtGui.QBrush() if value is True: brush.setColor(QtGui.QColor("Black")) else: brush.setColor(QtGui.QColor("Red")) cmd_fmt = QtGui.QTextCharFormat() cmd_fmt.setForeground(brush) self.calendar.setDateTextFormat(QDate(cur_date.year, cur_date.month, index + 1), cmd_fmt)
def import_file(self, default_path=None): if type(default_path) is str: self.file_path = default_path else: self.file_path = self.open_file_dialog( default_path=r"C:\Users\achonor\Desktop\KaoqinXML") if self.file_path is None: return print(self.file_path) #修改编码 self.convert(self.file_path) #删除回车 self.delete_nr(self.file_path) xml_data = Functions.load_XML(self.file_path) CommonDatas.get_instance().reset_data(xml_data) #数据加载完成 self.actionExport_File.setEnabled(True) self.actionEdit_WorkTime.setEnabled(True) self.refresh_UI()
def get_day_work_time(self, day): start, end = CommonDatas.get_instance().create_work_time(day) if Functions.is_same_day(start, self.in_date): start = self.in_date if Functions.is_same_day(end, self.out_date): end = self.out_date #午休 lunch_break_start, lunch_break_end = CommonDatas.get_instance( ).create_lunch_break(day) #获取请假时间 leave_times = self.get_leave_time_range(day) for leave_time in leave_times: if leave_time[0] <= start and start < leave_time[1]: start = leave_time[1] if lunch_break_start <= start and start <= lunch_break_end: start = lunch_break_end if end <= leave_time[1] and leave_time[0] < end: end = leave_time[0] if lunch_break_start <= end and end <= lunch_break_end: end = lunch_break_start return start, end
def refresh_select_member(self, new_member=None): if new_member is not None: self.select_member = new_member if self.select_member is None: return self.select_member.calc_all_data() cur_year = CommonDatas.get_instance().start_date.year cur_month = CommonDatas.get_instance().start_date.month for index in range(1, CommonDatas.get_instance().days_number + 1): brush = QtGui.QBrush() if not self.select_member.in_company(index): #没入职或者离职了 brush.setColor(QtGui.QColor('Black')) elif not Configs.get_instance().get_is_work_time(index): #休息日 brush.setColor(QtGui.QColor('Black')) elif 0 < self.select_member.is_late_or_early(index): #迟到 brush.setColor(QtGui.QColor('Red')) else: brush.setColor(QtGui.QColor('Green')) cmd_fmt = QtGui.QTextCharFormat() cmd_fmt.setForeground(brush) self.calendar.setDateTextFormat(QDate(cur_year, cur_month, index), cmd_fmt) self.leaveMember.setCurrentText(self.select_member.name) self.inDateEdit.setDateTime(self.select_member.in_date) self.outDateEdit.setDateTime(self.select_member.out_date) #显示 member = self.select_member self.detailedInfo.setText(member.name) self.detailedInfo.append("缺卡次数:{0}".format(member.lack_checkin_count)) self.detailedInfo.append("迟到早退天数:{0} 罚款总数:{1}".format( member.late_or_early_count, member.late_or_early_fine_number)) self.detailedInfo.append("工作天数:{0} 休息天数:{1}".format( member.work_days, member.holidays)) self.detailedInfo.append("请假次数:{0} 请假时长:{1}".format( member.get_leave_count(), Functions.timedelta2string(member.get_leave_duration())))
def leave_string2ecxel_text(date_time_str_list): from Scripts.Data.CommonDatas import CommonDatas ret_str = "" for idx in range(0, len(date_time_str_list), 2): start_date = datetime.strptime(date_time_str_list[idx], "%Y-%m-%d %H:%M") end_date = datetime.strptime(date_time_str_list[idx + 1], "%Y-%m-%d %H:%M") while start_date < end_date: day_end_date = CommonDatas.get_instance().create_work_time( start_date.day)[1] if end_date < day_end_date: day_end_date = end_date ret_str += "{0}月{1}日({2}:{3:02d}-{4}:{5:02d}) ".format( start_date.month, start_date.day, start_date.hour, start_date.minute, day_end_date.hour, day_end_date.minute) if start_date.day == CommonDatas.get_instance().days_number: break else: start_date = CommonDatas.get_instance().create_work_time( start_date.day + 1)[0] return ret_str
def save_leave(self): leaveText = self.leaveEdit.toPlainText() member_name = self.leaveMember.currentText() self.edit_leave_member = CommonDatas.get_instance().get_member_by_name( member_name) leaveTextList = leaveText.splitlines() if self.edit_leave_member.set_leaves(leaveTextList): self.saveResultLabel.setText("成功") self.saveResultLabel.setStyleSheet("color:green") else: self.saveResultLabel.setText("失败") self.saveResultLabel.setStyleSheet("color:red") self.click_member()
def export_file(self): error_tips = None miss_member = False settings_configs = Configs.get_instance().get_config("settings") if False and not os.path.isfile(settings_configs["detail_table_path"]): error_tips = "请选择明细表模板" elif False and not os.path.isfile( settings_configs["summary_table_path"]): error_tips = "请选择汇总表模板" elif not os.path.isdir(settings_configs["out_path"]): error_tips = "请选择输出文件路径" else: show_out_member = self.showOutMember.isChecked() for name, member in CommonDatas.get_instance().get_all_member_dict( show_out_member).items(): if len(member.email) <= 0: error_tips = name + "的邮箱没填写" elif len(member.bank_card) <= 0: error_tips = name + "的银行卡号没填写" if error_tips is not None: miss_member = member break if error_tips is not None: reply = QMessageBox.warning(self.widget, 'Message', error_tips, QMessageBox.Yes, QMessageBox.Yes) if reply == QMessageBox.Yes: if miss_member is not False: self.open_edit_member_data(member=miss_member) else: self.open_settings() return try: #导出明细表 ExportXLSX.export_detail_table( settings_configs["detail_table_path"], settings_configs["out_path"]) #导出汇总表 ExportXLSX.export_summary_table( settings_configs["summary_table_path"], settings_configs["out_path"]) except PermissionError as exc: QMessageBox.warning(self.widget, "Error", "文件导出错误" + str(exc), QMessageBox.Yes) else: os.system("start explorer " + settings_configs["out_path"].replace("/", "\\"))
def load_config_data(self): start_date = CommonDatas.get_instance().start_date member_config = Configs.get_instance().get_member_config(self.name) self.email = member_config["email"] self.bank_card = member_config["bank_card"] self.profession = member_config["profession"] self.department_1 = member_config["department_1"] self.department_2 = member_config["department_2"] self.in_date = Functions.string2datetime(member_config["in_date"]) self.out_date = Functions.string2datetime(member_config["out_date"]) self.ignore = (self.out_date.year == start_date.year and self.out_date.month < start_date.month) if self.in_date.year == start_date.year and start_date.month < self.in_date.month: #还没入职 self.ignore = True if self.in_date.year == start_date.year and self.in_date.month == start_date.month and 15 < self.in_date.day: #上个月15号以后入职不统计 self.ignore = True
def export_detail_table(template_path, out_dir): start_date = CommonDatas.get_instance().start_date out_path = os.path.join(out_dir, "{0:02d}月考勤明细.xlsx".format(start_date.month)) #out_path = copy_template(template_path, out_dir, "{0:02d}月考勤明细{1}") xlsx_table = openpyxl.Workbook() xlsx_sheet = xlsx_table.get_active_sheet() #成员信息 member_dict = CommonDatas.get_instance().get_all_member_dict() member_list = list(member_dict.values()) #初始表格样式 sheet_row, sheet_column = init_detail_sheet( xlsx_sheet, len(member_list), CommonDatas.get_instance().days_number) #成员数据 for index, member in enumerate(member_list): row = 5 + index member.calc_all_data() #序号 xlsx_sheet.cell(row, 1).value = index + 1 #名字 xlsx_sheet.cell(row, 2).value = member.name #打卡情况 for day in range(1, CommonDatas.get_instance().days_number + 1): colum1 = 2 + 2 * day - 1 #上午 colum2 = colum1 + 1 #下午 cur_date = CommonDatas.get_instance().create_datetime(day=day) xlsx_cell1 = xlsx_sheet.cell(row, colum1) xlsx_cell2 = xlsx_sheet.cell(row, colum2) #初始化 xlsx_cell1.value = "√" xlsx_cell2.value = "√" if not member.in_company(day): #没入职 xlsx_sheet.merge_cells(None, row, colum1, row, colum2) xlsx_cell1.value = "未入职" elif not Configs.get_instance().get_is_work_time(day): #休息 xlsx_cell1.value = "休息" xlsx_cell2.value = "休息" elif 0 < member.is_late_or_early(day): #迟到或者早退 duration, late, early = member.get_late_and_early_time(day) if 1440 == duration or 480 == duration: xlsx_sheet.merge_cells(None, row, colum1, row, colum2) xlsx_cell1.fill = const_fill_purple xlsx_cell1.value = "缺卡" else: if 0 < late: xlsx_cell1.fill = const_fill_yellow xlsx_cell1.value = "迟到{0}分钟".format(late) if 0 < early: xlsx_cell2.fill = const_fill_yellow xlsx_cell2.value = " 早退{0}分钟".format(early) #计算请假 leave_duration, am_duration, pm_duration = member.get_leave_duration( day) work_day_duration = CommonDatas.get_instance( ).get_work_day_duration() if leave_duration == work_day_duration: #全天请假 xlsx_cell1.fill = const_fill_red xlsx_cell2.fill = const_fill_red xlsx_sheet.merge_cells(None, row, colum1, row, colum2) xlsx_cell1.value = "请假" + Functions.timedelta2string( leave_duration) else: if 0 < am_duration.total_seconds(): xlsx_cell1.fill = const_fill_red xlsx_cell1.value = "请假" + Functions.timedelta2string( am_duration) if 0 < pm_duration.total_seconds(): xlsx_cell2.fill = const_fill_red xlsx_cell2.value = "请假" + Functions.timedelta2string( pm_duration) cloum_offset = 2 + 2 * CommonDatas.get_instance().days_number + 1 #请假次数 xlsx_sheet.cell(row, cloum_offset).value = member.get_leave_count() #请假小时 xlsx_sheet.cell(row, cloum_offset + 1).value = Functions.timedelta2string( member.get_leave_duration()) #迟到次数 xlsx_sheet.cell(row, cloum_offset + 2).value = member.late_or_early_count #缺卡次数 xlsx_sheet.cell(row, cloum_offset + 3).value = member.lack_checkin_count #罚款金额 xlsx_sheet.cell(row, cloum_offset + 4).value = member.late_or_early_fine_number xlsx_table.save(out_path)
def init_detail_sheet(sheet, member_number, day_number): #行列数 sheet_row = 4 + member_number sheet_column = 2 + 2 * day_number + 5 #年月信息 start_date = CommonDatas.get_instance().start_date #行高列宽 sheet.row_dimensions[1].height = 45 for index in range(2, sheet_row + 1): sheet.row_dimensions[index].height = 20 sheet.column_dimensions["A"].width = 5 sheet.column_dimensions["B"].width = 8.5 for index in range(3, sheet_column - 4): index_str = utils.get_column_letter(index) sheet.column_dimensions[index_str].width = 8.5 sheet.column_dimensions[utils.get_column_letter(sheet_column - 4)].width = 9 sheet.column_dimensions[utils.get_column_letter(sheet_column - 3)].width = 9 sheet.column_dimensions[utils.get_column_letter(sheet_column - 2)].width = 9 sheet.column_dimensions[utils.get_column_letter(sheet_column - 1)].width = 11 sheet.column_dimensions[utils.get_column_letter(sheet_column)].width = 13 #标题 sheet.merge_cells(None, 1, 1, 1, sheet_column) for index in range(1, sheet_column + 1): sheet.cell(1, index).border = const_border set_cell_fromat(sheet.cell(1, 1), font=const_title_font, border=None) sheet.cell(1, 1).value = "{0}月考勤表(黄色填充代表迟到,蓝色填充代表未打卡,红色填充代表请假)".format( start_date.month) #表头 sheet.merge_cells(None, 2, 1, 3, 2) #日历星期 for day in range(1, 31 + 1): column = 2 + 2 * day - 1 day_cell = sheet.cell(2, column) week_cell = sheet.cell(3, column) set_cell_fromat(day_cell) set_cell_fromat(week_cell) if day <= CommonDatas.get_instance().days_number: cur_date = CommonDatas.get_instance().create_datetime(day=day) sheet.merge_cells(None, 2, column, 2, column + 1) day_cell.number_format = 'm月d日' day_cell.value = cur_date #星期 sheet.merge_cells(None, 3, column, 3, column + 1) week_cell.value = Functions.weekday2string(cur_date.weekday()) else: pass #xlsx_sheet.delete_cols(column, 2) #统计头部 sheet.merge_cells(None, 2, sheet_column - 4, 3, sheet_column) #序号,姓名 set_cell_fromat(sheet.cell(4, 1)) sheet.cell(4, 1).value = "序号" set_cell_fromat(sheet.cell(4, 2)) sheet.cell(4, 2).value = "名字" #上下班打卡 for index in range(3, sheet_column - 4): set_cell_fromat(sheet.cell(4, index)) sheet.cell(4, index).value = Functions.three_param_operator( 1 == index % 2, "上班打卡", "下班打卡") #请假次数 set_cell_fromat(sheet.cell(4, sheet_column - 4)) sheet.cell(4, sheet_column - 4).value = "请假次数" #请假小时 set_cell_fromat(sheet.cell(4, sheet_column - 3)) sheet.cell(4, sheet_column - 3).value = "请假小时" #迟到次数 set_cell_fromat(sheet.cell(4, sheet_column - 2)) sheet.cell(4, sheet_column - 2).value = "迟到次数" #忘打卡次数 set_cell_fromat(sheet.cell(4, sheet_column - 1)) sheet.cell(4, sheet_column - 1).value = "忘打卡次数" #罚款金额(元) set_cell_fromat(sheet.cell(4, sheet_column)) sheet.cell(4, sheet_column).value = "罚款金额(元)" #成员格式 for index in range(1, member_number + 1): row = 4 + index #序号 set_cell_fromat(sheet.cell(row, 1)) #名字 set_cell_fromat(sheet.cell(row, 2)) #打卡信息 for idx in range(3, sheet_column - 4): set_cell_fromat(sheet.cell(row, idx)) #统计信息 set_cell_fromat(sheet.cell(row, sheet_column - 4)) set_cell_fromat(sheet.cell(row, sheet_column - 3)) set_cell_fromat(sheet.cell(row, sheet_column - 2)) set_cell_fromat(sheet.cell(row, sheet_column - 1)) set_cell_fromat(sheet.cell(row, sheet_column)) return sheet_row, sheet_column
def export_summary_table(template_path, out_dir): #out_path = copy_template(template_path, out_dir, "{0:02d}月考勤汇总{1}") start_date = CommonDatas.get_instance().start_date end_date = CommonDatas.get_instance().end_date out_path = os.path.join(out_dir, "{0:02d}月考勤汇总.xlsx".format(start_date.month)) xlsx_table = openpyxl.Workbook() xlsx_sheet_summary = xlsx_table.get_active_sheet() xlsx_sheet_summary.title = "考勤汇总" xlsx_sheet_other = xlsx_table.create_sheet("请假加迟到次数明细") xlsx_sheet_member = xlsx_table.create_sheet("邮箱和卡号") #成员信息 member_dict = CommonDatas.get_instance().get_all_member_dict() member_list = list(member_dict.values()) member_number = len(member_list) #初始化考勤汇总sheet init_summary_sheet(xlsx_sheet_summary, member_number) #初始化请假加迟到次数明细 init_other_sheet(xlsx_sheet_other, member_number) #初始化邮箱卡号 init_member_sheet(xlsx_sheet_member, member_number) #生成考勤汇总sheet for index, member in enumerate(member_list): row = index + 2 #序号 xlsx_sheet_summary.cell(row, 1).value = index + 1 #入职时间 xlsx_sheet_summary.cell(row, 2).value = member.in_date.strftime("%Y.%m.%d") #姓名 xlsx_sheet_summary.cell(row, 3).value = member.name #职位 xlsx_sheet_summary.cell(row, 4).value = member.profession #部门 xlsx_sheet_summary.cell(row, 5).value = member.department_1 #事业部 xlsx_sheet_summary.cell(row, 6).value = member.department_2 #考勤 attendance = member.get_attendance() if 0 != attendance: xlsx_sheet_summary.cell(row, 7).value = attendance if member.in_date.year == start_date.year and member.in_date.month + 1 == start_date.month and 15 < member.in_date.day: xlsx_sheet_summary.cell(row, 7).value = "需手动计算上月考勤 + " + str(attendance) #离职时间 if start_date <= member.out_date and member.out_date <= end_date: xlsx_sheet_summary.cell( row, 8).value = member.out_date.strftime("%Y.%m.%d") #请假和迟到次数总和 leave_late_early_count = member.get_leave_count( ) + member.late_or_early_count if 0 != leave_late_early_count: xlsx_sheet_summary.cell(row, 9).value = leave_late_early_count # 生成考勤汇总sheet结束 #生成请假加迟到次数明细sheet for index, member in enumerate(member_list): row = index + 2 #姓名 xlsx_sheet_other.cell(row, 1).value = member.name #事假 if 0 < member.leave_durations.total_seconds(): xlsx_sheet_other.cell(row, 2).value = Functions.timedelta2string( member.leave_durations) #病假 #年假 #迟到 if 0 < member.late_or_early_count: xlsx_sheet_other.cell(row, 5).value = member.late_or_early_count #请假时间 xlsx_sheet_other.cell(row, 6).value = Functions.leave_string2ecxel_text( member.get_leave_text_list()) #生成邮箱卡号sheet for index, member in enumerate(member_list): row = index + 2 #姓名 xlsx_sheet_member.cell(row, 1).value = member.name #卡号 xlsx_sheet_member.cell(row, 2).value = member.bank_card #邮箱 xlsx_sheet_member.cell(row, 3).value = member.email xlsx_table.save(out_path)
def click_member(self): member_name = self.memberList.currentIndex().data() if member_name is None: return member = CommonDatas.get_instance().get_member_by_name(member_name) self.refresh_select_member(member)