王志强@三亚理工职业学院分享 http://blog.sciencenet.cn/u/m13903001357 我从没见过月光,也没见有过月下的你。

博文

批量处理线上期末试卷(python+vba)

已有 2703 次阅读 2022-12-30 12:10 |系统分类:教学心得

引言

2022秋学期学校进行了线上期末考试,然而,期末试卷作为学科评估的重要材料,对存档有一定的要求。本文记录了使用程序自动化办公的方法,将导出的试卷批量重命名,并按学号顺序合并为一个PDF文件,提高存档质量,节约打印时间。

“期末试卷打包工具” 下载地址:

https://pan.baidu.com/s/1gYFgCMsOGyl__91DEWL-2g?pwd=sylg 

注意:需解压压缩包后程序方能正常使用。

准备工作

1. 以学习通为例,按班级导出word版本的试卷(选择word排版格式,整洁,方便修改)。

01.png

2. 教务系统中导出的学生名单。

3. 效果预览

51.jpg

52.jpg

程序设计

1. 标准化

假定导出的word版本的试卷中的指定位置有学生的姓名,我们在教务系统导出的学生名单中找到姓名对应的学号、班级等信息,重命名学生的试卷为“学号+姓名+班级+考试科目.docx”。

2. 修改卷面信息

学生填写的院系、班级、学号信息都有可能不准确,我们要以教务系统的为唯一标准,通过检索学生的姓名得到对应的院系、班级、学号信息,写入试卷。如果有需要,我们还需要在试卷的指定位置填写阅卷人、核分人的信息。

3. 导出为PDF文件

为方便打印与存档,我们需要写程序将修改后的word版试卷批量导出为pdf文件。

4. 合并PDF文件

将班级中所有学生的PDF格式试卷合并为一个PDF文件,方便后续打印。


程序实现

阅读程序实现的细节需要一定的计算机编程基础,不再赘述,python编译使用的源代码附于文末。


程序的使用

1. 下载程序包

“期末试卷打包工具” 下载地址:

https://pan.baidu.com/s/1gYFgCMsOGyl__91DEWL-2g?pwd=sylg 

提取码:sylg 复制这段内容后打开百度网盘手机App,操作更方便哦

3个程序使用的核心代码均来自互联网,没有二次开发。解压缩后,有3个文件夹:“1. 生成标准试卷” “2. 生成pdf” “3. 合并PDF”,请按步骤依次运行。

提示:python程序在windows10中编译,如果与你的系统不兼容,请使用源代码重新编译。

2. 生成学生信息对应的json文件

res文件夹中有一个名为stuInfo2.json的文件,其格式为:{"学号1":"姓名1","学号2":"姓名2",…},你也可以先将学生信息复制到stu.csv表格中,再通过运行csv2json.exe将csv表格中的内容导出为json文件。其中,csv表格中第一列是学号,第二列是姓名(姓名不能重复)。

02.png

3. 生成规范的试卷

将学习通中的试卷复制到程序“修改试卷.exe”所在的文件夹,双击运行。

03.png


4. 生成PDF


5. 合并PDF

34.png



附:源代码

  1#主程序代码
 2from win32com import client as wc
 3import os
 4import docx
 5import json
 6from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
 7from docx.shared import Pt
 8import warnings
 9from setttings import *
