教学相关工具
从超星导出压缩包提取某些类型文件,如.docx、.doc等文件
使用场景:如期末收取学生实习报告,从超星导出后进行处理,几秒钟就处理完毕,不再是几小时;也不再需要课代表统一收集整理,事后再整理。
import os
import pathlib
import py7zr
import rarfile
import shutil
import tempfile
import zipfile
# 文件名计数器,在一个压缩包里可能有多个相同后缀的文档,解压后形成 学号-姓名.ext 学号-姓名-1.ext 等等
name_counter = {}
def main():
# 请修改下面参数
extract([], "2.zip", [".doc", ".docx"], "实习报告")
def extract(layers=[], filename=None, exts=[], dst=None, delete=False):
"""从指定的压缩文件中提取指定的文件类型。
layers: list[str] 当前所处的层,即压缩包文件名列表
filename: str 压缩包名
exts: list[str] 文件类型列表
dst: str 目标路径
delete: 提取完毕后是否删除压缩包
"""
print(filename)
# 创建路径
if dst and (not os.path.exists(dst)):
os.mkdir(dst)
# 添加到新的层,防止嵌套干扰
newlayers = []
for name in layers:
newlayers.append(name)
newlayers.append(filename)
if filename.endswith(".7z"):
extract_7z(newlayers, filename, exts, dst, delete)
if filename.endswith(".rar"):
extract_rar(newlayers, filename, exts, dst, delete)
if filename.endswith(".zip"):
extract_zip(newlayers, filename, exts, dst, delete)
if delete:
os.remove(filename)
# 获取文件名,如:"/path/test.txt" => "test.txt"
def get_name(filename):
return pathlib.Path(filename).name
# 获取文件名主干,如:"/path/test.txt" => "test"
def get_stem(filename):
return pathlib.Path(filename).stem
# 获取文件名后缀,如:"/path/test.txt" => ".txt"
def get_suffix(filename):
return pathlib.Path(filename).suffix
# 处理文件列表,列表中所有文件已经解压
def process_files(temppath, layers, files, exts, dst, delete):
for file in files:
src = os.path.join(temppath, file)
ext = get_suffix(file)
if ext in exts:
newname = get_name(file)
if len(layers) >= 2:
stem = get_stem(layers[1])
newname = stem + ext
if newname in name_counter:
count = name_counter[newname]
newname = stem + "-" + str(count) + ext
name_counter[newname] = count + 1
else:
name_counter[newname] = 1
if dst:
shutil.copyfile(src, os.path.join(dst, newname))
else:
shutil.copyfile(src, newname)
os.remove(src)
if ext in [".7z", ".rar", ".zip"]:
extract(layers, src, exts, dst, True)
def extract_7z(layers, filename, exts, dst, delete):
files = []
temppath = tempfile.TemporaryDirectory().name
with py7zr.SevenZipFile(filename) as sz:
for file in sz.getnames():
ext = get_suffix(file)
if ext in exts + [".7z", ".rar", ".zip"]:
files.append(file)
sz.extract(path=temppath, targets=files)
process_files(temppath, layers, files, exts, dst, delete)
def extract_rar(layers, filename, exts, dst, delete):
files = []
temppath = tempfile.TemporaryDirectory().name
with rarfile.RarFile(filename) as rf:
for file in rf.namelist():
ext = get_suffix(file)
if ext in exts + [".7z", ".rar", ".zip"]:
files.append(file)
rf.extract(file, temppath)
process_files(temppath, layers, files, exts, dst, delete)
def extract_zip(layers, filename, exts, dst, delete):
files = []
temppath = tempfile.TemporaryDirectory().name
with zipfile.ZipFile(filename, allowZip64=True, metadata_encoding="gbk") as zf:
for file in zf.namelist():
ext = get_suffix(file)
if ext in exts + [".7z", ".rar", ".zip"]:
files.append(file)
zf.extract(file, temppath)
process_files(temppath, layers, files, exts, dst, delete)
if __name__ == "__main__":
main()
删除无用目录,如android源码中的build、.idea、.gradle等目录
学生提交的作品中总有一些不需要的内容,用这个脚本方便处理。
import os
import shutil
def main():
# 请修改下面参数
rmdirs(
"/run/media/znw/Ventoy/刻盘/移动平台应用开发-2023计算机科学与及技术(专升本)2班-卓能文/7.实验报告资料/",
[".git", ".idea", ".gradle", "build"],
)
def rmdirs(top, deldirs):
"""
删除指定目录下,部分子目录(可以嵌套在层次很深的子目录下)
top: 根目录
deldirs: 需要删除的目录列表
"""
for root, dirs, files in os.walk(top, topdown=False):
for name in dirs:
if name in deldirs:
print(root, "/", name, sep="")
shutil.rmtree(os.path.join(root, name), ignore_errors=True)
if __name__ == "__main__":
main()
对文件及目录统一命名
学生提交的文档无论怎么强调,收上来的名字五花八门。文件名中只要包含学生的学号或名字,统一命名成“学号-姓名.ext”形式。
from os import path
import os
import pandas as pd
def main():
# 请修改下面参数
tidynames("data.xlsx", "源码")
def nametidy(data, root, filename):
"""
目录或文件名标准化处理
"""
dir = path.dirname(filename)
base = path.basename(filename)
name, ext = path.splitext(base)
if "-" in name:
parts = name.split("-")
if len(parts) == 2:
if parts[0] in data["学号"].values and parts[1] in data["姓名"].values:
return
for i, row in data.iterrows():
if str(row["学号"]) in name or row["姓名"] in name:
newname = f"{row['学号']}-{row['姓名']}{ext}"
newfilename = path.join(root, dir, newname)
os.rename(path.join(root, filename), newfilename)
def tidynames(datafilename, top):
"""
从datafilename excel文件中导入学号、姓名,然后在top目录下遍历所有的文件或目录,只要文件名或目录名包含学号或姓名,统一修改成“学号-姓名”或“学号-姓名.ext”格式。
datefilename: xlsx文件,有两列,包含“学号、姓名”
top: 从指定的目录开始处理
"""
data = pd.read_excel(datafilename)
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
nametidy(data, root, name)
for name in dirs:
nametidy(data, root, name)
if __name__ == "__main__":
main()
对实验报告统一打分
再也不需要打开,编辑、保存了。
import docx
import os
import pandas as pd
# 实验报告所在路径
base = "/run/media/znw/Ventoy/刻盘/数据可视化-2022级计算机科学与技术(专升本)5班-卓能文/9.实验资料/1.平时实验资料/实验1- ECharts数据可视化(一)"
data = pd.read_excel("data.xlsx")
for i, datarow in data.iterrows():
path = str(datarow.学号) + "-" + datarow.姓名
filename = base + "/" + path + "/" + path + ".docx"
if os.path.exists(filename):
try:
doc = docx.Document(filename)
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
if cell.text.startswith("成绩评定"): # 检查单元格内容
cell.text = "" # 清空单元格内容
cell.paragraphs[0].add_run("成绩评定:").bold = True
cell.paragraphs[0].add_run("\n" + str(datarow.成绩))
doc.save(filename)
except Exception as e:
print(path + " 文件读取失败。原因: " + str(e))
finally:
continue
else:
print(path + " 不存在")
用数据及模板批量生成.docx文档
适合生成项目评分表之类的文档。准备一个template.docx文档,在需要用数据替换的地方,用{字段名}
标注。程序将data.txt
中的数据替换模板中的内容,并给每个学生成一个学号-姓名.docx
文档。
源码 Windows版 Linux版,其它操作系统请自行编译。
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/lukasjarosch/go-docx"
)
func main() {
content, err := os.ReadFile("data.txt")
if err != nil {
log.Fatal(err)
}
lines := strings.Split(string(content), "\n")
headers := strings.Fields(strings.TrimSpace(lines[0]))
for _, line := range lines[1:] {
replaceMap := docx.PlaceholderMap{}
fields := strings.Fields(line)
for i, header := range headers {
replaceMap[header] = fields[i]
}
// read and parse the template docx
doc, err := docx.Open("template.docx")
if err != nil {
panic(err)
}
// replace the keys with values from replaceMap
err = doc.ReplaceAll(replaceMap)
if err != nil {
panic(err)
}
// write out a new file
fileName := fmt.Sprintf("%s-%s.docx", fields[0], fields[1])
err = doc.WriteToFile(fileName)
if err != nil {
panic(err)
}
}
}
教务系统期末成绩导入
将平时成绩或期末成绩按照学号、姓名、成绩
在excel中准备好,替换源码中相应位置数据,然后将整个脚本复制到到浏览器中运行即完成成绩录入,方便、快捷、不易出错。
// 为了避免在FireFox中多次运行出现重复定义问题
// 包装开始
(() => {
// 修改下面的成绩,可以直接从excel复制粘贴,成绩的顺序必须和教务系统的名单一致
const scoreRaw = `221124010014 吴芳 87
221124010062 邹敏 81
221124010105 李昌蔚 92
221124010114 余传奇 92
221124010155 刘长青 83
221124010172 马欢 91
221124010033 罗阿甲 92
221124010138 罗超 92
221124010161 江小朋 98
221124010166 毛呷明 86
221124010174 益西翁扎 81
221124010128 高梓晔 82
221124010147 王尧 95
221124010154 周含鸣 95
221124010167 吴坤生 94
221124010007 王樱蓉 83
221124010049 汪洋 89
221124010050 陈杰 84
221124010090 赵禹辰 81
221124010037 吴星怡 94
221124010064 郑龙 83
221124010111 陈天祺 95
221124010143 刘静 97
221124010148 刘雨煌 81
221124010183 纪垚林 86
221124010184 格桑卓玛 90
221124010035 许越 81
221124010063 刘威 81
221124010091 邓灵 83
221124010135 宋庆 93
221124010140 刘津良 83
221124010134 谯小峰 98
221124010139 陈飞 91
221124010162 王炯炯
221124010182 易小勇 94
221124010003 廖禹婷 87
221124010065 胡开红 98
221124010066 彭凯歌 84
221124010122 何家豪 82
221124010149 马清玲 98
`;
// 分数
const scores = {};
// 按行进行切分
const rows = scoreRaw.trim().split("\n");
for (const row of rows) {
const fields = row
.trim()
.split(/ |\t/)
.map((e) => e.trim())
.filter((e) => e !== "");
scores[fields[0]] = fields;
}
// 找到录入成绩对应的文档
let doc = document; // 默认为top文档,即当前页面所在的文档
for (let i = 0; i < frames.length; i++) {
const d = frames[i].document;
if (d.title === "教学记录-YETHAN以专教学信息服务平台") {
doc = d;
break;
}
}
// 填充到每个输入框
let ids = doc.getElementsByName("studentId"); // 平时成绩录入时学号
if (ids.length === 0) {
ids = doc.getElementsByName("student_id"); // 期末成绩录入时学号
}
const marks = doc.getElementsByName("mark");
for (let i = 0; i < marks.length; i++) {
const id = ids[i].value;
const row = scores[id];
if (row) {
// 检查是否有该学生记录
const score = row[2];
if (score) {
// 判断成绩是否存在
marks[i].value = score;
} else {
alert(`学号:${id} 学生无成绩!!!`);
}
} else {
alert(`学号:${id} 学生无记录!!!`);
}
}
// 包装结束
})();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话