最近我遇到了一个有趣的问题,我的导师想要提前将下一届的学生分配给各位教师。这听起来似乎没什么大不了的,但实际上,这可是个挺头疼的事情。

幻想一下,你作为一名导师,要负责领导一群研究生。你希望这些学生和你的研究方向相符,又能发挥他们的潜力。但问题是,假如分配不公正,可能会导致资源浪费,甚至影响到学生的学习和研究。

所以,我决议动手写一个随机分配的脚原本处理这个问题。这样一来,分配就不会受到个人喜爱或偏见的影响,而是彻底随机的,公正并且透明。

在这篇博客里,我将和大家共享我的思路和完成进程。希望这关于遇到类似问题的人有所启示和协助!

主要技术栈

  • Dash结构:Dash是根据Python的Web应用结构,用于构建交互式Web应用程序。
  • Pandas库:Pandas是Python中用于数据处理和剖析的强壮库。

分配进程

首先,让我们简要地了解一下这个脚本的作业原理。我编写了一个根据Dash的Python应用程序,其间包含了两个重要功用:手动分配和随机分配。

  • 手动分配: 用户可以手动输入学生的学号和导师的名字,然后单击按钮将学生分配给指定的导师。这关于一些特殊情况下的指定分配非常有用。
  • 随机分配: 用户可以上传包含导师和学生信息的CSV或Excel文件,然后单击按钮履行随机分配。在这种形式下,脚本将根据每个导师可以分配的学生数量随机将学生分配给导师。

大致逻辑

现在,让我们深入了解一下脚本的逻辑。我使用了Dash来构建用户界面,这是一个根据Python的Web应用程序结构。经过Dash,我可以轻松地创立交互式组件,并将它们与Python函数进行衔接。

在数据处理方面,我使用了Pandas库来处理上传的CSV或Excel文件。Pandas供给了丰厚的数据操作功用,使我可以轻松地读取、处理和操作数据。

随机分配的部分则利用了Python的随机模块。我首先将导师和他们可以分配的学生数量存储在一个字典中,然后根据每个导师的指定数量随机选择学生,并将它们分配给对应的导师。

最终,我使用了Dash的回调函数来完成交互逻辑。每当用户履行操作时,回调函数将被触发,并相应地更新界面或履行其他操作。

代码

当我规划这个随机分配脚本时,我将功用分为几个部分。让我逐段解释每个部分的代码:

1. 导入必要的库和模块


import random
import time
import dash
from dash import dcc, html
from dash.dependencies import Input, Output, State
import pandas as pd
import base64
import io

在这一部分,我导入了所需的Python库和模块。这些库包括Dash用于构建Web应用程序,Pandas用于数据处理,以及其他一些用于处理文件上传和编码的模块。

2. 设置Dash应用程序


app = dash.Dash(__name__)

这行代码创立了一个Dash应用程序实例。我将其命名为app,并指定__name__作为应用程序的称号。

3. 规划应用程序的布局


app.layout = html.Div([
    # 上传导师信息文件的组件
    dcc.Upload(
        id='upload-data1',
        children=html.Div([
            '上传CSV或Excel文件(教师)'
        ]),
        style={...},
        accept='.csv, .xlsx'
    ),
    # 上传学生信息文件的组件
    dcc.Upload(
        id='upload-data2',
        children=html.Div([
            '上传一个CSV或Excel文件(学生)'
        ]),
        style={...},
        accept='.csv, .xlsx'
    ),
    # 输入学生学号和教师名字的组件
    html.Div([
        html.Label('学生学号:'),
        dcc.Input(id='student-id', type='text', value=''),
        html.Label('教师名字:'),
        dcc.Input(id='teacher-name', type='text', value=''),
        html.Button('分配教师', id='assign-button', n_clicks=0),
        html.Div(id='assign-output')
    ]),
    # 开端随机分配按钮和设置每个教师分配的学生数量
    html.Div([
        html.Label('每个教师分配的学生数量:'),
        html.Button('开端随机分配', id='random-button', n_clicks=0),
        html.Div(id='random-output')
    ])
])

这段代码定义了Dash应用程序的布局。我创立了几个上传文件和输入框组件,以便用户上传导师和学生的信息文件,并手动分配学生给指定的导师。一起,我还增加了一个按钮,用于开端随机分配学生。

4. 解析上传的文件


# 上传函数
def parse_contents(contents, filename,target):
    global teacher_df, student_df,teacher_df_all,student_df_all
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # 读取上传的CSV文件,一起指定学号列的数据类型为字符串
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')), dtype={'学号': str})
        elif 'xls' in filename or 'xlsx' in filename:
            # 读取上传的Excel文件,一起指定学号列的数据类型为字符串
            df = pd.read_excel(io.BytesIO(decoded), dtype={'学号': str})
    except Exception as e:
        print(e)
        return html.Div([
            '产生错误,无法读取文件'
        ])
    # 根据目标标识存储到相应的全局变量中
    if target == 'teacher':
        teacher_df = df
        teacher_df_all = df
    elif target == 'student':
        student_df = df
        student_df_all = df
    return df.to_dict('records')

这个函数用于解析上传的CSV或Excel文件,并将其转换为Pandas DataFrame格式。我使用了Dash的上传组件来处理文件上传操作,并在这个函数中完成了具体的解析逻辑。

5. 手动分配学生给指定导师


@app.callback(Output('assign-output', 'children'),
              Input('assign-button', 'n_clicks'),
              State('student-id', 'value'),
              State('teacher-name', 'value'))
def assign_student_to_teacher(n_clicks, student_id, teacher_name):
    ...

