Python制作排班小工具【一】
  MnuOoTXPgNLZ 2023年11月25日 19 0

一、背景

因工作需要,工作日需要安排人员值守加班......

故使用Python制作一个排班小工具,需求说明如下:

1.自定义参与排班人员数量、每日值班人数、值班组数量

2.相邻天值班人员不重复

3.所有参与人员轮流排班

4.【TODO】支持在未安装python环境的windows电脑上运行

例子:

共7人参与排班(赵一、周二、张三、李四、吴五、郑六、王七),每日2人值班,第一次生成5个值班组,第二次生成3个值班组。结果如下:

第一周

周一(张三、李四)  周二(赵一、周二)  周三(吴五、郑六)

周四(王七、张三)  周五(李四、赵一)

第二周

周一(周二、吴五)  周二(郑六、王七)  周三(张三、李四)

二、代码实现

Step1

考虑到有两种情况:

1.每日值班人数*值班组数量<参与排班人员数量

2.每日值班人数*值班组数量≥参与排班人员数量

这里使用yield函数编写一个迭代生成器,从列表中循环取值。

def yield_num(lst, each_group_num, group_num):
    """
    排班列表迭代生成器
    :param lst: 目标列表
    :param each_group_num: 每组人数
    :param group_num: 组数
    :return:
    """
    for i in range(group_num):
        target_list = []
        for j in range(each_group_num):
            idx = (i * each_group_num + j) % len(lst)  # 当前取的数量%随机列表长度,取余得下标
            target_list.append(lst[idx])
        yield target_list  # 迭代生成器:记住上次返回的位置,下次迭代就从这个位置之后开始

print("【每日值班人数*值班组数量<参与排班人员数量】")
li1 = list(range(1, 8))
result_li1 = [i for i in first_yield_num(li, 2, 5)]
print(li1)
print(result_li1)

print('='*100)

print("【每日值班人数*值班组数量≥参与排班人员数量】")
li2 = list(range(1, 16))
result_li2 = [i for i in first_yield_num(li2, 2, 5)]
print(li2)
print(result_li2)

运行结果:

Python制作排班小工具【一】_工具开发

Step2

然后再来实现多次运行时,接着上次的结果继续从列表取值。

1.分初次运行和后续运行两种情况从列表取值

2.操作Yaml文件记录初始列表和上次取值的下标

3.初次运行生成Yaml文件,写入初始列表和取值下标

4.后续运行更新Yaml文件记录

5.打乱序号列表的顺序,增加随机性

import os
import random
import yaml


def first_yield_num(lst, each_group_num, group_num):
    """
    排班列表迭代生成器,初次生成成员序号列表和记录文件
    :param lst: 目标列表
    :param each_group_num: 每组人数
    :param group_num: 组数
    :return:
    """
    for i in range(group_num):
        target_list = []
        for j in range(each_group_num):
            idx = (i * each_group_num + j) % len(lst)  # 当前取的数量%随机列表长度,取余得下标
            target_list.append(lst[idx])
        yield target_list  # 迭代生成器:记住上次返回的位置,下次迭代就从这个位置之后开始
    yaml_write('random_list.yaml', {'random_list': lst, 'idx': idx + 1})


def subsequent_yield_num(lst, each_group_num, group_num, next_idx):
    """
    排班列表迭代生成器,后续生成成员序号列表并更新记录文件
    :param lst: 目标列表
    :param each_group_num: 每组人数
    :param group_num: 组数
    :param next_idx: 记录的目标列表下标
    :return:
    """
    for i in range(group_num):
        target_list = []
        for j in range(each_group_num):
            idx = (i * each_group_num + j + next_idx) % len(lst)
            target_list.append(lst[idx])
        yield target_list  # 迭代生成器:记住上次返回的位置,下次迭代就从这个位置之后开始
    yaml_clear('random_list.yaml')
    yaml_write('random_list.yaml', {'random_list': lst, 'idx': idx + 1})


def yaml_write(file, data):
    """
    数据写入yaml文件
    :param file: 文件路径
    :param data: 字典数据
    :return:
    """
    with open(file, 'a', encoding='utf-8') as f:
        yaml.dump(data, stream=f, allow_unicode=True, sort_keys=False)


def yaml_read(file):
    """
    读取yaml文件
    :param file: 文件路径
    :return:
    """
    with open(file=file, mode="r", encoding="utf-8") as f:
        data = yaml.safe_load(f.read())
    return data


def yaml_clear(file):
    """
    清空yaml文件
    :param file: 文件路径
    :return:
    """
    with open(file, 'w', encoding='utf-8') as f:
        f.truncate()


def scheduling(people_num, each_group_num, group_num):
    """
    生成分组成员序号排班列表,同一组内的成员序号不重复,且相邻组的成员序号不重复
    保存上一次执行记录,下次执行时继续生成
    :param people_num: 成员数
    :param each_group_num: 每组人数
    :param group_num: 组数
    :return:
    """
    # 根据上一次的记录生成排班成员序号列表,并更新记录文件
    try:
        record = yaml_read('random_list.yaml')  # 读取上一次的记录
        random_list = record['random_list']
        last_idx = record['idx']
        print('【后续运行】')
        print(random_list)
        result = [i for i in subsequent_yield_num(random_list, each_group_num, group_num, last_idx)]
        return result
    # 若记录文件不存在或者为空,生成成员序号列表和记录文件
    except FileNotFoundError or TypeError:
        random_list = list(range(1, people_num + 1))  # 成员序号列表
        random.shuffle(random_list)  # 随机重排序号列表
        print('【初次运行】')
        print(random_list)
        result = [i for i in first_yield_num(random_list, each_group_num, group_num)]
        return result


