<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import csv
import xlrd
import platform
from datetime import datetime
from find_absence import find_absence

'''
输入表格：
0:部门 1:姓名 2:考勤号码 3:日期时间 4:机器号 5:编号(空) 6:比对方式 7:卡号

输出表格：
0:部门 1:姓名 2:考勤号码 3:工作日 4:上班时间 5:下班时间 6:比对方式 7:出勤情况 8:工时 

缺勤统计表：
0:部门 1:姓名 2:考勤号码 3:工作日 4:上班时间 5:下班时间 6:比对方式 7:出勤情况 8:工时
'''

DEPT = 0
NAME = 1
NUMB = 2
DATE = 3

'''
将字符串类型时间转化为数字类型时间
:param time_string - 时间字符串，如 9:30:59
:return double类型的时间
'''
def string_to_time(time_string):
    time = time_string.split(':')
    numeric = float(time[0])
    numeric += float(time[1]) / 60
    return numeric


'''
出勤时间检测
:param department - 部门名称，如'技术部' 
:param get_on_time - 上班时间，如'8:59:59'
:param get_off_time - 下班时间，如'18:00:00'
:return 出勤检测结果，字符串类型
'''
def turnout_checking(department, get_on_time, get_off_time):

    result = ''

    # 得到实际上班时间和下班时间
    get_on = string_to_time(get_on_time)
    get_off = string_to_time(get_off_time)

    # 根据部门调整基准上下班时间
    on_duty = 9.0
    off_duty = 18.0
    # 技术部实行弹性工作制，9:30-10:00均可
    if department == '技术部':
        on_duty = 10.0
        # 根据上班时间调整下班时间
        if get_on < 9.5:
            off_duty = 18.5
        elif get_on > 10.0:
            off_duty = 19.0
        else:
            off_duty = get_on + 9.0

    if on_duty < get_on <= (on_duty + 0.5):
        result += '迟到'
    elif get_on > on_duty + 0.5:
        result += '旷工'

    if (off_duty - 0.5) <= get_off < off_duty:
        result += '早退'
    elif get_off < off_duty - 0.5:
        result += '旷工'

    if result == '':
        result = '正常'

    # 疑似漏打卡的情况
    if get_off - get_on <= 1.0:
        result = '疑似漏打卡'

    return result


'''
生成工作日的日历，将周末与法定休息日删去
:param input_year - 输入一个年份，整型
:param input_month - 输入一个月份，整型
:return 一个数组，保存了所有工作日，数组元素为字符串格式，如'2017/5/30'
'''
def work_calendar(input_year, input_month):
    days = []

    # 生成日历
    if input_month in [1, 3, 5, 7, 8, 10, 12]:
        days_num = 31
    elif input_month in [4, 6, 9, 11]:
        days_num = 30
    else:
        if input_year % 4 == 0 and (input_year % 100 != 0 or input_year % 400 == 0):
            days_num = 29
        else:
            days_num = 28

    # 设置可以输入起止时间
    while True:
        date_period = input('输入起止时间 (如: 19-23): ')
        start_date, end_date = map(lambda x: int(x), date_period.split('-'))
        if start_date < 1 or end_date > days_num:
            print('输入日期超出了当月范围! (1 ~ %d)' % days_num)
            continue
        break

    for day in range(start_date, end_date + 1):
        days.append('%d/%d/%d' % (input_year, input_month, day))

    # 刨除法定节假日
    while True:
        holidays = input('输入法定节日，空格隔开 (若没有，输入N): ')
        if holidays in 'Nn':
            break
        holidays = holidays.split(' ')
        holidays = list(map(lambda x: int(x), holidays))

        # 检查输入的合法性
        if not set(holidays).issubset(set(range(start_date, end_date + 1))):
            print('请检查输入的合法性! (%d ~ %d)' % (start_date, end_date))
            continue

        # 从日历列表中移除法定假日
        for holiday in holidays:
            days.remove('%d/%d/%d' % (input_year, input_month, holiday))
        break

    # 刨除周末
    # days = list(filter(lambda x: datetime.strptime(x, '%Y/%m/%d').isoweekday() < 6, days))
    days = [day for day in days if datetime.strptime(day, '%Y/%m/%d').isoweekday() < 6]

    # 添加法定补班的周末
    while True:
        work_weekends = input('输入补班周末，空格隔开 (若没有，输入N): ')
        if work_weekends in 'Nn':
            break
        work_weekends = work_weekends.split(' ')
        work_weekends = list(map(lambda x: int(x), work_weekends))

        # 检查输入的合法性
        if not set(work_weekends).issubset(set(range(start_date, end_date + 1))):
            print('请检查输入的合法性! (%d ~ %d)' % (start_date, end_date))
            continue

        for work_weekend in work_weekends:
            days.append('%d/%d/%d' % (input_year, input_month, work_weekend))
        break

    return days