10
11
12def get_stu_info(file_path):
13    file_temp = docx.Document(file_path)
14    paras=file_temp.paragraphs
15    flag=0
16    for para in paras:
17        if para.text.find(name_eigen)>-1:
18            runs = para.text.split(" ")
19            for run in runs:
20                run=run.strip()
21                # 如果姓名关键字后有空格,后面的run就是姓名
22                if flag==1 and  len(run)>0:
23                    return run.strip()
24
25
26                if len(run)>0 and run.find(name_eigen)>-1:
27                    run_splits=run.split(name_eigen)
28                    for run_s in run_splits:
29                        # 如果姓名关键字后没空格,那么姓名紧跟在后面
30                        if flag==1 and len(run_s.strip())>0:
31                            return run_s
32                        if len(run_s.strip())==0:
33                            flag=1
34                        else:
35                            flag=0
36
37
38
39
40
41def insert_stu_num(file_path,stu_num,cls_name,stu_name):
42    file_temp = docx.Document(file_path)
43
44    tables = file_temp.tables
45    for table in tables:
46        for ii in range(len(table.rows)):
47            for jj in range(len(table.columns)):
48                cellData = str(table.cell(ii, jj).text).strip()
49                if cellData.find(calcu_eigen) > -1 and jj+1<len(table.columns):
50                    table.cell(ii, jj+1).text=calcu_name
51                    table.cell(ii, jj+1).alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
52                    table.cell(ii, jj+1). paragraphs[0].runs[0].font.size = Pt(8)
53
54                if cellData.find(marker_eigen) > -1 and jj+1<len(table.columns):
55                    table.cell(ii, jj+1).text=marker_name
56                    table.cell(ii, jj+1).alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
57                    table.cell(ii, jj+1). paragraphs[0].runs[0].font.size = Pt(8)
58
59                if cellData.find(chker_eigen) > -1 and jj+1<len(table.columns):
60                    table.cell(ii, jj+1).text=chker_name
61                    table.cell(ii, jj+1).alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
62                    table.cell(ii, jj+1). paragraphs[0].runs[0].font.size = Pt(8)          
63
64
65    paras=file_temp.paragraphs
66    flag=0
67    for para in paras:
68        for run in para.runs:
69            if flag>2:
70                # print("学习通排版后的试卷,正在直接修改学生的班级学号信息...")
71                para.text="院系:"+str(depart_name)+"  班级:"+str(cls_name)+"  姓名:"+str(stu_name)+"  学号:"+str(stu_num)
72                file_temp.save(os.getcwd()+'\\'+str(stu_num)+"+"+str(stu_name)+"+"+str(cls_name)+"+"+str(subject_name)+".docx")
73                return              
74
75            if run.text.find(stu_num_eigen)>-1:
76                flag+=1
77            elif run.text.find(cls_eigen)>-1:
78                flag+=1
79            elif run.text.find(depart_eigen)>-1:
80                flag+=1
81            elif run.text.find(major_eigen)>-1:
82                flag+=1
83
84    # 不是学习通的排版后试卷
85    print(str(stu_name),"的试卷不是学习通排版后的格式,效果可能不理想...")
86    for para in paras:
87        for run in para.runs:
88            if run.text.find(stu_num_eigen)>-1:
89                flag=91
90            elif run.text.find(cls_eigen)>-1:
91                flag=92
92            elif run.text.find(depart_eigen)>-1:
93                flag=93
94            elif run.text.find(major_eigen)>-1:
95                flag=94
96            else:
97                flag=0
98
99            if flag==91:
100                run.text=str(stu_num)
101            elif flag==92:
102                run.text=str(cls_name)
103            elif flag==93:
104                run.text=str(depart_name)
105            elif flag==94:
106                run.text=str(major_name)
107
108
109    file_temp.save(os.getcwd()+'\\'+str(stu_num)+"+"+str(stu_name)+"+"+str(cls_name)+"+"+str(subject_name)+".docx")             
110    return    
111
112if __name__ == '__main__':
113    # 将doc转为docx文件,方便使用docx插件修改word文档
114    # 假定学生试卷和主程序放在一个目录中
115    word = wc.Dispatch("Word.Application")
116    lst_folders = os.listdir()
117
118
119    p_info="正在生成临时文件"
120    print(p_info)
121    ii=0
122    for lst_folder in lst_folders:
123        if str(lst_folder).endswith(".doc"):
124            os.system("cls")
125            ii+=1
126
127            if ii%3==0:
128                p_info="正在生成临时文件 /"
129            elif ii%3==1:
130                p_info="正在生成临时文件 --"
131            else:
132                p_info="正在生成临时文件 \\"
133            print(p_info)
134            doc = word.Documents.Open(os.getcwd()+"\\"+lst_folder)
135            # 为转换好的docx添加前缀,便于更好地区分
136            doc.SaveAs(os.getcwd()+"\\"+prefix_name+"+"+str(lst_folder)+"x"12
137    doc.Close()
138    word.Quit()
139
140
141    # 将教务系统中导出的学生信息存储到stuInfo2.json中
142    # json的格式为: "学号": "姓名"
143    with open('./res/stuInfo2.json''r', encoding='utf-8'as fp:
144        stu_infos = json.load(fp)
145        os.system("cls")
146        cls_name=input('请输入班级名称:')
147
148        lst_folders = os.listdir()
149        warn_infos=[]
150        for lst_folder in lst_folders:
151            print('.',end="")
152            if str(lst_folder).endswith(".docx"and str(lst_folder).startswith(prefix_name):
153                stu_name=get_stu_info(os.getcwd()+'\\'+lst_folder)
154                stu_num="未找到"
155                for stu in stu_infos:
156                    if stu_infos[stu]==stu_name:
157                        stu_num=stu
158                        break
159                if stu_num=="未找到":
160                    warn_infos.append([lst_folder,"姓名:"+str(stu_name)," 学号没有找到"])      
161
162                insert_stu_num(os.getcwd()+'\\'+lst_folder,stu_num,cls_name,stu_name)
163        print("\n\n")
164        for warn_info in warn_infos:
165            print(warn_info)
166    input("已完成...")

settings.py,用来读取res文件夹中的配置信息,如阅读人、院系、科目名称等。

 1# 配置文件代码
2# settings.py
3
4import os
5import sys, time
6
7
8
9Settings={"文档前缀":" ","考试科目":" ","试卷姓名特征字":"姓名","复核人特征字":"","阅卷人特征字":"","合分人特征字":"","复核人姓名":"","阅卷人姓名":"","合分人姓名":"","院系特征字":"","学号特征字":"","班级特征字":"","专业特征字":"","院系名称":"","专业名称":""}
10
11with open('./res/settings.ini''r', encoding='utf-8'as fp:
12    for line in fp:
13        if not line.strip().startswith('\ufeff'and len(line.strip())>2 and not line.strip().startswith("#"and len(line.split('='))>0 and len(line.split('=')[1].strip())>0:
14            Settings[line.split('=')[0].strip()]=line.split('=')[1].strip()
15
16
17
18
19prefix_name=Settings['文档前缀']
20subject_name=Settings['考试科目']
21name_eigen=Settings['试卷姓名特征字']
22chker_eigen=Settings["复核人特征字"]
23chker_name=Settings["复核人姓名"]
24calcu_eigen=Settings["合分人特征字"]
25calcu_name=Settings["合分人姓名"]
26marker_eigen=Settings["阅卷人特征字"]
27marker_name=Settings["阅卷人姓名"]
28depart_eigen=Settings["院系特征字"]
29cls_eigen=Settings["班级特征字"]
30stu_num_eigen=Settings["学号特征字"]
31major_eigen=Settings["专业特征字"]
32depart_name=Settings["院系名称"]
33major_name=Settings["专业名称"]
34
35
36for st in Settings:
37    print(st,"\t",Settings[st])
38print("配置文件中的信息如上表所示,程序将根据word文档中的特征字定位要修改的内容。")
39print("你可以手动修改位于res文件夹中的配置文件:settings.ini")
40print("若要修改配置文件,请在10秒内关闭程序")
41
42for ii in range(10):
43    print(".",end="")
44    sys.stdout.flush()
45    time.sleep(1)






https://blog.sciencenet.cn/blog-3273400-1369764.html

上一篇:腾讯会议搭档:旷课迟到早退统计
下一篇:机房助手使用指南(适用于高校机房课程)
收藏 IP: 59.50.32.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-25 18:47

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部