File size: 11,310 Bytes
88af790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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}")