这个回调函数用于处理手动分配学生给指定导师的操作。当用户点击分配按钮时,该函数将被触发,并根据输入的学生学号和导师名字履行分配操作。

6. 更新上传文件后的界面


# 烘托第一个表格
@app.callback(Output('output-data-upload1', 'children'),
              Input('upload-data1', 'contents'),
              Input('upload-data1', 'filename'),
              Input('assign-output', 'children'))
def update_output1(contents, filename, assign_output):
    global teacher_df
    if contents is not None or teacher_df is not None:
        # 判别哪个输入触发了回调
        ctx = dash.callback_context
        triggered_input = ctx.triggered[0]['prop_id'].split('.')[0]
        if triggered_input == 'assign-output':
            # 假如是 assign-output 触发的回调,履行相应的操作
            # 返回更新后的内容
            print("触发分配更新烘托回调")
        else:
            children = parse_contents(contents, filename, "teacher")
        return html.Div([  # 返回一个 Div 包裹,确保在回调中可以更新内容
            html.Table([
                html.Thead(
                    [html.Tr([html.Th(col) for col in teacher_df.columns])]
                ),
                html.Tbody([
                    html.Tr([
                        html.Td(val) for val in row
                    ]) for row in teacher_df.values
                ])
            ]),
            assign_output  # 显现分配输出成果
        ])
    else:
        return assign_output  # 显现分配输出成果

这个回调函数用于更新上传文件后的界面。当用户上传了导师信息文件时,该函数将被触发,并更新界面以显现上传的文件内容。

7. 履行随机分配学生操作


# 履行随机分配学生操作
# 在回调函数中增加保存到CSV的逻辑
@app.callback(
    Output('random-output', 'children'),
    Input('random-button', 'n_clicks')
)
def random_assign_students_callback(n_clicks):
    global teacher_df, student_df, teacher_students, teacher_df_all, student_df_all
    if n_clicks > 0 and teacher_df is not None and student_df is not None:
        # 以教师名单中的人数列为根据,将教师及其对应的人数转换为字典
        teacher_dict = dict(zip(teacher_df['教师'], teacher_df['人数']))
        # 调用随机分配学生函数,传入教师人数字典和学生列表
        random_results = random_assign_students(teacher_dict, student_df)
        # 将已指定的教师和学生信息加入到随机分配成果中
        for teacher, students in teacher_students_zhiding.items():
            if teacher in random_results:
                random_results[teacher].extend(students)
            else:
                random_results[teacher] = students
        # 将随机分配成果保存为CSV文件
        save_random_results_to_csv(random_results)
        # 显现分配成果
        output = html.Div([
            html.H3('随机分配成果:'),
        ])
        # 遍历每个教师的学生列表
        for teacher, students in random_results.items():
            # 创立一个包含教师名和学生数量的表格
            teacher_table = html.Table([
                html.Thead(html.Tr([html.Th('教师'), html.Th('学生数量')])),
                html.Tbody([
                    html.Tr([
                        html.Td(teacher),
                        html.Td(len(students))
                    ])
                ])
            ])
            # 创立一个包含学生学号、名字、班级和电话的表格
            student_table = html.Table([
                html.Thead(html.Tr([html.Th('学生学号'), html.Th('学生名字'), html.Th('班级'), html.Th('电话')])),
                html.Tbody([
                    html.Tr([
                        html.Td(student[0]),  # 学生学号
                        html.Td(student[1]),  # 学生名字
                        html.Td(student_df_all.loc[student_df_all['学号'] == student[0], '班级'].values[0]),  # 班级
                        html.Td(student_df_all.loc[student_df_all['学号'] == student[0], '电话'].values[0])  # 电话
                    ]) for student in students
                ])
            ])
            # 将教师表格和学生表格放入一个 Div 中
            output.children.append(html.Div([teacher_table, student_table]))
    elif n_clicks > 0:
        output = html.Div('请先上传教师和学生信息文件')
    else:
        output = html.Div()
    return output

这个回调函数用于履行随机分配学生的操作。当用户点击随机分配按钮时,该函数将被触发,并随机将学生分配给导师。

8. 保存随机分配成果到CSV文件


def save_random_results_to_csv(random_results):
    # 创立一个空的DataFrame来存储成果
    result_df = pd.DataFrame(columns=['教师', '学生学号', '学生名字', '班级', '电话'])
    # 遍历每个教师的学生列表,将成果增加到DataFrame中
    for teacher, students in random_results.items():
        temp_df = pd.DataFrame(students, columns=['学生学号', '学生名字'])
        temp_df['教师'] = teacher
        temp_df['班级'] = temp_df['学生学号'].apply(lambda x: student_df_all.loc[student_df_all['学号'] == x, '班级'].values[0])
        temp_df['电话'] = temp_df['学生学号'].apply(lambda x: student_df_all.loc[student_df_all['学号'] == x, '电话'].values[0])
        result_df = pd.concat([result_df, temp_df], ignore_index=True)
    # 保存DataFrame为CSV文件
    result_df.to_csv('随机分配成果.csv', index=False)

这个函数用于将随机分配的成果保存到CSV文件中。我将每个导师分配的学生信息保存到一个CSV文件中,以便进一步剖析或导入到其他系统中。

结束语

这个脚本被我上传到了Gitee,有jy感兴趣的话可以下载下来玩玩,假如各位感兴趣的大佬给个star。
导师随机分配: 这个脚本是一个根据Dash结构的Web应用程序,用于教师和学生信息的办理和随机、指定分配。 (gitee.com)