print(scheduling(7, 2, 5))
print("=" * 100)

初次运行结果:

Python制作排班小工具【一】_值班排班_02

Python制作排班小工具【一】_生成器_03

后续运行结果:

Python制作排班小工具【一】_值班排班_04

Python制作排班小工具【一】_python_05

Step3

如果有人请假或者新增人员等特殊情况发生,删除Yaml记录文件后运行重新生成新的排班即可。

同时优化一下代码的打印展示效果。

完整代码:

import os
import random
import time

import yaml


def first_yield_num(lst, each_group_num, group_num):
    """
    排班列表迭代生成器,初次生成成员序号列表和记录文件
    :param lst: 目标列表
    :param each_group_num: 每组人数
    :param group_num: 组数
    :return:
    """
    for i in range(group_num):
        target_list = []
        for j in range(each_group_num):
            idx = (i * each_group_num + j) % len(lst)  # 当前取的数量%随机列表长度,取余得下标
            target_list.append(lst[idx])
        yield target_list  # 迭代生成器:记住上次返回的位置,下次迭代就从这个位置之后开始
    yaml_write('random_list.yaml', {'random_list': lst, 'idx': idx + 1})


def subsequent_yield_num(lst, each_group_num, group_num, next_idx):
    """
    排班列表迭代生成器,后续生成成员序号列表并更新记录文件
    :param lst: 目标列表
    :param each_group_num: 每组人数
    :param group_num: 组数
    :param next_idx: 记录的目标列表下标
    :return:
    """
    for i in range(group_num):
        target_list = []
        for j in range(each_group_num):
            idx = (i * each_group_num + j + next_idx) % len(lst)
            target_list.append(lst[idx])
        yield target_list  # 迭代生成器:记住上次返回的位置,下次迭代就从这个位置之后开始
    yaml_clear('random_list.yaml')
    yaml_write('random_list.yaml', {'random_list': lst, 'idx': idx + 1})


def yaml_write(file, data):
    """
    数据写入yaml文件
    :param file: 文件路径
    :param data: 字典数据
    :return:
    """
    with open(file, 'a', encoding='utf-8') as f:
        yaml.dump(data, stream=f, allow_unicode=True, sort_keys=False)


def yaml_read(file):
    """
    读取yaml文件
    :param file: 文件路径
    :return:
    """
    with open(file=file, mode="r", encoding="utf-8") as f:
        data = yaml.safe_load(f.read())
    return data


def yaml_clear(file):
    """
    清空yaml文件
    :param file: 文件路径
    :return:
    """
    with open(file, 'w', encoding='utf-8') as f:
        f.truncate()


def del_file(file_path):
    """
    删除文件
    :param file_path:
    :return:
    """
    if os.path.exists(file_path):
        os.remove(file_path)


def scheduling(people_num, each_group_num, group_num):
    """
    生成分组成员序号排班列表,同一组内的成员序号不重复,且相邻组的成员序号不重复
    保存上一次执行记录,下次执行时继续生成
    :param people_num: 成员数
    :param each_group_num: 每组人数
    :param group_num: 组数
    :return:
    """
    # 根据上一次的记录生成排班成员序号列表,并更新记录文件
    try:
        record = yaml_read('random_list.yaml')  # 读取上一次的记录
        random_list = record['random_list']
        last_idx = record['idx']
        print('【后续运行】')
        print(f"参与排班人员:{[name_li[n - 1] for n in random_list]}")
        result = [i for i in subsequent_yield_num(random_list, each_group_num, group_num, last_idx)]
        return result
    # 若记录文件不存在或者为空,生成成员序号列表和记录文件
    except FileNotFoundError or TypeError:
        random_list = list(range(1, people_num + 1))  # 成员序号列表
        random.shuffle(random_list)  # 随机重排序号列表
        print('【初次运行】')
        print(f"参与排班人员:{[name_li[n - 1] for n in random_list]}")
        result = [i for i in first_yield_num(random_list, each_group_num, group_num)]
        return result


# 本年的第几周
week_num = int(time.strftime("%W")) + 1
name_li = ['赵一', '周二', '张三', '李四', '吴五', '郑六', '王七']
weak_li = ['周一', '周二', '周三', '周四', '周五']
result_li = scheduling(7, 2, 5)
print(f"【第{week_num}周】")
for k in range(len(result_li)):
    print(f"\t{weak_li[k]}:{[name_li[j - 1] for j in result_li[k]]}")
print("=" * 100)

# # 若参与排班的人员发生变化,则删除记录文件后再重新运行
# del_file('random_list.yaml')

运行结果:

Python制作排班小工具【一】_工具开发_06

Python制作排班小工具【一】_工具开发_07

三、问题

1.参与排班的人员若发生变动,直接删除记录文件后再重新排班,会存在新排班不能接上之前的记录的问题

2.缺少每次运行时生成的排班表记录

3.从运行结果可以看出我们已经基本实现了需求说明中提到的前3点,第4点将在接下来的文章中进行实现

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月25日 0

暂无评论

推荐阅读
MnuOoTXPgNLZ
最新推荐 更多