DMKJ_Test / test.py
GGuantdx's picture
yeeees
88af790 verified
Raw
History Blame Contribute Delete
11.3 kB
import streamlit as st
import pandas as pd
import io
import re
import difflib
# ==========================================
# 页面基础设置
# ==========================================
# 使用 centered 布局可以让页面更集中,两列宽度的拖拽框面积更大、更美观
st.set_page_config(page_title="竞赛积分全自动赋分系统", page_icon="🏆", layout="centered")
st.title("🏆 竞赛积分全自动赋分系统")
st.markdown("请在下方依次上传对应的四个数据文件(**支持点击上传,或直接将文件拖拽至虚线框内**)。")
st.divider() # 添加一条分割线,提升视觉层次
# ==========================================
# 文件上传区 (2x2 网格布局,增大拖拽面积)
# ==========================================
col1, col2 = st.columns(2)
with col1:
f_comp = st.file_uploader("请上传《需要录入的竞赛名录.xlsx》", type=["xlsx", "xls", "csv"])
with col2:
f_score = st.file_uploader("请上传《积分赋分参考.xlsx》", type=["xlsx", "xls", "csv"])
col3, col4 = st.columns(2)
with col3:
f_student = st.file_uploader("请上传《学生获奖统计.xlsx》", type=["xlsx", "xls", "csv"])
with col4:
f_template = st.file_uploader("请上传《系统批量导入模板.xlsx》", type=["xlsx", "xls", "csv"])
# 辅助函数:文件读取
def load_data(file, as_raw=False):
file.seek(0)
if file.name.endswith('.csv'):
return pd.read_csv(file, header=None if as_raw else 'infer')
else:
return pd.read_excel(file, header=None if as_raw else 0)
# ==========================================
# 数据处理区
# ==========================================
if f_comp and f_score and f_student and f_template:
st.success("✅ 文件已全部就绪,请点击下方按钮开始处理数据。")
if st.button("🚀 一键处理并生成导入文件", type="primary", use_container_width=True):
with st.spinner("系统正在处理数据,请稍候..."):
try:
# 1. 加载数据
df_comp = load_data(f_comp, as_raw=False)
df_score = load_data(f_score, as_raw=True)
df_student = load_data(f_student, as_raw=False)
df_template = load_data(f_template, as_raw=True)
valid_comps = df_comp['竞赛名称'].dropna().astype(str).unique().tolist()
# 名称清洗与匹配逻辑
def clean_name(name):
name = re.sub(r'(20\d{2}|第[一二三四五六七八九十百]+届|年度)', '', name)
name = re.sub(r'(总决赛|决赛|校内选拔赛|校内赛|选拔赛|系列赛)', '', name)
return re.sub(r'[^\w\u4e00-\u9fa5]', '', name).lower()
comp_clean_map = {c: clean_name(c) for c in valid_comps if len(clean_name(c)) >= 2}
student_comps_unique = df_student['竞赛项目名称'].dropna().astype(str).unique()
mapping_dict = {}
for s_name in student_comps_unique:
s_clean = clean_name(s_name)
best_match, best_match_score = None, 0
for c_orig, c_clean in comp_clean_map.items():
if c_clean in s_clean or s_clean in c_clean:
if len(c_clean) > best_match_score:
best_match_score = len(c_clean)
best_match = c_orig
if not best_match:
best_ratio = 0
for c_orig, c_clean in comp_clean_map.items():
ratio = difflib.SequenceMatcher(None, s_clean, c_clean).ratio()
if ratio > best_ratio:
best_ratio = ratio
best_match = c_orig
if best_ratio > 0.55: mapping_dict[s_name] = best_match
else:
mapping_dict[s_name] = best_match
df_student['最终匹配名录名称'] = df_student['竞赛项目名称'].map(mapping_dict)
df_matched_students = df_student.dropna(subset=['最终匹配名录名称']).copy()
# 2. 赋分规则解析
score_dict = {}
for index, row in df_score.iterrows():
row_str = "".join([str(v) for v in row.values])
level = None
if '院级' in row_str: level = '院级'
elif '校级' in row_str: level = '校级'
elif '省部级' in row_str or '市级' in row_str: level = '省部级(北京市级)'
elif '国家' in row_str: level = '国家级及以上'
if level:
nums = []
for v in row.values:
val_str = str(v).strip()
if re.match(r'^\d+(\.\d+)?$', val_str):
nums.append(float(val_str))
if len(nums) >= 10:
nums = nums[-10:]
score_dict[level] = {
'队长': {
'一等奖及以上': nums[0], '二等奖': nums[1],
'三等奖': nums[2], '优秀奖': nums[3], '参与但未获奖': nums[4]
},
'队员': {
'一等奖及以上': nums[5], '二等奖': nums[6],
'三等奖': nums[7], '优秀奖': nums[8], '参与但未获奖': nums[9]
}
}
if not score_dict:
st.error("数据读取失败:无法在《积分赋分参考.xlsx》中读取到有效的数字分数,请检查文件是否上传正确。")
st.stop()
# 3. 计算得分
def calculate_score(row):
try:
sort_val = float(row.get('获奖者排序', 0))
except:
sort_val = 0
role = '队长' if sort_val == 1.0 else '队员'
raw_level = str(row.get('获奖级别', '')).strip()
level = ''
if '国家' in raw_level: level = '国家级及以上'
elif '省' in raw_level or '市' in raw_level: level = '省部级(北京市级)'
elif '校' in raw_level: level = '校级'
elif '院' in raw_level: level = '院级'
raw_award = str(row.get('获奖等级', '')).strip()
award = '参与但未获奖'
if '特等' in raw_award or '一等' in raw_award: award = '一等奖及以上'
elif '二等' in raw_award: award = '二等奖'
elif '三等' in raw_award: award = '三等奖'
elif '优秀' in raw_award: award = '优秀奖'
if level in score_dict and role in score_dict[level] and award in score_dict[level][role]:
return score_dict[level][role][award]
return 0
df_matched_students['发放学分值'] = df_matched_students.apply(calculate_score, axis=1)
def format_award_for_export(award_str):
award_str = str(award_str).strip()
if '优秀' in award_str:
return '三等奖'
return award_str
df_matched_students['导出用奖项名称'] = df_matched_students['获奖等级'].apply(format_award_for_export)
# 4. 生成最终系统模板
head_rows = min(3, len(df_template))
template_head = df_template.iloc[0:head_rows].copy()
result_cols = None
for i in range(len(df_template)):
row_vals = [str(v) for v in df_template.iloc[i].values]
if '学号' in row_vals and '姓名' in row_vals:
result_cols = df_template.iloc[i].tolist()
break
if result_cols is None:
result_cols = ['学号', '姓名', '开始时间', '结束时间', '内容', '活动一级分类', '活动二级分类', '活动等级', '奖项内容', '学分类型', '发放学分值']
df_result = pd.DataFrame(columns=result_cols)
df_result['学号'] = df_matched_students['获奖者学号']
df_result['姓名'] = df_matched_students['获奖者姓名']
df_result['开始时间'] = df_matched_students['获奖时间']
df_result['内容'] = df_matched_students['最终匹配名录名称']
df_result['活动一级分类'] = '学科竞赛'
df_result['活动二级分类'] = '学科竞赛'
df_result['活动等级'] = df_matched_students['获奖级别']
df_result['奖项内容'] = df_matched_students['导出用奖项名称']
df_result['学分类型'] = '竞赛加分'
df_result['发放学分值'] = df_matched_students['发放学分值']
df_result.columns = template_head.columns
final_df = pd.concat([template_head, df_result], ignore_index=True)
output = io.BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
final_df.to_excel(writer, index=False, header=False)
processed_data = output.getvalue()
# ==========================================
# 处理完成及下载区
# ==========================================
st.divider()
st.success(f"数据处理完毕!共生成 {len(df_result)} 条积分记录。")
st.download_button(
label="📥 点击下载最终导入文件 (Excel格式)",
data=processed_data,
file_name="最终系统批量导入文件.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True
)
st.markdown("### 数据预览 (节选前10条)")
df_result.columns = result_cols
st.dataframe(df_result.head(10), use_container_width=True)
except Exception as e:
st.error(f"处理数据时出现异常,请检查文件格式。错误详情:{e}")