diff --git a/工具v3/Ui_批量收录题目.py b/工具v3/Ui_批量收录题目.py new file mode 100644 index 00000000..3c3d4fba --- /dev/null +++ b/工具v3/Ui_批量收录题目.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file '批量收录题目.ui' +## +## Created by: Qt User Interface Compiler version 6.6.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QCheckBox, QHBoxLayout, QLabel, + QLineEdit, QPushButton, QSizePolicy, QSplitter, + QWidget) + +class Ui_Form(object): + def setupUi(self, Form): + if not Form.objectName(): + Form.setObjectName(u"Form") + Form.resize(342, 109) + self.pushButton_exec = QPushButton(Form) + self.pushButton_exec.setObjectName(u"pushButton_exec") + self.pushButton_exec.setGeometry(QRect(260, 10, 71, 91)) + font = QFont() + font.setBold(True) + self.pushButton_exec.setFont(font) + self.splitter = QSplitter(Form) + self.splitter.setObjectName(u"splitter") + self.splitter.setGeometry(QRect(10, 10, 241, 88)) + self.splitter.setOrientation(Qt.Vertical) + self.widget = QWidget(self.splitter) + self.widget.setObjectName(u"widget") + self.horizontalLayout = QHBoxLayout(self.widget) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.label = QLabel(self.widget) + self.label.setObjectName(u"label") + + self.horizontalLayout.addWidget(self.label) + + self.lineEdit_startingid = QLineEdit(self.widget) + self.lineEdit_startingid.setObjectName(u"lineEdit_startingid") + self.lineEdit_startingid.setDragEnabled(False) + self.lineEdit_startingid.setReadOnly(False) + + self.horizontalLayout.addWidget(self.lineEdit_startingid) + + self.splitter.addWidget(self.widget) + self.layoutWidget = QWidget(self.splitter) + self.layoutWidget.setObjectName(u"layoutWidget") + self.horizontalLayout_2 = QHBoxLayout(self.layoutWidget) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.label_2 = QLabel(self.layoutWidget) + self.label_2.setObjectName(u"label_2") + + self.horizontalLayout_2.addWidget(self.label_2) + + self.lineEdit_editor = QLineEdit(self.layoutWidget) + self.lineEdit_editor.setObjectName(u"lineEdit_editor") + + self.horizontalLayout_2.addWidget(self.lineEdit_editor) + + self.splitter.addWidget(self.layoutWidget) + self.layoutWidget_2 = QWidget(self.splitter) + self.layoutWidget_2.setObjectName(u"layoutWidget_2") + self.horizontalLayout_3 = QHBoxLayout(self.layoutWidget_2) + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.label_3 = QLabel(self.layoutWidget_2) + self.label_3.setObjectName(u"label_3") + + self.horizontalLayout_3.addWidget(self.label_3) + + self.lineEdit_origin = QLineEdit(self.layoutWidget_2) + self.lineEdit_origin.setObjectName(u"lineEdit_origin") + + self.horizontalLayout_3.addWidget(self.lineEdit_origin) + + self.splitter.addWidget(self.layoutWidget_2) + self.layoutWidget_3 = QWidget(self.splitter) + self.layoutWidget_3.setObjectName(u"layoutWidget_3") + self.horizontalLayout_4 = QHBoxLayout(self.layoutWidget_3) + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.label_4 = QLabel(self.layoutWidget_3) + self.label_4.setObjectName(u"label_4") + + self.horizontalLayout_4.addWidget(self.label_4) + + self.lineEdit_suffix = QLineEdit(self.layoutWidget_3) + self.lineEdit_suffix.setObjectName(u"lineEdit_suffix") + self.lineEdit_suffix.setEnabled(False) + + self.horizontalLayout_4.addWidget(self.lineEdit_suffix) + + self.checkBox_suffix = QCheckBox(self.layoutWidget_3) + self.checkBox_suffix.setObjectName(u"checkBox_suffix") + + self.horizontalLayout_4.addWidget(self.checkBox_suffix) + + self.splitter.addWidget(self.layoutWidget_3) + + self.retranslateUi(Form) + + QMetaObject.connectSlotsByName(Form) + # setupUi + + def retranslateUi(self, Form): + Form.setWindowTitle(QCoreApplication.translate("Form", u"\u6279\u91cf\u6536\u5f55\u65b0\u9898", None)) + self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u6536\u5f55\u65b0\u9898", None)) + self.label.setText(QCoreApplication.translate("Form", u"\u8d77\u59cbID", None)) + self.lineEdit_startingid.setInputMask("") + self.lineEdit_startingid.setPlaceholderText(QCoreApplication.translate("Form", u"\u6570\u5b57\u9898\u53f7(\u901a\u5e38\u662f10000n+1)", None)) + self.label_2.setText(QCoreApplication.translate("Form", u"\u7f16\u8f91\u8005", None)) + self.lineEdit_editor.setPlaceholderText(QCoreApplication.translate("Form", u"\u7f16\u8f91\u8005\u59d3\u540d", None)) + self.label_3.setText(QCoreApplication.translate("Form", u"\u6765\u6e90", None)) + self.lineEdit_origin.setText(QCoreApplication.translate("Form", u"\u81ea\u62df\u9898\u76ee", None)) + self.lineEdit_origin.setPlaceholderText(QCoreApplication.translate("Form", u"\u9898\u76ee\u6765\u6e90, \u901a\u5e38\u4e3a \u81ea\u62df\u9898\u76ee", None)) + self.label_4.setText(QCoreApplication.translate("Form", u"\u540e\u7f00", None)) + self.lineEdit_suffix.setText(QCoreApplication.translate("Form", u"\u8bd5\u9898", None)) + self.lineEdit_suffix.setPlaceholderText(QCoreApplication.translate("Form", u"\u901a\u5e38\u4e3a \u8bd5\u9898", None)) + self.checkBox_suffix.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u540e\u7f00", None)) + # retranslateUi + diff --git a/工具v3/database_tools_2.py b/工具v3/database_tools_2.py index 4d99c1d8..414c680c 100644 --- a/工具v3/database_tools_2.py +++ b/工具v3/database_tools_2.py @@ -54,8 +54,13 @@ def SaveTextFile(data,filepath): #写入文本格式的文件 return filepath #返回文件名 def AppendTextFile(string,filepath): #在文本文件后添加内容 - with open(filepath,"a",encoding = "u8") as f: - f.write(string.strip()+"\n\n") + try: + with open(filepath,"a",encoding = "u8") as f: + f.write(string.strip()+"\n\n") + except: + makedir(os.path.split(filepath)[0]) + with open(filepath,"w",encoding = "u8") as f: + f.write(string.strip()+"\n\n") return filepath #返回文件名 def SortDict(adict): #按字典项顺序排序字典 @@ -272,9 +277,13 @@ def spareIDs(): #返回空闲题号, 已更新为适合mariadb的版本 return output #返回的是一个多行的字符串, 每一行中含有一个空闲题号的闭区间 -def NextSpareID(num,adict): #返回adict中下一个空闲的题号 +def NextSpareID(num): #返回adict中下一个空闲的题号 + mydb = connect(hostname = "wwylss.synology.me", port = "13306", username="root", pwd="Wwy@0018705", db = "tikutest") + mycursor = mydb.cursor() + mycursor.execute("SELECT ID FROM problems;") + idlist = [ret[0] for ret in mycursor.fetchall()] num = int(num) - while str(num).zfill(6) in adict: + while str(num).zfill(6) in idlist: num += 1 return num @@ -385,41 +394,10 @@ def CreateNewProblem(id,content,origin,dict,editor): # 构建一道新题目的 return NewProblem # 返回一道新题目的字典, 已赋新的ID, 内容, 来源和编辑者 -def AddProblemstoDict(startingid,raworigin,problems,editor,indexdescription,thedict): #将来自GenerateProblemListFromString的列表中的题目添加到thedict字典 - id = int(startingid) - currentsuffix = problems[0][1] - problemindex = 0 - for p_and_suffix in problems: - p, suffix = p_and_suffix - pid = str(id).zfill(6) - if pid in thedict: - print("ID %s 已被使用."%pid) - return 1 - else: - if suffix == currentsuffix: - problemindex += 1 - else: - problemindex = 1 - currentsuffix = suffix - origin = raworigin + suffix + indexdescription.strip() + ("" if indexdescription.strip() == "" else str(problemindex)) - newproblem = CreateNewProblem(pid,p.strip(),origin,thedict,GetDate() + "\t" + editor) - if "blank" in p: - newproblem["genre"] = "填空题" - newproblem["space"] = "" - elif "bracket" in p: - newproblem["genre"] = "选择题" - newproblem["space"] = "" - else: - newproblem["genre"] = "解答题" - newproblem["space"] = "4em" - thedict[pid] = newproblem - maxsim,argmaxsim = detectmaxsim(pid,[pid],thedict) - print("已收录题号: %s, 最接近题目: %s, 相似程度: %.3f, 题目类型: %s, 题目来源: %s, 题目内容: %s"%(pid,argmaxsim,maxsim,newproblem["genre"],origin,p)) - id += 1 - return 0 - -def AddProblemstoDict2024(startingid,raworigin,problems,editor,indexed,thedict): #将来自GenerateProblemListFromString的列表中的题目添加到thedict字典, 返回题号列表(包括用老题号替代的题目) +def AddProblemstoDict2024(startingid,raworigin,problems,editor,indexed): #将来自GenerateProblemListFromString的列表中的题目添加到thedict字典, 返回题号列表(包括用老题号替代的题目) + mydb = connect(hostname = "wwylss.synology.me", port = "13306", username="root", pwd="Wwy@0018705", db = "tikutest") + mycursor = mydb.cursor() idlist = [] id = int(startingid) currentsuffix = problems[0][1] @@ -427,50 +405,60 @@ def AddProblemstoDict2024(startingid,raworigin,problems,editor,indexed,thedict): for p_and_suffix_and_meta in problems: p, suffix, meta = p_and_suffix_and_meta pid = str(id).zfill(6) - if pid in thedict: - print("ID %s 已被使用."%pid) - return 1 + if suffix == currentsuffix: + problemindex += 1 else: - if suffix == currentsuffix: - problemindex += 1 + problemindex = 1 + currentsuffix = suffix + if indexed: + origin = {"来源": raworigin + suffix, "题号": problemindex} + else: + origin = {"来源": raworigin + suffix} + if not "rep" in meta: + if "blank" in p: + genre = "填空题" + space = "" + elif "bracket" in p: + genre = "选择题" + space = "" else: - problemindex = 1 - currentsuffix = suffix - if indexed: - origin = {"来源": raworigin + suffix, "题号": problemindex} - else: - origin = {"来源": raworigin + suffix} - if not "rep" in meta: - newproblem = CreateNewProblem(pid,p.strip(),origin,thedict,GetDate() + "\t" + editor) - if "blank" in p: - newproblem["genre"] = "填空题" - newproblem["space"] = "" - elif "bracket" in p: - newproblem["genre"] = "选择题" - newproblem["space"] = "" - else: - newproblem["genre"] = "解答题" - newproblem["space"] = "4em" - if "same" in meta: - for sid in meta["same"]: - thedict[sid]["same"].append(pid) - newproblem["same"].append(sid) - if "related" in meta: - for sid in meta["related"]: - thedict[sid]["related"].append(pid) - newproblem["related"].append(sid) - if "unrelated" in meta: - for sid in meta["unrelated"]: - thedict[sid]["unrelated"].append(pid) - newproblem["unrelated"].append(sid) - thedict[pid] = newproblem - maxsim,argmaxsim = detectmaxsim(pid,[pid],thedict) - print("已收录题号: %s, 最接近题目: %s, 相似程度: %.3f, 题目类型: %s, 题目来源: %s, 题目内容: %s"%(pid,argmaxsim,maxsim,newproblem["genre"],origin,p)) - id += 1 - idlist.append(pid) - else: - idlist.append(meta["rep"]) - print(f"该题 {idlist[-1]} {p} 已在题库中, 不必收录.") + genre = "解答题" + space = "4em" + sql = "INSERT INTO problems (ID,content,genre,origin,space) VALUE (%s,%s,%s,%s,%s);" + val = (pid,p.strip(),genre,json.dumps(origin,ensure_ascii=False),space) + mycursor.execute(sql,val) + sql = "INSERT INTO edit_history (ID,date,editor) VALUE (%s,%s,%s);" + val = (pid,GetDate(),editor.strip()) + mycursor.execute(sql,val) + if "same" in meta: + sql = "INSERT INTO same (ID,SAME_ID) VALUE (%s,%s);" + for sid in meta["same"]: + val = (pid, sid) + if pid > sid: + val = (sid, pid) + mycursor.execute(sql,val) + if "related" in meta: + sql = "INSERT INTO related (ID,RELATED_ID) VALUE (%s,%s);" + for rid in meta["related"]: + val = (pid, rid) + if pid > rid: + val = (rid, pid) + mycursor.execute(sql,val) + if "unrelated" in meta: + sql = "INSERT INTO unrelated (ID,UNRELATED_ID) VALUE (%s,%s);" + for uid in meta["unrelated"]: + val = (pid, uid) + if pid > uid: + val = (uid, pid) + mycursor.execute(sql,val) + print(f"已收录题号: {pid}, 题目类型: {genre}, 题目来源: {origin['来源'] + ('试题'+str(origin['题号'])) if '题号' in origin else ''}, 题目内容: {p.strip()}") + id += 1 + idlist.append(pid) + else: + idlist.append(meta["rep"]) + print(f"该题 {idlist[-1]} {p} 已在题库中, 不必收录.") + mydb.commit() + mydb.close() return idlist def CreateIDLinks(old_id_list,new_id_list,*thedict): #建立已有id和新id之间的联系, thedict为可选, 选中的话即为当前字典, 会从new_id_list中排除当前字典中有的项 diff --git a/工具v3/批量收录题目.py b/工具v3/批量收录题目.py new file mode 100644 index 00000000..afe4ca69 --- /dev/null +++ b/工具v3/批量收录题目.py @@ -0,0 +1,51 @@ +from PySide6.QtWidgets import QWidget, QApplication, QFileDialog +from Ui_批量收录题目 import Ui_Form +from database_tools_2 import * + +class MyWindow(QWidget,Ui_Form): + def __init__(self): + super().__init__() + self.setupUi(self) + self.bind() + + def bind(self): + self.suffix_checked = True + self.checkBox_suffix.toggled.connect(self.togglesuffix) + self.pushButton_exec.clicked.connect(self.exec) + def togglesuffix(self): + self.suffix_checked = self.checkBox_suffix.isChecked() + if self.suffix_checked: + self.lineEdit_suffix.setEnabled(True) + else: + self.lineEdit_suffix.setDisabled(True) + def exec(self): + starting_id = self.lineEdit_startingid.text().strip().zfill(6) + raworigin = self.lineEdit_origin.text().strip() + filename = "临时文件/新题比对.tex" #题目的来源.tex文件 + editor = self.lineEdit_editor.text().strip() + if self.checkBox_suffix.isChecked(): + Indexed = True + else: + Indexed = False + idlistpath = "文本文件/新题收录列表.txt" + problems = GenerateProblemListFromString2024(ReadTextFile(filename)) + # pro_dict = load_dict("../题库0.3/Problems.json") + idlist = AddProblemstoDict2024(NextSpareID(starting_id),raworigin,problems,editor,Indexed) + # save_dict(SortDict(pro_dict),r"../题库0.3/Problems.json") + AppendTextFile(f"{GetDate()}-{GetTime()}\n{generate_exp(idlist)}",idlistpath) + os.system(f"code {idlistpath}") + + + + + + + + + +if __name__ == '__main__': + app = QApplication([]) + windows = MyWindow() + windows.show() + app.exec() + diff --git a/工具v3/批量收录题目.ui b/工具v3/批量收录题目.ui new file mode 100644 index 00000000..d72201f3 --- /dev/null +++ b/工具v3/批量收录题目.ui @@ -0,0 +1,147 @@ + + + Form + + + + 0 + 0 + 342 + 109 + + + + 批量收录新题 + + + + + 260 + 10 + 71 + 91 + + + + + true + + + + 收录新题 + + + + + + 10 + 10 + 241 + 88 + + + + Qt::Vertical + + + + + + + 起始ID + + + + + + + + + + false + + + false + + + 数字题号(通常是10000n+1) + + + + + + + + + + + 编辑者 + + + + + + + 编辑者姓名 + + + + + + + + + + + 来源 + + + + + + + 自拟题目 + + + 题目来源, 通常为 自拟题目 + + + + + + + + + + + 后缀 + + + + + + + false + + + 试题 + + + 通常为 试题 + + + + + + + 使用后缀 + + + + + + + + + +