'''
统计出勤信息
:param excel_path - 输入Excel文件存储的路径
:return void，将统计结果写入到输入文件同文件夹下的statistics.csv中
Notice: Excel文件必须是.xlsx，否则会出现编码错误
'''
def statistics(excel_path='/Users/hayden/Desktop/checkin.xlsx', encoding='gbk'):

    # 根据操作系统确定路径分隔符
    path_separator = '\\'
    if platform.system() == 'Darwin':
        path_separator = '/'

    # 读取Excel数据
    data = xlrd.open_workbook(excel_path)

    # 获取第一张Sheet的表格
    table = data.sheets()[0]

    # 计算行数
    rows_number = table.nrows

    # 创建要写入的csv文件，记录统计结果
    csv_path = path_separator.join(excel_path.split(path_separator)[:-1])
    csv_path += path_separator
    csv_path += 'statistics.csv'
    csv_file = open(csv_path, 'w', encoding=encoding)
    writer = csv.writer(csv_file)

    # 写入表头
    header = table.row_values(0)
    header[3] = '工作日'
    header[4] = '上班时间'
    header[5] = '下班时间'
    header[7] = '出勤情况'
    # 再加一行工时
    header.append('工时')
    writer.writerow(header)

    previous_row = table.row_values(1)

    # 获取表格的年份、月份
    year = int(previous_row[DATE].split('/')[0])
    month = int(previous_row[DATE].split('/')[1])
    workdays = work_calendar(year, month)<commit_msg>分离出下班时间计算的函数<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import csv
import xlrd
import platform
from datetime import datetime
from find_absence import find_absence

'''
输入表格：
0:部门 1:姓名 2:考勤号码 3:日期时间 4:机器号 5:编号(空) 6:比对方式 7:卡号

输出表格：
0:部门 1:姓名 2:考勤号码 3:工作日 4:上班时间 5:下班时间 6:比对方式 7:出勤情况 8:工时 

缺勤统计表：
0:部门 1:姓名 2:考勤号码 3:工作日 4:上班时间 5:下班时间 6:比对方式 7:出勤情况 8:工时
'''


DEPT = 0
NAME = 1
NUMB = 2
DATE = 3
ON = 4
OFF = 5
CHECK = 6
ATTENDANCE = 7


'''
将字符串类型时间转化为数字类型时间。
:param string_time - 时间字符串，如 '9:30:59'。
:return double类型的时间，如 9.5。
'''
def string_to_time(string_time):
    time = string_time.split(':')
    numeric_time = float(time[0])
    numeric_time += float(time[1]) / 60
    return numeric_time


'''
判断各部门人员应该的上班和下班时间。
:param department - 部门名称，如 '技术部'。
:param get_on - 上班时间，用来动态判断应该的下班时间，适用于技术部。
:return 应该的上班和下班时间, double类型。 
'''
def schedule_for_department(department, get_on):

    # 根据部门调整基准上下班时间
    on_duty = 9.0
    off_duty = 18.0

    # 技术部实行弹性工作制，9:30-10:00均可
    if department == '技术部':
        on_duty = 10.0

        # 根据上班时间调整下班时间
        if get_on < 9.5:
            off_duty = 18.5
        elif get_on > 10.0:
            off_duty = 19.0
        else:
            off_duty = get_on + 9.0

    return on_duty, off_duty


'''
出勤时间检测
:param department - 部门名称，如 '技术部'。 
:param get_on_time - 上班时间，如 '8:59:59'。
:param get_off_time - 下班时间，如 '18:00:00'。
:return 出勤检测结果，字符串类型，如 '正常'、'早退'、'旷工'等。
'''
def turnout_checking(department, get_on_time, get_off_time):

    result = ''

    # 得到实际上班时间和下班时间
    get_on = string_to_time(get_on_time)
    get_off = string_to_time(get_off_time)

    # 根据部门和上班时间，计算出上班和下班的时间
    on_duty, off_duty = schedule_for_department(department, get_on)

    # 检查上班时间
    if on_duty < get_on <= (on_duty + 0.5):
        result += '迟到'
    elif get_on > on_duty + 0.5:
        result += '旷工'

    # 检查下班时间
    if (off_duty - 0.5) <= get_off < off_duty:
        result += '早退'
    elif get_off < off_duty - 0.5:
        result += '旷工'

    # 输出结果
    if result == '':
        result = '正常'

    # 疑似漏打卡的情况
    if get_off - get_on <= 1.0:
        result = '疑似漏打卡'

    return result


