最近我遇到了一个有趣的问题,我的导师想要提前将下一届的学生分配给各位老师。这听起来似乎没什么大不了的,但实际上,这可是个挺头疼的事情。
想象一下,你作为一名导师,要负责领导一群研究生。你希望这些学生和你的研究方向相符,又能发挥他们的潜力。但问题是,如果分配不公平,可能会导致资源浪费,甚至影响到学生的学习和研究。
所以,我决定动手写一个随机分配的脚本来解决这个问题。这样一来,分配就不会受到个人喜好或偏见的影响,而是完全随机的,公平而且透明。
在这篇博客里,我将和大家分享我的思路和实现过程。希望这对于遇到类似问题的人有所启发和帮助!
主要技术栈
- Dash框架:Dash是基于Python的Web应用框架,用于构建交互式Web应用程序。
- Pandas库:Pandas是Python中用于数据处理和分析的强大库。
分配过程
首先,让我们简要地了解一下这个脚本的工作原理。我编写了一个基于Dash的Python应用程序,其中包含了两个重要功能:手动分配和随机分配。
- 手动分配: 用户可以手动输入学生的学号和导师的姓名,然后单击按钮将学生分配给指定的导师。这对于一些特殊情况下的指定分配非常有用。
- 随机分配: 用户可以上传包含导师和学生信息的CSV或Excel文件,然后单击按钮执行随机分配。在这种模式下,脚本将根据每个导师可以分配的学生数量随机将学生分配给导师。
大致逻辑
现在,让我们深入了解一下脚本的逻辑。我使用了Dash来构建用户界面,这是一个基于Python的Web应用程序框架。通过Dash,我可以轻松地创建交互式组件,并将它们与Python函数进行连接。
在数据处理方面,我使用了Pandas库来处理上传的CSV或Excel文件。Pandas提供了丰富的数据操作功能,使我能够轻松地读取、处理和操作数据。
随机分配的部分则利用了Python的随机模块。我首先将导师和他们可以分配的学生数量存储在一个字典中,然后根据每个导师的指定数量随机选择学生,并将它们分配给对应的导师。
最后,我使用了Dash的回调函数来实现交互逻辑。每当用户执行操作时,回调函数将被触发,并相应地更新界面或执行其他操作。
代码
当我设计这个随机分配脚本时,我将功能分为几个部分。让我逐段解释每个部分的代码:
1. 导入必要的库和模块
python
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应用程序
python
app = dash.Dash(__name__)
这行代码创建了一个Dash应用程序实例。我将其命名为app
,并指定__name__
作为应用程序的名称。
3. 设计应用程序的布局
python
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. 解析上传的文件
python
# 上传函数
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. 手动分配学生给指定导师
python
@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. 更新上传文件后的界面
python
# 渲染第一个表格
@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. 执行随机分配学生操作
python
# 执行随机分配学生操作
# 在回调函数中添加保存到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文件
python
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)