003《Python数据分析、挖掘与可视化(第2版)》/模拟高考录取工作.py
from json import load

def main(zhiyuan_fn, kaosheng_fn):
    failure = 0
    # 读取志愿信息
    zhiyuan_result = {}
    with open(zhiyuan_fn, encoding='utf8') as fp:
        # 跳过第一行的表头
        fp.readline()
        for line in fp:
            key, num, course = line.split(',')
            # 计划人数,选科要求,拟录取学生名单,设置每个志愿的初始状态
            zhiyuan_result[key] = (int(num), course.strip(), [])

    # 读取学生位次、选科以及志愿填报信息
    # 数据格式为:{'考生1':(位次, '选科情况','志愿1', '志愿2', ..., '志愿96), ...}
    with open(kaosheng_fn, encoding='utf8') as fp:
        data = load(fp)
    # 按位次升序排序
    # 格式为:[('考生1', (位次, '选科情况','志愿1', '志愿2', ..., '志愿96)), ...]
    data = sorted(data.items(), key=lambda item:item[1][0])

    # 开始投档
    for kaosheng, (weici, xuanke, *tianbaozhiyuan) in data:
        xuanke = set(xuanke.split('+'))
        # 按顺序遍历该考生填报的志愿名称
        for zhy in tianbaozhiyuan:
            # 获取该志愿的计划人数、选科要求、已录取考生名单
            renshu, xuanke_yaoqiu, mingdan = zhiyuan_result[zhy]
            if renshu == len(mingdan):
                # 该志愿已录满,检查下一个志愿
                continue
            else:
                # 选科要求可能的形式有:不限、物理、物理+化学、
                # 物理+化学+生物、物理|化学、物理|化学|生物
                condition = ((xuanke_yaoqiu == '不限') or
                             (xuanke_yaoqiu in xuanke) or
                             ('+' in xuanke_yaoqiu and
                              set(xuanke_yaoqiu.split('+')) <= xuanke) or
                             ('|' in xuanke_yaoqiu and
                              set(xuanke_yaoqiu.split('|')) & xuanke))
                if condition:
                    zhiyuan_result[zhy][2].append(kaosheng)
                    print(f'{kaosheng} 被 {zhy} 录取。')
                    break
        else:
            # 填报的所有志愿都没有录取
            failure += 1
            print(f'{kaosheng} 录取失败。')
    func = lambda key:zhiyuan_result[key][0]>len(zhiyuan_result[key][2])
    zhengjizhiyuan = tuple(filter(func, zhiyuan_result.keys()))
    # 返回录取失败的考生人数和需要征集志愿(即没招满)的志愿数量
    return (failure, len(zhengjizhiyuan))

print(main('各志愿计划人数与选科要求.csv', '考生位次选科与志愿填报.json'))