'''
生成工作日的日历，将周末与法定休息日删去
:param input_year - 输入一个年份，整型
:param input_month - 输入一个月份，整型
:return 一个数组，保存了所有工作日，数组元素为字符串格式，如'2017/5/30'
'''
def work_calendar(input_year, input_month):
    days = []

    # 生成日历
    if input_month in [1, 3, 5, 7, 8, 10, 12]:
        days_num = 31
    elif input_month in [4, 6, 9, 11]:
        days_num = 30
    else:
        if input_year % 4 == 0 and (input_year % 100 != 0 or input_year % 400 == 0):
            days_num = 29
        else:
            days_num = 28

    # 设置可以输入起止时间
    while True:
        date_period = input('输入起止时间 (如: 19-23): ')
        start_date, end_date = map(lambda x: int(x), date_period.split('-'))
        if start_date < 1 or end_date > days_num:
            print('输入日期超出了当月范围! (1 ~ %d)' % days_num)
            continue
        break

    for day in range(start_date, end_date + 1):
        days.append('%d/%d/%d' % (input_year, input_month, day))

    # 刨除法定节假日
    while True:
        holidays = input('输入法定节日，空格隔开 (若没有，输入N): ')
        if holidays in 'Nn':
            break
        holidays = holidays.split(' ')
        holidays = list(map(lambda x: int(x), holidays))

        # 检查输入的合法性
        if not set(holidays).issubset(set(range(start_date, end_date + 1))):
            print('请检查输入的合法性! (%d ~ %d)' % (start_date, end_date))
            continue

        # 从日历列表中移除法定假日
        for holiday in holidays:
            days.remove('%d/%d/%d' % (input_year, input_month, holiday))
        break

    # 刨除周末
    # days = list(filter(lambda x: datetime.strptime(x, '%Y/%m/%d').isoweekday() < 6, days))
    days = [day for day in days if datetime.strptime(day, '%Y/%m/%d').isoweekday() < 6]

    # 添加法定补班的周末
    while True:
        work_weekends = input('输入补班周末，空格隔开 (若没有，输入N): ')
        if work_weekends in 'Nn':
            break
        work_weekends = work_weekends.split(' ')
        work_weekends = list(map(lambda x: int(x), work_weekends))

        # 检查输入的合法性
        if not set(work_weekends).issubset(set(range(start_date, end_date + 1))):
            print('请检查输入的合法性! (%d ~ %d)' % (start_date, end_date))
            continue

        for work_weekend in work_weekends:
            days.append('%d/%d/%d' % (input_year, input_month, work_weekend))
        break

    return days


'''
统计出勤信息
:param excel_path - 输入Excel文件存储的路径
:return void，将统计结果写入到输入文件同文件夹下的statistics.csv中
Notice: Excel文件必须是.xlsx，否则会出现编码错误
'''
def statistics(excel_path='/Users/hayden/Desktop/checkin.xlsx', encoding='gbk'):

    # 根据操作系统确定路径分隔符
    path_separator = '\\'
    if platform.system() == 'Darwin':
        path_separator = '/'

    # 读取Excel数据
    data = xlrd.open_workbook(excel_path)

    # 获取第一张工作表
    table = data.sheets()[0]

    # 计算行数
    rows_number = table.nrows

    # 创建要写入的csv文件，记录统计结果
    csv_path = path_separator.join(excel_path.split(path_separator)[:-1])
    csv_path += path_separator
    csv_path += 'statistics.csv'

    csv_file = open(csv_path, 'w', encoding=encoding)
    writer = csv.writer(csv_file)

    # 写入表头
    header = table.row_values(0)
    header[3] = '工作日'
    header[4] = '上班时间'
    header[5] = '下班时间'
    header[7] = '出勤情况'
    # 再加一行工时
    header.append('工时')
    writer.writerow(header)

    previous_row = table.row_values(1)

    # 获取表格的年份、月份
    year = int(previous_row[DATE].split('/')[0])
    month = int(previous_row[DATE].split('/')[1])
    workdays = work_calendar(year, month)
{'fraction_non_alphanumeric': 0.1371698113207547, 'fraction_numerical': 0.028867924528301888, 'mean_word_length': 2.585052417991207, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '708603', 'n_tokens_mistral': 5119, 'n_tokens_neox': 5095, 'n_words': 857}