diff --git a/工具v4/Ui_tool_panel.py b/工具v4/Ui_tool_panel.py
new file mode 100644
index 00000000..51ca0290
--- /dev/null
+++ b/工具v4/Ui_tool_panel.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'tool_panel.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, QHBoxLayout, QMainWindow, QMenuBar,
+ QPushButton, QSizePolicy, QStatusBar, QTabWidget,
+ QWidget)
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ if not MainWindow.objectName():
+ MainWindow.setObjectName(u"MainWindow")
+ MainWindow.resize(800, 600)
+ self.centralwidget = QWidget(MainWindow)
+ self.centralwidget.setObjectName(u"centralwidget")
+ self.tabWidget_main = QTabWidget(self.centralwidget)
+ self.tabWidget_main.setObjectName(u"tabWidget_main")
+ self.tabWidget_main.setGeometry(QRect(0, 40, 800, 520))
+ self.tabWidget_main.setTabShape(QTabWidget.Triangular)
+ self.tab_luru = QWidget()
+ self.tab_luru.setObjectName(u"tab_luru")
+ self.tabWidget_luru = QTabWidget(self.tab_luru)
+ self.tabWidget_luru.setObjectName(u"tabWidget_luru")
+ self.tabWidget_luru.setGeometry(QRect(0, 0, 800, 490))
+ self.tabWidget_luru.setTabPosition(QTabWidget.West)
+ self.tabWidget_luru.setTabShape(QTabWidget.Triangular)
+ self.tab_kxth = QWidget()
+ self.tab_kxth.setObjectName(u"tab_kxth")
+ self.tabWidget_luru.addTab(self.tab_kxth, "")
+ self.tab_bdsl = QWidget()
+ self.tab_bdsl.setObjectName(u"tab_bdsl")
+ self.tabWidget_luru.addTab(self.tab_bdsl, "")
+ self.tab_tjgl = QWidget()
+ self.tab_tjgl.setObjectName(u"tab_tjgl")
+ self.tabWidget_luru.addTab(self.tab_tjgl, "")
+ self.tabWidget_main.addTab(self.tab_luru, "")
+ self.tab_weihu = QWidget()
+ self.tab_weihu.setObjectName(u"tab_weihu")
+ self.tabWidget_weihu = QTabWidget(self.tab_weihu)
+ self.tabWidget_weihu.setObjectName(u"tabWidget_weihu")
+ self.tabWidget_weihu.setGeometry(QRect(0, 0, 800, 490))
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tabWidget_weihu.sizePolicy().hasHeightForWidth())
+ self.tabWidget_weihu.setSizePolicy(sizePolicy)
+ self.tabWidget_weihu.setTabPosition(QTabWidget.West)
+ self.tabWidget_weihu.setTabShape(QTabWidget.Triangular)
+ self.tab_tjzd = QWidget()
+ self.tab_tjzd.setObjectName(u"tab_tjzd")
+ self.tabWidget_weihu.addTab(self.tab_tjzd, "")
+ self.tab_bjtm = QWidget()
+ self.tab_bjtm.setObjectName(u"tab_bjtm")
+ self.tabWidget_weihu.addTab(self.tab_bjtm, "")
+ self.tab_xxdr = QWidget()
+ self.tab_xxdr.setObjectName(u"tab_xxdr")
+ self.tabWidget_weihu.addTab(self.tab_xxdr, "")
+ self.tab_sddr = QWidget()
+ self.tab_sddr.setObjectName(u"tab_sddr")
+ self.tabWidget_weihu.addTab(self.tab_sddr, "")
+ self.tab_tkdr = QWidget()
+ self.tab_tkdr.setObjectName(u"tab_tkdr")
+ self.tabWidget_weihu.addTab(self.tab_tkdr, "")
+ self.tab_dygg = QWidget()
+ self.tab_dygg.setObjectName(u"tab_dygg")
+ self.tabWidget_weihu.addTab(self.tab_dygg, "")
+ self.tab_tqda = QWidget()
+ self.tab_tqda.setObjectName(u"tab_tqda")
+ self.tabWidget_weihu.addTab(self.tab_tqda, "")
+ self.tab_gxsy = QWidget()
+ self.tab_gxsy.setObjectName(u"tab_gxsy")
+ self.tabWidget_weihu.addTab(self.tab_gxsy, "")
+ self.tab_tjjc = QWidget()
+ self.tab_tjjc.setObjectName(u"tab_tjjc")
+ self.tabWidget_weihu.addTab(self.tab_tjjc, "")
+ self.tab_xxxz = QWidget()
+ self.tab_xxxz.setObjectName(u"tab_xxxz")
+ self.tabWidget_weihu.addTab(self.tab_xxxz, "")
+ self.tabWidget_main.addTab(self.tab_weihu, "")
+ self.tab_shiyong = QWidget()
+ self.tab_shiyong.setObjectName(u"tab_shiyong")
+ self.tabWidget_shiyong = QTabWidget(self.tab_shiyong)
+ self.tabWidget_shiyong.setObjectName(u"tabWidget_shiyong")
+ self.tabWidget_shiyong.setGeometry(QRect(0, 0, 800, 490))
+ self.tabWidget_shiyong.setTabPosition(QTabWidget.West)
+ self.tabWidget_shiyong.setTabShape(QTabWidget.Triangular)
+ self.tab_sxth = QWidget()
+ self.tab_sxth.setObjectName(u"tab_sxth")
+ self.tabWidget_shiyong.addTab(self.tab_sxth, "")
+ self.tab_ndsx = QWidget()
+ self.tab_ndsx.setObjectName(u"tab_ndsx")
+ self.tabWidget_shiyong.addTab(self.tab_ndsx, "")
+ self.tab_xtby = QWidget()
+ self.tab_xtby.setObjectName(u"tab_xtby")
+ self.tabWidget_shiyong.addTab(self.tab_xtby, "")
+ self.tab_sctp = QWidget()
+ self.tab_sctp.setObjectName(u"tab_sctp")
+ self.tabWidget_shiyong.addTab(self.tab_sctp, "")
+ self.tab_hqth = QWidget()
+ self.tab_hqth.setObjectName(u"tab_hqth")
+ self.tabWidget_shiyong.addTab(self.tab_hqth, "")
+ self.tabWidget_main.addTab(self.tab_shiyong, "")
+ self.tab_beikezu = QWidget()
+ self.tab_beikezu.setObjectName(u"tab_beikezu")
+ self.tabWidget_beikezu = QTabWidget(self.tab_beikezu)
+ self.tabWidget_beikezu.setObjectName(u"tabWidget_beikezu")
+ self.tabWidget_beikezu.setGeometry(QRect(0, 0, 800, 490))
+ self.tabWidget_beikezu.setTabPosition(QTabWidget.West)
+ self.tabWidget_beikezu.setTabShape(QTabWidget.Triangular)
+ self.tab_jglr = QWidget()
+ self.tab_jglr.setObjectName(u"tab_jglr")
+ self.tabWidget_beikezu.addTab(self.tab_jglr, "")
+ self.tab_dtlr = QWidget()
+ self.tab_dtlr.setObjectName(u"tab_dtlr")
+ self.tabWidget_beikezu.addTab(self.tab_dtlr, "")
+ self.tab_jysc = QWidget()
+ self.tab_jysc.setObjectName(u"tab_jysc")
+ self.tabWidget_beikezu.addTab(self.tab_jysc, "")
+ self.tabWidget_main.addTab(self.tab_beikezu, "")
+ self.tab_latex = QWidget()
+ self.tab_latex.setObjectName(u"tab_latex")
+ self.tabWidget = QTabWidget(self.tab_latex)
+ self.tabWidget.setObjectName(u"tabWidget")
+ self.tabWidget.setGeometry(QRect(0, 0, 800, 490))
+ self.tabWidget.setTabPosition(QTabWidget.West)
+ self.tabWidget.setTabShape(QTabWidget.Triangular)
+ self.tab_wbzh = QWidget()
+ self.tab_wbzh.setObjectName(u"tab_wbzh")
+ self.tabWidget.addTab(self.tab_wbzh, "")
+ self.tab_hist = QWidget()
+ self.tab_hist.setObjectName(u"tab_hist")
+ self.tabWidget.addTab(self.tab_hist, "")
+ self.tabWidget_main.addTab(self.tab_latex, "")
+ self.horizontalLayoutWidget = QWidget(self.centralwidget)
+ self.horizontalLayoutWidget.setObjectName(u"horizontalLayoutWidget")
+ self.horizontalLayoutWidget.setGeometry(QRect(30, 0, 741, 31))
+ self.horizontalLayout = QHBoxLayout(self.horizontalLayoutWidget)
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.pushButton_tiku = QPushButton(self.horizontalLayoutWidget)
+ self.pushButton_tiku.setObjectName(u"pushButton_tiku")
+
+ self.horizontalLayout.addWidget(self.pushButton_tiku)
+
+ self.pushButton_tikutest = QPushButton(self.horizontalLayoutWidget)
+ self.pushButton_tikutest.setObjectName(u"pushButton_tikutest")
+
+ self.horizontalLayout.addWidget(self.pushButton_tikutest)
+
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QMenuBar(MainWindow)
+ self.menubar.setObjectName(u"menubar")
+ self.menubar.setGeometry(QRect(0, 0, 800, 22))
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QStatusBar(MainWindow)
+ self.statusbar.setObjectName(u"statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+
+ self.tabWidget_main.setCurrentIndex(3)
+ self.tabWidget_luru.setCurrentIndex(2)
+ self.tabWidget_weihu.setCurrentIndex(5)
+ self.tabWidget_shiyong.setCurrentIndex(4)
+ self.tabWidget_beikezu.setCurrentIndex(1)
+ self.tabWidget.setCurrentIndex(1)
+
+
+ QMetaObject.connectSlotsByName(MainWindow)
+ # setupUi
+
+ def retranslateUi(self, MainWindow):
+ MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"\u9898\u5e93\u5de5\u5177\u9762\u677fv4", None))
+ self.tabWidget_luru.setTabText(self.tabWidget_luru.indexOf(self.tab_kxth), QCoreApplication.translate("MainWindow", u"\u7a7a\u95f2\u9898\u53f7", None))
+ self.tabWidget_luru.setTabText(self.tabWidget_luru.indexOf(self.tab_bdsl), QCoreApplication.translate("MainWindow", u"\u6bd4\u5bf9\u5e76\u6536\u5f55", None))
+ self.tabWidget_luru.setTabText(self.tabWidget_luru.indexOf(self.tab_tjgl), QCoreApplication.translate("MainWindow", u"\u6dfb\u52a0\u5173\u8054", None))
+ self.tabWidget_main.setTabText(self.tabWidget_main.indexOf(self.tab_luru), QCoreApplication.translate("MainWindow", u"\u5f55\u5165", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_tjzd), QCoreApplication.translate("MainWindow", u"\u6dfb\u52a0\u5b57\u6bb5\u6570\u636e", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_bjtm), QCoreApplication.translate("MainWindow", u"\u7f16\u8f91\u9898\u76ee", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_xxdr), QCoreApplication.translate("MainWindow", u"\u5c0f\u95f2\u5bfc\u5165", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_sddr), QCoreApplication.translate("MainWindow", u"\u624b\u52a8\u5bfc\u5165", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_tkdr), QCoreApplication.translate("MainWindow", u"\u7edf\u8003\u5bfc\u5165", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_dygg), QCoreApplication.translate("MainWindow", u"\u5355\u5143\u6302\u94a9", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_tqda), QCoreApplication.translate("MainWindow", u"\u63d0\u53d6\u7b54\u6848", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_gxsy), QCoreApplication.translate("MainWindow", u"\u5171\u4eab\u4f7f\u7528\u8bb0\u5f55", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_tjjc), QCoreApplication.translate("MainWindow", u"\u6dfb\u52a0\u57fa\u7840\u77e5\u8bc6", None))
+ self.tabWidget_weihu.setTabText(self.tabWidget_weihu.indexOf(self.tab_xxxz), QCoreApplication.translate("MainWindow", u"\u5c0f\u95f2\u4e0b\u8f7d", None))
+ self.tabWidget_main.setTabText(self.tabWidget_main.indexOf(self.tab_weihu), QCoreApplication.translate("MainWindow", u"\u7ef4\u62a4", None))
+ self.tabWidget_shiyong.setTabText(self.tabWidget_shiyong.indexOf(self.tab_sxth), QCoreApplication.translate("MainWindow", u"\u5173\u952e\u5b57\u7b5b\u9009\u9898\u53f7", None))
+ self.tabWidget_shiyong.setTabText(self.tabWidget_shiyong.indexOf(self.tab_ndsx), QCoreApplication.translate("MainWindow", u"\u6b63\u786e\u7387\u7b5b\u9009\u9898\u53f7", None))
+ self.tabWidget_shiyong.setTabText(self.tabWidget_shiyong.indexOf(self.tab_xtby), QCoreApplication.translate("MainWindow", u"\u9009\u9898\u7f16\u8bd1", None))
+ self.tabWidget_shiyong.setTabText(self.tabWidget_shiyong.indexOf(self.tab_sctp), QCoreApplication.translate("MainWindow", u"\u9898\u53f7\u751f\u6210\u56fe\u7247", None))
+ self.tabWidget_shiyong.setTabText(self.tabWidget_shiyong.indexOf(self.tab_hqth), QCoreApplication.translate("MainWindow", u"\u83b7\u53d6\u9898\u53f7", None))
+ self.tabWidget_main.setTabText(self.tabWidget_main.indexOf(self.tab_shiyong), QCoreApplication.translate("MainWindow", u"\u4f7f\u7528", None))
+ self.tabWidget_beikezu.setTabText(self.tabWidget_beikezu.indexOf(self.tab_jglr), QCoreApplication.translate("MainWindow", u"\u8bb2\u4e49\u7ed3\u6784\u7f16\u53f7\u5f55\u5165", None))
+ self.tabWidget_beikezu.setTabText(self.tabWidget_beikezu.indexOf(self.tab_dtlr), QCoreApplication.translate("MainWindow", u"\u7b54\u9898\u7eb8\u5bf9\u5e94\u4fe1\u606f\u5f55\u5165", None))
+ self.tabWidget_beikezu.setTabText(self.tabWidget_beikezu.indexOf(self.tab_jysc), QCoreApplication.translate("MainWindow", u"\u7cfb\u5217\u8bb2\u4e49\u751f\u6210", None))
+ self.tabWidget_main.setTabText(self.tabWidget_main.indexOf(self.tab_beikezu), QCoreApplication.translate("MainWindow", u"\u5907\u8bfe\u7ec4", None))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_wbzh), QCoreApplication.translate("MainWindow", u"\u6587\u672c\u8f6c\u6362\u5904\u7406", None))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_hist), QCoreApplication.translate("MainWindow", u"\u76f4\u65b9\u56fe\u4ee3\u7801\u751f\u6210", None))
+ self.tabWidget_main.setTabText(self.tabWidget_main.indexOf(self.tab_latex), QCoreApplication.translate("MainWindow", u"LaTeX\u4ee3\u7801\u76f8\u5173", None))
+ self.pushButton_tiku.setText(QCoreApplication.translate("MainWindow", u"\u6b63\u5f0f\u6570\u636e\u5e93", None))
+ self.pushButton_tikutest.setText(QCoreApplication.translate("MainWindow", u"\u6d4b\u8bd5\u6570\u636e\u5e93", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_下载小闲答题数据.py b/工具v4/Ui_下载小闲答题数据.py
new file mode 100644
index 00000000..5fd08d69
--- /dev/null
+++ b/工具v4/Ui_下载小闲答题数据.py
@@ -0,0 +1,116 @@
+# -*- 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, QHeaderView, QLabel, QLineEdit,
+ QPushButton, QSizePolicy, QTableWidget, QTableWidgetItem,
+ QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_chrome = QPushButton(Form)
+ self.pushButton_chrome.setObjectName(u"pushButton_chrome")
+ self.pushButton_chrome.setGeometry(QRect(130, 10, 75, 24))
+ self.pushButton_driver = QPushButton(Form)
+ self.pushButton_driver.setObjectName(u"pushButton_driver")
+ self.pushButton_driver.setGeometry(QRect(130, 50, 75, 24))
+ self.pushButton_outputfolder = QPushButton(Form)
+ self.pushButton_outputfolder.setObjectName(u"pushButton_outputfolder")
+ self.pushButton_outputfolder.setGeometry(QRect(130, 90, 75, 24))
+ self.label_chrome = QLabel(Form)
+ self.label_chrome.setObjectName(u"label_chrome")
+ self.label_chrome.setGeometry(QRect(210, 10, 221, 21))
+ self.label_driver = QLabel(Form)
+ self.label_driver.setObjectName(u"label_driver")
+ self.label_driver.setGeometry(QRect(210, 50, 221, 21))
+ self.label_outputfolder = QLabel(Form)
+ self.label_outputfolder.setObjectName(u"label_outputfolder")
+ self.label_outputfolder.setGeometry(QRect(210, 90, 221, 21))
+ self.pushButton_openbrowser = QPushButton(Form)
+ self.pushButton_openbrowser.setObjectName(u"pushButton_openbrowser")
+ self.pushButton_openbrowser.setGeometry(QRect(130, 130, 71, 51))
+ self.pushButton_login = QPushButton(Form)
+ self.pushButton_login.setObjectName(u"pushButton_login")
+ self.pushButton_login.setGeometry(QRect(210, 130, 71, 51))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(400, 130, 201, 51))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(450, 10, 54, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(450, 50, 54, 16))
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(450, 90, 71, 16))
+ self.lineEdit_startdate = QLineEdit(Form)
+ self.lineEdit_startdate.setObjectName(u"lineEdit_startdate")
+ self.lineEdit_startdate.setGeometry(QRect(510, 10, 91, 20))
+ self.lineEdit_enddate = QLineEdit(Form)
+ self.lineEdit_enddate.setObjectName(u"lineEdit_enddate")
+ self.lineEdit_enddate.setGeometry(QRect(510, 50, 91, 20))
+ self.lineEdit_graderegex = QLineEdit(Form)
+ self.lineEdit_graderegex.setObjectName(u"lineEdit_graderegex")
+ self.lineEdit_graderegex.setGeometry(QRect(530, 90, 71, 20))
+ self.pushButton_getlist = QPushButton(Form)
+ self.pushButton_getlist.setObjectName(u"pushButton_getlist")
+ self.pushButton_getlist.setGeometry(QRect(290, 130, 101, 51))
+ self.tableWidget = QTableWidget(Form)
+ if (self.tableWidget.columnCount() < 2):
+ self.tableWidget.setColumnCount(2)
+ __qtablewidgetitem = QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
+ __qtablewidgetitem1 = QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
+ self.tableWidget.setObjectName(u"tableWidget")
+ self.tableWidget.setGeometry(QRect(130, 190, 471, 271))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u4e0b\u8f7d\u5c0f\u95f2\u7b54\u9898\u7eb8", None))
+ self.pushButton_chrome.setText(QCoreApplication.translate("Form", u"chrome\u8def\u5f84", None))
+ self.pushButton_driver.setText(QCoreApplication.translate("Form", u"driver\u8def\u5f84", None))
+ self.pushButton_outputfolder.setText(QCoreApplication.translate("Form", u"\u8f93\u51fa\u8def\u5f84", None))
+ self.label_chrome.setText(QCoreApplication.translate("Form", u"\u6b64\u5904\u663e\u793achrome.exe\u7684\u8def\u5f84", None))
+ self.label_driver.setText(QCoreApplication.translate("Form", u"\u6b64\u5904\u663e\u793achrome webdriver\u7684\u8def\u5f84", None))
+ self.label_outputfolder.setText(QCoreApplication.translate("Form", u"\u6b64\u5904\u663e\u793a\u8f93\u51fa\u7684zip\u6587\u4ef6\u7684\u8def\u5f84", None))
+ self.pushButton_openbrowser.setText(QCoreApplication.translate("Form", u"\u5f00\u542f\u6d4f\u89c8\u5668", None))
+ self.pushButton_login.setText(QCoreApplication.translate("Form", u"\u767b\u5f55", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u4e0b\u8f7d\u7b54\u9898\u60c5\u51b5", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u8d77\u59cb\u65e5\u671f", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u7ec8\u6b62\u65e5\u671f", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u5e74\u7ea7(regex)", None))
+ self.lineEdit_startdate.setPlaceholderText(QCoreApplication.translate("Form", u"yyyymmdd", None))
+ self.lineEdit_enddate.setPlaceholderText(QCoreApplication.translate("Form", u"yyyymmdd", None))
+ self.lineEdit_graderegex.setPlaceholderText(QCoreApplication.translate("Form", u"\u9ad8[\u4e00\u4e8c]", None))
+ self.pushButton_getlist.setText(QCoreApplication.translate("Form", u"\u83b7\u53d6\u7b54\u9898\u5361\u5217\u8868", None))
+ ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0)
+ ___qtablewidgetitem.setText(QCoreApplication.translate("Form", u"\u7b54\u9898\u7eb8\u540d\u79f0", None));
+ ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1)
+ ___qtablewidgetitem1.setText(QCoreApplication.translate("Form", u"\u5df2\u4e0b\u8f7d", None));
+ # retranslateUi
+
diff --git a/工具v4/Ui_修改metadata.py b/工具v4/Ui_修改metadata.py
new file mode 100644
index 00000000..024ee78e
--- /dev/null
+++ b/工具v4/Ui_修改metadata.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file '修改metadata.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, QPushButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(220, 190, 321, 101))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u4fee\u6539metadata", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u4fee\u6539metadata", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_共享使用记录.py b/工具v4/Ui_共享使用记录.py
new file mode 100644
index 00000000..1911b1b9
--- /dev/null
+++ b/工具v4/Ui_共享使用记录.py
@@ -0,0 +1,41 @@
+# -*- 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, QPushButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(270, 170, 251, 131))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u5171\u4eab\u4f7f\u7528\u8bb0\u5f55", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u76f8\u540c\u9898\u76ee\u5171\u4eab\u4f7f\u7528\u8bb0\u5f55", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_单一题号转为图片.py b/工具v4/Ui_单一题号转为图片.py
new file mode 100644
index 00000000..39078eb7
--- /dev/null
+++ b/工具v4/Ui_单一题号转为图片.py
@@ -0,0 +1,65 @@
+# -*- 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, QLabel, QLineEdit, QPushButton,
+ QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(240, 160, 31, 16))
+ self.lineEdit_ID = QLineEdit(Form)
+ self.lineEdit_ID.setObjectName(u"lineEdit_ID")
+ self.lineEdit_ID.setGeometry(QRect(270, 160, 71, 20))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(240, 190, 31, 16))
+ self.lineEdit_dpi = QLineEdit(Form)
+ self.lineEdit_dpi.setObjectName(u"lineEdit_dpi")
+ self.lineEdit_dpi.setGeometry(QRect(270, 190, 71, 20))
+ self.pushButton_convert = QPushButton(Form)
+ self.pushButton_convert.setObjectName(u"pushButton_convert")
+ self.pushButton_convert.setGeometry(QRect(350, 160, 75, 51))
+ self.pushButton_open = QPushButton(Form)
+ self.pushButton_open.setObjectName(u"pushButton_open")
+ self.pushButton_open.setGeometry(QRect(430, 160, 75, 51))
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(240, 220, 51, 16))
+ self.lineEdit_message = QLineEdit(Form)
+ self.lineEdit_message.setObjectName(u"lineEdit_message")
+ self.lineEdit_message.setGeometry(QRect(300, 220, 201, 20))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u5355\u4e00\u9898\u53f7\u8f6c\u4e3a\u56fe\u7247", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u9898\u53f7", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"DPI", None))
+ self.pushButton_convert.setText(QCoreApplication.translate("Form", u"\u8f6c\u6362", None))
+ self.pushButton_open.setText(QCoreApplication.translate("Form", u"\u6253\u5f00", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u9644\u52a0\u6587\u5b57", None))
+ self.lineEdit_message.setPlaceholderText(QCoreApplication.translate("Form", u"\u8f93\u5165\u9644\u52a0\u6587\u5b57, \u7559\u7a7a\u8868\u793a\u65e0\u9644\u52a0\u6587\u5b57", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_单元挂钩.py b/工具v4/Ui_单元挂钩.py
new file mode 100644
index 00000000..e3af40b5
--- /dev/null
+++ b/工具v4/Ui_单元挂钩.py
@@ -0,0 +1,58 @@
+# -*- 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, QLabel, QLineEdit, QPushButton,
+ QRadioButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(200, 190, 54, 16))
+ self.radioButton_phase2 = QRadioButton(Form)
+ self.radioButton_phase2.setObjectName(u"radioButton_phase2")
+ self.radioButton_phase2.setGeometry(QRect(200, 240, 191, 20))
+ self.lineEdit_ids = QLineEdit(Form)
+ self.lineEdit_ids.setObjectName(u"lineEdit_ids")
+ self.lineEdit_ids.setGeometry(QRect(200, 210, 271, 20))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(490, 160, 71, 111))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.radioButton_phase1 = QRadioButton(Form)
+ self.radioButton_phase1.setObjectName(u"radioButton_phase1")
+ self.radioButton_phase1.setGeometry(QRect(200, 170, 141, 20))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u5355\u5143\u6302\u94a9", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u9898\u53f7", None))
+ self.radioButton_phase2.setText(QCoreApplication.translate("Form", u"\u6536\u5f55\u5355\u5143\u4fe1\u606f\u81f3metadata\u9636\u6bb5", None))
+ self.lineEdit_ids.setPlaceholderText(QCoreApplication.translate("Form", u"\u8f93\u5165\u9898\u53f7, \u7559\u7a7a\u8868\u793a\u9488\u5bf9\u6240\u6709\u6ca1\u6709\u5bf9\u5e94\u5355\u5143\u7684\u9898\u76ee", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u8fd0\u884c", None))
+ self.radioButton_phase1.setText(QCoreApplication.translate("Form", u"\u81ea\u9002\u5e94\u8d4b\u4e88\u5355\u5143\u4fe1\u606f", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_寻找空闲题号.py b/工具v4/Ui_寻找空闲题号.py
new file mode 100644
index 00000000..005e014d
--- /dev/null
+++ b/工具v4/Ui_寻找空闲题号.py
@@ -0,0 +1,63 @@
+# -*- 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, QFrame, QLabel, QPushButton,
+ QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label_used = QLabel(Form)
+ self.label_used.setObjectName(u"label_used")
+ self.label_used.setGeometry(QRect(20, 50, 260, 401))
+ self.label_used.setFrameShape(QFrame.StyledPanel)
+ self.label_used.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
+ self.label_available = QLabel(Form)
+ self.label_available.setObjectName(u"label_available")
+ self.label_available.setGeometry(QRect(290, 50, 260, 401))
+ self.label_available.setFrameShape(QFrame.StyledPanel)
+ self.label_available.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(30, 20, 71, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(290, 20, 71, 16))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(570, 50, 141, 401))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u5bfb\u627e\u7a7a\u95f2\u9898\u53f7", None))
+ self.label_used.setText(QCoreApplication.translate("Form", u"TextLabel", None))
+ self.label_available.setText(QCoreApplication.translate("Form", u"TextLabel", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u5df2\u4f7f\u7528\u9898\u53f7", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u7a7a\u95f2\u9898\u53f7", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u751f\u6210\u5df2\u4f7f\u7528\u9898\u53f7\u5217\u8868\n"
+"\u53ca\u7a7a\u95f2\u9898\u53f7\u5217\u8868", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_手动统计结果导入.py b/工具v4/Ui_手动统计结果导入.py
new file mode 100644
index 00000000..df678792
--- /dev/null
+++ b/工具v4/Ui_手动统计结果导入.py
@@ -0,0 +1,58 @@
+# -*- 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, QLabel, QPlainTextEdit, QPushButton,
+ QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.plainTextEdit = QPlainTextEdit(Form)
+ self.plainTextEdit.setObjectName(u"plainTextEdit")
+ self.plainTextEdit.setEnabled(True)
+ self.plainTextEdit.setGeometry(QRect(250, 150, 181, 141))
+ self.plainTextEdit.setReadOnly(True)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(250, 130, 101, 16))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(440, 130, 75, 161))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u624b\u52a8\u7edf\u8ba1\u7ed3\u679c\u5bfc\u5165", None))
+ self.plainTextEdit.setPlainText(QCoreApplication.translate("Form", u"[BEGIN]\n"
+"##20240101\n"
+"**2026\u5c4a\u9ad8\u4e0001\u73ed\n"
+"1 0.875\n"
+"10001 0.825 0.630\n"
+"[END]", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u624b\u52a8\u7edf\u8ba1\u7ed3\u679c\u6a21\u677f", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u5bfc\u5165\u4e3a\n"
+"metadata", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_批量收录新题.py b/工具v4/Ui_批量收录新题.py
new file mode 100644
index 00000000..a7ea39bd
--- /dev/null
+++ b/工具v4/Ui_批量收录新题.py
@@ -0,0 +1,150 @@
+# -*- 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, QPlainTextEdit, QPushButton, QSizePolicy,
+ QSplitter, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.plainTextEdit_raw = QPlainTextEdit(Form)
+ self.plainTextEdit_raw.setObjectName(u"plainTextEdit_raw")
+ self.plainTextEdit_raw.setGeometry(QRect(10, 30, 281, 441))
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(20, 10, 251, 16))
+ self.pushButton_step1 = QPushButton(Form)
+ self.pushButton_step1.setObjectName(u"pushButton_step1")
+ self.pushButton_step1.setGeometry(QRect(300, 50, 70, 400))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_step1.setFont(font)
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(360, 10, 321, 16))
+ self.plainTextEdit_compared = QPlainTextEdit(Form)
+ self.plainTextEdit_compared.setObjectName(u"plainTextEdit_compared")
+ self.plainTextEdit_compared.setGeometry(QRect(380, 30, 281, 301))
+ self.pushButton_step2 = QPushButton(Form)
+ self.pushButton_step2.setObjectName(u"pushButton_step2")
+ self.pushButton_step2.setGeometry(QRect(670, 50, 70, 400))
+ self.pushButton_step2.setFont(font)
+ self.splitter = QSplitter(Form)
+ self.splitter.setObjectName(u"splitter")
+ self.splitter.setGeometry(QRect(380, 340, 281, 131))
+ self.splitter.setOrientation(Qt.Vertical)
+ self.layoutWidget = QWidget(self.splitter)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.horizontalLayout = QHBoxLayout(self.layoutWidget)
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label_3 = QLabel(self.layoutWidget)
+ self.label_3.setObjectName(u"label_3")
+
+ self.horizontalLayout.addWidget(self.label_3)
+
+ self.lineEdit_startingid = QLineEdit(self.layoutWidget)
+ 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.layoutWidget)
+ self.layoutWidget_2 = QWidget(self.splitter)
+ self.layoutWidget_2.setObjectName(u"layoutWidget_2")
+ self.horizontalLayout_2 = QHBoxLayout(self.layoutWidget_2)
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.label_4 = QLabel(self.layoutWidget_2)
+ self.label_4.setObjectName(u"label_4")
+
+ self.horizontalLayout_2.addWidget(self.label_4)
+
+ self.lineEdit_editor = QLineEdit(self.layoutWidget_2)
+ self.lineEdit_editor.setObjectName(u"lineEdit_editor")
+
+ self.horizontalLayout_2.addWidget(self.lineEdit_editor)
+
+ self.splitter.addWidget(self.layoutWidget_2)
+ self.layoutWidget_3 = QWidget(self.splitter)
+ self.layoutWidget_3.setObjectName(u"layoutWidget_3")
+ self.horizontalLayout_3 = QHBoxLayout(self.layoutWidget_3)
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.label_5 = QLabel(self.layoutWidget_3)
+ self.label_5.setObjectName(u"label_5")
+
+ self.horizontalLayout_3.addWidget(self.label_5)
+
+ self.lineEdit_origin = QLineEdit(self.layoutWidget_3)
+ self.lineEdit_origin.setObjectName(u"lineEdit_origin")
+
+ self.horizontalLayout_3.addWidget(self.lineEdit_origin)
+
+ self.splitter.addWidget(self.layoutWidget_3)
+ self.layoutWidget_4 = QWidget(self.splitter)
+ self.layoutWidget_4.setObjectName(u"layoutWidget_4")
+ self.horizontalLayout_4 = QHBoxLayout(self.layoutWidget_4)
+ self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
+ self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.label_6 = QLabel(self.layoutWidget_4)
+ self.label_6.setObjectName(u"label_6")
+
+ self.horizontalLayout_4.addWidget(self.label_6)
+
+ self.lineEdit_suffix = QLineEdit(self.layoutWidget_4)
+ 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_4)
+ self.checkBox_suffix.setObjectName(u"checkBox_suffix")
+
+ self.horizontalLayout_4.addWidget(self.checkBox_suffix)
+
+ self.splitter.addWidget(self.layoutWidget_4)
+
+ 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.label.setText(QCoreApplication.translate("Form", u"\u8981\u5904\u7406\u7684\u9898\u76ee(\u6309 item \u5207\u5206, \u53ef\u4ee5\u5305\u542b\u6587\u4ef6\u5934)", None))
+ self.pushButton_step1.setText(QCoreApplication.translate("Form", u"\u65b0\u9898\u6bd4\u5bf9", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u542b\u6709\u76f8\u4f3c\u9898\u76ee\u7684LaTeX\u6e90\u4ee3\u7801, \u590d\u5236\u5230LaTeX\u7f16\u8f91\u5668\u4eba\u5de5\u6807\u6ce8", None))
+ self.pushButton_step2.setText(QCoreApplication.translate("Form", u"\u6536\u5f55\u9898\u76ee", None))
+ self.label_3.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_4.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_5.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_6.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/工具v4/Ui_指定题号编译pdf.py b/工具v4/Ui_指定题号编译pdf.py
new file mode 100644
index 00000000..6e9125cd
--- /dev/null
+++ b/工具v4/Ui_指定题号编译pdf.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file '指定题号编译pdf.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, QPlainTextEdit, QPushButton, QRadioButton,
+ QSizePolicy, QVBoxLayout, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(500, 279, 221, 131))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(300, 79, 61, 16))
+ self.lineEdit_path = QLineEdit(Form)
+ self.lineEdit_path.setObjectName(u"lineEdit_path")
+ self.lineEdit_path.setGeometry(QRect(300, 99, 421, 20))
+ self.pushButton_SelectPath = QPushButton(Form)
+ self.pushButton_SelectPath.setObjectName(u"pushButton_SelectPath")
+ self.pushButton_SelectPath.setGeometry(QRect(640, 69, 81, 24))
+ self.verticalLayoutWidget = QWidget(Form)
+ self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
+ self.verticalLayoutWidget.setGeometry(QRect(300, 139, 81, 271))
+ self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label_4 = QLabel(self.verticalLayoutWidget)
+ self.label_4.setObjectName(u"label_4")
+ self.label_4.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
+
+ self.verticalLayout.addWidget(self.label_4)
+
+ self.radioButton_student = QRadioButton(self.verticalLayoutWidget)
+ self.radioButton_student.setObjectName(u"radioButton_student")
+ self.radioButton_student.setChecked(True)
+
+ self.verticalLayout.addWidget(self.radioButton_student)
+
+ self.radioButton_teacher = QRadioButton(self.verticalLayoutWidget)
+ self.radioButton_teacher.setObjectName(u"radioButton_teacher")
+
+ self.verticalLayout.addWidget(self.radioButton_teacher)
+
+ self.verticalLayoutWidget_2 = QWidget(Form)
+ self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
+ self.verticalLayoutWidget_2.setGeometry(QRect(400, 139, 91, 271))
+ self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.label_5 = QLabel(self.verticalLayoutWidget_2)
+ self.label_5.setObjectName(u"label_5")
+
+ self.verticalLayout_2.addWidget(self.label_5)
+
+ self.checkBox_space = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_space.setObjectName(u"checkBox_space")
+
+ self.verticalLayout_2.addWidget(self.checkBox_space)
+
+ self.checkBox_ans = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_ans.setObjectName(u"checkBox_ans")
+
+ self.verticalLayout_2.addWidget(self.checkBox_ans)
+
+ self.checkBox_objs = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_objs.setObjectName(u"checkBox_objs")
+ self.checkBox_objs.setCheckable(True)
+ self.checkBox_objs.setTristate(False)
+
+ self.verticalLayout_2.addWidget(self.checkBox_objs)
+
+ self.checkBox_tags = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_tags.setObjectName(u"checkBox_tags")
+
+ self.verticalLayout_2.addWidget(self.checkBox_tags)
+
+ self.checkBox_solution = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_solution.setObjectName(u"checkBox_solution")
+
+ self.verticalLayout_2.addWidget(self.checkBox_solution)
+
+ self.checkBox_usages = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_usages.setObjectName(u"checkBox_usages")
+
+ self.verticalLayout_2.addWidget(self.checkBox_usages)
+
+ self.checkBox_origin = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_origin.setObjectName(u"checkBox_origin")
+
+ self.verticalLayout_2.addWidget(self.checkBox_origin)
+
+ self.checkBox_remark = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_remark.setObjectName(u"checkBox_remark")
+
+ self.verticalLayout_2.addWidget(self.checkBox_remark)
+
+ self.verticalLayoutWidget_3 = QWidget(Form)
+ self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3")
+ self.verticalLayoutWidget_3.setGeometry(QRect(500, 139, 222, 121))
+ self.verticalLayout_usagepanel = QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_usagepanel.setObjectName(u"verticalLayout_usagepanel")
+ self.verticalLayout_usagepanel.setContentsMargins(0, 0, 0, 0)
+ self.label_6 = QLabel(self.verticalLayoutWidget_3)
+ self.label_6.setObjectName(u"label_6")
+
+ self.verticalLayout_usagepanel.addWidget(self.label_6)
+
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.label_9 = QLabel(self.verticalLayoutWidget_3)
+ self.label_9.setObjectName(u"label_9")
+
+ self.horizontalLayout.addWidget(self.label_9)
+
+ self.lineEdit_high = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_high.setObjectName(u"lineEdit_high")
+ self.lineEdit_high.setEnabled(True)
+
+ self.horizontalLayout.addWidget(self.lineEdit_high)
+
+ self.label_10 = QLabel(self.verticalLayoutWidget_3)
+ self.label_10.setObjectName(u"label_10")
+
+ self.horizontalLayout.addWidget(self.label_10)
+
+ self.lineEdit_low = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_low.setObjectName(u"lineEdit_low")
+
+ self.horizontalLayout.addWidget(self.lineEdit_low)
+
+
+ self.verticalLayout_usagepanel.addLayout(self.horizontalLayout)
+
+ self.label_7 = QLabel(self.verticalLayoutWidget_3)
+ self.label_7.setObjectName(u"label_7")
+
+ self.verticalLayout_usagepanel.addWidget(self.label_7)
+
+ self.lineEdit_grades = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_grades.setObjectName(u"lineEdit_grades")
+
+ self.verticalLayout_usagepanel.addWidget(self.lineEdit_grades)
+
+ self.plainTextEdit_allitems = QPlainTextEdit(Form)
+ self.plainTextEdit_allitems.setObjectName(u"plainTextEdit_allitems")
+ self.plainTextEdit_allitems.setGeometry(QRect(30, 219, 261, 191))
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(30, 199, 54, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(31, 50, 60, 16))
+ self.plainTextEdit_newitems = QPlainTextEdit(Form)
+ self.plainTextEdit_newitems.setObjectName(u"plainTextEdit_newitems")
+ self.plainTextEdit_newitems.setGeometry(QRect(30, 69, 261, 71))
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(40, 149, 241, 28))
+ self.horizontalLayout_4 = QHBoxLayout(self.layoutWidget)
+ self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
+ self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.pushButton_add = QPushButton(self.layoutWidget)
+ self.pushButton_add.setObjectName(u"pushButton_add")
+
+ self.horizontalLayout_4.addWidget(self.pushButton_add)
+
+ self.horizontalLayout_3 = QHBoxLayout()
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.pushButton_delete = QPushButton(self.layoutWidget)
+ self.pushButton_delete.setObjectName(u"pushButton_delete")
+
+ self.horizontalLayout_3.addWidget(self.pushButton_delete)
+
+ self.horizontalLayout_2 = QHBoxLayout()
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.label_11 = QLabel(self.layoutWidget)
+ self.label_11.setObjectName(u"label_11")
+ self.label_11.setMaximumSize(QSize(20, 16777215))
+
+ self.horizontalLayout_2.addWidget(self.label_11)
+
+ self.lineEdit_index = QLineEdit(self.layoutWidget)
+ self.lineEdit_index.setObjectName(u"lineEdit_index")
+ self.lineEdit_index.setMaximumSize(QSize(20, 16777215))
+
+ self.horizontalLayout_2.addWidget(self.lineEdit_index)
+
+ self.label_12 = QLabel(self.layoutWidget)
+ self.label_12.setObjectName(u"label_12")
+ self.label_12.setMaximumSize(QSize(20, 16777215))
+
+ self.horizontalLayout_2.addWidget(self.label_12)
+
+
+ self.horizontalLayout_3.addLayout(self.horizontalLayout_2)
+
+
+ self.horizontalLayout_4.addLayout(self.horizontalLayout_3)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u6307\u5b9a\u9898\u53f7\u7f16\u8bd1", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u5f00\u59cb\u751f\u6210\u4e0e\u7f16\u8bd1", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u8def\u5f84", None))
+ self.pushButton_SelectPath.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u8def\u5f84", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"\u7248\u5f0f\u9009\u62e9", None))
+ self.radioButton_student.setText(QCoreApplication.translate("Form", u"\u5b66\u751f\u7248", None))
+ self.radioButton_teacher.setText(QCoreApplication.translate("Form", u"\u6559\u5e08\u7248", None))
+ self.label_5.setText(QCoreApplication.translate("Form", u"\u5185\u5bb9\u9009\u9879", None))
+ self.checkBox_space.setText(QCoreApplication.translate("Form", u"\u9898\u540e\u7a7a\u95f4", None))
+ self.checkBox_ans.setText(QCoreApplication.translate("Form", u"\u7b54\u6848", None))
+ self.checkBox_objs.setText(QCoreApplication.translate("Form", u"\u8bfe\u65f6\u76ee\u6807", None))
+ self.checkBox_tags.setText(QCoreApplication.translate("Form", u"\u9898\u76ee\u6807\u7b7e", None))
+ self.checkBox_solution.setText(QCoreApplication.translate("Form", u"\u89e3\u7b54\u4e0e\u63d0\u793a", None))
+ self.checkBox_usages.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u8bb0\u5f55", None))
+ self.checkBox_origin.setText(QCoreApplication.translate("Form", u"\u6765\u6e90", None))
+ self.checkBox_remark.setText(QCoreApplication.translate("Form", u"\u5907\u6ce8", None))
+ self.label_6.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u8bb0\u5f55\u9009\u62e9(\u90fd\u7559\u7a7a\u4e3a\u5168\u663e\u793a)", None))
+ self.label_9.setText(QCoreApplication.translate("Form", u"\u9ad8", None))
+ self.label_10.setText(QCoreApplication.translate("Form", u"\u4f4e", None))
+ self.label_7.setText(QCoreApplication.translate("Form", u"\u5c4a\u522b\u9009\u62e9(\u7528 \",\" \u5206\u9694, \u7559\u7a7a\u4e3a\u5168\u663e\u793a)", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u9898\u53f7\u5217\u8868", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u6807\u9898\u4e0e\u9898\u53f7", None))
+ self.plainTextEdit_newitems.setPlaceholderText(QCoreApplication.translate("Form", u"\u5982: \u96c6\u5408: 2:30,32 \u6216 \"\u96c6\u5408\": \"2:30\", \"\u51fd\u6570\": \"5,60,71:75\"(\u53ef\u6709\u56de\u8f66)", None))
+ self.pushButton_add.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u4e00\u9879\u6216\u591a\u9879", None))
+ self.pushButton_delete.setText(QCoreApplication.translate("Form", u"\u5220\u53bb\u4e00\u9879", None))
+ self.label_11.setText(QCoreApplication.translate("Form", u"\u7b2c", None))
+ self.label_12.setText(QCoreApplication.translate("Form", u"\u9879", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_文件或文本框提取答案.py b/工具v4/Ui_文件或文本框提取答案.py
new file mode 100644
index 00000000..37e38c5f
--- /dev/null
+++ b/工具v4/Ui_文件或文本框提取答案.py
@@ -0,0 +1,84 @@
+# -*- 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, QLabel, QLineEdit, QPlainTextEdit,
+ QPushButton, QRadioButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.plainTextEdit_rawString = QPlainTextEdit(Form)
+ self.plainTextEdit_rawString.setObjectName(u"plainTextEdit_rawString")
+ self.plainTextEdit_rawString.setGeometry(QRect(40, 100, 311, 331))
+ self.lineEdit_filepath = QLineEdit(Form)
+ self.lineEdit_filepath.setObjectName(u"lineEdit_filepath")
+ self.lineEdit_filepath.setGeometry(QRect(40, 50, 331, 20))
+ self.lineEdit_filepath.setReadOnly(True)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(40, 30, 54, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(40, 80, 101, 16))
+ self.lineEdit_2 = QLineEdit(Form)
+ self.lineEdit_2.setObjectName(u"lineEdit_2")
+ self.lineEdit_2.setGeometry(QRect(492, 50, 211, 20))
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(490, 30, 54, 16))
+ self.radioButton_text = QRadioButton(Form)
+ self.radioButton_text.setObjectName(u"radioButton_text")
+ self.radioButton_text.setGeometry(QRect(610, 90, 95, 20))
+ self.radioButton_file = QRadioButton(Form)
+ self.radioButton_file.setObjectName(u"radioButton_file")
+ self.radioButton_file.setGeometry(QRect(610, 120, 95, 20))
+ self.pushButton_selectfilepath = QPushButton(Form)
+ self.pushButton_selectfilepath.setObjectName(u"pushButton_selectfilepath")
+ self.pushButton_selectfilepath.setGeometry(QRect(380, 50, 101, 24))
+ self.label_4 = QLabel(Form)
+ self.label_4.setObjectName(u"label_4")
+ self.label_4.setGeometry(QRect(360, 80, 91, 16))
+ self.plainTextEdit_ans = QPlainTextEdit(Form)
+ self.plainTextEdit_ans.setObjectName(u"plainTextEdit_ans")
+ self.plainTextEdit_ans.setGeometry(QRect(360, 100, 241, 331))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(610, 150, 101, 281))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u6587\u4ef6\u6216\u6587\u672c\u6846\u63d0\u53d6\u7b54\u6848", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u8def\u5f84", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u542b\u7b54\u6848\u7684\u6e90\u4ee3\u7801", None))
+ self.lineEdit_2.setText(QCoreApplication.translate("Form", u"\u7b54\u6848: \\textcolor{blue}{", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u7b54\u6848\u524d\u7f00", None))
+ self.radioButton_text.setText(QCoreApplication.translate("Form", u"\u6587\u672c\u6846\u751f\u6210", None))
+ self.radioButton_file.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u751f\u6210", None))
+ self.pushButton_selectfilepath.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u6587\u4ef6\u8def\u5f84", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"\u7b54\u6848metadata", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u63d0\u53d6\u7b54\u6848", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_文本转换处理.py b/工具v4/Ui_文本转换处理.py
new file mode 100644
index 00000000..e83d1147
--- /dev/null
+++ b/工具v4/Ui_文本转换处理.py
@@ -0,0 +1,96 @@
+# -*- 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, QLabel, QPlainTextEdit, QPushButton,
+ QRadioButton, QSizePolicy, QVBoxLayout, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.plainTextEdit_origin = QPlainTextEdit(Form)
+ self.plainTextEdit_origin.setObjectName(u"plainTextEdit_origin")
+ self.plainTextEdit_origin.setGeometry(QRect(10, 30, 310, 430))
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(10, 10, 54, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(430, 10, 54, 16))
+ self.plainTextEdit_dest = QPlainTextEdit(Form)
+ self.plainTextEdit_dest.setObjectName(u"plainTextEdit_dest")
+ self.plainTextEdit_dest.setGeometry(QRect(430, 30, 310, 430))
+ self.plainTextEdit_dest.setAutoFillBackground(False)
+ self.plainTextEdit_dest.setReadOnly(True)
+ self.pushButton_convert = QPushButton(Form)
+ self.pushButton_convert.setObjectName(u"pushButton_convert")
+ self.pushButton_convert.setGeometry(QRect(330, 280, 91, 71))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_convert.setFont(font)
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(330, 100, 97, 126))
+ self.verticalLayout = QVBoxLayout(self.layoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.radioButton_mathpix = QRadioButton(self.layoutWidget)
+ self.radioButton_mathpix.setObjectName(u"radioButton_mathpix")
+
+ self.verticalLayout.addWidget(self.radioButton_mathpix)
+
+ self.radioButton_textcircled = QRadioButton(self.layoutWidget)
+ self.radioButton_textcircled.setObjectName(u"radioButton_textcircled")
+
+ self.verticalLayout.addWidget(self.radioButton_textcircled)
+
+ self.radioButton_multiple = QRadioButton(self.layoutWidget)
+ self.radioButton_multiple.setObjectName(u"radioButton_multiple")
+
+ self.verticalLayout.addWidget(self.radioButton_multiple)
+
+ self.radioButton_puctuations = QRadioButton(self.layoutWidget)
+ self.radioButton_puctuations.setObjectName(u"radioButton_puctuations")
+
+ self.verticalLayout.addWidget(self.radioButton_puctuations)
+
+ self.radioButton_answers = QRadioButton(self.layoutWidget)
+ self.radioButton_answers.setObjectName(u"radioButton_answers")
+
+ self.verticalLayout.addWidget(self.radioButton_answers)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u6587\u672c\u8f6c\u6362\u5904\u7406", None))
+ self.plainTextEdit_origin.setPlaceholderText(QCoreApplication.translate("Form", u"\u5c06\u6e90\u6587\u672c \u7c98\u8d34 \u5728\u8fd9\u4e2a\u6587\u672c\u6846\u5185", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u6e90\u6587\u672c", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u76ee\u6807\u6587\u672c", None))
+ self.plainTextEdit_dest.setPlaceholderText(QCoreApplication.translate("Form", u"\u6309 \u8f6c\u6362 \u4ee5\u83b7\u5f97\u76ee\u6807\u6587\u672c", None))
+ self.pushButton_convert.setText(QCoreApplication.translate("Form", u"\u8f6c\u6362", None))
+ self.radioButton_mathpix.setText(QCoreApplication.translate("Form", u"Mathpix", None))
+ self.radioButton_textcircled.setText(QCoreApplication.translate("Form", u"\u5706\u5708\u6570\u5b57", None))
+ self.radioButton_multiple.setText(QCoreApplication.translate("Form", u"\u591a\u9009\u8f6c\u586b\u7a7a", None))
+ self.radioButton_puctuations.setText(QCoreApplication.translate("Form", u"\u6807\u70b9\u8f6c\u534a\u89d2", None))
+ self.radioButton_answers.setText(QCoreApplication.translate("Form", u"\u5df2\u6709\u7b54\u6848\u6807\u7ea2", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_新增基础知识梳理.py b/工具v4/Ui_新增基础知识梳理.py
new file mode 100644
index 00000000..92d62985
--- /dev/null
+++ b/工具v4/Ui_新增基础知识梳理.py
@@ -0,0 +1,60 @@
+# -*- 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, QLabel, QLineEdit, QPushButton,
+ QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(200, 160, 91, 16))
+ self.lineEdit_filepath = QLineEdit(Form)
+ self.lineEdit_filepath.setObjectName(u"lineEdit_filepath")
+ self.lineEdit_filepath.setGeometry(QRect(200, 180, 381, 20))
+ self.lineEdit_filepath.setReadOnly(True)
+ self.pushButton_selectfilepath = QPushButton(Form)
+ self.pushButton_selectfilepath.setObjectName(u"pushButton_selectfilepath")
+ self.pushButton_selectfilepath.setGeometry(QRect(494, 150, 91, 24))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(200, 210, 291, 51))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(500, 210, 81, 51))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u65b0\u589e\u57fa\u7840\u77e5\u8bc6\u68b3\u7406", None))
+ self.label.setText(QCoreApplication.translate("Form", u"LaTeX\u6587\u4ef6\u4f4d\u7f6e", None))
+ self.pushButton_selectfilepath.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u6587\u4ef6", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"LaTeX\u6587\u4ef6\u4e2d\u6709\u4e00\u4e2aenumerate\u73af\u5883.\n"
+"\u6bcf\u4e00\u4e2aitem\u540e\u9762\u6709\u4e00\u4e2a\u4e2d\u62ec\u53f7\u5305\u56f4\u7684\u4fe1\u606f, \u4e4b\u540e\u662f\u5185\u5bb9.\n"
+"\u4f8b\u5982: \\item [K0101/K0101001B,K0101002B] \u5185\u5bb9", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u57fa\u7840\u77e5\u8bc6", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_根据正确率选择题号.py b/工具v4/Ui_根据正确率选择题号.py
new file mode 100644
index 00000000..b7eac7ce
--- /dev/null
+++ b/工具v4/Ui_根据正确率选择题号.py
@@ -0,0 +1,148 @@
+# -*- 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, QLabel, QLineEdit, QPlainTextEdit,
+ QPushButton, QSizePolicy, QVBoxLayout, QWidget)
+
+class Ui_widget(object):
+ def setupUi(self, widget):
+ if not widget.objectName():
+ widget.setObjectName(u"widget")
+ widget.resize(760, 490)
+ self.verticalLayoutWidget = QWidget(widget)
+ self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
+ self.verticalLayoutWidget.setGeometry(QRect(180, 10, 91, 92))
+ self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label = QLabel(self.verticalLayoutWidget)
+ self.label.setObjectName(u"label")
+
+ self.verticalLayout.addWidget(self.label)
+
+ self.lineEdit_startdate = QLineEdit(self.verticalLayoutWidget)
+ self.lineEdit_startdate.setObjectName(u"lineEdit_startdate")
+
+ self.verticalLayout.addWidget(self.lineEdit_startdate)
+
+ self.label_2 = QLabel(self.verticalLayoutWidget)
+ self.label_2.setObjectName(u"label_2")
+
+ self.verticalLayout.addWidget(self.label_2)
+
+ self.lineEdit_enddate = QLineEdit(self.verticalLayoutWidget)
+ self.lineEdit_enddate.setObjectName(u"lineEdit_enddate")
+
+ self.verticalLayout.addWidget(self.lineEdit_enddate)
+
+ self.verticalLayoutWidget_2 = QWidget(widget)
+ self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
+ self.verticalLayoutWidget_2.setGeometry(QRect(280, 10, 91, 92))
+ self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.label_3 = QLabel(self.verticalLayoutWidget_2)
+ self.label_3.setObjectName(u"label_3")
+
+ self.verticalLayout_2.addWidget(self.label_3)
+
+ self.lineEdit_lowerbound = QLineEdit(self.verticalLayoutWidget_2)
+ self.lineEdit_lowerbound.setObjectName(u"lineEdit_lowerbound")
+
+ self.verticalLayout_2.addWidget(self.lineEdit_lowerbound)
+
+ self.label_4 = QLabel(self.verticalLayoutWidget_2)
+ self.label_4.setObjectName(u"label_4")
+
+ self.verticalLayout_2.addWidget(self.label_4)
+
+ self.lineEdit_upperbound = QLineEdit(self.verticalLayoutWidget_2)
+ self.lineEdit_upperbound.setObjectName(u"lineEdit_upperbound")
+
+ self.verticalLayout_2.addWidget(self.lineEdit_upperbound)
+
+ self.verticalLayoutWidget_3 = QWidget(widget)
+ self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3")
+ self.verticalLayoutWidget_3.setGeometry(QRect(380, 10, 91, 92))
+ self.verticalLayout_3 = QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_3.setObjectName(u"verticalLayout_3")
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.label_5 = QLabel(self.verticalLayoutWidget_3)
+ self.label_5.setObjectName(u"label_5")
+ self.label_5.setMaximumSize(QSize(16777215, 20))
+
+ self.verticalLayout_3.addWidget(self.label_5)
+
+ self.lineEdit_classregex = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_classregex.setObjectName(u"lineEdit_classregex")
+
+ self.verticalLayout_3.addWidget(self.lineEdit_classregex)
+
+ self.pushButton_exec = QPushButton(widget)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(480, 10, 91, 91))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.label_6 = QLabel(widget)
+ self.label_6.setObjectName(u"label_6")
+ self.label_6.setGeometry(QRect(180, 110, 54, 16))
+ self.plainTextEdit_ids = QPlainTextEdit(widget)
+ self.plainTextEdit_ids.setObjectName(u"plainTextEdit_ids")
+ self.plainTextEdit_ids.setGeometry(QRect(180, 130, 391, 81))
+ self.plainTextEdit_ids.setReadOnly(True)
+ self.label_7 = QLabel(widget)
+ self.label_7.setObjectName(u"label_7")
+ self.label_7.setGeometry(QRect(180, 220, 141, 16))
+ self.plainTextEdit_cautionids = QPlainTextEdit(widget)
+ self.plainTextEdit_cautionids.setObjectName(u"plainTextEdit_cautionids")
+ self.plainTextEdit_cautionids.setGeometry(QRect(180, 240, 391, 81))
+ self.plainTextEdit_cautionids.setReadOnly(True)
+ self.label_8 = QLabel(widget)
+ self.label_8.setObjectName(u"label_8")
+ self.label_8.setGeometry(QRect(180, 330, 54, 16))
+ self.plainTextEdit_subproblems = QPlainTextEdit(widget)
+ self.plainTextEdit_subproblems.setObjectName(u"plainTextEdit_subproblems")
+ self.plainTextEdit_subproblems.setGeometry(QRect(180, 350, 391, 111))
+ self.plainTextEdit_subproblems.setReadOnly(True)
+
+ self.retranslateUi(widget)
+
+ QMetaObject.connectSlotsByName(widget)
+ # setupUi
+
+ def retranslateUi(self, widget):
+ widget.setWindowTitle(QCoreApplication.translate("widget", u"\u6839\u636e\u6b63\u786e\u7387\u9009\u62e9\u9898\u53f7", None))
+ self.label.setText(QCoreApplication.translate("widget", u"\u8d77\u59cb\u65e5\u671f", None))
+ self.lineEdit_startdate.setPlaceholderText(QCoreApplication.translate("widget", u"yyyymmdd", None))
+ self.label_2.setText(QCoreApplication.translate("widget", u"\u7ec8\u6b62\u65e5\u671f", None))
+ self.lineEdit_enddate.setText("")
+ self.lineEdit_enddate.setPlaceholderText(QCoreApplication.translate("widget", u"yyyymmdd", None))
+ self.label_3.setText(QCoreApplication.translate("widget", u"\u6b63\u786e\u7387\u4e0b\u754c", None))
+ self.lineEdit_lowerbound.setPlaceholderText(QCoreApplication.translate("widget", u"\u5982: 0.5", None))
+ self.label_4.setText(QCoreApplication.translate("widget", u"\u6b63\u786e\u7387\u4e0a\u754c", None))
+ self.lineEdit_upperbound.setText("")
+ self.lineEdit_upperbound.setPlaceholderText(QCoreApplication.translate("widget", u"\u5982: 0.73", None))
+ self.label_5.setText(QCoreApplication.translate("widget", u"\u73ed\u7ea7\u6b63\u5219\u8868\u8fbe\u5f0f", None))
+ self.lineEdit_classregex.setText("")
+ self.lineEdit_classregex.setPlaceholderText(QCoreApplication.translate("widget", u"\u5982: 2026", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("widget", u"\u751f\u6210\u5217\u8868", None))
+ self.label_6.setText(QCoreApplication.translate("widget", u"\u9898\u53f7", None))
+ self.label_7.setText(QCoreApplication.translate("widget", u"\u4f7f\u7528\u8bb0\u5f55\u957f\u5ea6\u4e0d\u4e00\u81f4\u9898\u53f7", None))
+ self.label_8.setText(QCoreApplication.translate("widget", u"\u5173\u6ce8\u5c0f\u9898", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_添加关联题目.py b/工具v4/Ui_添加关联题目.py
new file mode 100644
index 00000000..61590250
--- /dev/null
+++ b/工具v4/Ui_添加关联题目.py
@@ -0,0 +1,98 @@
+# -*- 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, QHBoxLayout, QLabel, QLineEdit,
+ QPushButton, QSizePolicy, QVBoxLayout, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(460, 120, 81, 81))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(250, 120, 196, 80))
+ self.verticalLayout = QVBoxLayout(self.layoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.label = QLabel(self.layoutWidget)
+ self.label.setObjectName(u"label")
+ self.label.setMinimumSize(QSize(60, 0))
+
+ self.horizontalLayout.addWidget(self.label)
+
+ self.lineEdit_oldids = QLineEdit(self.layoutWidget)
+ self.lineEdit_oldids.setObjectName(u"lineEdit_oldids")
+
+ self.horizontalLayout.addWidget(self.lineEdit_oldids)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.horizontalLayout_2 = QHBoxLayout()
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.label_2 = QLabel(self.layoutWidget)
+ self.label_2.setObjectName(u"label_2")
+
+ self.horizontalLayout_2.addWidget(self.label_2)
+
+ self.lineEdit_newid = QLineEdit(self.layoutWidget)
+ self.lineEdit_newid.setObjectName(u"lineEdit_newid")
+
+ self.horizontalLayout_2.addWidget(self.lineEdit_newid)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+
+ self.horizontalLayout_3 = QHBoxLayout()
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.label_3 = QLabel(self.layoutWidget)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setMinimumSize(QSize(60, 0))
+
+ self.horizontalLayout_3.addWidget(self.label_3)
+
+ self.lineEdit_editor = QLineEdit(self.layoutWidget)
+ self.lineEdit_editor.setObjectName(u"lineEdit_editor")
+
+ self.horizontalLayout_3.addWidget(self.lineEdit_editor)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout_3)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u5173\u8054\u9898\u76ee", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u5173\u8054\u9898\u76ee", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u65e7\u9898\u53f7", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u65b0\u8d77\u59cb\u9898\u53f7", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u7f16\u8f91\u8005", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_生成直方图代码.py b/工具v4/Ui_生成直方图代码.py
new file mode 100644
index 00000000..b776f3b8
--- /dev/null
+++ b/工具v4/Ui_生成直方图代码.py
@@ -0,0 +1,150 @@
+# -*- 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, QHBoxLayout, QLabel, QLineEdit,
+ QPlainTextEdit, QPushButton, QSizePolicy, QTextBrowser,
+ QVBoxLayout, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_generatexlist = QPushButton(Form)
+ self.pushButton_generatexlist.setObjectName(u"pushButton_generatexlist")
+ self.pushButton_generatexlist.setGeometry(QRect(10, 130, 121, 24))
+ self.textBrowser = QTextBrowser(Form)
+ self.textBrowser.setObjectName(u"textBrowser")
+ self.textBrowser.setEnabled(False)
+ self.textBrowser.setGeometry(QRect(10, 160, 121, 211))
+ self.label_6 = QLabel(Form)
+ self.label_6.setObjectName(u"label_6")
+ self.label_6.setGeometry(QRect(140, 10, 121, 16))
+ self.plainTextEdit_yinfo = QPlainTextEdit(Form)
+ self.plainTextEdit_yinfo.setObjectName(u"plainTextEdit_yinfo")
+ self.plainTextEdit_yinfo.setGeometry(QRect(140, 30, 521, 181))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(670, 30, 71, 181))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.label_7 = QLabel(Form)
+ self.label_7.setObjectName(u"label_7")
+ self.label_7.setGeometry(QRect(140, 220, 71, 16))
+ self.plainTextEdit_latexcode = QPlainTextEdit(Form)
+ self.plainTextEdit_latexcode.setObjectName(u"plainTextEdit_latexcode")
+ self.plainTextEdit_latexcode.setGeometry(QRect(140, 240, 601, 221))
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(10, 10, 121, 108))
+ self.verticalLayout = QVBoxLayout(self.layoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.label = QLabel(self.layoutWidget)
+ self.label.setObjectName(u"label")
+
+ self.horizontalLayout.addWidget(self.label)
+
+ self.lineEdit_xmin = QLineEdit(self.layoutWidget)
+ self.lineEdit_xmin.setObjectName(u"lineEdit_xmin")
+
+ self.horizontalLayout.addWidget(self.lineEdit_xmin)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.horizontalLayout_2 = QHBoxLayout()
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.label_2 = QLabel(self.layoutWidget)
+ self.label_2.setObjectName(u"label_2")
+
+ self.horizontalLayout_2.addWidget(self.label_2)
+
+ self.lineEdit_xmax = QLineEdit(self.layoutWidget)
+ self.lineEdit_xmax.setObjectName(u"lineEdit_xmax")
+
+ self.horizontalLayout_2.addWidget(self.lineEdit_xmax)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+
+ self.horizontalLayout_5 = QHBoxLayout()
+ self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
+ self.label_5 = QLabel(self.layoutWidget)
+ self.label_5.setObjectName(u"label_5")
+
+ self.horizontalLayout_5.addWidget(self.label_5)
+
+ self.lineEdit_xwidth = QLineEdit(self.layoutWidget)
+ self.lineEdit_xwidth.setObjectName(u"lineEdit_xwidth")
+
+ self.horizontalLayout_5.addWidget(self.lineEdit_xwidth)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout_5)
+
+ self.horizontalLayout_4 = QHBoxLayout()
+ self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
+ self.label_4 = QLabel(self.layoutWidget)
+ self.label_4.setObjectName(u"label_4")
+
+ self.horizontalLayout_4.addWidget(self.label_4)
+
+ self.lineEdit_xlabel = QLineEdit(self.layoutWidget)
+ self.lineEdit_xlabel.setObjectName(u"lineEdit_xlabel")
+
+ self.horizontalLayout_4.addWidget(self.lineEdit_xlabel)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout_4)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u751f\u6210\u76f4\u65b9\u56fe\u4ee3\u7801", None))
+ self.pushButton_generatexlist.setText(QCoreApplication.translate("Form", u"\u751f\u6210\u6a2a\u5750\u6807\u5e8f\u5217", None))
+ self.textBrowser.setHtml(QCoreApplication.translate("Form", u"\n"
+"
\n"
+"\u4e0d\u6539\u53d8\u6a2a\u5750\u6807\u7684\u503c, \u5728\u9017\u53f7\u540e\u8f93\u5165\u7eb5\u5750\u6807\u7684\u503c
\n"
+"\u5982\u679c\u6807\u7b7e\u548c\u7eb5\u5750\u6807\u7684\u503c\u4e0d\u540c, \u5728\u540e\u9762\u7684\u65b9\u62ec\u53f7\u5185\u8f93\u5165\u6807\u7b7e"
+ "p>\n"
+"
\u5982:
\n"
+"(10,0.02)[]
\n"
+"(20,0.04)[a]
\n"
+"(30,0.04][]
\n"
+"
", None))
+ self.label_6.setText(QCoreApplication.translate("Form", u"\u8f93\u5165\u7eb5\u5750\u6807\u4fe1\u606f", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u751f\u6210\u4ee3\u7801", None))
+ self.label_7.setText(QCoreApplication.translate("Form", u"\u76f4\u65b9\u56fe\u4ee3\u7801", None))
+ self.label.setText(QCoreApplication.translate("Form", u"x\u6700\u5c0f\u503c", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"x\u6700\u5927\u503c", None))
+ self.label_5.setText(QCoreApplication.translate("Form", u"x\u5bbd\u5ea6", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"x\u8f74label", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_答题纸对应.py b/工具v4/Ui_答题纸对应.py
new file mode 100644
index 00000000..5d32e49d
--- /dev/null
+++ b/工具v4/Ui_答题纸对应.py
@@ -0,0 +1,160 @@
+# -*- 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, QComboBox, QHBoxLayout, QLabel,
+ QLineEdit, QPlainTextEdit, QPushButton, QSizePolicy,
+ QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(180, 90, 81, 16))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setEnabled(False)
+ self.pushButton_exec.setGeometry(QRect(440, 140, 111, 191))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(180, 60, 371, 22))
+ self.horizontalLayout = QHBoxLayout(self.layoutWidget)
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label = QLabel(self.layoutWidget)
+ self.label.setObjectName(u"label")
+
+ self.horizontalLayout.addWidget(self.label)
+
+ self.lineEdit_xiaoxianid = QLineEdit(self.layoutWidget)
+ self.lineEdit_xiaoxianid.setObjectName(u"lineEdit_xiaoxianid")
+
+ self.horizontalLayout.addWidget(self.lineEdit_xiaoxianid)
+
+ self.layoutWidget1 = QWidget(Form)
+ self.layoutWidget1.setObjectName(u"layoutWidget1")
+ self.layoutWidget1.setGeometry(QRect(180, 110, 371, 23))
+ self.horizontalLayout_2 = QHBoxLayout(self.layoutWidget1)
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.label_3 = QLabel(self.layoutWidget1)
+ self.label_3.setObjectName(u"label_3")
+
+ self.horizontalLayout_2.addWidget(self.label_3)
+
+ self.lineEdit_structure = QLineEdit(self.layoutWidget1)
+ self.lineEdit_structure.setObjectName(u"lineEdit_structure")
+ self.lineEdit_structure.setMaximumSize(QSize(20, 16777215))
+
+ self.horizontalLayout_2.addWidget(self.lineEdit_structure)
+
+ self.label_4 = QLabel(self.layoutWidget1)
+ self.label_4.setObjectName(u"label_4")
+
+ self.horizontalLayout_2.addWidget(self.label_4)
+
+ self.comboBox_grade = QComboBox(self.layoutWidget1)
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.setObjectName(u"comboBox_grade")
+
+ self.horizontalLayout_2.addWidget(self.comboBox_grade)
+
+ self.label_5 = QLabel(self.layoutWidget1)
+ self.label_5.setObjectName(u"label_5")
+
+ self.horizontalLayout_2.addWidget(self.label_5)
+
+ self.comboBox_semester = QComboBox(self.layoutWidget1)
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.setObjectName(u"comboBox_semester")
+
+ self.horizontalLayout_2.addWidget(self.comboBox_semester)
+
+ self.label_6 = QLabel(self.layoutWidget1)
+ self.label_6.setObjectName(u"label_6")
+
+ self.horizontalLayout_2.addWidget(self.label_6)
+
+ self.comboBox_index = QComboBox(self.layoutWidget1)
+ self.comboBox_index.setObjectName(u"comboBox_index")
+
+ self.horizontalLayout_2.addWidget(self.comboBox_index)
+
+ self.plainTextEdit_marks = QPlainTextEdit(Form)
+ self.plainTextEdit_marks.setObjectName(u"plainTextEdit_marks")
+ self.plainTextEdit_marks.setGeometry(QRect(180, 160, 251, 171))
+ self.label_7 = QLabel(Form)
+ self.label_7.setObjectName(u"label_7")
+ self.label_7.setGeometry(QRect(180, 140, 111, 16))
+ self.label_next = QLabel(Form)
+ self.label_next.setObjectName(u"label_next")
+ self.label_next.setGeometry(QRect(180, 340, 161, 16))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u7b54\u9898\u7eb8\u5bf9\u5e94", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u5bf9\u5e94\u8bb2\u4e49\u4ee3\u7801", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u7b54\u9898\u7eb8\u5bf9\u5e94", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u5c0f\u95f2\u8bd5\u5377ID", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u7c7b\u578b", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"\u5c4a\u522b", None))
+ self.comboBox_grade.setItemText(0, QCoreApplication.translate("Form", u"2022", None))
+ self.comboBox_grade.setItemText(1, QCoreApplication.translate("Form", u"2023", None))
+ self.comboBox_grade.setItemText(2, QCoreApplication.translate("Form", u"2024", None))
+ self.comboBox_grade.setItemText(3, QCoreApplication.translate("Form", u"2025", None))
+ self.comboBox_grade.setItemText(4, QCoreApplication.translate("Form", u"2026", None))
+ self.comboBox_grade.setItemText(5, QCoreApplication.translate("Form", u"2027", None))
+ self.comboBox_grade.setItemText(6, QCoreApplication.translate("Form", u"2028", None))
+ self.comboBox_grade.setItemText(7, QCoreApplication.translate("Form", u"2029", None))
+
+ self.label_5.setText(QCoreApplication.translate("Form", u"\u5b66\u671f", None))
+ self.comboBox_semester.setItemText(0, QCoreApplication.translate("Form", u"00", None))
+ self.comboBox_semester.setItemText(1, QCoreApplication.translate("Form", u"01", None))
+ self.comboBox_semester.setItemText(2, QCoreApplication.translate("Form", u"02", None))
+ self.comboBox_semester.setItemText(3, QCoreApplication.translate("Form", u"03", None))
+ self.comboBox_semester.setItemText(4, QCoreApplication.translate("Form", u"04", None))
+ self.comboBox_semester.setItemText(5, QCoreApplication.translate("Form", u"05", None))
+ self.comboBox_semester.setItemText(6, QCoreApplication.translate("Form", u"06", None))
+
+ self.label_6.setText(QCoreApplication.translate("Form", u"\u5e8f\u53f7", None))
+ self.plainTextEdit_marks.setPlaceholderText(QCoreApplication.translate("Form", u"\u5c06\u5c0f\u95f2\u5e73\u53f0\u7b54\u9898\u7eb8 \u4fee\u6539 \u754c\u9762\u4e2d\u95f4\u9760\u53f3\u7684\u6240\u6709\u5206\u6570\u590d\u5236\u5230\u8be5\u6587\u672c\u6846, \u7559\u7a7a\u8868\u793a\u5747\u4e3a1\u5206", None))
+ self.label_7.setText(QCoreApplication.translate("Form", u"\u5c0f\u95f2\u5e73\u53f0\u5404\u9898\u6ee1\u5206", None))
+ self.label_next.setText(QCoreApplication.translate("Form", u"\u8bf7\u5230\u7ec8\u7aef\u5b8c\u6210\u4e4b\u540e\u7684\u64cd\u4f5c", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_系列讲义生成.py b/工具v4/Ui_系列讲义生成.py
new file mode 100644
index 00000000..53c42abc
--- /dev/null
+++ b/工具v4/Ui_系列讲义生成.py
@@ -0,0 +1,229 @@
+# -*- 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, QRadioButton, QSizePolicy,
+ QVBoxLayout, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.lineEdit_regex = QLineEdit(Form)
+ self.lineEdit_regex.setObjectName(u"lineEdit_regex")
+ self.lineEdit_regex.setGeometry(QRect(120, 60, 431, 20))
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(30, 60, 91, 16))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(570, 60, 171, 16))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(510, 310, 221, 131))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(30, 90, 61, 16))
+ self.lineEdit_path = QLineEdit(Form)
+ self.lineEdit_path.setObjectName(u"lineEdit_path")
+ self.lineEdit_path.setGeometry(QRect(30, 110, 701, 20))
+ self.pushButton_SelectPath = QPushButton(Form)
+ self.pushButton_SelectPath.setObjectName(u"pushButton_SelectPath")
+ self.pushButton_SelectPath.setGeometry(QRect(660, 80, 75, 24))
+ self.verticalLayoutWidget = QWidget(Form)
+ self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
+ self.verticalLayoutWidget.setGeometry(QRect(30, 170, 81, 111))
+ self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label_4 = QLabel(self.verticalLayoutWidget)
+ self.label_4.setObjectName(u"label_4")
+ self.label_4.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
+
+ self.verticalLayout.addWidget(self.label_4)
+
+ self.radioButton_student = QRadioButton(self.verticalLayoutWidget)
+ self.radioButton_student.setObjectName(u"radioButton_student")
+ self.radioButton_student.setChecked(True)
+
+ self.verticalLayout.addWidget(self.radioButton_student)
+
+ self.radioButton_teacher = QRadioButton(self.verticalLayoutWidget)
+ self.radioButton_teacher.setObjectName(u"radioButton_teacher")
+
+ self.verticalLayout.addWidget(self.radioButton_teacher)
+
+ self.verticalLayoutWidget_2 = QWidget(Form)
+ self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
+ self.verticalLayoutWidget_2.setGeometry(QRect(250, 170, 151, 271))
+ self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.label_5 = QLabel(self.verticalLayoutWidget_2)
+ self.label_5.setObjectName(u"label_5")
+
+ self.verticalLayout_2.addWidget(self.label_5)
+
+ self.checkBox_space = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_space.setObjectName(u"checkBox_space")
+
+ self.verticalLayout_2.addWidget(self.checkBox_space)
+
+ self.checkBox_ans = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_ans.setObjectName(u"checkBox_ans")
+
+ self.verticalLayout_2.addWidget(self.checkBox_ans)
+
+ self.checkBox_objs = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_objs.setObjectName(u"checkBox_objs")
+ self.checkBox_objs.setCheckable(True)
+ self.checkBox_objs.setTristate(False)
+
+ self.verticalLayout_2.addWidget(self.checkBox_objs)
+
+ self.checkBox_tags = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_tags.setObjectName(u"checkBox_tags")
+
+ self.verticalLayout_2.addWidget(self.checkBox_tags)
+
+ self.checkBox_solution = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_solution.setObjectName(u"checkBox_solution")
+
+ self.verticalLayout_2.addWidget(self.checkBox_solution)
+
+ self.checkBox_usages = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_usages.setObjectName(u"checkBox_usages")
+
+ self.verticalLayout_2.addWidget(self.checkBox_usages)
+
+ self.checkBox_origin = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_origin.setObjectName(u"checkBox_origin")
+
+ self.verticalLayout_2.addWidget(self.checkBox_origin)
+
+ self.checkBox_remark = QCheckBox(self.verticalLayoutWidget_2)
+ self.checkBox_remark.setObjectName(u"checkBox_remark")
+
+ self.verticalLayout_2.addWidget(self.checkBox_remark)
+
+ self.verticalLayoutWidget_3 = QWidget(Form)
+ self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3")
+ self.verticalLayoutWidget_3.setGeometry(QRect(510, 170, 222, 121))
+ self.verticalLayout_usagepanel = QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_usagepanel.setObjectName(u"verticalLayout_usagepanel")
+ self.verticalLayout_usagepanel.setContentsMargins(0, 0, 0, 0)
+ self.label_6 = QLabel(self.verticalLayoutWidget_3)
+ self.label_6.setObjectName(u"label_6")
+
+ self.verticalLayout_usagepanel.addWidget(self.label_6)
+
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.label_9 = QLabel(self.verticalLayoutWidget_3)
+ self.label_9.setObjectName(u"label_9")
+
+ self.horizontalLayout.addWidget(self.label_9)
+
+ self.lineEdit_high = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_high.setObjectName(u"lineEdit_high")
+ self.lineEdit_high.setEnabled(True)
+
+ self.horizontalLayout.addWidget(self.lineEdit_high)
+
+ self.label_10 = QLabel(self.verticalLayoutWidget_3)
+ self.label_10.setObjectName(u"label_10")
+
+ self.horizontalLayout.addWidget(self.label_10)
+
+ self.lineEdit_low = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_low.setObjectName(u"lineEdit_low")
+
+ self.horizontalLayout.addWidget(self.lineEdit_low)
+
+
+ self.verticalLayout_usagepanel.addLayout(self.horizontalLayout)
+
+ self.label_7 = QLabel(self.verticalLayoutWidget_3)
+ self.label_7.setObjectName(u"label_7")
+
+ self.verticalLayout_usagepanel.addWidget(self.label_7)
+
+ self.lineEdit_grades = QLineEdit(self.verticalLayoutWidget_3)
+ self.lineEdit_grades.setObjectName(u"lineEdit_grades")
+
+ self.verticalLayout_usagepanel.addWidget(self.lineEdit_grades)
+
+ self.verticalLayoutWidget_4 = QWidget(Form)
+ self.verticalLayoutWidget_4.setObjectName(u"verticalLayoutWidget_4")
+ self.verticalLayoutWidget_4.setGeometry(QRect(30, 300, 81, 141))
+ self.verticalLayout_3 = QVBoxLayout(self.verticalLayoutWidget_4)
+ self.verticalLayout_3.setObjectName(u"verticalLayout_3")
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.label_8 = QLabel(self.verticalLayoutWidget_4)
+ self.label_8.setObjectName(u"label_8")
+
+ self.verticalLayout_3.addWidget(self.label_8)
+
+ self.checkBox_singlenote = QCheckBox(self.verticalLayoutWidget_4)
+ self.checkBox_singlenote.setObjectName(u"checkBox_singlenote")
+
+ self.verticalLayout_3.addWidget(self.checkBox_singlenote)
+
+ self.checkBox_seriesnote = QCheckBox(self.verticalLayoutWidget_4)
+ self.checkBox_seriesnote.setObjectName(u"checkBox_seriesnote")
+ self.checkBox_seriesnote.setChecked(True)
+
+ self.verticalLayout_3.addWidget(self.checkBox_seriesnote)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u7cfb\u5217\u8bb2\u4e49\u751f\u6210", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u8bb2\u4e49\u7f16\u53f7\u8868\u8fbe\u5f0f", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u7528 \",\" \u5206\u9694\u7684\u4e00\u7cfb\u5217\u6b63\u5219\u8868\u8fbe\u5f0f", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u5f00\u59cb\u751f\u6210\u4e0e\u7f16\u8bd1", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u8def\u5f84", None))
+ self.pushButton_SelectPath.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u8def\u5f84", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"\u7248\u5f0f\u9009\u62e9", None))
+ self.radioButton_student.setText(QCoreApplication.translate("Form", u"\u5b66\u751f\u7248", None))
+ self.radioButton_teacher.setText(QCoreApplication.translate("Form", u"\u6559\u5e08\u7248", None))
+ self.label_5.setText(QCoreApplication.translate("Form", u"\u5185\u5bb9\u9009\u9879", None))
+ self.checkBox_space.setText(QCoreApplication.translate("Form", u"\u9898\u540e\u7a7a\u95f4", None))
+ self.checkBox_ans.setText(QCoreApplication.translate("Form", u"\u7b54\u6848", None))
+ self.checkBox_objs.setText(QCoreApplication.translate("Form", u"\u8bfe\u65f6\u76ee\u6807", None))
+ self.checkBox_tags.setText(QCoreApplication.translate("Form", u"\u9898\u76ee\u6807\u7b7e", None))
+ self.checkBox_solution.setText(QCoreApplication.translate("Form", u"\u89e3\u7b54\u4e0e\u63d0\u793a", None))
+ self.checkBox_usages.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u8bb0\u5f55", None))
+ self.checkBox_origin.setText(QCoreApplication.translate("Form", u"\u6765\u6e90", None))
+ self.checkBox_remark.setText(QCoreApplication.translate("Form", u"\u5907\u6ce8", None))
+ self.label_6.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u8bb0\u5f55\u9009\u62e9(\u90fd\u7559\u7a7a\u4e3a\u5168\u663e\u793a)", None))
+ self.label_9.setText(QCoreApplication.translate("Form", u"\u9ad8", None))
+ self.label_10.setText(QCoreApplication.translate("Form", u"\u4f4e", None))
+ self.label_7.setText(QCoreApplication.translate("Form", u"\u5c4a\u522b\u9009\u62e9(\u7528 \",\" \u5206\u9694, \u7559\u7a7a\u4e3a\u5168\u663e\u793a)", None))
+ self.label_8.setText(QCoreApplication.translate("Form", u"\u7f16\u8bd1\u9009\u9879", None))
+ self.checkBox_singlenote.setText(QCoreApplication.translate("Form", u"\u5355\u5f20\u7f16\u8bd1", None))
+ self.checkBox_seriesnote.setText(QCoreApplication.translate("Form", u"\u5408\u96c6\u7f16\u8bd1", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_统考数据导入.py b/工具v4/Ui_统考数据导入.py
new file mode 100644
index 00000000..eb9146bc
--- /dev/null
+++ b/工具v4/Ui_统考数据导入.py
@@ -0,0 +1,176 @@
+# -*- 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, QHBoxLayout, QLabel, QLineEdit,
+ QPlainTextEdit, QPushButton, QSizePolicy, QVBoxLayout,
+ QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(120, 170, 381, 54))
+ self.verticalLayout_2 = QVBoxLayout(self.layoutWidget)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2 = QHBoxLayout()
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.label_2 = QLabel(self.layoutWidget)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setMinimumSize(QSize(180, 0))
+ self.label_2.setMaximumSize(QSize(180, 16777215))
+
+ self.horizontalLayout_2.addWidget(self.label_2)
+
+ self.pushButton_excel = QPushButton(self.layoutWidget)
+ self.pushButton_excel.setObjectName(u"pushButton_excel")
+ self.pushButton_excel.setMinimumSize(QSize(60, 0))
+
+ self.horizontalLayout_2.addWidget(self.pushButton_excel)
+
+
+ self.verticalLayout_2.addLayout(self.horizontalLayout_2)
+
+ self.lineEdit_excelfile = QLineEdit(self.layoutWidget)
+ self.lineEdit_excelfile.setObjectName(u"lineEdit_excelfile")
+ self.lineEdit_excelfile.setReadOnly(True)
+
+ self.verticalLayout_2.addWidget(self.lineEdit_excelfile)
+
+ self.layoutWidget_2 = QWidget(Form)
+ self.layoutWidget_2.setObjectName(u"layoutWidget_2")
+ self.layoutWidget_2.setGeometry(QRect(120, 260, 194, 22))
+ self.horizontalLayout_4 = QHBoxLayout(self.layoutWidget_2)
+ self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
+ self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.label_4 = QLabel(self.layoutWidget_2)
+ self.label_4.setObjectName(u"label_4")
+ self.label_4.setMinimumSize(QSize(100, 0))
+
+ self.horizontalLayout_4.addWidget(self.label_4)
+
+ self.lineEdit_grade = QLineEdit(self.layoutWidget_2)
+ self.lineEdit_grade.setObjectName(u"lineEdit_grade")
+
+ self.horizontalLayout_4.addWidget(self.lineEdit_grade)
+
+ self.layoutWidget_3 = QWidget(Form)
+ self.layoutWidget_3.setObjectName(u"layoutWidget_3")
+ self.layoutWidget_3.setGeometry(QRect(120, 290, 194, 22))
+ self.horizontalLayout_5 = QHBoxLayout(self.layoutWidget_3)
+ self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
+ self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.label_5 = QLabel(self.layoutWidget_3)
+ self.label_5.setObjectName(u"label_5")
+ self.label_5.setMinimumSize(QSize(100, 0))
+
+ self.horizontalLayout_5.addWidget(self.label_5)
+
+ self.lineEdit_classnum = QLineEdit(self.layoutWidget_3)
+ self.lineEdit_classnum.setObjectName(u"lineEdit_classnum")
+
+ self.horizontalLayout_5.addWidget(self.lineEdit_classnum)
+
+ self.plainTextEdit = QPlainTextEdit(Form)
+ self.plainTextEdit.setObjectName(u"plainTextEdit")
+ self.plainTextEdit.setGeometry(QRect(510, 110, 141, 201))
+ self.plainTextEdit.setReadOnly(True)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(320, 230, 181, 81))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.layoutWidget1 = QWidget(Form)
+ self.layoutWidget1.setObjectName(u"layoutWidget1")
+ self.layoutWidget1.setGeometry(QRect(120, 110, 381, 54))
+ self.verticalLayout = QVBoxLayout(self.layoutWidget1)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.label = QLabel(self.layoutWidget1)
+ self.label.setObjectName(u"label")
+ self.label.setMinimumSize(QSize(180, 0))
+ self.label.setMaximumSize(QSize(180, 16777215))
+
+ self.horizontalLayout.addWidget(self.label)
+
+ self.pushButton_paper = QPushButton(self.layoutWidget1)
+ self.pushButton_paper.setObjectName(u"pushButton_paper")
+ self.pushButton_paper.setMinimumSize(QSize(60, 0))
+
+ self.horizontalLayout.addWidget(self.pushButton_paper)
+
+
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.lineEdit_paperfile = QLineEdit(self.layoutWidget1)
+ self.lineEdit_paperfile.setObjectName(u"lineEdit_paperfile")
+ self.lineEdit_paperfile.setReadOnly(True)
+
+ self.verticalLayout.addWidget(self.lineEdit_paperfile)
+
+ self.layoutWidget2 = QWidget(Form)
+ self.layoutWidget2.setObjectName(u"layoutWidget2")
+ self.layoutWidget2.setGeometry(QRect(120, 230, 194, 22))
+ self.horizontalLayout_3 = QHBoxLayout(self.layoutWidget2)
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.label_3 = QLabel(self.layoutWidget2)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setMinimumSize(QSize(100, 0))
+
+ self.horizontalLayout_3.addWidget(self.label_3)
+
+ self.lineEdit_date = QLineEdit(self.layoutWidget2)
+ self.lineEdit_date.setObjectName(u"lineEdit_date")
+ self.lineEdit_date.setMaximumSize(QSize(100, 16777215))
+
+ self.horizontalLayout_3.addWidget(self.lineEdit_date)
+
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u7edf\u8003\u6570\u636e\u5bfc\u5165", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u7edf\u8ba1\u6587\u4ef6(.xlsx)", None))
+ self.pushButton_excel.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u9009\u62e9", None))
+ self.label_4.setText(QCoreApplication.translate("Form", u"\u5e74\u7ea7", None))
+ self.lineEdit_grade.setPlaceholderText(QCoreApplication.translate("Form", u"\u5982 2026\u5c4a\u9ad8\u4e00", None))
+ self.label_5.setText(QCoreApplication.translate("Form", u"\u73ed\u7ea7\u6570", None))
+ self.lineEdit_classnum.setPlaceholderText(QCoreApplication.translate("Form", u"\u5982 12", None))
+ self.plainTextEdit.setDocumentTitle("")
+ self.plainTextEdit.setPlainText(QCoreApplication.translate("Form", u"\u8bf4\u660e:\n"
+".xlsx\u6587\u4ef6\u9700\u8981\u6709\u4e00\u4e2a\u540d\u4e3a\"\u96be\u5ea6\u7edf\u8ba1\"\u7684\u6570\u636e\u8868.\n"
+"\u7b2c\u4e00\u5217\u4ece\u7b2c\u4e8c\u884c\u8d77\u4e3a\u73ed\u7ea7\u53f7, \u7b2c\u4e00\u884c\u4ece\u7b2c\u4e8c\u5217\u8d77\u4e3a\u9898\u53f7.\n"
+"\u4f8b\u5982: 1,2,3,4,5,61,62,71,72,73,8,9,101,102,...\n"
+"", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u7edf\u8003\u6570\u636e\u5bfc\u5165\n"
+"metadata.txt", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u8bd5\u5377\u6587\u4ef6(.tex\u6216.pdf)", None))
+ self.pushButton_paper.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u9009\u62e9", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u65e5\u671f(yyyymmdd)", None))
+ self.lineEdit_date.setPlaceholderText(QCoreApplication.translate("Form", u"\u5982 20240101", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_获取小闲平台使用数据.py b/工具v4/Ui_获取小闲平台使用数据.py
new file mode 100644
index 00000000..9525eabe
--- /dev/null
+++ b/工具v4/Ui_获取小闲平台使用数据.py
@@ -0,0 +1,88 @@
+# -*- 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, QHeaderView, QLabel,
+ QLineEdit, QPushButton, QSizePolicy, QTableWidget,
+ QTableWidgetItem, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_file = QPushButton(Form)
+ self.pushButton_file.setObjectName(u"pushButton_file")
+ self.pushButton_file.setGeometry(QRect(620, 70, 91, 24))
+ self.lineEdit_threshold = QLineEdit(Form)
+ self.lineEdit_threshold.setObjectName(u"lineEdit_threshold")
+ self.lineEdit_threshold.setGeometry(QRect(620, 30, 91, 20))
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(623, 10, 61, 20))
+ self.pushButton_folder = QPushButton(Form)
+ self.pushButton_folder.setObjectName(u"pushButton_folder")
+ self.pushButton_folder.setGeometry(QRect(620, 110, 91, 24))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(620, 270, 91, 75))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.tableWidget = QTableWidget(Form)
+ if (self.tableWidget.columnCount() < 3):
+ self.tableWidget.setColumnCount(3)
+ __qtablewidgetitem = QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
+ __qtablewidgetitem1 = QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
+ __qtablewidgetitem2 = QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2)
+ if (self.tableWidget.rowCount() < 1):
+ self.tableWidget.setRowCount(1)
+ self.tableWidget.setObjectName(u"tableWidget")
+ self.tableWidget.setGeometry(QRect(10, 20, 600, 451))
+ self.tableWidget.setRowCount(1)
+ self.tableWidget.setColumnCount(3)
+ self.tableWidget.horizontalHeader().setDefaultSectionSize(130)
+ self.checkBox = QCheckBox(Form)
+ self.checkBox.setObjectName(u"checkBox")
+ self.checkBox.setGeometry(QRect(620, 230, 91, 20))
+ self.pushButton_validcheck = QPushButton(Form)
+ self.pushButton_validcheck.setObjectName(u"pushButton_validcheck")
+ self.pushButton_validcheck.setGeometry(QRect(620, 160, 91, 51))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
+ self.pushButton_file.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u6587\u4ef6", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u8bbe\u5b9a\u9608\u503c", None))
+ self.pushButton_folder.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u6587\u4ef6\u5939", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u5f00\u59cb\u5206\u6790", None))
+ ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0)
+ ___qtablewidgetitem.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u540d", None));
+ ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1)
+ ___qtablewidgetitem1.setText(QCoreApplication.translate("Form", u"\u7b54\u9898\u7eb8\u540d\u79f0", None));
+ ___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(2)
+ ___qtablewidgetitem2.setText(QCoreApplication.translate("Form", u"\u4e2d\u4f4d\u626b\u63cf\u65e5\u671f", None));
+ self.checkBox.setText(QCoreApplication.translate("Form", u"Append\u6a21\u5f0f", None))
+ self.pushButton_validcheck.setText(QCoreApplication.translate("Form", u"\u68c0\u67e5\u6709\u6548\u6027", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_获取题号.py b/工具v4/Ui_获取题号.py
new file mode 100644
index 00000000..8eec38ed
--- /dev/null
+++ b/工具v4/Ui_获取题号.py
@@ -0,0 +1,62 @@
+# -*- 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, QLabel, QLineEdit, QPlainTextEdit,
+ QPushButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(10, 10, 54, 16))
+ self.pushButton_selectfilepath = QPushButton(Form)
+ self.pushButton_selectfilepath.setObjectName(u"pushButton_selectfilepath")
+ self.pushButton_selectfilepath.setGeometry(QRect(670, 10, 75, 24))
+ self.plainTextEdit = QPlainTextEdit(Form)
+ self.plainTextEdit.setObjectName(u"plainTextEdit")
+ self.plainTextEdit.setGeometry(QRect(10, 100, 741, 361))
+ self.plainTextEdit.setReadOnly(True)
+ self.lineEdit = QLineEdit(Form)
+ self.lineEdit.setObjectName(u"lineEdit")
+ self.lineEdit.setGeometry(QRect(10, 40, 741, 20))
+ self.lineEdit.setReadOnly(True)
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(10, 80, 54, 16))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(670, 70, 75, 24))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u83b7\u53d6\u9898\u53f7", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u8def\u5f84", None))
+ self.pushButton_selectfilepath.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u6587\u4ef6", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u9898\u53f7", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u83b7\u53d6\u9898\u53f7", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_讲义结构与内容录入.py b/工具v4/Ui_讲义结构与内容录入.py
new file mode 100644
index 00000000..8a33510e
--- /dev/null
+++ b/工具v4/Ui_讲义结构与内容录入.py
@@ -0,0 +1,115 @@
+# -*- 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, QComboBox, QHBoxLayout, QLabel,
+ QLineEdit, QPushButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(460, 200, 75, 24))
+ self.layoutWidget = QWidget(Form)
+ self.layoutWidget.setObjectName(u"layoutWidget")
+ self.layoutWidget.setGeometry(QRect(220, 200, 221, 23))
+ self.horizontalLayout = QHBoxLayout(self.layoutWidget)
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.label = QLabel(self.layoutWidget)
+ self.label.setObjectName(u"label")
+
+ self.horizontalLayout.addWidget(self.label)
+
+ self.lineEdit_structure = QLineEdit(self.layoutWidget)
+ self.lineEdit_structure.setObjectName(u"lineEdit_structure")
+ self.lineEdit_structure.setMaximumSize(QSize(20, 16777215))
+
+ self.horizontalLayout.addWidget(self.lineEdit_structure)
+
+ self.label_2 = QLabel(self.layoutWidget)
+ self.label_2.setObjectName(u"label_2")
+
+ self.horizontalLayout.addWidget(self.label_2)
+
+ self.comboBox_grade = QComboBox(self.layoutWidget)
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.addItem("")
+ self.comboBox_grade.setObjectName(u"comboBox_grade")
+
+ self.horizontalLayout.addWidget(self.comboBox_grade)
+
+ self.label_3 = QLabel(self.layoutWidget)
+ self.label_3.setObjectName(u"label_3")
+
+ self.horizontalLayout.addWidget(self.label_3)
+
+ self.comboBox_semester = QComboBox(self.layoutWidget)
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.addItem("")
+ self.comboBox_semester.setObjectName(u"comboBox_semester")
+
+ self.horizontalLayout.addWidget(self.comboBox_semester)
+
+ self.label_next = QLabel(Form)
+ self.label_next.setObjectName(u"label_next")
+ self.label_next.setGeometry(QRect(400, 240, 141, 16))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u6267\u884c", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u79cd\u7c7b", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u5c4a\u522b", None))
+ self.comboBox_grade.setItemText(0, QCoreApplication.translate("Form", u"2022", None))
+ self.comboBox_grade.setItemText(1, QCoreApplication.translate("Form", u"2023", None))
+ self.comboBox_grade.setItemText(2, QCoreApplication.translate("Form", u"2024", None))
+ self.comboBox_grade.setItemText(3, QCoreApplication.translate("Form", u"2025", None))
+ self.comboBox_grade.setItemText(4, QCoreApplication.translate("Form", u"2026", None))
+ self.comboBox_grade.setItemText(5, QCoreApplication.translate("Form", u"2027", None))
+ self.comboBox_grade.setItemText(6, QCoreApplication.translate("Form", u"2028", None))
+ self.comboBox_grade.setItemText(7, QCoreApplication.translate("Form", u"2029", None))
+
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u5b66\u671f", None))
+ self.comboBox_semester.setItemText(0, QCoreApplication.translate("Form", u"00", None))
+ self.comboBox_semester.setItemText(1, QCoreApplication.translate("Form", u"01", None))
+ self.comboBox_semester.setItemText(2, QCoreApplication.translate("Form", u"02", None))
+ self.comboBox_semester.setItemText(3, QCoreApplication.translate("Form", u"03", None))
+ self.comboBox_semester.setItemText(4, QCoreApplication.translate("Form", u"04", None))
+ self.comboBox_semester.setItemText(5, QCoreApplication.translate("Form", u"05", None))
+ self.comboBox_semester.setItemText(6, QCoreApplication.translate("Form", u"06", None))
+
+ self.label_next.setText(QCoreApplication.translate("Form", u"\u8bf7\u5230\u7ec8\u7aef\u6267\u884c\u4e0b\u4e00\u6b65\u64cd\u4f5c", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_题号筛选器.py b/工具v4/Ui_题号筛选器.py
new file mode 100644
index 00000000..0e0454c8
--- /dev/null
+++ b/工具v4/Ui_题号筛选器.py
@@ -0,0 +1,136 @@
+# -*- 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, QFrame, QLCDNumber,
+ QLabel, QLineEdit, QPushButton, QSizePolicy,
+ QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ Form.setWindowOpacity(1.000000000000000)
+ self.pushButton_content = QPushButton(Form)
+ self.pushButton_content.setObjectName(u"pushButton_content")
+ self.pushButton_content.setGeometry(QRect(120, 30, 75, 24))
+ self.pushButton_obj = QPushButton(Form)
+ self.pushButton_obj.setObjectName(u"pushButton_obj")
+ self.pushButton_obj.setGeometry(QRect(120, 60, 75, 24))
+ self.pushButton_tag = QPushButton(Form)
+ self.pushButton_tag.setObjectName(u"pushButton_tag")
+ self.pushButton_tag.setGeometry(QRect(120, 90, 75, 24))
+ self.pushButton_usage = QPushButton(Form)
+ self.pushButton_usage.setObjectName(u"pushButton_usage")
+ self.pushButton_usage.setGeometry(QRect(120, 120, 75, 24))
+ self.pushButton_origin = QPushButton(Form)
+ self.pushButton_origin.setObjectName(u"pushButton_origin")
+ self.pushButton_origin.setGeometry(QRect(120, 150, 75, 24))
+ self.pushButton_genre = QPushButton(Form)
+ self.pushButton_genre.setObjectName(u"pushButton_genre")
+ self.pushButton_genre.setGeometry(QRect(120, 180, 75, 24))
+ self.pushButton_ans = QPushButton(Form)
+ self.pushButton_ans.setObjectName(u"pushButton_ans")
+ self.pushButton_ans.setGeometry(QRect(120, 210, 75, 24))
+ self.pushButton_solution = QPushButton(Form)
+ self.pushButton_solution.setObjectName(u"pushButton_solution")
+ self.pushButton_solution.setGeometry(QRect(120, 240, 75, 24))
+ self.pushButton_same = QPushButton(Form)
+ self.pushButton_same.setObjectName(u"pushButton_same")
+ self.pushButton_same.setGeometry(QRect(120, 270, 75, 24))
+ self.pushButton_related = QPushButton(Form)
+ self.pushButton_related.setObjectName(u"pushButton_related")
+ self.pushButton_related.setGeometry(QRect(120, 300, 75, 24))
+ self.pushButton_remark = QPushButton(Form)
+ self.pushButton_remark.setObjectName(u"pushButton_remark")
+ self.pushButton_remark.setGeometry(QRect(120, 330, 75, 24))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(490, 110, 161, 91))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_exec.setFont(font)
+ self.pushButton_undo = QPushButton(Form)
+ self.pushButton_undo.setObjectName(u"pushButton_undo")
+ self.pushButton_undo.setGeometry(QRect(120, 380, 75, 24))
+ self.pushButton_clearAll = QPushButton(Form)
+ self.pushButton_clearAll.setObjectName(u"pushButton_clearAll")
+ self.pushButton_clearAll.setGeometry(QRect(120, 410, 75, 24))
+ self.label_conditions = QLabel(Form)
+ self.label_conditions.setObjectName(u"label_conditions")
+ self.label_conditions.setGeometry(QRect(220, 110, 261, 331))
+ self.label_conditions.setFrameShape(QFrame.StyledPanel)
+ self.label_conditions.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
+ self.label_conditions.setWordWrap(False)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(220, 90, 71, 16))
+ self.checkBox_not = QCheckBox(Form)
+ self.checkBox_not.setObjectName(u"checkBox_not")
+ self.checkBox_not.setGeometry(QRect(380, 30, 111, 20))
+ self.lineEdit_SingleCondition = QLineEdit(Form)
+ self.lineEdit_SingleCondition.setObjectName(u"lineEdit_SingleCondition")
+ self.lineEdit_SingleCondition.setGeometry(QRect(220, 60, 261, 20))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(220, 30, 151, 16))
+ self.label_3 = QLabel(Form)
+ self.label_3.setObjectName(u"label_3")
+ self.label_3.setGeometry(QRect(490, 30, 91, 16))
+ self.lcdNumber_resCount = QLCDNumber(Form)
+ self.lcdNumber_resCount.setObjectName(u"lcdNumber_resCount")
+ self.lcdNumber_resCount.setGeometry(QRect(490, 50, 161, 51))
+ self.lcdNumber_resCount.setDigitCount(6)
+ self.lcdNumber_resCount.setProperty("value", 0.000000000000000)
+ self.pushButton_savebuild = QPushButton(Form)
+ self.pushButton_savebuild.setObjectName(u"pushButton_savebuild")
+ self.pushButton_savebuild.setGeometry(QRect(490, 240, 161, 201))
+ self.pushButton_savebuild.setFont(font)
+ self.checkBox_Detailed = QCheckBox(Form)
+ self.checkBox_Detailed.setObjectName(u"checkBox_Detailed")
+ self.checkBox_Detailed.setGeometry(QRect(490, 210, 79, 20))
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u9898\u53f7\u7b5b\u9009\u5668", None))
+ self.pushButton_content.setText(QCoreApplication.translate("Form", u"\u5185\u5bb9", None))
+ self.pushButton_obj.setText(QCoreApplication.translate("Form", u"\u76ee\u6807\u7f16\u53f7", None))
+ self.pushButton_tag.setText(QCoreApplication.translate("Form", u"\u6807\u7b7e", None))
+ self.pushButton_usage.setText(QCoreApplication.translate("Form", u"\u4f7f\u7528\u8bb0\u5f55", None))
+ self.pushButton_origin.setText(QCoreApplication.translate("Form", u"\u51fa\u5904", None))
+ self.pushButton_genre.setText(QCoreApplication.translate("Form", u"\u9898\u76ee\u7c7b\u578b", None))
+ self.pushButton_ans.setText(QCoreApplication.translate("Form", u"\u7b54\u6848", None))
+ self.pushButton_solution.setText(QCoreApplication.translate("Form", u"\u89e3\u7b54\u4e0e\u63d0\u793a", None))
+ self.pushButton_same.setText(QCoreApplication.translate("Form", u"\u76f8\u540c\u9898\u53f7", None))
+ self.pushButton_related.setText(QCoreApplication.translate("Form", u"\u76f8\u5173\u9898\u53f7", None))
+ self.pushButton_remark.setText(QCoreApplication.translate("Form", u"\u5907\u6ce8", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u8fd0\u884c", None))
+ self.pushButton_undo.setText(QCoreApplication.translate("Form", u"\u64a4\u9500\u4e0a\u4e00\u4e2a", None))
+ self.pushButton_clearAll.setText(QCoreApplication.translate("Form", u"\u6e05\u9664", None))
+ self.label_conditions.setText("")
+ self.label.setText(QCoreApplication.translate("Form", u"\u5f53\u524d\u6761\u4ef6:", None))
+ self.checkBox_not.setText(QCoreApplication.translate("Form", u"\u9009\u4e2d\u8868\u793a\u4e0d\u5305\u542b", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u8f93\u5165\u6761\u4ef6, \u7528\",\"\u5206\u9694\u8868\u793a\"\u6216\"", None))
+ self.label_3.setText(QCoreApplication.translate("Form", u"\u7b5b\u9009\u7ed3\u679c\u6570\u76ee:", None))
+ self.pushButton_savebuild.setText(QCoreApplication.translate("Form", u"\u4fdd\u5b58\u548c\u7f16\u8bd1", None))
+ self.checkBox_Detailed.setText(QCoreApplication.translate("Form", u"\u8be6\u7ec6\u4fe1\u606f", None))
+ # retranslateUi
+
diff --git a/工具v4/Ui_题目内容直接编辑.py b/工具v4/Ui_题目内容直接编辑.py
new file mode 100644
index 00000000..930ee941
--- /dev/null
+++ b/工具v4/Ui_题目内容直接编辑.py
@@ -0,0 +1,84 @@
+# -*- 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, QLabel, QLineEdit, QPlainTextEdit,
+ QPushButton, QSizePolicy, QWidget)
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ if not Form.objectName():
+ Form.setObjectName(u"Form")
+ Form.resize(760, 490)
+ self.label = QLabel(Form)
+ self.label.setObjectName(u"label")
+ self.label.setGeometry(QRect(60, 110, 54, 16))
+ self.lineEdit_ID = QLineEdit(Form)
+ self.lineEdit_ID.setObjectName(u"lineEdit_ID")
+ self.lineEdit_ID.setGeometry(QRect(60, 130, 71, 20))
+ self.pushButton_content = QPushButton(Form)
+ self.pushButton_content.setObjectName(u"pushButton_content")
+ self.pushButton_content.setGeometry(QRect(60, 160, 75, 24))
+ self.pushButton_genre = QPushButton(Form)
+ self.pushButton_genre.setObjectName(u"pushButton_genre")
+ self.pushButton_genre.setGeometry(QRect(60, 190, 75, 24))
+ self.pushButton_ans = QPushButton(Form)
+ self.pushButton_ans.setObjectName(u"pushButton_ans")
+ self.pushButton_ans.setGeometry(QRect(60, 220, 75, 24))
+ self.pushButton_solution = QPushButton(Form)
+ self.pushButton_solution.setObjectName(u"pushButton_solution")
+ self.pushButton_solution.setGeometry(QRect(60, 250, 75, 24))
+ self.pushButton_origin = QPushButton(Form)
+ self.pushButton_origin.setObjectName(u"pushButton_origin")
+ self.pushButton_origin.setGeometry(QRect(60, 280, 75, 24))
+ self.pushButton_remarks = QPushButton(Form)
+ self.pushButton_remarks.setObjectName(u"pushButton_remarks")
+ self.pushButton_remarks.setGeometry(QRect(60, 310, 75, 24))
+ self.label_2 = QLabel(Form)
+ self.label_2.setObjectName(u"label_2")
+ self.label_2.setGeometry(QRect(140, 110, 191, 16))
+ self.plainTextEdit_toedit = QPlainTextEdit(Form)
+ self.plainTextEdit_toedit.setObjectName(u"plainTextEdit_toedit")
+ self.plainTextEdit_toedit.setGeometry(QRect(140, 130, 471, 201))
+ self.pushButton_exec = QPushButton(Form)
+ self.pushButton_exec.setObjectName(u"pushButton_exec")
+ self.pushButton_exec.setGeometry(QRect(620, 130, 61, 91))
+ self.pushButton_tocommit = QPushButton(Form)
+ self.pushButton_tocommit.setObjectName(u"pushButton_tocommit")
+ self.pushButton_tocommit.setGeometry(QRect(620, 230, 61, 101))
+ font = QFont()
+ font.setBold(True)
+ self.pushButton_tocommit.setFont(font)
+
+ self.retranslateUi(Form)
+
+ QMetaObject.connectSlotsByName(Form)
+ # setupUi
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QCoreApplication.translate("Form", u"\u9898\u5e93\u5185\u5bb9\u76f4\u63a5\u7f16\u8f91", None))
+ self.label.setText(QCoreApplication.translate("Form", u"\u9898\u53f7", None))
+ self.pushButton_content.setText(QCoreApplication.translate("Form", u"\u9898\u76ee\u5185\u5bb9", None))
+ self.pushButton_genre.setText(QCoreApplication.translate("Form", u"\u7c7b\u578b", None))
+ self.pushButton_ans.setText(QCoreApplication.translate("Form", u"\u7b54\u6848", None))
+ self.pushButton_solution.setText(QCoreApplication.translate("Form", u"\u89e3\u7b54", None))
+ self.pushButton_origin.setText(QCoreApplication.translate("Form", u"\u6765\u6e90", None))
+ self.pushButton_remarks.setText(QCoreApplication.translate("Form", u"\u5907\u6ce8\u5217\u8868", None))
+ self.label_2.setText(QCoreApplication.translate("Form", u"\u9898\u53f7\u4e3a \u7684 \u5b57\u6bb5", None))
+ self.pushButton_exec.setText(QCoreApplication.translate("Form", u"\u4fee\u6539", None))
+ self.pushButton_tocommit.setText(QCoreApplication.translate("Form", u"\u63d0\u4ea4", None))
+ # retranslateUi
+
diff --git a/工具v4/database_tools_2.py b/工具v4/database_tools_2.py
new file mode 100644
index 00000000..bca97b35
--- /dev/null
+++ b/工具v4/database_tools_2.py
@@ -0,0 +1,2819 @@
+import json,re,os,Levenshtein,fitz,time,sys,subprocess
+import pandas as pd
+import numpy as np
+import pyperclip
+import mysql.connector
+import tqdm
+
+
+BuildFullScheme = {
+ "输出路径": "临时文件",
+ "教师版": True,
+ "字段显示设置": {
+ "题后空间": True,
+ "课时目标": True,
+ "题目标签": True,
+ "答案": True,
+ "解答与提示": True,
+ "使用记录": [3,-1],
+ "使用记录说明": "[a,b]表示显示最好的a个和最差b个, 有-2表示不显示, 无-2但有-1表示全部显示",
+ "来源": True,
+ "备注": True,
+ "届别": []
+ }
+ }
+
+db_user = "tikuuser"
+db_pwd = "Kjmathds_2024"
+db_port = "13306"
+db_host = "wwylss.synology.me"
+
+
+def get_git_username():
+ command = "git config --global user.name"
+ stream = os.popen(command)
+ username = stream.read().strip()
+ return username
+
+def connect(hostname,port,username,pwd,db):
+ mydb = mysql.connector.connect(host = hostname, port = port, user = username, password = pwd, database = db)
+ return mydb
+
+def GetDate(): #获得当前日期
+ currentdate = str(time.localtime().tm_year)+str(time.localtime().tm_mon).zfill(2)+str(time.localtime().tm_mday).zfill(2)
+ return currentdate #返回当前日期yyyymmdd
+
+def GetTime(): #获得当前时间
+ t = time.localtime()
+ currenttime = f"{t.tm_hour:0>2}{t.tm_min:0>2}{t.tm_sec:0>2}"
+ return currenttime #返回当前时间hhmmss
+
+def datestrtotimestamp(date_str): # 将yyyymmdd的时间转为时间戳timestamp(以分钟为单位的整数)
+ date_obj = time.strptime(date_str, "%Y%m%d")
+ timestamp = int(time.mktime(date_obj))
+ return timestamp
+
+def ReadTextFile(filepath): #读取文本格式的文件
+ with open(filepath,"r",encoding="u8") as f:
+ data = f.read()
+ return data #返回文本格式文件的内容
+
+def SaveTextFile(data,filepath): #写入文本格式的文件
+ pathname = os.path.dirname(filepath)
+ if not os.path.exists(pathname):
+ makedir(pathname)
+ with open(filepath,"w",encoding="u8") as f:
+ f.write(data)
+ return filepath #返回文件名
+
+def AppendTextFile(string,filepath): #在文本文件后添加内容
+ 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): #按字典项顺序排序字典
+ return dict(sorted(adict.items())) #返回排序后的字典
+
+
+#读取存储json数据库相关(不限于题号数据库)
+
+def load_dict(filename): #根据filename读取json数据库并转化为python字典
+ with open(filename,"r",encoding = "u8") as f:
+ adict = json.loads(f.read())
+ return adict #返回python字典
+
+def save_dict(adict,filepath): #将adict字典转化为json文件并保存至filename文件中
+ try:
+ jsondata = json.dumps(adict,indent=4,ensure_ascii=False)
+ SaveTextFile(jsondata,filepath)
+ return filepath # 成功则返回文件路径
+ except:
+ print("保存 %s 文件失败"%filepath)
+ return 1 # 失败则返回1
+
+
+
+def pre_treating(string): #删除字符串中对比较无用的字符, 以供比较
+ string = re.sub(r"\\begin\{center\}[\s\S]*?\\end\{center\}","",string)
+ string = re.sub(r"(bracket\{\d+\})|(blank\{\d+\})|(fourch)|(twoch)|(onech)","",string)
+ string = re.sub(r"[\s\\\{\}\$\(\)\[\]]","",string)
+ string = re.sub(r"[\n\t]","",string)
+ string = re.sub(r"(displaystyle)|(overrightarrow)|(overline)","",string)
+ string = re.sub(r"[,\.:;?]","",string)
+ return string #返回处理后的字符串
+
+def findsru(id,alist): #在same_list中寻找与id相同的题号
+ set1 = set([pair[1] for pair in alist if pair[0] == id])
+ set2 = set([pair[0] for pair in alist if pair[1] == id])
+ return sorted(list(set1 | set2))
+
+
+
+def findsameinDB(id,database): #在数据库中寻找与id相同的题号
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT SAME_ID FROM same WHERE ID = (%s);"
+ val = (id,)
+ mycursor.execute(sql,val)
+ set1 = set([ret[0] for ret in mycursor.fetchall()])
+ sql = "SELECT ID FROM same WHERE SAME_ID = (%s);"
+ mycursor.execute(sql,val)
+ set2 = set([ret[0] for ret in mycursor.fetchall()])
+ return sorted(list(set1 | set2))
+
+def findrelatedinDB(id,database): #在数据库中寻找与id关联的题号
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT RELATED_ID FROM related WHERE ID = (%s);"
+ val = (id,)
+ mycursor.execute(sql,val)
+ set1 = set([ret[0] for ret in mycursor.fetchall()])
+ sql = "SELECT ID FROM related WHERE RELATED_ID = (%s);"
+ mycursor.execute(sql,val)
+ set2 = set([ret[0] for ret in mycursor.fetchall()])
+ return sorted(list(set1 | set2))
+
+def get_unit_tags(id,database):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT tagname FROM tagcorresp WHERE ID = (%s);"
+ val = (id,)
+ mycursor.execute(sql,val)
+ unittags = [ret[0] for ret in mycursor.fetchall() if "单元" in ret[0]]
+ mydb.close()
+ return unittags
+
+def get_problem_content(id,database):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT content FROM problems WHERE ID = (%s);"
+ val = (id,)
+ mycursor.execute(sql,val)
+ content = mycursor.fetchall()[0][0]
+ mydb.close()
+ return content
+
+
+def generate_same_list(database):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID,SAME_ID FROM same;")
+ same_list = [(ret[0],ret[1]) for ret in mycursor.fetchall()]
+ mydb.close()
+ return same_list.copy()
+
+def generate_related_list(database):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID,RELATED_ID FROM related;")
+ related_list = [(ret[0],ret[1]) for ret in mycursor.fetchall()]
+ mydb.close()
+ return related_list.copy()
+
+def treat_dict(database): #对整个题库字典中的内容部分进行预处理,删除无用字符
+ treated_dict = {}
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID,content FROM problems;")
+ p_dict = {id:content for id,content in mycursor.fetchall()}
+ # mycursor.execute("SELECT ID,SAME_ID FROM same;")
+ # same_list = [(ret[0],ret[1]) for ret in mycursor.fetchall()]
+ for id in p_dict:
+ treated_dict[id] = {}
+ treated_dict[id] = pre_treating(p_dict[id])
+ mydb.close()
+ return treated_dict #返回处理后的字典, 含内容字段及相同题目字段
+
+def detectmaxsim(currentid,excludelist,adict): #检测与已知题目关联程度最大的题目(除外列表之外的部分)
+ maxsim = -1
+ argmaxsim = "000000"
+ for id in adict:
+ if not id in excludelist and not "OBSOLETE" in adict[id]["content"]:
+ simrate = Levenshtein.jaro(adict[id]["content"],adict[currentid]["content"])
+ if simrate > maxsim:
+ maxsim = simrate
+ argmaxsim = id
+ return (maxsim,argmaxsim) #返回最大关联系数与关联程度最大的题号
+
+def stringmaxsim(string,adict,listlength):
+ # maxsim = -1
+ # argmaxsim = "000000"
+ maxsimlist = []
+ string = pre_treating(string)
+ for id in adict:
+ if not "OBS" in adict[id]:
+ simrate = Levenshtein.jaro(adict[id],string)
+ # if simrate > maxsim:
+ # maxsim = simrate
+ # argmaxsim = id
+ if len(maxsimlist) < listlength:
+ maxsimlist.append((id,simrate))
+ elif simrate > maxsimlist[-1][1]:
+ maxsimlist = maxsimlist[:-1]
+ maxsimlist.append((id,simrate))
+ maxsimlist = sorted(maxsimlist,key = lambda x: x[1], reverse = True)
+ return maxsimlist #返回最大关联的listlength个题号及关联系数
+
+def generate_sim_group(startingids,prodict,max_size,threshold): #生成与已知题块startingids(冒号和逗号连接的字符串)关联程度超过threshold的题目组, 超过max_size即停止
+ id_list = generate_number_set(startingids)
+ output_list = []
+ treated_dict = treat_dict(prodict)
+ continue_flag = True
+ while continue_flag:
+ appending_id_list = []
+ for oldid in id_list:
+ for newid in [id for id in prodict if not id in id_list and not id in output_list and not "OBSOLETE" in treated_dict[id]["content"]]:
+ simrate = Levenshtein.jaro(treated_dict[oldid]["content"],treated_dict[newid]["content"])
+ if simrate >= threshold:
+ appending_id_list.append(newid)
+ output_list = output_list + id_list.copy()
+ id_list = appending_id_list.copy()
+ if len(appending_id_list) == 0 or len(output_list)>max_size:
+ continue_flag = False
+ return generate_exp(sorted(output_list)) #返回冒号和逗号连接的题目组字符串
+
+
+def generate_problem_series(startingid,length,adict): #在adict字典里返回从startingid开始的一系列题号, 每一题都是与上一题的关联程度最大的
+ excludelist = [startingid]
+ currentid = startingid
+ for i in range(length):
+ maxsim,currentid = detectmaxsim(currentid,excludelist,adict)
+ excludelist.append(currentid)
+ return ",".join(excludelist) #返回按顺序的题号列表
+
+
+def generate_number_set(string,*thedict): #根据可能含有":"和","的题号字符串生成一个用逗号分隔的六位题号列表, 例如"1:3,5"会生成["000001","000002","000003","000005"]
+#可变参数*dict如果存在, 将只生成dict的keys中包含的题号列表
+ string = re.sub(r"[\n\s]","",string).strip()
+ while not string[-1] in "0123456789":
+ string = string[:-1]
+ string = RefinePunctuations(string)
+ string_list = string.split(",")
+ numbers_list = []
+ for s in string_list:
+ if not ":" in s:
+ numbers_list.append(s.zfill(6))
+ else:
+ start,end = s.split(":")
+ for ind in range(int(start),int(end)+1):
+ numbers_list.append(str(ind).zfill(6))
+ if len(thedict) == 0:
+ return numbers_list #返回六位题号列表
+ elif len(thedict) == 1 and type(thedict[0]) == dict:
+ numbers_list = [id for id in numbers_list if id in thedict[0]]
+ return numbers_list #返回字典中存在的六位题号列表
+ else:
+ return "输入参数有误"
+
+def generate_classid(string): #返回班级列表
+ string = re.sub(r"[\n\s]","",string).strip()
+ while not string[-1] in "0123456789":
+ string = string[:-1]
+ string = RefinePunctuations(string)
+ string_list = string.split(",")
+ numbers_list = []
+ for s in string_list:
+ if not ":" in s:
+ numbers_list.append(s.zfill(2)+"班")
+ else:
+ start,end = s.split(":")
+ for ind in range(int(start),int(end)+1):
+ numbers_list.append(str(ind).zfill(2)+"班")
+ return numbers_list #返回2位班级号列表
+
+
+def generate_id_set(string,*thedict): #除了生成题号列表外, 还能根据首字母生成基础知识编号列表或课时目标列表
+ string = RefinePunctuations(string)
+ if re.findall(r"[BXK]",string) == []:
+ if thedict == ():
+ return generate_number_set(string)
+ else:
+ return generate_number_set(string,thedict)
+ else:
+ if string[0] == "B":
+ string = re.sub("B","",string)
+ return ["B"+i[-5:] for i in generate_number_set(string)]
+ elif string[0] == "K":
+ suffix = string.strip()[-1]
+ string = re.sub("[KBX]","",string)
+ return ["K"+i.zfill(7)+suffix for i in generate_number_set(string)]
+
+def generate_exp(id_list): #根据题号列表生成字符串式的含":"和","的题号字符串, 例如["000001","000002","000003","000005"]生成"000001:000003,000005", 若列表为空则生成"无有效题号"
+ if not len(id_list) == 0:
+ exp_list = []
+ start = id_list[0]
+ current = start
+ end = start
+ for id in id_list[1:]:
+ # print(id,current)
+ if int(id)-1 == int(current):
+ current = id
+ end = id
+ else:
+ if not start == end:
+ exp_list.append('"'+start+":"+end+'"')
+ else:
+ exp_list.append('"'+start+'"')
+ start = id
+ current = id
+ end = id
+ if not start == end:
+ exp_list.append('"'+start+":"+end+'"')
+ else:
+ exp_list.append('"'+start+'"')
+ exp_str = ",".join(exp_list).replace('"',"")
+ else:
+ exp_str = "无有效题号"
+ return exp_str #返回含有":"或","的题号字符串
+
+def parsePDF(filePath): #提取pdf文件中的字符
+ with fitz.open(filePath) as doc:
+ text = ""
+ for page in doc.pages():
+ text += page.get_text() + "\n"
+ return text
+
+def extractIDs(filePath): #提取.txt,.tex或.pdf文件中的题号, 返回含有":"或","的题号字符串
+ if filePath[-4:] == ".txt" or filePath[-4:] == ".tex":
+ with open(filePath,"r",encoding = "u8") as f:
+ data = f.read()
+ elif filePath[-4:] == ".pdf":
+ data = parsePDF(filePath)
+ else:
+ return "格式不正确"
+ ids = re.findall(r"\((\d{6})\)",data)
+ return generate_exp(ids)
+
+
+def usedIDs(database_name): #返回已使用题号, 已更新为适合mariadb的版本
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database_name)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID FROM problems;")
+ idlist = [ret[0] for ret in mycursor.fetchall()]
+ used_str = generate_exp(idlist)
+ used_list = used_str.split(",")
+ output = ""
+ for group in used_list:
+ interval = group.split(":")
+ start = interval[0]
+ end = interval[-1]
+ output += "首个已使用id: %s, 直至: %s"%(str(start).zfill(6),str(end).zfill(6)) + "\n"
+ output = output.strip()
+ mydb.close()
+ return output #返回的是一个多行的字符串, 每一行中含有一个已使用题号的闭区间
+
+def spareIDs(database): #返回空闲题号, 已更新为适合mariadb的版本
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID FROM problems;")
+ idlist = [ret[0] for ret in mycursor.fetchall()]
+ used_str = generate_exp(idlist)
+ used_list = used_str.split(",")
+ output = ""
+ for group in range(len(used_list)-1):
+ output += "首个空闲id: %s, 直至: %s"%(str(int(used_list[group][-6:])+1).zfill(6),str(int(used_list[group+1][:6])-1).zfill(6)) + "\n"
+ output += "首个空闲id: %s, 直至: %s"%(str(int(used_list[-1][-6:])+1).zfill(6),"999999")
+ mydb.close()
+ return output #返回的是一个多行的字符串, 每一行中含有一个空闲题号的闭区间
+
+
+def NextSpareID(num,database): #返回adict中下一个空闲的题号
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ 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 idlist:
+ num += 1
+ mydb.close()
+ return num
+
+def NextSpareIDBlock(num,database): #返回adict中下一个空闲的题号块
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID FROM problems;")
+ idlist = [ret[0] for ret in mycursor.fetchall()]
+ start = NextSpareID(num,database)
+ larger_ID_list = [int(id) for id in idlist if int(id)>start]
+ if larger_ID_list == []:
+ end = 999999
+ else:
+ end = min(larger_ID_list) - 1
+ mydb.close()
+ return str(start)+":"+str(end)
+
+def GenerateProblemListFromString(data): #从来自.tex文件的字符串生成题目列表, 每个item是一道题目, 新一行的%用作前缀
+ try:
+ data = re.findall(r"\\begin\{document\}([\s\S]*?)\\end\{document\}",data)[0]
+ except:
+ pass
+ data = re.sub(r"\n{2,}","\n",data)
+ data = re.sub(r"\\item",r"\\enditem\\item",data)
+ data = re.sub(r"\\end\{enumerate\}",r"\\enditem",data) #切除无关信息, 保留关键信息
+ problempositions = []
+ for item in re.finditer(r"\\item([\s\S]*?)\\enditem",data):
+ problempositions.append(item.regs[1]) #确定题目内容所在位置
+ problem_list = []
+ for pos in problempositions:
+ content = data[pos[0]:pos[1]].strip()
+ content = re.sub(r"\n\%[\s\S]*$","",content) #题目内容
+ content = re.sub(r"\\\\$","",content) # 删去题目最后一行处可能存在的\\
+ subdata = data[:pos[0]] #开始寻找出处中缀
+ suflist = re.findall(r"\n(\%\s{0,}[\S]+)\n",subdata)
+ if len(suflist) == 0:
+ suffix = ""
+ else:
+ suffix = suflist[-1].replace("%","").strip()
+ problem_list.append((content,suffix))
+ return problem_list #返回一个列表, 每一项是一个由 题目内容 和 题目来源前缀 组成的元组
+
+
+def GenerateProblemListFromString2024(data): #从来自.tex文件的字符串生成题目列表, 每个item是一道题目, 新一行的%用作前缀, item后面的方括号放与题库的交互信息(如[rep3214]表示不添加, 用003214代替; [s2521;r354;u10021,20024]表示和2521相同, 和354相关, 和10021,20024无关), 返回三元组(题目内容, 题目来源前缀, 交互信息列表)的字典
+ try:
+ data = re.findall(r"\\begin\{document\}([\s\S]*?)\\end\{document\}",data)[0]
+ except:
+ pass
+ data = re.sub(r"\n{2,}","\n",data)
+ data = re.sub(r"\\definecolor[^\n]*\n","\n",data)
+ data = re.sub(r"\\begin\{tcolorbox\}[\s\S]*?\\end\{tcolorbox\}","\n",data)
+ data = re.sub(r"\\item",r"\\enditem\\item",data)
+ data = re.sub(r"\\end\{enumerate\}",r"\\enditem",data) #切除无关信息, 保留关键信息
+ problempositions = []
+ for item in re.finditer(r"\\item([\s\S]*?)\\enditem",data):
+ problempositions.append(item.regs[1]) #确定题目内容所在位置
+ problem_list = []
+ for pos in problempositions:
+ content_raw = data[pos[0]:pos[1]].strip()
+ content_raw = re.sub(r"\n\%[\s\S]*$","",content_raw) #题目内容
+ content_raw = re.sub(r"\\\\$","",content_raw) # 删去题目最后一行处可能存在的\\
+ content_raw = content_raw.strip() # 删去前后多余的空格
+ meta = {}
+ if not content_raw[0] == "[": # 根据方括号内的内容生成交互信息, 这是无方括号的内容, 无meta
+ content = content_raw
+ else:
+ same_id_list = []
+ related_id_list = []
+ unrelated_id_list = []
+ content = re.sub(r"^\[[A-Za-z\d,\s]*?\]","",content_raw).strip()
+ metaraw = re.findall(r"\[.*\]",content_raw)[0]
+ metaraw = re.sub(r"[\[\]]","",metaraw)
+ metalist = metaraw.split(",")
+ for metaitem in metalist:
+ metaitem = metaitem.upper()
+ if metaitem.startswith("REP"):
+ meta = {"rep":metaitem[3:].zfill(6)}
+ elif metaitem.startswith("S"):
+ same_id_list.append(metaitem[1:].zfill(6))
+ elif metaitem.startswith("R"):
+ related_id_list.append(metaitem[1:].zfill(6))
+ elif metaitem.startswith("U"):
+ unrelated_id_list.append(metaitem[1:].zfill(6))
+ if not "rep" in meta:
+ meta = {"same":same_id_list,"related":related_id_list,"unrelated":unrelated_id_list}
+ subdata = data[:pos[0]] #开始寻找出处中缀
+ suflist = re.findall(r"\n(\%\s{0,}[\S]+)\n",subdata)
+ if len(suflist) == 0:
+ suffix = ""
+ else:
+ suffix = suflist[-1].replace("%","").strip()
+ problem_list.append((content,suffix,meta))
+ return problem_list #返回一个列表, 每一项是一个由 题目内容 和 题目来源前缀 和 交互信息字典 组成的元组
+
+
+def CreateEmptyProblem(problem): # 根据已有的题目创建新的空题目
+ NewProblem = problem.copy()
+ for field in NewProblem:
+ if type(NewProblem[field]) == str:
+ NewProblem[field] = ""
+ elif type(NewProblem[field]) == list:
+ NewProblem[field] = []
+ elif type(NewProblem[field]) == int or type(NewProblem[field]) == float:
+ NewProblem[field] = -1
+ return NewProblem #返回一个空题目的字典, ID和内容待赋值
+
+def CreateNewProblem(id,content,origin,dict,editor): # 构建一道新题目的字典
+ NewProblem = CreateEmptyProblem(dict["000001"])
+ NewProblem["id"] = str(id).zfill(6)
+ NewProblem["content"] = content
+ NewProblem["origin"] = origin
+ NewProblem["edit"] = [editor]
+ return NewProblem # 返回一道新题目的字典, 已赋新的ID, 内容, 来源和编辑者
+
+def AddRelatedProblemToDB(id,content,oid,editor,database):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ id = str(id).zfill(6)
+ content = content.strip()
+ if "blank" in content:
+ genre = "填空题"
+ space = ""
+ elif "bracket" in content:
+ genre = "选择题"
+ space = ""
+ else:
+ genre = "解答题"
+ space = "4em"
+ origin_json = {"来源": "改编题目", "前序": str(oid).zfill(6)}
+ origin = json.dumps(origin_json,ensure_ascii=False)
+ sql = "INSERT INTO problems (ID,content,genre,origin,space) VALUE (%s,%s,%s,%s,%s);"
+ val = (id,content,genre,origin,space)
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO edit_history (ID,date,editor) VALUE (%s,%s,%s);"
+ val = (id,GetDate(),editor.strip())
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO related (ID,RELATED_ID) VALUE (%s,%s);"
+ val = (id, oid)
+ if id > oid:
+ val = (oid, id)
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,id,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),"加关联题",id,content)
+ mycursor.execute(sql,val)
+ mydb.commit()
+ mydb.close()
+ return id
+
+def AddProblemstoDict2024(startingid,raworigin,problems,editor,indexed,database): #将来自GenerateProblemListFromString的列表中的题目添加到thedict字典, 返回题号列表(包括用老题号替代的题目)
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ idlist = []
+ id = int(startingid)
+ currentsuffix = problems[0][1]
+ problemindex = 0
+ for p_and_suffix_and_meta in problems:
+ p, suffix, meta = p_and_suffix_and_meta
+ pid = str(id).zfill(6)
+ if suffix == currentsuffix:
+ problemindex += 1
+ else:
+ 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:
+ 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)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,id,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),"新增题目",pid,p.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): #建立已有id和新id之间的联系, thedict为可选, 选中的话即为当前字典, 会从new_id_list中排除当前字典中有的项
+ if len(old_id_list)>len(new_id_list):
+ return "新ID个数不足."
+ else:
+ id_links = []
+ for i in range(len(old_id_list)):
+ id_links.append((old_id_list[i],new_id_list[i]))
+ return id_links # 返回id联系, 每个元组表示一对id, 前者是旧id, 后者是新id
+
+
+def CreateRelatedProblems(links,database): # 根据links关联生成待编辑的新题目字典, 等待编辑修改
+ output = "\\begin{enumerate}\n"
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT content FROM problems WHERE ID = %s;"
+ for id,rid in links:
+ val = (id,)
+ mycursor.execute(sql,val)
+ ret = mycursor.fetchone()[0]
+ output += f"\\item ({id}->{rid}) {ret}\n\n"
+ output = f"{output}\n\\end{{enumerate}}\n"
+ mydb.close()
+ return output #正常返回0
+
+def ImportRelatedProblems(new_json,main_json): # 导入编辑过的关联题目json文件到主数据库
+ pro_dict = load_dict(main_json)
+ new_dict = load_dict(new_json)
+ edited = []
+ for id in new_dict:
+ new_id = new_dict[id]["id"].replace("待替换","") #新题号后需要跟"待替换"字样
+ if new_id in pro_dict:
+ print("题号有重复")
+ return 1 #异常返回1
+ else:
+ edited.append(new_id)
+ pro_dict[new_id] = new_dict[id].copy()
+ pro_dict[new_id]["id"] = new_id
+ pro_dict[id]["related"] += [new_id]
+ pro_dict[new_id]["related"] += [id]
+ p = pro_dict[new_id]["content"]
+ if "blank" in p:
+ pro_dict[new_id]["genre"] = "填空题"
+ pro_dict[new_id]["genre"] = ""
+ elif "bracket" in p:
+ pro_dict[new_id]["genre"] = "选择题"
+ pro_dict[new_id]["genre"] = ""
+ else:
+ pro_dict[new_id]["genre"] = "解答题"
+ pro_dict[new_id]["genre"] = "4em"
+ print("导入关联题目 %s -> %s 信息成功."%(id,new_id))
+ save_dict(SortDict(pro_dict),main_json) #保存至目标pro_dict文件
+ return generate_exp(edited) #正常返回新题号list
+
+
+def strip_suffix(originalString, suf_words_list): # 字符串去除指定后缀
+ for sw in suf_words_list:
+ output = re.sub(sw+r"[\S]*$","",originalString)
+ return(output) # 返回原字符串中截去suf_words_list及之后字符的部分
+
+def get_striped_origin(pro_dict,id,suf_words_list): # 题目来源去除指定后缀
+ return strip_suffix(pro_dict[id]["origin"],suf_words_list) # 返回去除指定后缀后的题目来源
+
+
+def SeperateFirstLine(string): # 切割一个含有换行的字符串
+ thelist = string.split("\n")
+ firstline = thelist[0].strip()
+ contents = "\n".join([lines.strip() for lines in thelist[1:]])
+ return (firstline,contents) # 返回第一行, 其余行形成的二元组, 每一行前后的空格都被删去
+
+def ObtainDatatoModify(metadatafilepath,fields_dict,idlist): #从metadata文件中获取需要修改的综合信息, metadata文件用双回车区分项, 单行的项表示此后的字段, 不小于两行的项中第一行是题目id, 第二行起是要修改的内容.
+#fieldsdictpath是字段信息数据库文件路径
+ data = ReadTextFile(metadatafilepath)
+ datalines = data.split("\n")
+ data = ""
+ for l in datalines:
+ if l.strip() in fields_dict:
+ data += l + "\n\n"
+ else:
+ data += l + "\n"
+ data = re.sub(r"\n[\s\n]*\n","\n\n",data.strip()) #去除一些无意义的空格和多余的回车
+ datalist = data.split("\n\n")
+ to_modify_list = []
+ currentfield = "NotAField"
+ for line in datalist:
+ if line.strip() in fields_dict:
+ currentfield = line.strip()
+ elif not "\n" in line:
+ currentfield = "NotAField"
+ else:
+ id,content = SeperateFirstLine(line)
+ if str(id).zfill(6) in idlist:
+ to_modify_list.append((currentfield,str(id).zfill(6),content))
+ else:
+ print(f"其中 ID {id} 有误, 已忽略!!")
+ return to_modify_list #返回一个列表, 每一项是一个三元组, 三项依次为字段, 题号, 要修改的内容
+
+def FloatToInt(string): #从字符串返回浮点数,如果值非常接近整数则返回该整数
+ f = float(string)
+ if abs(f-round(f))<0.01:
+ f = round(f)
+ return f #返回浮点数或整数
+
+def OverwriteData(prodict,fieldsdict,field_id_and_content): #用覆盖方式修改题目某字段信息
+ field,id,content = field_id_and_content
+ fieldType = fieldsdict[field]["FieldType"]
+ if not id in prodict:
+ print("题号 %s 并不在数据库中"%id)
+ else:
+ if fieldType == "str":
+ prodict[id][field] = content
+ elif fieldType == "int" or fieldType == "float":
+ content = FloatToInt(content)
+ prodict[id][field] = content
+ print("已覆盖 %s 的 %s 字段, 内容为 %s"%(id,field,content))
+ return (id,field,content) #返回三元组: 题号, 字段, 覆盖内容
+
+def AppendData(prodict,fieldsdict,field_id_and_content): #用添加方式修改题目某字段信息
+ field,id,content = field_id_and_content
+ fieldType = fieldsdict[field]["FieldType"]
+ if not id in prodict:
+ print("题号 %s 并不在数据库中"%id)
+ else:
+ if fieldType == "str":
+ if content.strip() in prodict[id][field]:
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id,field,content))
+ elif prodict[id][field].strip() == "":
+ prodict[id][field] = content.strip()
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id,field,content))
+ else:
+ prodict[id][field] = (prodict[id][field].strip() + "\\\\\n" + content).strip()
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id,field,content))
+ elif fieldType == "list":
+ lines = [line.strip() for line in content.split("\n")]
+ for line in lines:
+ if line in prodict[id][field]:
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id,field,line))
+ else:
+ prodict[id][field] = prodict[id][field].copy() + [line]
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id,field,line))
+ return (id,field,content) #返回三元组: 题号, 字段, 内容
+
+def AppendMutualData(prodict,field_id_and_content): #添加两个id之间的same, related, unrelated关联
+ field,id,content = field_id_and_content
+ lines = [str(line).zfill(6) for line in content.split("\n")]
+ for id2 in lines:
+ if not id in prodict:
+ print("题号 %s 并不在数据库中"%id)
+ elif not id2 in prodict:
+ print("题号 %s 并不在数据库中"%id2)
+ else:
+ if id2 in prodict[id][field]:
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id,field,id2))
+ else:
+ prodict[id][field] = prodict[id][field] + [id2]
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id,field,id2))
+ if id in prodict[id2][field]:
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id2,field,id))
+ else:
+ prodict[id2][field] = prodict[id2][field] + [id]
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id2,field,id))
+ return (id,field,content) #返回三元组: 题号, 字段, 内容
+
+def AppendObjData(prodict,objdict,field_id_and_content): #添加目标编号数据
+ field,id,content = field_id_and_content
+ if not id in prodict:
+ print("题号 %s 并不在数据库中"%id)
+ else:
+ lines = [line.strip() for line in content.split("\n")]
+ for objid in lines:
+ if not objid in objdict:
+ print("目标编号 %s 并不在数据库中"%objid)
+ elif objid in prodict[id][field]:
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id,field,objid))
+ else:
+ prodict[id][field] = prodict[id][field] + [objid]
+ print("已于 %s 的 %s 字段执行添加, 编号为 %s, 内容为 %s"%(id,field,objid,objdict[objid]["content"]))
+ return (id,field,content) #返回三元组: 题号, 字段, 内容
+
+def AppendUsageData(prodict,field_id_and_content): #添加使用记录数据
+ field,id,content = field_id_and_content
+ lines = [re.sub(r"\s+",r"\t",line.strip()) for line in content.strip().split("\n")]
+ pending_list = []
+ for line in lines:
+ if not "FORCE" in line.upper():
+ time_stripped = re.sub(r"^\d{4,}\t","",line) #读取去除时间的数据
+ if time_stripped in "\n".join(prodict[id][field]):
+ print("题号 %s 的 %s 字段, 内容 %s 已存在"%(id,field,line))
+ else:
+ classid = [info for info in line.split("\t") if len(re.findall(r"[班高一二三]",info))>0][0] # 读取班级号
+ if classid in "\n".join(prodict[id][field]):
+ print("班级 %s 在题号为 %s 的题目处已有使用记录, 在pending list中记录"%(classid,id))
+ oldinfo = [usage for usage in prodict[id][field] if classid in usage]
+ pending_list = pending_list + [(id,line,oldinfo)]
+ else:
+ prodict[id][field].append(line)
+ print("已于 %s 的 %s 字段执行添加, 内容为 %s"%(id,field,line))
+ else:
+ line = re.sub(r"\s+",r"\t",re.sub(r"FORCE","",line.upper())).strip()
+ prodict[id][field].append(line)
+ print("已于 %s 的 %s 字段执行强制添加, 内容为 %s"%(id,field,line))
+ output = ""
+ for item in pending_list:
+ id,new,oldlist = item
+ output += id + "\n"
+ output += new+"\tFORCE\n"
+ output += "\n".join(oldlist)+"\n\n"
+ return (field,id,content,output) #返回四元组: 题号, 字段, 内容, 待确定是否要添加的字符串(不含FORCE字样的行为旧结果,含FORCE字样的行为新结果,FORCE是运行后强制添加)
+
+def AppendUsageData2024(prodict,field_id_and_content):
+ field,id,content = field_id_and_content
+ lines = [re.sub(r"\s+",r"\t",line.strip()) for line in content.strip().split("\n")]
+ pending_list = []
+ for line in lines:
+ if not "FORCE" in line.upper():
+ time_stripped = re.sub(r"^\d{4,}\t","",line) #读取去除时间的数据
+ if time_stripped in "\n".join(prodict[id][field]):
+ print(f"******题号 {id} 的 {field} 字段, 内容 {line} 已存在")
+ else:
+ usage = parseUsage(line)
+ oldusages = prodict[id]["usages"]
+ importflag = True
+ for u in oldusages:
+ oldusage = parseUsage(u)
+ if usage["classid"] == oldusage["classid"] and abs(datestrtotimestamp(usage["date"])-datestrtotimestamp(oldusage["date"])) < 7*86400 and usagelistdifference(usage["difficulties"],oldusage["difficulties"])<0.05:
+ print(f"######班级 {usage['classid']} 在题号为 {id} 的题目处已有非常类似的使用记录, 不作记录")
+ importflag = False
+ break
+ if importflag:
+ for u in oldusages:
+ oldusage = parseUsage(u)
+ if usage["classid"] == oldusage["classid"]:
+ print(f"!!!!!!班级 {usage['classid']} 在题号为 {id} 的题目处已有使用记录, 在pending list中记录")
+ oldinfo = [v for v in prodict[id][field] if usage['classid'] in v]
+ pending_list = pending_list + [(id,line,oldinfo)]
+ importflag = False
+ break
+ if importflag:
+ prodict[id][field].append(line)
+ print(f"已于 {id} 的 {field} 字段执行添加, 内容为 {line}")
+ else:
+ line = re.sub(r"\s+",r"\t",re.sub(r"FORCE","",line.upper())).strip()
+ prodict[id][field].append(line)
+ print(f"&&&&&&&&&&&& 已于 {id} 的 {field} 字段执行强制添加, 内容为 {line} &&&&&&")
+ output = ""
+ for item in pending_list:
+ id,new,oldlist = item
+ output += id + "\n"
+ output += new+"\tFORCE\n"
+ output += "\n".join(oldlist)+"\n\n"
+ return (field,id,content,output) #返回四元组: 题号, 字段, 内容, 待确定是否要添加的字符串(不含FORCE字样的行为旧结果,含FORCE字样的行为新结果,FORCE是运行后强制添加)
+
+
+def generateUsageList(db,id):
+ mycursor = db.cursor()
+ mycursor.execute("SELECT date,classname,diff FROM usages WHERE ID = %s;",(id,))
+ tuplelist = mycursor.fetchall()
+ return tuplelist
+
+
+def ImportMetadata(db,metadatafilepath): #metadata自动修改, 根据字段自适应修改, 参数为题库字典, 目标字典, 字段字典, metadata文本文件路径, 待确定是否替换的内容的存放路径
+ fieldsdict = {"objs":"objappend",
+ "obj":"objappend", # 别名
+ "tags":"tagappend",
+ "tag":"tagappend", #别名
+ "ans":"overwrite",
+ "solution":"overwrite",
+ "usages":"usageappend",
+ "usage":"usageappend",
+ "same":"mutualappend",
+ "related":"mutualappend",
+ "unrelated":"mutualappend",
+ "space":"overwrite",
+ "remark":"remarkappend",
+ "remarks":"remarkappend"
+ }
+ mycursor = db.cursor()
+ startdate = GetDate()
+ starttime = GetTime()
+ pendingusagelist = []
+ mycursor.execute("SELECT ID FROM problems;")
+ id_list_in_DB = [ret[0] for ret in mycursor.fetchall()]
+ mycursor.execute("SELECT objid FROM lessonobj;")
+ obj_list_in_DB = [ret[0] for ret in mycursor.fetchall()]
+ data_to_modify = ObtainDatatoModify(metadatafilepath,fieldsdict,id_list_in_DB)
+ print(data_to_modify)
+ for item in tqdm.tqdm(data_to_modify):
+ field = item[0]
+ if field == "NotAField":
+ print(f"项目 {item[1]} {item[2]} 字段名有误, 未对数据库作更改, 请检查!!!")
+ db.rollback()
+ db.close()
+ return 1
+ else:
+ method = fieldsdict[field]
+ if method == "overwrite":
+ overwriteinDB(db,field,item[1],item[2])
+ elif method == "mutualappend":
+ mutualappendinDB(db,field,item[1],item[2],id_list_in_DB)
+ elif method == "objappend":
+ objappendinDB(db,item[1],item[2],obj_list_in_DB+["KNONE"])
+ elif method == "tagappend":
+ tagappendinDB(db,item[1],item[2])
+ elif method == "remarkappend":
+ remarkappendinDB(db,item[1],item[2])
+ elif method == "usageappend":
+ pending_list = usageappendinDB(db,item[1],item[2])
+ pendingusagelist += pending_list.copy()
+ for pending_item in pendingusagelist:
+ print(f"新增记录: {pending_item[0]}")
+ print("原有记录:")
+ for u in pending_item[1]:
+ print(u)
+ insertflag = input("是否导入该数据?(Y/[N])").upper()
+ if insertflag == "Y":
+ forceusageappendinDB(db,pending_item[0])
+
+
+ sql = f"SELECT id FROM logs WHERE DATE > %s OR DATE = %s AND TIME >= %s;"
+ val = (startdate,startdate,starttime)
+ mycursor.execute(sql,val)
+ changed_id_list = [item[0] for item in mycursor.fetchall()]
+ return changed_id_list # 已在数据库中修改, 之后需要将数据库写入一次, 返回1表示字段名有误, 返回其他表示成功进行了修改
+
+def overwriteinDB(mydb,field,id,content_string): #覆盖ans,solution,space字段的内容并在logs中记录
+ mycursor = mydb.cursor()
+ sql = f"SELECT {field} FROM problems WHERE ID = %s;"
+ val = (id,)
+ mycursor.execute(sql,val)
+ original_string = mycursor.fetchall()[0][0]
+ sql = f"UPDATE problems SET {field} = %s WHERE ID = %s;"
+ val = (content_string,id)
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"更改 {field} 数据",f"{original_string} -> {content_string}")
+ mycursor.execute(sql,val)
+
+def mutualappendinDB(mydb,field,id,content_string,idlist): #新增same,related,unrelated数据
+ mycursor = mydb.cursor()
+ content_list = [item.strip().zfill(6) for item in content_string.split("\n") if not item.strip() == ""]
+ for id2 in content_list:
+ smallid = min(id,id2)
+ bigid = max(id,id2)
+ if smallid in idlist and bigid in idlist:
+ sql = f"SELECT ID, {field.upper()}_ID from {field} WHERE ID = %s AND {field.upper()}_ID = %s;"
+ val = (smallid,bigid)
+ mycursor.execute(sql,val)
+ ret_list = mycursor.fetchall()
+ if len(ret_list) == 0:
+ sql = f"INSERT INTO {field} SET ID = %s, {field.upper()}_ID = %s;"
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"新增 {field} 配对",f"{smallid} <-> {bigid}")
+ mycursor.execute(sql,val)
+ else:
+ print(f"{smallid} 或 {bigid} 中有错误, 请检查")
+
+def objappendinDB(mydb,id,content_string,objlist): #新增obj对应
+ mycursor = mydb.cursor()
+ content_list = [item.strip() for item in content_string.split("\n") if not item.strip() == ""]
+ for objid in content_list:
+ objid = objid.upper()
+ if not objid in objlist:
+ print(f"{objid} 有误, 请检查!!!")
+ else:
+ sql = f"SELECT ID, obj_ID FROM objcorresp WHERE ID = %s AND obj_ID = %s;"
+ val = (id,objid)
+ mycursor.execute(sql,val)
+ ret_list = mycursor.fetchall()
+ if len(ret_list) == 0:
+ sql = f"INSERT INTO objcorresp SET ID = %s, obj_ID = %s;"
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"新增目标对应",f"{id}: {objid}")
+ mycursor.execute(sql,val)
+
+
+
+def tagappendinDB(mydb,id,content_string): #新增tag对应
+ mycursor = mydb.cursor()
+ content_list = [item.strip() for item in content_string.split("\n") if not item.strip() == ""]
+ for tag in content_list:
+ sql = f"SELECT ID, tagname FROM tagcorresp WHERE ID = %s AND tagname = %s;"
+ val = (id,tag)
+ mycursor.execute(sql,val)
+ ret_list = mycursor.fetchall()
+ if len(ret_list) == 0:
+ sql = f"INSERT INTO tagcorresp SET ID = %s, tagname = %s;"
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"新增标签",f"{id}: {tag}")
+ mycursor.execute(sql,val)
+
+def remarkappendinDB(mydb,id,content_string): #新增备注
+ mycursor = mydb.cursor()
+ content_list = [item.strip() for item in content_string.split("\n") if not item.strip() == ""]
+ for line in content_list:
+ date,remark = parseRemark(line)
+ sql = f"SELECT ID,date,remark_content FROM remarks WHERE ID = %s AND date = %s AND remark_content LIKE %s;"
+ val = (id,date,"%"+remark+"%")
+ mycursor.execute(sql,val)
+ ret_list = mycursor.fetchall()
+ if len(ret_list) == 0 or len(remark) == 0: #如果原来的记录里remark为空也需要更新
+ sql = f"INSERT INTO remarks SET ID = %s, date = %s, remark_content = %s;"
+ val = (id,date,remark)
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"新增备注",f"{id}: {remark}")
+ mycursor.execute(sql,val)
+
+def detectsimUsage(tuple1,tuple2): #对比两个使用记录的tuple是否非常相近
+ id1,date1,classname1,diff1 = tuple1
+ id2,date2,classname2,diff2 = tuple2
+ if id1 == id2 and classname1 == classname2 and abs(datestrtotimestamp(date1)-datestrtotimestamp(date2)) < 7*86400 and usagelistdifference(diff1,diff2)<0.05:
+ return True
+ else:
+ return False
+
+def tostrlist(difflist):
+ return "[" + ", ".join(['"'+f"{float(data):.3f}"+'"' for data in difflist]) + "]"
+
+def forceusageappendinDB(mydb,rec): #强制新增使用记录
+ mycursor = mydb.cursor()
+ id = rec[0]
+ date = rec[1]
+ classname = rec[2]
+ diff = rec[3]
+ print(id,date,classname,diff)
+ sql = f"INSERT INTO usages (ID,date,classname,diff) VALUE (%s,%s,%s,%s);"
+ val = (id,date,classname,tostrlist(diff))
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"强制新增使用记录",f"{id}\t{date}\t{classname}\t{tostrlist(diff)}")
+ mycursor.execute(sql,val)
+
+def usageappendinDB(mydb,id,content_string): #新增使用记录
+ pending_list = []
+ mycursor = mydb.cursor()
+ content_list = [parseUsage(item) for item in content_string.split("\n") if not item.strip() == ""]
+ for rec in content_list:
+ date = rec["date"]
+ classname = rec["classid"]
+ diff = rec["difficulties"]
+ sql = f"SELECT ID,date,classname,diff FROM usages WHERE ID = %s and classname = %s;"
+ val = (id,classname)
+ mycursor.execute(sql,val)
+ oldusage_list = mycursor.fetchall()
+ if len(oldusage_list) > 0:
+ alike = False
+ for usage_raw in oldusage_list:
+ usage_raw = list(usage_raw)
+ usage = usage_raw[:3]+[json.loads(usage_raw[3])]
+ if detectsimUsage((id,date,classname,diff),usage):
+ alike = True
+ break
+ if alike == False:
+ pending_list.append(((id,date,classname,diff),oldusage_list.copy()))
+ else:
+ sql = f"INSERT INTO usages (ID,date,classname,diff) VALUE (%s,%s,%s,%s);"
+ val = (id,date,classname,tostrlist(diff))
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,ID,action,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),id,f"新增使用记录",f"{id}\t{date}\t{classname}\t{tostrlist(diff)}")
+ mycursor.execute(sql,val)
+ return pending_list
+
+
+
+
+def parseUsage(usagestring): #对单行usage信息进行分词
+ usagedict = {}
+ datalist = re.sub(r"\s+",r"\t",usagestring.strip()).split("\t")
+ if re.findall(r"20[\d]{2,6}",datalist[0]) != [] and re.findall(r"[\u4e00-\u9fa5]",datalist[0]) == []:
+ date = datalist.pop(0)
+ else:
+ date = ""
+ usagedict["date"] = date
+ if re.findall(r"[高一二三班]",datalist[0]) != []:
+ classid = datalist.pop(0)
+ else:
+ classid = ""
+ usagedict["classid"] = classid
+ usagedict["subproblems"] = len(datalist)
+ usagedict["difficulties"] = [float(u) for u in datalist]
+ usagedict["glossdiff"] = round(sum(usagedict["difficulties"])/usagedict["subproblems"],3)
+ return usagedict #返回词典, 含date日期, classid班级标识, subproblems小题数目, difficulties得分率, glossdiff小题平均得分率
+
+def StringSubstitute(regex,template,stringtuple): # 用stringtuple里的字符串逐个替换template中的符合某种regex规则的字符串
+ toSub = re.findall(regex,template)
+ if not len(toSub) == len(stringtuple):
+ print("长度不符.")
+ return 1 #若长度不符则显示“长度不符”并返回1
+ else:
+ output = template
+ for i in range(len(toSub)):
+ output = output.replace(toSub[i],stringtuple[i])
+ return output #若长度符合则返回替换后的字符串
+
+def GenerateTexDataforEditing(id_string,prodict,templatefilepath,editor): # 根据id_string的题号列表在prodict中生成一个可修改的tex文件, 包含题目内容,答案和解答,editor表示编辑者
+ id_list = generate_number_set(id_string,prodict)
+ output = "编辑者: " + editor + "\n\n\\begin{enumerate}\n\n"
+ for id in id_list:
+ content = prodict[id]["content"]
+ answer = prodict[id]["ans"]
+ solution = prodict[id]["solution"]
+ remark = prodict[id]["remark"]
+ output += "\\item (%s) "%str(id).zfill(6) + content + "\n\n" + "答案: " + answer + "\n\n" + "解答与提示: " + solution + "\n\n" + "备注: " + remark + "\n\n"
+ output += "\\end{enumerate}"
+ template = ReadTextFile(templatefilepath)
+ texdata = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",template,[output])
+ return texdata # 返回Tex文件的字符串, 待保存至tex文件
+
+
+def GetEditedProblems(string): #从.tex文件所读取的字符串中取出正文内容, 并切割成以题号为key的字典, 内容为[题目内容, 答案, 解答与提示, 备注]四元数组
+ bodydata = re.findall(r"\\begin\{enumerate\}([\s\S]*)\\end\{enumerate\}",string)[0]+r"\item"
+ problems = re.findall(r"\((\d{6})\)([\s\S]*?)\\item",bodydata)
+ edited_dict = {}
+ for problem in problems:
+ id, pro_string = problem
+ edited_dict[id] = parseProblem(pro_string)
+ return edited_dict # 返回以题号为key, 内容为[题目内容, 答案, 解答与提示, 备注]四元数组的字典
+
+def parseProblem(string): # 对以不小于两个回车切分的四段式字符串进行分词(通常第二段有"答案:", 第三段有"解答与提示:", 第四段有"备注:")
+ data = string.strip()
+ data = re.sub(r"\n{2,}","\n\n",data)
+ content,ans,solution,remark = data.split("\n\n")
+ content = content.strip()
+ ans = re.sub("答案:","",ans).strip()
+ solution = re.sub("解答与提示:","",solution).strip()
+ remark = re.sub("备注:","",remark).strip()
+ return (content,ans,solution,remark) # 返回四元组(题目内容, 答案, 解答与提示, 备注)
+
+def ModifyProblembyTeX(id_string,prodict,toeditfilepath,editor): # vscode打开.tex文件修改题目的内容, 答案与解答
+
+ # 读取题号列表并生成待编辑的.tex文件
+ texdata = GenerateTexDataforEditing(id_string,prodict,"模板文件/题目编辑.txt",editor)
+ SaveTextFile(texdata,toeditfilepath)
+
+ # 打开待编辑的.tex文件
+ print("编辑完成后保存文件, 关闭文件继续...")
+ os.system("code -w -g "+toeditfilepath)
+
+ # 生成修改后的题号与内容, 答案, 解答与提示的对应
+ editor = GetDate() + "\t" + editor
+ editedtexdata = ReadTextFile(toeditfilepath)
+ edited_dict = GetEditedProblems(editedtexdata)
+ editedIDList = []
+
+ # 将.tex文件中的修改反映到原题库字典, 并保存
+ for id in edited_dict:
+ content, ans, solution, remark = edited_dict[id]
+ if not (content == prodict[id]["content"] and ans == prodict[id]["ans"] and solution == prodict[id]["solution"] and remark == prodict[id]["remark"]):
+ prodict[id]["content"] = content
+ prodict[id]["ans"] = ans
+ prodict[id]["solution"] = solution
+ prodict[id]["remark"] = remark
+ prodict[id]["edit"] = prodict[id]["edit"] + [editor]
+ editedIDList.append(id)
+
+ save_dict(prodict,"../题库0.3/Problems.json")
+ return generate_exp(editedIDList) # 返回编辑过的字典
+
+
+def RemoveMutualLink(metadata,prodict): # 删除双向关联id(same,related,unrelated), 输入为一个字符串, 每行表示一对题号, 用空格或制表符隔开
+ lines = [re.sub(r"[\s]+",r"\t",line).strip() for line in metadata.split("\n")]
+ for line in lines:
+ if not line == []:
+ id1,id2 = line.split("\t")
+ id1 = id1.zfill(6)
+ id2 = id2.zfill(6)
+ for field in ["same","related","unrelated"]:
+ if id1 in prodict[id2][field]:
+ prodict[id2][field].remove(id1)
+ if id2 in prodict[id1][field]:
+ prodict[id1][field].remove(id2)
+ return 0 # 返回0
+
+def jsonEditProblemMetadata(id_string,prodict,editor): #用vscode在json模式下编辑题目综合信息
+ jsontoeditpath = "临时文件/problem_edit.json"
+ idlist = generate_number_set(id_string,prodict)
+ edit_dict = {}
+ for id in idlist:
+ edit_dict[id] = prodict[id].copy()
+ save_dict(edit_dict,jsontoeditpath)
+ #打开待编辑的json文件
+ os.system("code -w -g "+jsontoeditpath)
+ #编辑后关闭文件窗口自动执行下面步骤
+ editeddict = load_dict(jsontoeditpath)
+ editlist = []
+ for id in editeddict:
+ if not prodict[id] == editeddict[id]:
+ prodict[id] = editeddict[id].copy()
+ prodict[id]["edit"] = prodict[id]["edit"] + [GetDate() + "\t" + editor]
+ editlist.append(id)
+ return(generate_exp(editlist)) # 返回编辑过的题号字符串
+
+def GetSamePairs(prodict): #获取已标注的相同题目组
+ same_groups = []
+ for id in prodict:
+ same = prodict[id]["same"]
+ if len(same) > 0 and not len(prodict[id]["usages"]) == 0:
+ same_groups.append([id]+same)
+ return(same_groups) #返回相同题目组, 每组由一些相同的题目构成
+
+def ShareSameUsagesinDB(id1,id2,db): #有问题, 待修改
+ mycursor = db.cursor()
+ sql = "SELECT date,classname,diff FROM usages WHERE ID = (%s);"
+ val = (id1,)
+ mycursor.execute(sql,val)
+ id1_usages_list = mycursor.fetchall()
+ val = (id2,)
+ mycursor.execute(sql,val)
+ id2_usages_list = mycursor.fetchall()
+ for item in set(id1_usages_list)-set(id2_usages_list):
+ print(f"{id1} -> {id2}: {item}")
+ sql = "INSERT INTO usages (ID,date,classname,diff) VALUE (%s,%s,%s,%s);"
+ val = (id2,item[0],item[1],item[2])
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,db_content) VALUE (%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),"分享使用数据",f"{id1} -> {id2}: {item}")
+ mycursor.execute(sql,val)
+ for item in set(id2_usages_list)-set(id1_usages_list):
+ print(f"{id2} -> {id1}: {item}")
+ sql = "INSERT INTO usages (ID,date,classname,diff) VALUE (%s,%s,%s,%s);"
+ val = (id1,item[0],item[1],item[2])
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,db_content) VALUE (%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),"分享使用数据",f"{id2} -> {id1}: {item}")
+ mycursor.execute(sql,val)
+
+ return 0
+
+
+def ShareSameUsages(prodict,same_group): #对same_group中的所有题号共享使用记录
+ current_usages = []
+ for id in same_group:
+ for usage in prodict[id]["usages"]:
+ if not usage in current_usages:
+ current_usages = current_usages + [usage]
+ for id in same_group:
+ prodict[id]["usages"] = current_usages.copy()
+ return((same_group,current_usages)) #返回same_group中的题号列表 及 所有题号记录 组成的二元组
+
+def SortUsages(prodict): #对使用记录按字符串进行排序
+ for id in prodict:
+ usages = prodict[id]["usages"].copy()
+ usages.sort()
+ prodict[id]["usages"] = usages.copy()
+ return 0 #返回0
+
+
+def SortUsagesbyAverage(theusages): #根据使用记录每一条的平均值进行排序, 越高的在越前面
+ glossdifflist = []
+ for i in range(len(theusages)):
+ glossdiff = np.mean([float(d) for d in theusages[i][2]])
+ if glossdiff <= 0.999: #去除全对的使用记录
+ glossdifflist = glossdifflist + [(i,glossdiff)]
+ sortedglossdifflist = sorted(glossdifflist, key = lambda x:x[1], reverse = True)
+ newusages = [theusages[i[0]] for i in sortedglossdifflist]
+ return(newusages) # 返回排序后的list
+
+
+def StripSuffix(string, suf_words): #除去字符串前后的空格及suf_words中每一个符合regex的字符串及其之后的部分
+ string = string.strip()
+ for sw in suf_words:
+ string = re.sub(sw+r"[\S]*$","",string)
+ return(string) # 返回处理以后的字符串
+
+
+def generate_origin(origin_dict):
+ data = origin_dict["来源"]
+ if "题号" in origin_dict:
+ data = f"{data}试题{origin_dict['题号']}"
+ if "前序" in origin_dict:
+ data = f"{data}-改编自{origin_dict['前序']}"
+ return data
+
+
+def MatchConditioninMariaDB(condition_list,database):
+ corr_dict = {
+ "content": ('problems','content'),
+ "objs": ('objcorresp','obj_ID'),
+ "tags": ('tagcorresp','tagname'),
+ "origin": ('problems','origin'),
+ "genre": ('problems','genre'),
+ "ans": ('problems','ans'),
+ "solution": ('problems','solution'),
+ "remark": ('remarks','remark_content'),
+ "same": ('same','ID','SAME_ID'),
+ "related": ('related','ID','RELATED_ID'),
+ "usages": ('usages','date','classname','diff')
+ }
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID FROM problems WHERE NOT content REGEXP 'OBS';")
+ match = set([u[0] for u in mycursor.fetchall()])
+ for field,reverse,regexp in condition_list:
+ if not field in ["same","related","usages"]:
+ table,column = corr_dict[field]
+ sql = f"SELECT ID FROM {table} WHERE {column} REGEXP '{regexp}';"
+ mycursor.execute(sql)
+ newmatch = set([u[0] for u in mycursor.fetchall()])
+ elif field in ["same","related"]:
+ table,col0,col1 = corr_dict[field]
+ sql1 = f"SELECT {col1} FROM {table} WHERE {col0} REGEXP '{regexp}';"
+ mycursor.execute(sql1)
+ newmatch = set([u[0] for u in mycursor.fetchall()])
+ sql2 = f"SELECT {col0} FROM {table} WHERE {col1} REGEXP '{regexp}';"
+ mycursor.execute(sql2)
+ newmatch = newmatch | set([u[0] for u in mycursor.fetchall()])
+ elif field == "usages":
+ table,cols = corr_dict[field][0],corr_dict[field][1:]
+ newmatch = set([])
+ for i in range(len(cols)):
+ sql = f"SELECT ID FROM {table} WHERE {cols[i]} REGEXP '{regexp}';"
+ mycursor.execute(sql)
+ newmatch = newmatch | set([u[0] for u in mycursor.fetchall()])
+ if reverse:
+ match = match - newmatch
+ else:
+ match = match & newmatch
+ mydb.close()
+ return match
+
+def MatchCondition2024(problem,condition_list): #判断problem这一字典是否符合condition_list中的所有筛选条件
+ match = True #初始设定符合条件
+ for field, flag_not, matchexp in condition_list:
+ exps = [i.strip() for i in matchexp.split(",")]
+ if type(problem[field]) == list:
+ data = "\n".join(problem[field])
+ elif field == "origin":
+ data = generate_origin(problem[field])
+ else:
+ data = str(problem[field]) #至此将每个字段中的内容都转为string
+ if flag_not == False: #表示肯定的筛选
+ if all([re.findall(exp,data) == [] for exp in exps]): #如果有一个条件中的每个关键字都找不到, 就返回"不符合条件"(False)
+ return False
+ if flag_not == True:
+ if all([re.findall(exp,data) != [] for exp in exps]): #如果有一个条件中的每个关键字都能找到, 就返回"不符合条件"(False)
+ return False
+ return match #返回是否符合条件
+
+def MatchCondition(problem,condition_dict): #判断problem这一字典是否符合condition_dict中的所有筛选条件
+ match = True #初始设定符合条件
+ for fieldraw in [c for c in condition_dict if not "_not" in c and not condition_dict[c] == [""]]: #选出正向的条件([""]表示该条件不起作用)
+ cond_list = condition_dict[fieldraw]
+ if type(cond_list) == str:
+ cond_list = [cond_list]
+ field = re.sub(r"\d","",fieldraw)
+ if type(problem[field]) == list: #将题库字典中的相应字段转化成字符串string
+ string = "\n".join((problem[field]))
+ else:
+ string = str(problem[field])
+ current_match = False
+ for cond in cond_list: #有一个条件被满足, 就认为当前字段满足条件了
+ if len(re.findall(cond,string)) > 0:
+ current_match = True
+ if current_match == False:
+ match = False # 如果某个正向条件未满足, 那么match就赋值为False; 如果每一个正向条件都被满足, 那么match仍为True
+ for fieldraw in [c for c in condition_dict if "_not" in c and not condition_dict[c] == [""]]: #选出反向的条件([""]表示该条件不起作用)
+ cond_list = condition_dict[fieldraw]
+ fieldraw = fieldraw.replace("_not","")
+ field = re.sub(r"\d","",fieldraw)
+ if type(problem[field]) == list: #将题库字典中的相应字段转化成字符串string
+ string = "\n".join((problem[field]))
+ else:
+ string = str(problem[field])
+ current_match = True
+ for cond in cond_list: #有一个条件被满足, 就认为当前字段不被满足了
+ if len(re.findall(cond,string)) > 0:
+ current_match = False
+ if current_match == False:
+ match = False
+ return match #返回是否符合条件
+
+
+def get_color(value): # 根据得分率获得rgb颜色代码
+ value = float(value)
+ if value>=0.5:
+ (r,g)=(1,2-2*value)
+ else:
+ (r,g)=(2*value,1)
+ return "{%.3f,%.3f,0}"%(r,g) #返回用大括号包围的rgb数值
+
+def GenerateValueColorCode(matchobj): # 生成三位小数代表的颜色方框(黑色边框, 难度对应的底色)的LaTeX代码
+ value = matchobj.group(1)
+ return "\t\\fcolorbox[rgb]{0,0,0}%s{%s}"%(get_color(value),value) #返回代码
+
+def StudentsGetAfterContent(id,prodict,answered,spaceflag): #生成学生版讲义后的答案及空格, answered表示有无答案, spaceflag表示有无空格
+ string = ""
+ if "ans" in prodict[id]:
+ if answered:
+ string += "答案: \\textcolor{%s}{%s}\n\n"%(("red",prodict[id]["ans"]) if prodict[id]["ans"] != "" else ("blue","暂无答案"))
+ if spaceflag:
+ if "space" in prodict[id]:
+ if prodict[id]["space"] != "":
+ string += "\\vspace*{%s}\n\n"%prodict[id]["space"]
+ return string #生成学生讲义后的答案及空格
+
+def XeLaTeXCompile(filedir,filename,times = 2): #在filedir目录中用XeLaTeX编译filename文件两次(能编译出正确的引用和页码)
+ flagsuc = True
+ for i in range(times):
+ if os.system("xelatex -interaction=batchmode -output-directory=%s %s"%(filedir,filename)) == 0:
+ print(f"第 {i+1} 次编译 {filename} 成功.")
+ else:
+ flagsuc = False
+ return flagsuc # 若第二次编译成功则返回True, 否则返回False
+
+
+def XeLaTeXTest(editedid,adict,objdict,misc,templatepath,outdir,outfile): #对adict中的题号字符串editedid进行编译测试, 返回成功True或失败False
+ latex_raw = ReadTextFile(templatepath)
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+ if misc["教师版"] == True:
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学")
+ bodystring = "\\begin{enumerate}\n\n"
+ idlist = generate_number_set(editedid)
+ for id in idlist:
+ bodystring += generateLaTeXBodyContent(id,adict,objdict,misc)
+ bodystring += "\n\n\\end{enumerate}\n\n"
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,("测试",bodystring)) #替换标题和bodystring
+ SaveTextFile(latex_data,os.path.join(outdir,outfile))
+ succ = XeLaTeXCompile(outdir,outfile)
+ return succ
+
+def GenerateSectionBodyStringfromMariaDB(cursor,problems,sectiontitles,misc,consecutivenumbering= True): #生成学生版的.tex文件的主体内容
+ bodystring = ""
+ count = 0
+ for i in range(len(problems)):
+ if problems[i][0] in "0123456789":
+ idlist = generate_number_set(problems[i])
+ sectionstring = f"\\section{{{sectiontitles[i]}}}\n\\begin{{enumerate}}\n\\setcounter{{enumi}}{{{count if consecutivenumbering else 0}}}\n\n"
+ for id in idlist:
+ count += 1
+ sectionstring += generateLaTeXBodyContentfromMariaDB(cursor,id,misc)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ if problems[i][0] == "K":
+ idlist = problems[i].split(",")
+ sectionstring = f"\\section{{{sectiontitles[i]}}}\n\\begin{{enumerate}}\n\n"
+ for objid in idlist:
+ sectionstring += generateLaTeXobjsfromMariaDB(cursor,objid)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ if problems[i][0] == "B":
+ idlist = problems[i].split(",")
+ sectionstring = f"\\section{{{sectiontitles[i]}}}\n\\begin{{enumerate}}\n\n"
+ for bnid in idlist:
+ sectionstring += generateLaTeXbnsfromMariaDB(cursor,bnid)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ return bodystring #返回主题内容字符串
+
+# if not objlist == []:
+# output += "\\section{课时目标}\n\n"
+# output += "\\begin{enumerate}\n\n"
+# for objid in objlist:
+# output += "\\item %s \\ %s \n\n"%(objid,objdict[objid]["content"])
+# output += "\\end{enumerate}\n\n" #生成学习目标
+# if not bnlist == []:
+# output += "\\section{双基梳理}\n\n"
+# basic_body = ""
+# for bnid in bnlist:
+# basic_body += "\\item %s\n\n"%basicknowledgedict[bnid]["content"]
+# output += "\\begin{enumerate}\n\n %s\n\n\\end{enumerate}\n\n"%basic_body #生成基础知识梳理
+
+
+def GenerateStudentBodyString(problems,sectiontitles,pro_dict,consecutivenumbering= True,answered= True,spaceflag=True): #生成学生版的.tex文件的主体内容
+ bodystring = ""
+ if len(problems) == len(sectiontitles):
+ count = 0
+ for i in range(len(problems)):
+ idlist = generate_number_set(problems[i],pro_dict)
+ sectionstring = "\\section{%s}\n\\begin{enumerate}\n\\setcounter{enumi}{%d}\n\n"%(sectiontitles[i],count if consecutivenumbering else 0)
+ for id in idlist:
+ count += 1
+ aftercontent = StudentsGetAfterContent(id,pro_dict,answered,spaceflag)
+ sectionstring += "\\item {\\tiny(%s)} %s\n\n%s"%(id,pro_dict[id]["content"],aftercontent)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ else:
+ idstring = ",".join(problems)
+ idlist = generate_number_set(idstring,pro_dict)
+ sectionstring = "\\begin{enumerate}\n\n"
+ for id in idlist:
+ aftercontent = StudentsGetAfterContent(id,pro_dict,answered,spaceflag)
+ sectionstring += "\\item {\\tiny(%s)} %s\n\n%s"%(id,pro_dict[id]["content"],aftercontent)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ return bodystring #返回主题内容字符串
+
+
+def TeachersGetAfterContentColored(id,prodict,objdict,topandbottomusagestuple = (3,3), showobjs = True, showtags = True, showans = True, showsolution = True, showusages = True, showorigin = True, showremark = True): #生成教师版讲义后的答案及空格, topandbottomusagestuple表示保留得分率最高的使用记录与最低的使用记录的个数, 有负数表示不排列, 彩色背景
+ string = ""
+ objs = GenerateObjTexCode(id,prodict,objdict) if showobjs else ""
+ tags = ("\\begin{tcolorbox}[colback = orange!10!white, colframe = orange!10!white, breakable]\n标签: %s\n\n\\end{tcolorbox}\n"%("; ".join(prodict[id]["tags"]))) if showtags else ""
+ ans = ("\\begin{tcolorbox}[colback = red!10!white, colframe = red!10!white, breakable]\n答案: %s\n\n\\end{tcolorbox}\n"%(prodict[id]["ans"] if prodict[id]["ans"] != "" else "暂无答案")) if showans else ""
+ solution = ("\\begin{tcolorbox}[colback = green!10!white, colframe = green!10!white, breakable]\n解答或提示: %s\n\n\\end{tcolorbox}\n"%(prodict[id]["solution"] if prodict[id]["solution"] != "" else "暂无解答")) if showsolution else ""
+ # solution = ("解答或提示: \\textcolor{magenta}{%s}\n\n"%(prodict[id]["solution"] if prodict[id]["solution"] != "" else "暂无解答")) if showsolution else ""
+ origin = ("\\begin{tcolorbox}[colback = yellow!30!white, colframe = yellow!30!white, breakable]\n来源: %s\n\n\\end{tcolorbox}\n"%prodict[id]["origin"]) if showorigin else ""
+ remark = ("\\begin{tcolorbox}[colback = cyan!30!white, colframe = cyan!30!white, breakable]\n备注: %s\n\n\\end{tcolorbox}\n"%(prodict[id]["remark"] if prodict[id]["remark"] != "" else "暂无备注")) if showremark else ""
+ usages = ("\\begin{tcolorbox}[colback = lime!10!white, colframe = lime!10!white, breakable]\n使用记录(%s):\n\n%s\n\n\\end{tcolorbox}\n"%(str(topandbottomusagestuple),GenerateUsageTexCode(id,prodict,topandbottomusagestuple))) if showusages else ""
+ string += objs + tags + ans + solution + usages + origin + remark
+ return string #生成教师版讲义后的答案及空格
+
+def TeachersGetAfterContentPlain(id,prodict,objdict,topandbottomusagestuple = (3,3), showobjs = True, showtags = True, showans = True, showsolution = True, showusages = True, showorigin = True, showremark = True): #生成教师版讲义后的答案及空格, topandbottomusagestuple表示保留得分率最高的使用记录与最低的使用记录的个数, 有负数表示不排列
+ string = ""
+ objs = ("目标:\n\n%s\n\n"%GenerateObjTexCode(id,prodict,objdict)) if showobjs else ""
+ tags = ("标签: \\textcolor[rgb]{0.5,0.6,0.8}{%s}\n\n"%("; ".join(prodict[id]["tags"])) if not prodict[id]["tags"] == [] else "标签: \n\n") if showtags else ""
+ ans = ("答案: \\textcolor{%s}{%s}\n\n"%(("red",prodict[id]["ans"]) if prodict[id]["ans"] != "" else ("blue","暂无答案"))) if showans else ""
+ solution = ("解答或提示: \\textcolor{magenta}{%s}\n\n"%(prodict[id]["solution"] if prodict[id]["solution"] != "" else "暂无解答")) if showsolution else ""
+ origin = ("来源: %s\n\n"%prodict[id]["origin"]) if showorigin else ""
+ remark = ("备注: \\textcolor[rgb]{0,0.5,0.2}{%s}\n\n"%(prodict[id]["remark"] if prodict[id]["remark"] != "" else "暂无备注")) if showremark else ""
+ usages = ("使用记录:\n\n%s\n\n"%GenerateUsageTexCode(id,prodict,topandbottomusagestuple)) if showusages else ""
+ string += objs + tags + ans + solution + usages + origin + remark
+ return string #生成教师版讲义后的答案及空格
+
+
+def GenerateObjTexCode(id,prodict,objdict): #生成目标代号对应的学习目标字符串(含蓝色编码)
+ # string = ""
+ # if prodict[id]["objs"] != []:
+ string = "\n\\begin{tcolorbox}[colback = blue!10!white, colframe = blue!10!white, breakable]"
+ for objid in prodict[id]["objs"]:
+ if objid.upper() == "KNONE":
+ string += "\n\n%s\t%s"%(objid,"无当前有效关联目标")
+ else:
+ string += "\n\n%s\t%s"%(objid,objdict[objid]["content"])
+ if prodict[id]["objs"] == []:
+ string += "暂未关联\n"
+ string += "\n\n\\end{tcolorbox}\n"
+ return string #返回目标代码字符串
+
+def ChooseUsage(usages,topandbottomusagestuple): #生成题号对应的题目的使用记录, topandbottomusagestuple表示保留得分率最高的使用记录与最低的使用记录的个数, 有负数表示不排列, 两数之和大于记录数则从高到低排列后全部展示
+ top,bottom = topandbottomusagestuple
+ if top < 0 or bottom < 0:
+ return usages # 返回原本的全部使用记录
+ elif top + bottom > len(usages):
+ return SortUsagesbyAverage(usages) # 返回排列后的使用记录
+ else:
+ return (SortUsagesbyAverage(usages)[:top] + SortUsagesbyAverage(usages)[(len(usages)-bottom):]) # 返回排序后的最前面top个和最后面bottom个
+
+def GenerateUsageTexCode(usage_list_raw,misc): #根据topandbottomusagestuple的要求生成题号为id的题目的缩减版的使用记录列表, topandbottomusagestuple表示保留得分率最高的使用记录与最低的使用记录的个数, 有负数表示不排列, 两数之和大于记录数则从高到低排列后全部展示
+ grade = misc["字段显示设置"]["届别"]
+ topandbottomusagestuple = misc["字段显示设置"]["使用记录"]
+ usage_list = []
+ if len(grade) == 0:
+ usage_list = usage_list_raw.copy()
+ else:
+ for u in usage_list_raw:
+ for g in grade:
+ if re.findall(g,u[1]) != []:
+ usage_list.append(u)
+ break
+ usages = ChooseUsage(usage_list,topandbottomusagestuple)
+ usages_str = ["\t".join((u[0],u[1],("\t".join(u[2])))) for u in usages]
+ usagecode = re.sub("\\t([\d]\.[\d]{0,10})",GenerateValueColorCode,"\n\n".join(usages_str))
+ return usagecode #返回缩减后的使用记录列表
+
+def generateLaTeXBodyContentfromMariaDB(cursor,id,misc): #根据id,读取的json内容adict,和字典misc来生成讲义
+#misc 样例
+#{
+# "教师版": True, #如果设置为True则除了 题后空间 之外都进行判断并处理, 否则只处理 题后空间 和 答案
+# "字段显示设置": {
+# "题后空间": True,
+# "课时目标": True,
+# "题目标签": True,
+# "答案": True,
+# "解答与提示": True,
+# "使用记录": (3,-1),
+# "使用记录说明": "(a,b)表示显示最好的a个和最差b个, 有-2表示不显示, 无-2但有-1表示全部显示",
+# "来源": True,
+# "备注": True,
+# "届别": []
+# }
+# }
+ id = str(id).zfill(6)
+ sql = "SELECT content,ans,solution,origin,space FROM problems WHERE ID = %s;"
+ val = (id,)
+ cursor.execute(sql,val)
+ ret_list = cursor.fetchall()
+ if len(ret_list) != 0:
+ content,ans,solution,raw_origin,space = ret_list[0]
+ if ans is None:
+ ans = ""
+ if solution is None:
+ solution = ""
+ origin = json.loads(raw_origin)
+ #以下生成obj_list
+ sql = "SELECT obj_ID FROM objcorresp WHERE ID = %s;"
+ cursor.execute(sql,val)
+ ret_list = cursor.fetchall()
+ obj_list = []
+ for id_raw in ret_list:
+ obj_id = id_raw[0]
+ sql = "SELECT obj_content FROM lessonobj WHERE objid = %s;"
+ if not obj_id.upper() == "KNONE":
+ objval = (obj_id,)
+ cursor.execute(sql,objval)
+ obj_list.append(f"{obj_id}\t{cursor.fetchall()[0][0]}")
+ else:
+ cursor.fetchall()
+ obj_list.append(f"{obj_id}\t暂无目标对应")
+ sql = "SELECT tagname FROM tagcorresp WHERE ID = %s;"
+ cursor.execute(sql,val)
+ tag_list = [t[0] for t in cursor.fetchall()]
+ sql = "SELECT date,remark_content FROM remarks WHERE ID = %s;"
+ cursor.execute(sql,val)
+ remark_list = sorted([f"{t[0]}\t{t[1]}" for t in cursor.fetchall()])
+ sql = "SELECT date,classname,diff FROM usages WHERE ID = %s;"
+ cursor.execute(sql,val)
+ usages_fetched = [(u[0] if not u[0] is None else "",u[1],json.loads(u[2])) for u in cursor.fetchall()]
+ usages_raw = sorted(usages_fetched)
+ if not "教师版" in misc or misc["教师版"] == False:
+ output = f"\n\\item {{\\tiny ({id})}} {content}"
+ if "字段显示设置" in misc and "答案" in misc["字段显示设置"] and misc["字段显示设置"]["答案"] == True:
+ ans = ans if len(ans) > 0 else "暂无答案"
+ output += f"\n\n答案: \\textcolor{{{('red' if ans != '暂无答案' else 'blue')}}}{{{ans}}}\n\n"
+ if "字段显示设置" in misc and "题后空间" in misc["字段显示设置"] and misc["字段显示设置"]["题后空间"] == True:
+ space = f"\n\n\\vspace*{{{space}}}\n\n" if len(space) > 0 else ""
+ output += space
+ else:
+ output = f"\n\\item ({id}) {content}"
+ if "备注" in misc["字段显示设置"] and misc["字段显示设置"]["备注"] == True:
+ remark = r"\\".join(remark_list) if len(remark_list) > 0 else "暂无备注"
+ output += f"\n\n备注: \\textcolor[rgb]{{0,0.5,0.2}}{{{remark}}}\n\n"
+ if "课时目标" in misc["字段显示设置"] and misc["字段显示设置"]["课时目标"] == True:
+ if len(obj_list) == 0:
+ obj_string = "暂无目标"
+ else:
+ obj_string = '\n\n'.join(obj_list)
+ objs = f"\n\n目标:\n\n{obj_string}\n\n"
+ output += objs
+ if "题目标签" in misc["字段显示设置"] and misc["字段显示设置"]["题目标签"] == True:
+ if len(tag_list) == 0:
+ tags = "暂无标签"
+ else:
+ tags = '; '.join(tag_list)
+ output += f"\n\n标签: \\textcolor[rgb]{{0.5,0.6,0.8}}{{{tags}}}\n\n"
+ if "答案" in misc["字段显示设置"] and misc["字段显示设置"]["答案"] == True:
+ ans = ans if len(ans) > 0 else "暂无答案"
+ output += f"\n\n答案: \\textcolor{{{('red' if ans != '暂无答案' else 'blue')}}}{{{ans}}}\n\n"
+ if "解答与提示" in misc["字段显示设置"] and misc["字段显示设置"]["解答与提示"] == True:
+ solution = solution if len(solution) > 0 else "暂无解答或提示"
+ output += f"\n\n解答或提示: \\textcolor{{magenta}}{{{solution}}}\n\n"
+ if "使用记录" in misc["字段显示设置"] and type(misc["字段显示设置"]["使用记录"]) in (list,tuple) and not -2 in misc["字段显示设置"]["使用记录"]:
+ usages = f"\n\n使用记录:\n\n{GenerateUsageTexCode(usages_raw,misc)}\n\n"
+ output += usages
+ if "来源" in misc["字段显示设置"] and misc["字段显示设置"]["来源"] == True:
+ origin = generate_origin(origin) if len(origin) > 0 else "未记录来源"
+ output += f"\n\n来源: {origin}\n\n"
+ else:
+ output = f"\\item ({id}) 题号有误!!!"
+ return output
+
+
+
+def generateLaTeXobjsfromMariaDB(cursor,objid):
+ objid = str(objid).upper().strip()
+ if objid == "KNONE":
+ return ""
+ else:
+ sql = "SELECT obj_content FROM lessonobj WHERE objid = %s;"
+ val = (objid,)
+ cursor.execute(sql,val)
+ obj_content = cursor.fetchall()[0][0]
+ output = f"\\item {objid} {obj_content}\n\n"
+ return output
+
+def generateLaTeXbnsfromMariaDB(cursor,bnid):
+ bnid = str(bnid).upper().strip()
+ sql = "SELECT bn_content FROM basic_knowledges WHERE bn_id = %s;"
+ val = (bnid,)
+ cursor.execute(sql,val)
+ try:
+ bn_content = cursor.fetchall()[0][0]
+ except:
+ bn_content = "基础知识目标编号有误"
+ output = f"\\item {bn_content}\n\n"
+ return output
+
+
+def GenerateTeacherBodyString(problems,sectiontitles,prodict,objdict,consecutivenumbering = True,topandbottomusagestuple = (3,3),sectionname = "section", showobjs = True, showtags = True, showans = True, showsolution = True, showusages = True, showorigin = True, showremark = True, colored = False): #生成教师版的.tex文件的主体内容, 各项是否显示为可选
+ bodystring = ""
+ GetAfterContent = TeachersGetAfterContentPlain if not colored else TeachersGetAfterContentColored
+ if len(problems) == len(sectiontitles):
+ count = 0
+ for i in range(len(problems)):
+ idlist = generate_number_set(problems[i],prodict)
+ sectionstring = "\\%s{%s}\n\\begin{enumerate}\n\\setcounter{enumi}{%d}\n\n"%(sectionname,sectiontitles[i],count if consecutivenumbering else 0)
+ for id in idlist:
+ count += 1
+ aftercontent = GetAfterContent(id,prodict,objdict,topandbottomusagestuple = topandbottomusagestuple, showobjs = showobjs, showtags = showtags, showans = showans, showsolution = showsolution, showusages = showusages, showorigin = showorigin, showremark = showremark)
+ sectionstring += "\\item (%s) %s\n\n%s"%(id,prodict[id]["content"],aftercontent)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ else:
+ idstring = ",".join(problems)
+ idlist = generate_number_set(idstring,prodict)
+ sectionstring = "\\begin{enumerate}\n\n"
+ for id in idlist:
+ aftercontent = GetAfterContent(id,prodict,objdict,topandbottomusagestuple = topandbottomusagestuple, showobjs = showobjs, showtags = showtags, showans = showans, showsolution = showsolution, showusages = showusages, showorigin = showorigin, showremark = showremark)
+ sectionstring += "\\item (%s) %s\n\n%s"%(id,prodict[id]["content"],aftercontent)
+ sectionstring += "\\end{enumerate}\n\n"
+ bodystring += sectionstring
+ return bodystring #返回主体内容字符串
+
+def GetValidTexFiles(path): #获取 有题号的.tex文件列表 及 有题号的.tex文件所在的路径列表
+ texlist = []
+ pathlist = []
+ for root,folders,files in os.walk(path):
+ for file in files:
+ if file[-4:] == ".tex":
+ texdata = ReadTextFile(os.path.join(root,file))
+ ids = re.findall(r"\((\d{6})\)",texdata)
+ if len(ids) > 0:
+ pathlist = pathlist + [root]
+ texlist = texlist + [os.path.join(root,file)]
+ texlist = sorted(list(set(texlist)))
+ pathlist = sorted(list(set(pathlist)))
+ return (texlist,pathlist) # 返回 有题号的.tex文件列表 与 所在路径列表 组成的二元组
+
+
+def generate_lessonid_list(lessonidstr,lessonsdict): #根据lessonidstr的条件(开头字母与数字)返回课时代码列表
+ raw_criterion_list = lessonidstr.split(",")
+ criterion_list = []
+ for cr in raw_criterion_list: #生成满足要求的课时代码的前若干位
+ if not ":" in cr:
+ criterion_list.append(cr)
+ else:
+ criterion_list = criterion_list + ["K"+c[-4:] for c in generate_number_set(cr.upper().replace("K",""))]
+
+ lessons_list = []
+ for id in lessonsdict: #对每一课时作比对, 生成课时代码列表
+ for cr in criterion_list:
+ if id.startswith(cr):
+ lessons_list.append(id)
+ break
+ return lessons_list # 返回课时代码列表
+
+
+def GenerateLessonPreparationDraft(notetitle, outputdir, adict, prodict, objdict, lessonsdict, topandbottomusagestuple = (3,3), showobjs = True, showtags = True, showans = True, showsolution = True, showusages = True, showorigin = True, showremark = True): #根据adict中课时与题号的对应生成每一课时的备课草稿
+ output = "\\tableofcontents\n\\newpage\n\n"
+
+ outputfilepath = os.path.join(outputdir,notetitle+".tex")
+
+
+ for lid in adict:
+ output += "\\section{%s \ %s}\n\n"%(lid,lessonsdict[lid]["name"])
+ output += "\\subsection{课时目标}\n\n"
+ output += "\\begin{enumerate}\n\n"
+ for objid in objdict:
+ if objid.startswith(lid):
+ output += "\\item %s \\ %s \n\n"%(objid,objdict[objid]["content"])
+ output += "\\end{enumerate}\n\n"
+ output += GenerateTeacherBodyString([adict[lid]],["参考题目资源"],prodict,objdict,sectionname = "subsection",topandbottomusagestuple= topandbottomusagestuple, showobjs = showobjs, showtags = showtags, showans = showans, showsolution = showsolution, showusages = showusages, showorigin = showorigin, showremark = showremark)
+ output += "\\newpage\n\n"
+
+
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学") #替换掉模板中的姓名学号
+
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,output)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if XeLaTeXCompile(outputdir,notetitle+".tex"):
+ print("编译成功")
+ return 0 # 返回0
+ else:
+ print("编译失败")
+ return latex_data # 返回有错误的latex源代码
+
+def GenerateLessonPreparation(notetitle, outputdir, adict, prodict, objdict, basicknowledgedict, homeworkspaces = False): #根据adict生成每一课时的教案
+ output = "\\tableofcontents\n\\newpage\n\n"
+
+ outputfilepath = os.path.join(outputdir,notetitle+".tex")
+
+
+ for key in adict:
+ lessonid = key
+ lessonname = adict[key]["name"]
+ objlist = adict[key]["objects"]
+ bnlist = adict[key]["basicknowledges"]
+ eblist = adict[key]["examples_basic"]
+ ealist = adict[key]["examples_adv"]
+ worklist = adict[key]["work_inclass"]
+ homeworklist = adict[key]["homework"]
+ remarks = adict[key]["remarks"]
+ if not "复习" in lessonname:
+ output += "\\section{%s \ %s}\n\n"%(lessonid,lessonname)
+
+ if not objlist == []:
+ output += "\\subsection{课时目标}\n\n"
+ output += "\\begin{enumerate}\n\n"
+ for objid in objlist:
+ output += "\\item %s \\ %s \n\n"%(objid,objdict[objid]["content"])
+ output += "\\end{enumerate}\n\n" #生成学习目标
+ if not bnlist == []:
+ output += "\\subsection{双基梳理}\n\n"
+ basic_body = ""
+ for bnid in bnlist:
+ basic_body += "\\item %s\n\n"%basicknowledgedict[bnid]["content"]
+ output += "\\begin{enumerate}\n\n %s\n\n\\end{enumerate}\n\n"%basic_body #生成基础知识梳理
+ output += "\\subsection{知识体验}\n\n"
+ output += "\\subsubsection{必讲例题}\n\n"
+ output += GenerateStudentBodyString(eblist,[],prodict,consecutivenumbering=False,answered=False)
+ output += "\\subsubsection{选讲例题}\n\n"
+ output += GenerateStudentBodyString(ealist,[],prodict,consecutivenumbering=False,answered=False)
+ output += "\\subsection{巩固新知}\n\n"
+ output += GenerateStudentBodyString(worklist,[],prodict,consecutivenumbering=False,answered=False)
+ if not remarks == "":
+ output += "\\subsection{备注}\n\n"
+ output += remarks + "\n\n"
+ if homeworkspaces:
+ output += "\\newpage\n\n"
+ output += "\\subsection{课后作业}\n\n"
+ output += GenerateStudentBodyString(homeworklist,[],prodict,consecutivenumbering=False,answered=False,spaceflag=homeworkspaces)
+ output += "\\newpage\n\n"
+ else:
+ output += "\\section{%s \ %s}\n\n"%(lessonid,lessonname)
+ output += "\\subsection{复习题}\n\n"
+ output += GenerateStudentBodyString(homeworklist,[],prodict,consecutivenumbering=False,answered = False,spaceflag=homeworkspaces)
+ output += "\\newpage\n\n"
+
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学") #替换掉模板中的姓名学号
+
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,output)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if XeLaTeXCompile(outputdir,notetitle+".tex"):
+ print("编译成功")
+ return 0 # 返回0
+ else:
+ print("编译失败")
+ return latex_data # 返回有错误的latex源代码
+
+
+def GenerateSingleLessonPreparation(lessonid, outputdir, adict, prodict, objdict, basicknowledgedict, homeworkspaces = False, filename = "default"): #根据adict生成每一课时的教案
+ output = ""
+ notetitle = lessonid + r" \ " + adict[lessonid]["name"]
+ if filename == "default":
+ filename = lessonid + adict[lessonid]["name"]+".tex"
+ else:
+ filename = filename+".tex"
+ outputfilepath = os.path.join(outputdir,filename)
+ lessonname = adict[lessonid]["name"]
+ objlist = adict[lessonid]["objects"]
+ bnlist = adict[lessonid]["basicknowledges"]
+ eblist = adict[lessonid]["examples_basic"]
+ ealist = adict[lessonid]["examples_adv"]
+ worklist = adict[lessonid]["work_inclass"]
+ homeworklist = adict[lessonid]["homework"]
+ remarks = adict[lessonid]["remarks"]
+
+ if not "复习" in lessonname:
+ if not objlist == []:
+ output += "\\section{课时目标}\n\n"
+ output += "\\begin{enumerate}\n\n"
+ for objid in objlist:
+ output += "\\item %s \\ %s \n\n"%(objid,objdict[objid]["content"])
+ output += "\\end{enumerate}\n\n" #生成学习目标
+ if not bnlist == []:
+ output += "\\section{双基梳理}\n\n"
+ basic_body = ""
+ for bnid in bnlist:
+ basic_body += "\\item %s\n\n"%basicknowledgedict[bnid]["content"]
+ output += "\\begin{enumerate}\n\n %s\n\n\\end{enumerate}\n\n"%basic_body #生成基础知识梳理
+
+ output += "\\section{知识体验}\n\n"
+ # output += "\\subsection{必讲例题}\n\n"
+ output += GenerateStudentBodyString(eblist,[],prodict,consecutivenumbering=False,answered=False)
+ # output += "\\subsection{选讲例题}\n\n"
+ # output += GenerateStudentBodyString(ealist,[],prodict,consecutivenumbering=False,answered=False)
+ output += "\\section{巩固新知}\n\n"
+ output += GenerateStudentBodyString(worklist,[],prodict,consecutivenumbering=False,answered=False)
+ if not remarks == "":
+ output += "\\section{备注}\n\n"
+ output += remarks + "\n\n"
+ if homeworkspaces:
+ output += "\\newpage\n\n"
+ output += "\\section{课后作业}\n\n"
+ output += GenerateStudentBodyString(homeworklist,[],prodict,consecutivenumbering=False,answered=False,spaceflag=homeworkspaces)
+ else:
+ output += "\\section{复习题}\n\n"
+ output += GenerateStudentBodyString(homeworklist,[],prodict,consecutivenumbering=False,answered = False,spaceflag=homeworkspaces)
+
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学") #替换掉模板中的姓名学号
+
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,output)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if XeLaTeXCompile(outputdir,filename):
+ print("编译成功")
+ return 0 # 返回0
+ else:
+ print("编译失败")
+ return latex_data # 返回有错误的latex源代码
+
+
+def getlineindex(string,filepath): #返回字符串在文本文件中第一次出现的行数, 若未出现则返回1
+ with open(filepath,"r",encoding = "u8") as f:
+ lines = f.readlines()
+ for l in lines:
+ if string in l:
+ return lines.index(l)+1
+ return 1
+
+def getCopy(): # 获取剪切板内容
+ t = pyperclip.paste().replace("\r","")
+ return t
+
+
+def setCopy(string): # 写入剪切板内容
+ pyperclip.copy(string)
+
+ # wc.OpenClipboard()
+ # wc.EmptyClipboard()
+ # wc.SetClipboardData(win32con.CF_UNICODETEXT, string)
+ # wc.CloseClipboard()
+
+def itemizeProblems(string): #将题号替换为\item
+ string_list = string.split("\n")
+ itemed_list = []
+ for line in string_list:
+ if not "&" in line or "tabular" in line or "array" in line or "cases" in line:
+ itemed_list.append(re.sub(r"(?:(?:^|\n)+[例]*[\s]*[0-9]+[\s]*[\.、\s]+|\[[\s]*例[\s]*[0-9]*[\s]*\][\s]*)","\\n\\\\item ",line))
+ else:
+ itemed_list.append(line)
+ string = "\n".join(itemed_list)
+ return string
+
+def RefinePunctuations(raw_string):
+ puctuationsfulltosemi = {" ": " ","。": ". ",".": ". ",",": ", ",":": ": ",";": "; ","(": "(",")": ")","?": "? ","“": "``","”": "''", "【": "[", "】": "]", "!": "!"}
+ string = raw_string.strip()
+ for s in puctuationsfulltosemi:
+ string = re.sub(s,puctuationsfulltosemi[s],string) #将部分全角标记替换为半角
+ return string
+
+def RefineMathpix(raw_string): # 进一步修改mathpix得到的字符串
+ puctuationsfulltosemi = {" ": " ","。": ". ",".": ". ",",": ", ",":": ": ",";": "; ","(": "(",")": ")","?": "? ","“": "``","”": "''", "【": "[", "】": "]"}
+ replacestrings = {r"\\overparen": r"\\overset\\frown", "eqslant": "eq", r"\\vec": r"\\overrightarrow ", r"\\bar": r"\\overline", r"\\lim": r"\\displaystyle\\lim", r"\\sum":r"\\displaystyle\\sum", r"\\prod":r"\\displaystyle\\prod", r"\\mid":"|", r"\^\{\\prime\}":"'",r"e\^":r"\\mathrm{e}^",r"/\s*/":r"\\parallel "}
+ wrongrecog = {"雉":"锥","[粗秿]圆":"椭圆","投郑":"投掷","抛郑":"抛掷","范目":"范围","揷":"插","末见":"未见","末成":"未成","针角":"钝角","幕函数":"幂函数","末知":"未知","阀值":"阈值","祖[桓晅]":"祖暅","图象":"图像","末使用":"未使用","末来":"未来","竟赛":"竞赛"}
+ string = raw_string
+ string = re.sub(r"\\left([\.\(\[|])",lambda matchobj: "" if matchobj.group(1) == "." else matchobj.group(1),string) #删去括号前的\left标记
+ string = re.sub(r"\\right([\.\)\]|])",lambda matchobj: "" if matchobj.group(1) == "." else matchobj.group(1),string) #删去括号前的\right标记\
+ string = re.sub(r"\\left\\\{","\\{",string) #删去大括号前的\left标记
+ string = re.sub(r"\\right\\\}","\\}",string) #删去大括号前的\right标记
+ string = string.replace("\\left\\langle","\\langle").replace("\\right\\rangle","\\rangle")
+ for s in puctuationsfulltosemi:
+ string = re.sub(s,puctuationsfulltosemi[s],string) #将部分全角标记替换为半角
+ for s in replacestrings:
+ string = re.sub(s,replacestrings[s],string) #修改部分LaTeX命令成为惯用的
+ for s in wrongrecog:
+ string = re.sub(s,wrongrecog[s],string) #修改mathpix识别的一些常见错别字
+ string = RipTestPaperDesc(string)
+ string = re.sub(r"[\s]*(``|''|\}|\{) *",lambda matchobj: matchobj.group(1),string) #去除符号前后的空格
+ string = itemizeProblems(string) #将题号替换为\item
+ string = re.sub(r"\$\$","$",string) #行间公式替换为行内公式
+ string = re.sub(r"\$\s+\$"," ",string) #删除多余的$符号
+ string = re.sub(r"([,.:;?!])\$",lambda x:x.group(1)+" $",string) #标点和$符号分开
+ string = re.sub(r"\\left\\lvert\\,","|",string) #替换|的错误代码
+ string = re.sub(r"\\frac",r"\\dfrac",string) #替换frac为dfrac
+ string = re.sub(r"\n(?:A\.|\(A\))([\s\S]*?)(?:B\.|\(B\))([\s\S]*?)(?:C\.|\(C\))([\s\S]*?)(?:D\.|\(D\))([\s\S]*?)\n",lambda matchobj: "\n\\fourch{%s}{%s}{%s}{%s}\n"%(matchobj.group(1).strip(),matchobj.group(2).strip(),matchobj.group(3).strip(),matchobj.group(4).strip()),string+"\n\n") # 选择题的选择支处理
+ string = re.sub(r"[\.;](\}\{|\}\n)",lambda matchobj: matchobj.group(1),string) #去除选择题选项最末尾的句号或分号
+ string = re.sub(r"\n\s+","\n",string) #删除多余的回车
+ string = re.sub(r"\\q+uad","",string) #删除\quad,\qquad等
+ string = re.sub(r"\$\\q+uad\$","",string)#删除两边有$的\quad,\qquad等
+ string = re.sub(r"~","",string) #删除~
+ string = re.sub(r"\s*\([\s]{,10}\)",r"\\bracket{20}",string)
+ string = re.sub(r"\s*\\bracket\{20\}\s*\n",r"\\bracket{20}.\n",string)#行末无内容括号的处理
+ for i in range(2):
+ string = re.sub(r"(\^|_)\{([0-9a-zA-Z])\}",lambda matchobj: matchobj.group(1)+matchobj.group(2),string) #删除一些无用的大括号
+ for i in range(2):
+ string = re.sub(r"([0-9A-Z])\s+([0-9A-Z])",lambda matchobj: matchobj.group(1)+matchobj.group(2),string) #合并一些公式中的无效空格
+ for i in range(2):
+ string = re.sub(r"([\u4e00-\u9fa5])\s+([\u4e00-\u9fa5])",lambda matchobj: matchobj.group(1)+matchobj.group(2),string) #合并一些文本中的无效空格
+ string = re.sub(r"([CP])(_[^_\^]{,5}\^)",lambda x:r"\mathrm{"+x.group(1)+"}"+x.group(2),string) #处理排列数和组合数字母的正体
+ string = re.sub(r"([\^_])\{([-]{0,1})\\dfrac",lambda matchobj: matchobj.group(1) + "{" + matchobj.group(2) + "\\frac",string)
+ string = re.sub(r"ldots",r"cdots",string) #将ldots替换为cdots
+ string = re.sub(r"\\text\s*\{,\s*当\s*\}",", ",string)
+ string = re.sub(r"[\\{]*\\(begin|end)\{array\}(?:\{[rcl]*\}){0,1}",lambda matchobj: "\\" + matchobj.group(1) + "{cases}",string) #将分段函数的array环境改为cases环境
+ string = re.sub(r"([\u4e00-\u9fa5\$])[\s]*\n\\item",lambda matchobj: matchobj.group(1)+"\\blank{50}.\n\\item",string) #给中文或公式结尾的题目最后一行加上填空的空格.
+ string = re.sub(r"(是|为|(?:=\$))\s*([,.;\n])",lambda matchobj: matchobj.group(1) + "\\blank{50}" + ("." if matchobj.group(2) == "\n" else "") + matchobj.group(2),string) #给行中的题目需要的地方加上空格
+ string = re.sub(r"(有|为|(?:确定))([种条个])",lambda matchobj: matchobj.group(1) + "\\blank{50}" + matchobj.group(2),string) #给行中的题目需要的地方加上空格
+ if not "\\bracket" in string:
+ string = re.sub(r"([\u4e00-\u9fa5\$])(?:\\bracket\{20\})*[\.]*[\s]*\n\\fourch",lambda matchobj: matchobj.group(1)+"\\bracket{20}.\n\\fourch",string) #给中文或公式结尾的题目最后一行加上选择题的括号.
+ string = re.sub(r"\\bracket\{(\d*)\}\$",lambda matchobj: "$"+"\\bracket{"+matchobj.group(1)+"}",string)#交换出现在$之前的括号与$的位置
+ string = re.sub(r"(%[^\n]*)\\blank\{50\}\.",lambda matchobj:matchobj.group(1),string) #注释行不加\blank{50}
+ string = re.sub(r"\\blank\{50\}\.\s*\n\\fourch",r"\\bracket{20}."+"\n"+r"\\fourch",string) #选择题前的空格改为括号
+ string = re.sub(r"[\\\\]*\n(\(\d{1,2}\))(?:(?!\n)\s)*",lambda matchobj: "\\\\\n"+matchobj.group(1)+" ",string) #新一行的小题号回车前加上换行符
+ string = re.sub(r"\(([^\(\)]*(?:\\in|=|\\ge|\\le|\\ne|>|<|\\parallel|\\perp)[^\(\)]*)\)\$",lambda matchobj: "$($" + matchobj.group(1) + "$)",string) #公式最后的范围陈述的括号放到公式环境外
+ string = re.sub(r"\$\$\(","(",string) #删去上一步造成的多余双$
+ string = re.sub(r"\(\s\$","($",string) #删去首个括号内公式前的空格
+ string = re.sub(r"\(\\begin\{cases\}",r"\\begin{pmatrix}",string) #修改错误的\begin{cases})
+ string = re.sub(r"\\end\{cases\}\)",r"\\end{pmatrix}",string) #修改错误的(\end{cases}
+ string = SplitMathComma(string) #判断数学环境中的","是否需要用$ $分离, 如果需要则执行分离
+ string = MergeMathComma(string) #判断非数学环境中的","是否需要合并在一个数学环境中, 如果需要则执行合并
+ string = RefineCasesEnv(string) #美化cases环境
+ string = RefineChineseComma(string) #改顿号
+ string = RefineInterval(string) #改错误的dollars符号和括号的顺序
+ string = string.replace("$$"," ")
+ string = re.sub(r"\s+\\blank\{50\}",r"\\blank{50}",string)
+ string = re.sub(r"\s+\\bracket\{20\}",r"\\bracket{20}",string)
+
+
+ return string
+
+def RefineInterval(string):
+ newstring = string
+ matches = re.finditer(r"\+\\infty\$\)",newstring) #先处理右括号
+ poslist = [(match.start(),match.end()) for match in matches]
+ poslist.reverse() #从后往前逐一更改
+ for s,e in poslist:
+ leftdollar = newstring[:s].rfind("$")
+ leftbracket = leftdollar-1
+ lb = newstring[leftbracket]
+ if lb in ["[","("]: #如果上一个$前面是括号, 就交换这一对相应的$符号和括号
+ print("$"+lb+newstring[leftbracket+2:e-2] + ")$")
+ newstring = newstring[:leftbracket] + "$"+lb+newstring[leftbracket+2:e-2] + ")$" + newstring[e:]
+ matches = re.finditer(r"\(\$-\\infty",newstring)
+ poslist = [(match.start(),match.end()) for match in matches]
+ poslist.reverse() #从后往前逐一更改
+ for s,e in poslist:
+ # print(s,e)
+ rightdollar = newstring[e:].find("$")+e
+ rightbracket = rightdollar+1
+ rb = newstring[rightbracket]
+ if rb in ["]",")"]:
+ print("$("+newstring[s+2:rightbracket-1]+rb+"$")
+ newstring = newstring[:s]+"$("+newstring[s+2:rightbracket-1]+rb+"$"+newstring[rightbracket+1:]
+ newstring = newstring.replace("($-\\infty$, $+\\infty$)","$(-\\infty, +\\infty)$")
+ return newstring
+
+def RefineChineseComma(string): #顿号如果在数学环境中, 则在两侧加上$符号
+ CommaPositions = [match.start() for match in re.finditer("、",string)]
+ CommaPositions.reverse()
+ for pos in CommaPositions:
+ if string[:pos].count("$") % 2 == 1:
+ string = string[:pos].rstrip() + "$、$" + string[(pos+1):].lstrip()
+ return string
+
+def SplitMathComma(string): #判断数学环境中的","是否需要用$ $分离, 如果需要则执行分离
+ UnsplittedPairs = [(r"[\(\[]",r"[\)\]]"),(r"\{",r"\}"),(r"\\begin\{cases\}",r"\\end\{cases\}")]
+ lmatter,rmatter = (string,"")
+ while "," in lmatter:
+ pos = lmatter.rfind(",")
+ if lmatter[:pos].count("$") % 2 == 1:
+ stringtemp = lmatter+rmatter
+ start = stringtemp[:pos].rfind("$")
+ end = stringtemp[pos:].find("$")+pos #寻找,两侧的数学环境 stringtemp[start:end+1]
+ locallmatter = stringtemp[start+1:pos]
+ localrmatter = stringtemp[pos+1:end]
+ tosplit = True
+ for p1,p2 in UnsplittedPairs:
+ p1l = SubstringOccurence(p1,locallmatter)
+ p1r = SubstringOccurence(p1,localrmatter)
+ p2l = SubstringOccurence(p2,locallmatter)
+ p2r = SubstringOccurence(p2,localrmatter)
+ if len(p1l)>0 and len(p2l) == 0:
+ tosplit = False
+ if len(p2r)>0 and len(p1r) == 0:
+ tosplit = False
+ if len(p1l)*len(p2l)>0:
+ if p1l[-1]>p2l[-1]:
+ tosplit = False
+ if len(p1r)*len(p2r)>0:
+ if p1r[0]>p2r[0]:
+ tosplit = False
+ if len(SubstringOccurence(r"(?:=|\\ge|\\le|\\ne|\\in|>|<|\\parallel|\\perp)",locallmatter))*len(SubstringOccurence(r"(?:=|\\ge|\\le|\\ne|\\in|>|<|\\parallel|\\perp)",localrmatter)) == 0:
+ tosplit = False
+ if tosplit:
+ rmatter = ", $"+lmatter[pos+1:].lstrip()+rmatter.lstrip()
+ lmatter = lmatter[:pos]+"$"
+ else:
+ rmatter = lmatter[pos-1:]+rmatter
+ lmatter = lmatter[:pos-1]
+ else:
+ rmatter = lmatter[pos-1:]+rmatter
+ lmatter = lmatter[:pos-1]
+ return lmatter+rmatter
+
+def MergeMathComma(string): #判断非数学环境中的","是否需要合并在一个数学环境中, 如果需要则执行合并
+ UnsplittedPairs = [(r"[\(\[]",r"[\)\]]"),(r"\{",r"\}"),(r"\\begin\{cases\}",r"\\end\{cases\}")]
+ lmatter,rmatter = (string,"")
+ while not [item for item in re.finditer(r"\$,\s*\$",lmatter)] == []:
+ CommaPos = [(item.start(),item.end()) for item in re.finditer(r"\$,\s*\$",lmatter)]
+ ThePos = CommaPos[-1]
+ tempstring = lmatter+rmatter
+ lpos = tempstring[:ThePos[0]].rfind("$")
+ rpos = tempstring[ThePos[1]+1:].find("$") + ThePos[1] + 1
+ locallmatter = tempstring[lpos+1:ThePos[0]]
+ localrmatter = tempstring[ThePos[1]:rpos]
+ tomerge = False
+ for p1,p2 in UnsplittedPairs:
+ if len(SubstringOccurence(p1,locallmatter))-len(SubstringOccurence(p2,locallmatter)) == 1 and len(SubstringOccurence(p2,localrmatter))-len(SubstringOccurence(p1,localrmatter)) == 1:
+ tomerge = True
+ if tomerge:
+ rmatter = ", "+lmatter[ThePos[1]:]+rmatter
+ lmatter = lmatter[:ThePos[0]]
+ else:
+ rmatter = lmatter[ThePos[0]:]+rmatter
+ lmatter = lmatter[:ThePos[0]]
+ return lmatter+rmatter
+
+def ReverseMatchBrackets(string):#从最后开始寻找第一组匹配的括号, 返回左括号位置与右括号位置组成的数对
+ if not ")" in string:
+ return -1
+ else:
+ endpos = string.rfind(")")
+ lmatter = string[:endpos]
+ startpos = -1
+ for i in range(len(lmatter)-1,-1,-1):
+ templmatter = lmatter[i:]
+ if templmatter.count("(")-templmatter.count(")") == 1:
+ startpos = i
+ break
+ return (startpos,endpos)
+
+def RefineCasesEnv(string): # 美化cases环境
+ CasesPos = [(item.start(1),item.end(1)) for item in re.finditer(r"\\begin\{cases\}(.*?)\\end\{cases\}",string, re.DOTALL)] #找到所有的cases环境
+ CasesPos.reverse()
+ for start,end in CasesPos:
+ lmatter = string[:start]
+ rmatter = string[end:]
+ content = string[start:end]
+ lines = [item.strip() for item in content.split(r"\\")]
+ newlist = []
+ for line in lines:
+ newline = line
+ if line[-1] == ")":
+ lbracket,rbracket = ReverseMatchBrackets(line)
+ if re.findall(r"(?:=|<|>|\\ge|\\le|\\in)",line[lbracket:rbracket]) != []:
+ newline = line[:lbracket] + ", " + line[lbracket+1:rbracket] + "," #用逗号代替表示范围的括号
+ newline = re.sub(r",",",&",newline)
+ newlist.append(newline)
+ newcontent = r"\\".join(newlist)
+ newcontent = re.sub(r",[\s]*&[\s]*\\",r",\\",newcontent)
+ newcontent = re.sub(r",[\s]*&[\s]*$",",",newcontent) #给逗号添加&分栏符, 并去除无效的&
+ string = lmatter + newcontent + rmatter
+ string = re.sub(r"&[,\s]&","& ",string)
+ return string #返回处理后的字符串
+
+def RipTestPaperDesc(string):
+ string = re.sub(r"[一二三四五六][、\.\s]+(?:(?:填空)|(?:选择)|(?:解答))题","",string) #去除没有标分数的填空选择解答题描述
+ string = re.sub(r"\(本[大]*题满分[^\)]*\d+\s*分\)","",string) #去除带括号的分数描述
+ string = re.sub(r"本[大]*题[^\n]*?步骤\.{0,1}","",string) #去除解答题需要写出步骤提示
+ string = re.sub(r"\({0,1}本[大]*题共有[^\n]*\d+\s*分\.{0,1}\){0,1}","",string) #去除解答题单题分数提示
+ string = re.sub(r"每题有[^\n]*涂黑\.{0,1}","",string) #去除选择题提示
+ string = re.sub(r"考生[^\n]*结果\.*","",string) #去除作答提示
+ return string
+
+def SubstringOccurence(regex,string): #生成regex在string中出现的所有位置
+ poslist = [item.start() for item in re.finditer(regex,string)]
+ return poslist
+
+def select_grade_from_pro_dict(prodict,grades):
+ if len(grades) == 0:
+ return prodict
+ else:
+ gradelist = []
+ for g in grades:
+ if not "届" in g:
+ gradelist.append(g+"届")
+ else:
+ gradelist.append(g)
+ adict = prodict.copy()
+ for id in prodict:
+ raw_usages = prodict[id]["usages"].copy()
+ new_usages = []
+ for u in raw_usages:
+ for g in gradelist:
+ if g in u:
+ new_usages.append(u)
+ adict[id]["usages"] = new_usages.copy()
+ return adict
+
+def GenerateSingleLessonNotefromMariaDB(cursor,id,notesdict,templatepath,outputfilepath,misc,consecutivenumbering = False): #20240415版讲义生成
+ notetitle = id + r" \ " + notesdict["notes"][id]["name"]
+ structure = notesdict["structures"][id[0].upper()]["structure"]
+ note_contents = notesdict["notes"][id]
+ output = ""
+ sections_list = []
+ problems_list = []
+ for key in structure:
+ if not len(note_contents[key]) == 0:
+ sections_list.append(key)
+ problems_list.append(",".join(note_contents[key]))
+ rawoutput = GenerateSectionBodyStringfromMariaDB(cursor = cursor, problems=problems_list,sectiontitles=sections_list,misc=misc,consecutivenumbering= consecutivenumbering)
+ paragraphs = [p for p in rawoutput.split("\\section") if not p.strip() == ""]
+ for item in paragraphs:
+ sectionkey, content = re.findall(r"\{([\S]*)\}\n([\S\s]*)$",item)[0]
+ if not len(paragraphs) == 1:
+ output += "\\section{" + structure[sectionkey]["name"] + "}\n\n"
+ # if not structure[sectionkey]["spaceflag"] or answered:
+ # content = re.sub(r"\\vspace[\*]?\{[\S]*\}","\n",content)
+ output += content + "\n\n"
+
+ latex_raw = ReadTextFile(templatepath)
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学") #替换掉模板中的姓名学号
+
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,output)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if misc["编译单个文件"] == True:
+ outputdir,filename = os.path.split(outputfilepath)
+ print(f"{filename}编译中...")
+ if XeLaTeXCompile(outputdir,filename):
+ print("编译成功")
+ else:
+ print("编译失败")
+ return latex_data # 返回有错误的latex源代码
+
+def GenerateSingleLessonNote(id,notesdict,metadict,templatepath,outputfilepath,consecutivenumbering = False, answered = False): #20231215版讲义生成
+ notetitle = id + r" \ " + notesdict["notes"][id]["name"]
+ structure = notesdict["structures"][id[0].upper()]["structure"]
+ note_contents = notesdict["notes"][id]
+ output = ""
+ sections_list = []
+ problems_list = []
+ for key in structure:
+ if not len(note_contents[key]) == 0:
+ sections_list.append(key)
+ problems_list.append(",".join(note_contents[key]))
+ rawoutput = GenerateStudentBodyString(problems=problems_list,sectiontitles=sections_list,pro_dict=metadict,consecutivenumbering= consecutivenumbering, answered= answered, spaceflag = True)
+ paragraphs = [p for p in rawoutput.split("\\section") if not p.strip() == ""]
+ for item in paragraphs:
+ sectionkey, content = re.findall(r"\{([\S]*)\}\n([\S\s]*)$",item)[0]
+ if not len(paragraphs) == 1:
+ output += "\\section{" + structure[sectionkey]["name"] + "}\n\n"
+ if not structure[sectionkey]["spaceflag"] or answered:
+ content = re.sub(r"\\vspace[\*]?\{[\S]*\}","\n",content)
+ output += content + "\n\n"
+
+ latex_raw = ReadTextFile(templatepath)
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学") #替换掉模板中的姓名学号
+
+ if sys.platform == "darwin": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,output)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ outputdir,filename = os.path.split(outputfilepath)
+ print(f"{filename}编译中...")
+ if XeLaTeXCompile(outputdir,filename):
+ print("编译成功")
+ return latex_data # 返回0
+ else:
+ print("编译失败")
+ return latex_data # 返回有错误的latex源代码
+
+
+def getUnit(n): # 返回0-9的数字对应的单元名
+ unitlist = ["暂无对应","第一单元","第二单元","第三单元","第四单元","第五单元","第六单元","第七单元","第八单元","第九单元"]
+ if 0<=n<=9:
+ return unitlist[n]
+
+def getUnitNumber(string):
+ unitlist = ["暂无对应","第一单元","第二单元","第三单元","第四单元","第五单元","第六单元","第七单元","第八单元","第九单元"]
+ if string in unitlist[1:]:
+ return unitlist.index(string)
+
+def ExtractProblemIDs(paperdict,pro_dict):#从备课组材料的每一张讲义的dict(paperdict)中提取题号
+ output = []
+ for key in paperdict.keys():
+ if type(paperdict[key]) == list:
+ suslist = paperdict[key]
+ flag = True
+ for id in suslist:
+ if not id in pro_dict:
+ flag = False
+ break
+ if flag:
+ for id in suslist:
+ output.append(id)
+ return(output)
+
+
+def ParseZipname(zipfilename): #小闲平台的zip文件中获得试卷编号, 返回试卷编号字符串
+ xiaoxianpid = re.findall(r"^[可选_]*(\d*?)_",os.path.split(zipfilename)[1])
+ return xiaoxianpid[0]
+
+def FindFile(dir,filename): #在指定目录及子目录下寻找特定文件名的文件, 返回文件所在的路径列表
+ pathlist = []
+ for path,m,filenames in os.walk(dir):
+ if filename in filenames:
+ pathlist.append(path)
+ return pathlist
+
+def FindPaper(xiaoxianpid, answersheetpath): #根据小闲的试卷编号和答题纸对应json的根目录寻找题库的试卷编号,届别,题号, 返回(题库试卷编号,届别,题号列表), 如果未找到则返回False
+ answersheetpathlist = FindFile(answersheetpath,"答题纸对应.json")
+ foundpid = False
+ for dir in answersheetpathlist:
+ filepath = os.path.join(dir,"答题纸对应.json")
+ anssheetjson = load_dict(filepath)
+ if xiaoxianpid in anssheetjson:
+ foundpid = True
+ grade = "20"+re.findall(r"\d{2}届",dir)[0]
+ pid = anssheetjson[xiaoxianpid]["id"]
+ notesjson = load_dict(os.path.join(dir,"校本材料.json"))
+ if not "idlist" in anssheetjson[xiaoxianpid]:
+ idlist = []
+ for part in anssheetjson[xiaoxianpid]["parts"]:
+ idlist += notesjson["notes"][pid][part].copy()
+ else:
+ idlist = anssheetjson[xiaoxianpid]["idlist"]
+ if "marks" in anssheetjson[xiaoxianpid]:
+ marks = anssheetjson[xiaoxianpid]["marks"]
+ else:
+ marks = []
+ if "exclude" in anssheetjson[xiaoxianpid]:
+ excludejson = anssheetjson[xiaoxianpid]["exclude"]
+ else:
+ excludejson = {}
+ break
+ if foundpid:
+ return(pid,grade,idlist,marks,excludejson)
+ else:
+ return False
+
+def CheckPaperType(filepath,filename): #根据filepath(通常是小闲的zip解压出的目录)和filename(通常是"小题分_按学号(数学).xlsx")检测试卷类型, 未找到该文件则返回False, 找到文件且是日常试卷返回"日常卷", 找到文件且不是日常试卷返回"考试卷"
+ statsfilepathlist = FindFile(filepath,filename)
+ if statsfilepathlist == []:
+ return False
+ else:
+ dir = statsfilepathlist[0]
+ dfcurrent = pd.read_excel(os.path.join(dir,filename))
+ if re.findall(r"第\d*步",str(dfcurrent.loc[1,:])) == []:
+ return "日常卷"
+ else:
+ return "考试卷"
+
+def generateColIndexandMarks(filepath,statsfilename,paperinfo): #根据filepath(是一个有statsfilename的文件夹列表)中第一个路径中的数据文件, statsfilename数据文件名, 及paperinfo(FindPaper返回的结果)寻找excel文件中有效的列的位置和相应的满分分数
+ dir = filepath[0]
+ dfcurrent = pd.read_excel(os.path.join(dir,statsfilename))
+ validcols = []
+ for i in range(len(dfcurrent.columns)):
+ colname = str(dfcurrent.iloc[1,i])
+ if ("单选" in colname or "填空" in colname or "主观" in colname or "步" in colname) and re.findall("[ABCD]",colname) == []:
+ validcols.append(i)
+ for col in range(len(validcols)-1,-1,-1):
+ colname = str(dfcurrent.iloc[1,validcols[col]])
+ if "主观" in colname:
+ colname_main = re.findall(r"^([\d\.]*)[\($]",colname[2:])[0]
+ t = [dfcurrent.iloc[1,c] for c in validcols[col+1:]]
+ t = str(t)
+ if colname_main in t:
+ validcols.pop(col)
+ if paperinfo[3] == []:
+ marks = [1] * len(validcols)
+ else:
+ marks = paperinfo[3]
+ if len(marks) == len(validcols):
+ return (validcols,marks)
+ else:
+ return False
+
+
+def CheckValidity(classpath,gradename,threshold): #根据文件夹classpath, 年级名gradename和提交比例threshold, 检测提交人数是否不小于threshold, 返回(班级名, 提交是否有效)
+ classname_raw = re.findall(r"(高[一二三])(\d*?)班",classpath)[0]
+ classname = gradename + classname_raw[0] + classname_raw[1].zfill(2) + "班"
+ df = pd.read_excel(os.path.join(os.path.split(classpath)[0],"学科总体分析.xlsx"))
+ totalstudents = df.loc[2,df.columns[1]]
+ validstudents = df.loc[2,df.columns[2]]
+ classvalidflag = False
+ if threshold * totalstudents < validstudents:
+ print(f"{classname} 有效, 共 {totalstudents} 人, 提交 {validstudents} 人")
+ classvalidflag = True
+ else:
+ print(f"!!! {classname} 无效, 共 {totalstudents} 人, 提交 {validstudents} 人")
+ return (classname,classvalidflag)
+
+def generateIDtoUsageCorrespondence(idlist,validcols,names): #根据idlist(题库ID列表), validcols(有效列位置列表), names(题目名称列表)自动生成一个字典, 键值为题库ID, 内容为该题对应的列位置列表
+ corr_dict = {}
+ for i in range(len(idlist)):
+ ind = i+1
+ collist = []
+ for j in range(len(validcols)):
+ n = names[j]
+ if not "步" in n:
+ name = re.findall(r"^([^\(]*)",n)[0]
+ else:
+ name = re.findall(r"^([^第]*)",n)[0]
+ if ind == getindex(name):
+ collist.append(j)
+ corr_dict[idlist[i]] = collist
+ return corr_dict
+
+def CalculateUsages(statsfilepathlist,statsfilename,gradename,threshold,marks,correspondence_dict,validcols,date,exclude = {}): #根据统计数据所在的路径,文件名,年级,阈值,分数列表和题号列数(0-len(validcols))对应字典,以及原excel文件中的有效列位置validcols, 日期date, 生成usages的metadata.txt文件的内容, 如果有正确率大于1的则返回False
+ output = "usages\n\n\n"
+ validflag = True
+ marks = [int(mark) for mark in marks]
+ for dir in statsfilepathlist:
+ classname, valid = CheckValidity(dir,gradename,threshold)
+ if valid:
+ dfcurrent = pd.read_excel(os.path.join(dir,statsfilename))
+ means = dfcurrent.iloc[2:-2,validcols].mean()/marks
+ if max(means)>1:
+ print("满分数据有误!!!")
+ validflag = False
+ else:
+ means_out = [f"{t:.3f}" for t in means]
+ for id in correspondence_dict:
+ cols = correspondence_dict[id]
+ if not len(cols) == 0 and not (id in exclude and classname[-3:] in generate_classid(exclude[id])):
+ diffs = "\t".join([means_out[u] for u in cols])
+ usages = f"{date}\t{classname}\t{diffs}"
+ output += f"{id}\n{usages}\n\n\n"
+ if validflag:
+ return output
+ else:
+ return False
+
+
+def getindex(string,pos = 2):
+ para = string.split(".")
+ return int(para[pos-1])
+
+def generateListOfIDandContent(string): #根据标准的讲义LaTeX源码字符串(可能含答案)生成一个list, 每一项是一个(id,id后内容)的tuple
+ return re.findall(r"\((\d{6})\)([\s\S]*?)(?:(?:\\item)|(?:\\end\{enumerate\}))",string)
+
+def CountEffectiveBraces(string): #在string中统计有效的(LaTeX的\{和\}不算作有效)的大括号的个数, 返回(左大括号个数, 右大括号个数)
+ string_ref = re.sub(r"\\[\{\}]"," ",string)
+ return (string_ref.count("{"),string_ref.count("}"))
+
+def generateAnswerTex(content,anspreamble = "答案: \\textcolor{red}{"): #在从anspreamble开始的字符串后找到答案字符串(anspreamble中的字符不算)
+ startpos = content.index(anspreamble) + len(anspreamble) - 1
+ endpos = startpos + 1
+ l,r = CountEffectiveBraces(content[startpos:endpos])
+ while not l == r:
+ endpos += 1
+ l,r = CountEffectiveBraces(content[startpos:endpos])
+ return content[startpos+1:endpos-1].strip()
+
+def generateAnswerList(string,anspreamble = "答案: \\textcolor{red}{"): #从LaTeX源代码string中分析题号与对应的答案
+ alist = generateListOfIDandContent(string)
+ anslist = []
+ for a in alist:
+ id,content = a
+ if anspreamble in content:
+ anslist.append((id,generateAnswerTex(content,anspreamble)))
+ return anslist
+
+
+def unUnitted(idexp,database): #返回adict中未赋单元的id列表
+ idlist = generate_number_set(idexp)
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT ID FROM tagcorresp WHERE tagname REGEXP '第\\\\S*单元' or tagname = '暂无对应';")
+ unittedids = set([ret[0] for ret in mycursor.fetchall()])
+ ununittedids = sorted(list(set(idlist)-unittedids))
+ return ununittedids
+
+
+def AutoAssignTagNotoLaTeX(newlist,allsamelist,allrelatedlist,db):
+ output = "\\begin{enumerate}\n\n"
+ tagrecord = ""
+ print("正在生成简化题目的字典...")
+ treateddict = treat_dict(db)
+ print("简化题目字典生成完毕.")
+ for id in tqdm.tqdm(newlist):
+ samelist = findsru(id,allsamelist)
+ relatedlist = findsru(id,allrelatedlist)
+ if not samelist == []:
+ unittags = get_unit_tags(samelist[0],db)
+ elif not relatedlist == []:
+ unittags = get_unit_tags(relatedlist[0],db)
+ else:
+ simgroup = stringmaxsim(treateddict[id],treateddict,10)
+ unittags_raw = {}
+ for g in simgroup:
+ rid,sim = g
+ if not rid == id and sim > 0.75:
+ for t in get_unit_tags(rid,db):
+ if t in unittags_raw:
+ unittags_raw[t] += 1
+ else:
+ unittags_raw[t] = 1
+ if len(unittags_raw) == 0:
+ unittags = []
+ else:
+ unittags = [max(unittags_raw, key = lambda x: unittags_raw[x])]
+ tagnumbers = ""
+ for u in unittags:
+ tagnumbers += str(getUnitNumber(u))
+ output += f"\\item ({id})[{tagnumbers}] {get_problem_content(id,db)}\n\n"
+ tagrecord += f"{id}\t{tagnumbers}\n"
+ output += "\\end{enumerate}\n\n"
+ return (output,tagrecord)
+
+
+def UnitRectoMetadataText(data): #把data中的单元对应变为metadata.txt中的单元对应文本
+ lines = [l for l in data.split("\n") if l.strip() != ""]
+ output = "tags\n\n"
+ for l in lines:
+ id,unitno = l.split("\t")
+ units = ""
+ if not len(unitno.strip()) == 0:
+ for n in unitno:
+ units += f"{getUnit(int(n))}\n"
+ output += f"{id.zfill(6)}\n"
+ output += f"{units}\n\n"
+ return output
+
+def gitdiff(commitid1,commitid2,fileinrepo="题库0.3/Problems.json",tempfilepath = "临时文件/temp.json"): # 根据两个git 的commit id 比较某一个json文件的不同, 返回一个tuple, 第一项是新增key的变化, 第二项是同一个key下哪些字段变化了
+ os.system(f"git show {commitid1}:{fileinrepo} > {tempfilepath}")
+ json1 = load_dict("临时文件/temp.json")
+ os.system(f"git show {commitid2}:{fileinrepo} > {tempfilepath}")
+ json2 = load_dict("临时文件/temp.json")
+ os.remove("临时文件/temp.json")
+
+ diff_list = []
+ new_or_deleted_list = [id for id in json1.keys() if not id in json2.keys()] + [id for id in json2.keys() if not id in json1.keys()]
+ for id in [i for i in json1.keys() if i in json2.keys()]:
+ current_diff = []
+ for field in json1[id]:
+ if not json1[id][field] == json2[id][field]:
+ current_diff.append(field)
+ if not current_diff == []:
+ diff_list.append((id,current_diff.copy()))
+ return (new_or_deleted_list,diff_list)
+
+
+def CheckUsagesValidity(data): #检查使用记录数据是否合理(没有超过1的), 若有则返回1, 合理则返回0
+ diffs = re.findall(r"\s(\d+\.\d{3})[\s\n$]",data)
+ for d in diffs:
+ if float(d)>1:
+ print(f"有{d}, 数据有问题")
+ return 1
+ return 0
+
+def pairingbraces(string_raw,leftbracepos): #根据字符串中的左括号的位置寻找配对的右括号
+ types = {"(":"()","[":"[]","{":"{}"}
+ braces = "".join([types[i] for i in types])
+ string = string_raw
+ for i in braces:
+ string = string.replace("\\"+i," ") #去除LaTeX的括号
+ if not string[leftbracepos] in ("(","[","{"):
+ print("位置上的字符不是左括号")
+ return -1 #位置上不是括号, 返回-1
+ else:
+ braces = types[string[leftbracepos]]
+ lbrace = braces[0]
+ rbrace = braces[1]
+ count = 1
+ currentpos = leftbracepos + 1
+ while not currentpos == len(string):
+ if string[currentpos] == lbrace:
+ count += 1
+ elif string[currentpos] == rbrace:
+ count -= 1
+ if count == 0:
+ return currentpos # 返回配对的右括号位置
+ currentpos += 1
+ return -1 #未找到配对括号
+
+def InMathEnv(string,pos): #判断string的pos位置上的字符是否在math环境中
+ string = string.repalce("\$"," ")
+ if not string[pos] == "$" and string[:pos].count("$") % 2 == 1:
+ return True
+ else:
+ return False
+
+
+def MaskingMathEnv(string_raw): #把string_raw中数学环境中的符号都转为$后以新字符串的形式返回
+ string_raw = string_raw.replace(r"\$"," ")
+ countdollars = 0
+ string = ""
+ for i in range(len(string_raw)):
+ if string_raw[i] == "$":
+ countdollars = 1 - countdollars
+ if countdollars == 0:
+ string += string_raw[i]
+ else:
+ string += "$"
+ return string
+
+def MultiplechoicetoBlankFilling(string_raw): #把多选题的题干和选项转为填空题
+ firstchoicepos = re.search(r"(?:(?:four)|(?:two)|(?:one))ch",string_raw).span()[1]
+ headstring = string_raw[:firstchoicepos]
+ headstring = re.sub(r"\\bracket\{[\d]*\}",r"\\blank{50}",headstring)
+ headstring = re.sub(r"\n\\(?:(?:four)|(?:two)|(?:one))ch",r"\\\\\n",headstring)
+ string = string_raw[firstchoicepos:]
+ choices = []
+ while "{" in string:
+ endpos = pairingbraces(string,0)
+ choices.append(string[1:endpos])
+ string = string[endpos+1:]
+ output = ""
+ for i in range(len(choices)):
+ output += f"\\textcircled{{{i+1}}} {choices[i]}; "
+ output = headstring + output[:-2]+"."
+ return output
+
+def ExtractIDList(filepath): #从文件获取题目序号和ID的对应, 返回一个列表, 列表中的每个tuple都是(文件中的题目序号(int),六位题号(string))
+ if filepath[-4:] == ".pdf":
+ data = parsePDF(filepath)
+ idlist = re.findall(r"(\d+)\.[\s\n]*\((\d{6})\)",data)
+ else:
+ data = ReadTextFile(filepath)
+ enumerateBlocks = [item.strip() for item in re.findall(r"\\begin\{enumerate\}([\s\S]*?)\\end\{enumerate\}",data)]
+ for i in range(len(enumerateBlocks)):
+ if not enumerateBlocks[i].startswith("\\setcounter"):
+ enumerateBlocks[i] = "\\setcounter{enumi}{0}\n\n" + enumerateBlocks[i]
+ idlist = []
+ for item in enumerateBlocks:
+ enumilist = [i.span() for i in re.finditer(r"\\setcounter\{enumi\}\{(\d+)\}",item)]
+ enumilist.append((len(item),len(item)))
+ for i in range(len(enumilist)-1):
+ indstring = item[enumilist[i][0]:enumilist[i][1]]
+ ind = int(re.findall(r"\d+",indstring)[0])
+ bodylist = re.findall(r"\((\d{6})\)",item[enumilist[i][1]:enumilist[i+1][0]])
+ for id in bodylist:
+ ind += 1
+ idlist.append((str(ind),id))
+ idlist = [(int(id[0]),id[1]) for id in idlist]
+ return idlist
+
+def ExportIDList(filepath): #从.tex或.pdf文件获取题目序号和ID的对应(每一行每个题号分别对应第1,2,3...题, 空缺的题号的ID用999999表示), 返回题号字符串
+ idlist = ExtractIDList(filepath)
+ output = ""
+ currentoutput = []
+ for i in range(len(idlist)):
+ item = idlist[i]
+ if i == 0:
+ currentind = item[0]
+ currentoutput +=["999999"]*(item[0]-1)
+ currentoutput.append(item[1])
+ elif item[0] >= currentind + 1:
+ currentoutput+=(["999999"]*(item[0]-currentind-1))
+ currentind = item[0]
+ currentoutput.append(item[1])
+ else:
+ output += ",".join(currentoutput)+"\n\n"
+ currentoutput = []
+ currentind = item[0]
+ currentoutput +=["999999"]*(item[0]-1)
+ currentoutput.append(item[1])
+ output += ",".join(currentoutput)+"\n\n"
+ return output
+
+def makedir(dirpath): #递归创建文件夹
+ dirpath = dirpath.strip().replace("\\","/")
+ dirlist_raw = dirpath.split("/")
+ if ":" in dirlist_raw[0]:
+ dirlist_raw[1] = dirlist_raw[0] + "/" + dirlist_raw[1]
+ dirlist = dirlist_raw[1:].copy()
+ else:
+ dirlist = dirlist_raw.copy()
+ for i in range(len(dirlist)):
+ if not i == 0:
+ dirlist[i] = dirlist[i-1] + "/" + dirlist[i]
+ for dpath in dirlist:
+ try:
+ os.mkdir(dpath)
+ except:
+ pass
+ return dirlist
+
+
+def TitleIDStringtoTupleList(raw_string): #将类json的标题与题号对应变为(标题,题号字符串)的tuple之后组成list
+ corresponding_list = []
+ raw_string = RefinePunctuations(raw_string).replace("{","").replace("}","")
+ raw_list = [re.sub(r"(?:(?:\s)|(?:,\s*$))","",t) for t in raw_string.split("\n") if not t.strip() == ""]
+ for raw_item in raw_list:
+ item = raw_item.replace('"','').replace(";","")
+ sep = item.find(":")
+ title = item[:sep]
+ content = item[sep+1:]
+ corresponding_list.append((title,content))
+ return corresponding_list
+
+def TitleIDTupleListtoString(correspoinding_list): #将(标题,题号字符串)的tuple组成的list转化为更方便阅读的字符串, 前面有编号
+ output_string = ""
+ count = 0
+ for corresp in correspoinding_list:
+ count += 1
+ output_string += f"{count}. {corresp[0]}: {corresp[1]}\n"
+ return output_string
+
+def startfile(filepath): #跨平台打开文件或文件夹
+ isfile = os.path.isfile(filepath)
+ osname = sys.platform
+ if osname == "win32":
+ if not isfile:
+ ret = subprocess.Popen(["explorer",filepath])
+ if isfile:
+ ret = subprocess.Popen(["start",filepath], shell=True)
+ elif osname == "darwin":
+ ret = subprocess.Popen(["open",filepath])
+ elif osname == "linux":
+ ret = subprocess.Popen(["xdg-open",filepath])
+ return ret
+
+
+def PaintRedAnswers(string,prodict): #将prodict中已有的答案标红色
+ output = string
+ IDandContentList = generateListOfIDandContent(output)
+ for id,content in IDandContentList:
+ raw_string = id+")"+content
+ # u = output.index(raw_string)
+ if "textcolor{blue}" in raw_string:
+ new_ans = generateAnswerTex(content,anspreamble="\\textcolor{blue}{")
+ raw_ans = prodict[id]["ans"]
+ if new_ans == raw_ans:
+ sub_string = raw_string.replace("textcolor{blue}","textcolor{red}")
+ output = output.replace(raw_string,sub_string)
+ elif raw_ans == "" and new_ans != "暂无答案":
+ print(f"{id} 已有答案, 请运行提取答案控件以获取新录入的答案")
+ elif new_ans != "暂无答案":
+ print(f"{id} 新答案 {new_ans} 和原答案 {raw_ans} 有所不同, 请仔细检查")
+ return output
+
+
+def usagelistdifference(lista,listb): #比较两个usage数组的最大差别(绝对值), 数组长度不一致则返回1
+ if len(lista) != len(listb):
+ return 1
+ else:
+ maxdiff = 0
+ for i in range(len(lista)):
+ maxdiff = max(abs(float(lista[i])-float(listb[i])),maxdiff)
+ return maxdiff
+
+
+def RefineExclude(excludejson): #将excludejson的key变为6位题号
+ newjson = {}
+ for key in excludejson:
+ newjson[key.zfill(6)] = excludejson[key]
+ return newjson
+
+
+def ChooseIDsByUsageInterval(startdate,enddate,interval,classregex,database): #返回根据条件选出的题号字典及对应小题, 需要留意的记录长度不一的题号, 以及所有使用过的题号
+ validusages = []
+ usedproblems = []
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ sql = "SELECT ID,date,classname,diff FROM usages;"
+ mycursor.execute(sql)
+ usage_list = mycursor.fetchall()
+ for id,date,classname,diff_raw in usage_list:
+ if not date is None and startdate <= date <= enddate and re.findall(classregex,classname) != []:
+ validusages.append((id,date,classname,json.loads(diff_raw)))
+ if not id in usedproblems:
+ usedproblems.append(id)
+ cautionids = [] #使用记录长度不一的题目id
+ chosenproblems = {} #使用记录的平均值在thresholds中的题目id(键值为题号id, 内容为小题号, 内容为空表示没有小题)
+ for id in usedproblems:
+ usages = [u[1:] for u in validusages if u[0] == id]
+ subproblems = [len(u[2]) for u in usages]
+ if max(subproblems) != min(subproblems):
+ cautionids.append(id)
+ print(f"!!! 题号 {id} 的使用记录中小题数目不全相同, 请检查")
+ else:
+ for i in range(subproblems[0]):
+ difflist = [float(u[2][i]) for u in usages]
+ diffmean = np.mean(difflist)
+ if interval[0] <= diffmean <= interval[1]:
+ if subproblems[0] == 1:
+ chosenproblems[id] = []
+ elif not id in chosenproblems:
+ chosenproblems[id] = [i+1]
+ else:
+ chosenproblems[id].append(i+1)
+ mydb.close()
+ return (chosenproblems,cautionids,usedproblems) #返回根据条件选出的题号字典及对应小题, 需要留意的记录长度不一的题号, 以及所有使用过的题号
+
+
+def getAllIDsExp(database,obsincluded = True): #获取题库中所有题目的id(含:,)字符串, 不含
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = database)
+ mycursor = mydb.cursor()
+ if obsincluded:
+ mycursor.execute("SELECT ID FROM problems;")
+ else:
+ mycursor.execute("SELECT ID FROM problems WHERE NOT content REGEXP 'OBS';")
+ idlist = [ret[0] for ret in mycursor.fetchall()]
+ return generate_exp(idlist)
+
+def parseRemark(string): #从一行remark字符串中分出日期和内容, 返回 日期,内容 元组
+ remark_raw = string.replace(r"\\","").strip()
+ date_raw_list = re.findall(r"^\({0,1}\d{8}\){0,1}",remark_raw)
+ if len(date_raw_list) > 0:
+ date_raw = date_raw_list[0]
+ date = re.sub(r"[\(\)]","",date_raw)
+ remark = re.sub(date,"",remark_raw).strip().replace("()","")
+ else:
+ date = "00000000"
+ remark = remark_raw
+ return date,remark
+
+
+if __name__ == "__main__":
+ print("数据库工具, import用.")
\ No newline at end of file
diff --git a/工具v4/tool_panel.py b/工具v4/tool_panel.py
new file mode 100644
index 00000000..8a2a1353
--- /dev/null
+++ b/工具v4/tool_panel.py
@@ -0,0 +1,180 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QMainWindow, QVBoxLayout
+from Ui_tool_panel import Ui_MainWindow
+from PySide6.QtCore import Signal
+from 寻找空闲题号 import MyWindow_kxth
+from 生成直方图代码 import MyWindow_hist
+from 文本转换处理 import MyWindow_wbzh
+from 系列讲义生成 import MyWindow_jysc
+from 讲义结构与内容录入 import MyWindow_jglr
+from 答题纸对应 import MyWindow_dtlr
+from 获取题号 import MyWindow_hqth
+from 单一题号转为图片 import MyWindow_sctp
+from 指定题号编译pdf import MyWindow_xtby
+from 根据正确率选择题号 import MyWindow_ndsx
+from 关键字筛选题号 import MyWindow_sxth
+from 添加关联题目 import MyWindow_tjgl
+from 批量收录新题 import MyWindow_bdsl
+from 下载小闲答题数据 import MyWindow_xxxz
+from 新增基础知识梳理 import MyWindow_tjjc
+from 共享使用记录 import MyWindow_gxsy
+from 文件或文本框提取答案 import MyWindow_tqda
+from 单元挂钩 import MyWindow_dygg
+from 统考数据导入 import MyWindow_tkdr
+from 手动统计结果导入 import MyWindow_sddr
+from 获取小闲平台使用数据 import MyWindow_xxdr
+from 题目内容直接编辑 import MyWindow_bjtm
+from 修改metadata import MyWindow_tjzd
+from database_tools_2 import *
+
+class MyWindow(QMainWindow,Ui_MainWindow):
+ sendDBname = Signal(str) #定义一个字符串型信号
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.SelectTIKUTEST()
+ self.database = "tikutest"
+ self.pushButton_tiku.clicked.connect(self.SelectTIKU)
+ self.pushButton_tikutest.clicked.connect(self.SelectTIKUTEST) #以上为设置当前数据库的两个按钮
+
+ self.layout_kxth = QVBoxLayout(self.tab_kxth)
+ self.kxth = MyWindow_kxth(self.database)
+ self.layout_kxth.addWidget(self.kxth) #以上三行为初始化“空闲题号”tab
+
+ self.layout_bdsl = QVBoxLayout(self.tab_bdsl)
+ self.bdsl = MyWindow_bdsl(self.database)
+ self.layout_bdsl.addWidget(self.bdsl) #以上三行为初始化“新题录入”tab
+
+ self.layout_tjgl = QVBoxLayout(self.tab_tjgl)
+ self.tjgl = MyWindow_tjgl(self.database)
+ self.layout_tjgl.addWidget(self.tjgl) #以上三行为初始化“添加关联”tab
+
+ self.layout_tjzd = QVBoxLayout(self.tab_tjzd)
+ self.tjzd = MyWindow_tjzd(self.database)
+ self.layout_tjzd.addWidget(self.tjzd) #以上三行为初始化“编辑题目”tab
+
+ self.layout_bjtm = QVBoxLayout(self.tab_bjtm)
+ self.bjtm = MyWindow_bjtm(self.database)
+ self.layout_bjtm.addWidget(self.bjtm) #以上三行为初始化“编辑题目”tab
+
+ self.layout_xxdr = QVBoxLayout(self.tab_xxdr)
+ self.xxdr = MyWindow_xxdr()
+ self.layout_xxdr.addWidget(self.xxdr) #以上三行为初始化“小闲统计数据导入”tab
+
+
+ self.layout_sddr = QVBoxLayout(self.tab_sddr)
+ self.sddr = MyWindow_sddr()
+ self.layout_sddr.addWidget(self.sddr) #以上三行为初始化“手动统计数据导入”tab
+
+ self.layout_tkdr = QVBoxLayout(self.tab_tkdr)
+ self.tkdr = MyWindow_tkdr()
+ self.layout_tkdr.addWidget(self.tkdr) #以上三行为初始化“统考数据导入”tab
+
+ self.layout_dygg = QVBoxLayout(self.tab_dygg)
+ self.dygg = MyWindow_dygg(self.database)
+ self.layout_dygg.addWidget(self.dygg) #以上三行为初始化“单元挂钩”tab
+
+ self.layout_tqda = QVBoxLayout(self.tab_tqda)
+ self.tqda = MyWindow_tqda()
+ self.layout_tqda.addWidget(self.tqda) #以上三行为初始化“提取答案”tab
+
+ self.layout_gxsy = QVBoxLayout(self.tab_gxsy)
+ self.gxsy = MyWindow_gxsy(self.database)
+ self.layout_gxsy.addWidget(self.gxsy) #以上三行为初始化“共享使用数据”tab
+
+ self.layout_tjjc = QVBoxLayout(self.tab_tjjc)
+ self.tjjc = MyWindow_tjjc(self.database)
+ self.layout_tjjc.addWidget(self.tjjc) #以上三行为初始化“新增基础知识梳理”tab
+
+ self.layout_xxxz = QVBoxLayout(self.tab_xxxz)
+ self.xxxz = MyWindow_xxxz()
+ self.layout_xxxz.addWidget(self.xxxz) #以上三行为初始化“小闲下载”tab
+
+ self.layout_sxth = QVBoxLayout(self.tab_sxth)
+ self.sxth = MyWindow_sxth(self.database)
+ self.layout_sxth.addWidget(self.sxth) #以上三行为初始化“关键字筛选”tab
+
+ self.layout_ndsx = QVBoxLayout(self.tab_ndsx)
+ self.ndsx = MyWindow_ndsx(self.database)
+ self.layout_ndsx.addWidget(self.ndsx) #以上三行为初始化“按难度筛选”tab
+
+ self.layout_xtby = QVBoxLayout(self.tab_xtby)
+ self.xtby = MyWindow_xtby(self.database)
+ self.layout_xtby.addWidget(self.xtby) #以上三行为初始化“选题编译”tab
+
+ self.layout_sctp = QVBoxLayout(self.tab_sctp)
+ self.sctp = MyWindow_sctp(self.database)
+ self.layout_sctp.addWidget(self.sctp) #以上三行为初始化“生成图片”tab
+
+ self.layout_hqth = QVBoxLayout(self.tab_hqth)
+ self.hqth = MyWindow_hqth()
+ self.layout_hqth.addWidget(self.hqth) #以上三行为初始化“获取题号”tab
+
+ self.layout_jglr = QVBoxLayout(self.tab_jglr)
+ self.jglr = MyWindow_jglr()
+ self.layout_jglr.addWidget(self.jglr) #以上三行为初始化“讲义结构编号录入”tab
+
+ self.layout_dtlr = QVBoxLayout(self.tab_dtlr)
+ self.dtlr = MyWindow_dtlr()
+ self.layout_dtlr.addWidget(self.dtlr) #以上三行为初始化“答题纸对应信息录入”tab
+
+ self.layout_jysc = QVBoxLayout(self.tab_jysc)
+ self.jysc = MyWindow_jysc(self.database)
+ self.layout_jysc.addWidget(self.jysc) #以上三行为初始化“LaTeX代码转换”tab
+
+ self.layout_wbzh = QVBoxLayout(self.tab_wbzh)
+ self.wbzh = MyWindow_wbzh()
+ self.layout_wbzh.addWidget(self.wbzh) #以上三行为初始化“LaTeX代码转换”tab
+
+ self.layout_hist = QVBoxLayout(self.tab_hist)
+ self.hist = MyWindow_hist()
+ self.layout_hist.addWidget(self.hist) #以上三行为初始化“生成直方图代码”tab
+
+
+
+ for func in [
+ self.kxth.setdbname,
+ self.jysc.setdbname,
+ self.sctp.setdbname,
+ self.xtby.setdbname,
+ self.ndsx.setdbname,
+ self.sxth.setdbname,
+ self.tjgl.setdbname,
+ self.bdsl.setdbname,
+ self.tjjc.setdbname,
+ self.gxsy.setdbname,
+ self.dygg.setdbname,
+ self.bjtm.setdbname,
+ self.tjzd.setdbname
+ ]: #在列表中的tab里传送数据库名的连接
+ self.sendDBname.connect(func)
+ def sendValue(self):
+ self.sendDBname.emit(self.database) #释放信号
+
+ def SelectTIKU(self):
+ self.pushButton_tiku.setStyleSheet("background-color: green; font-weight: bold;") #绿色背景粗体
+ self.pushButton_tikutest.setStyleSheet("") #恢复原有背景, 取消粗体
+ # SaveTextFile("tiku","临时文件/databasename.txt")
+ self.database = "tiku"
+ self.sendValue()
+ def SelectTIKUTEST(self):
+ self.pushButton_tikutest.setStyleSheet("background-color: green; font-weight: bold;") #绿色背景粗体
+ self.pushButton_tiku.setStyleSheet("") #恢复原有背景, 取消粗体
+ # SaveTextFile("tikutest","临时文件/databasename.txt")
+ self.database = "tikutest"
+ self.sendValue()
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/tool_panel.ui b/工具v4/tool_panel.ui
new file mode 100644
index 00000000..af36c9ec
--- /dev/null
+++ b/工具v4/tool_panel.ui
@@ -0,0 +1,315 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ 题库工具面板v4
+
+
+
+
+
+ 0
+ 40
+ 800
+ 520
+
+
+
+ QTabWidget::Triangular
+
+
+ 3
+
+
+
+ 录入
+
+
+
+
+ 0
+ 0
+ 800
+ 490
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Triangular
+
+
+ 2
+
+
+
+ 空闲题号
+
+
+
+
+ 比对并收录
+
+
+
+
+ 添加关联
+
+
+
+
+
+
+ 维护
+
+
+
+
+ 0
+ 0
+ 800
+ 490
+
+
+
+
+ 0
+ 0
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Triangular
+
+
+ 5
+
+
+
+ 添加字段数据
+
+
+
+
+ 编辑题目
+
+
+
+
+ 小闲导入
+
+
+
+
+ 手动导入
+
+
+
+
+ 统考导入
+
+
+
+
+ 单元挂钩
+
+
+
+
+ 提取答案
+
+
+
+
+ 共享使用记录
+
+
+
+
+ 添加基础知识
+
+
+
+
+ 小闲下载
+
+
+
+
+
+
+ 使用
+
+
+
+
+ 0
+ 0
+ 800
+ 490
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Triangular
+
+
+ 4
+
+
+
+ 关键字筛选题号
+
+
+
+
+ 正确率筛选题号
+
+
+
+
+ 选题编译
+
+
+
+
+ 题号生成图片
+
+
+
+
+ 获取题号
+
+
+
+
+
+
+ 备课组
+
+
+
+
+ 0
+ 0
+ 800
+ 490
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Triangular
+
+
+ 1
+
+
+
+ 讲义结构编号录入
+
+
+
+
+ 答题纸对应信息录入
+
+
+
+
+ 系列讲义生成
+
+
+
+
+
+
+ LaTeX代码相关
+
+
+
+
+ 0
+ 0
+ 800
+ 490
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Triangular
+
+
+ 1
+
+
+
+ 文本转换处理
+
+
+
+
+ 直方图代码生成
+
+
+
+
+
+
+
+
+ 30
+ 0
+ 741
+ 31
+
+
+
+ -
+
+
+ 正式数据库
+
+
+
+ -
+
+
+ 测试数据库
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/下载小闲答题数据.py b/工具v4/下载小闲答题数据.py
new file mode 100644
index 00000000..62b81957
--- /dev/null
+++ b/工具v4/下载小闲答题数据.py
@@ -0,0 +1,335 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTableWidgetItem
+from PySide6.QtGui import QColor
+from Ui_下载小闲答题数据 import Ui_Form
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.common.keys import Keys
+import re,os,sys
+from datetime import datetime
+import numpy as np
+from time import sleep
+
+
+def GetPageInfo(driver): #学情报告页面, 显示页码和总页数
+ pageinfo = driver.find_element(By.CSS_SELECTOR,"[class*=styles__page___]").text
+ return pageinfo
+
+
+def SelectNext(driver): # 在打开后的报告页面选择下一个班级
+ try:
+ openlist = driver.find_element(By.XPATH,"/html/body/div[2]/div/div/div[2]/div[1]/div[2]/div/div/div")
+ sleep(0.1)
+ openlist.click()
+ sleep(0.1)
+ openlist.send_keys(Keys.ARROW_DOWN)
+ sleep(0.1)
+ openlist.send_keys(Keys.ENTER)
+ sleep(0.3)
+ return driver.find_element(By.XPATH,"/html/body/div[2]/div/div/div[2]/div[1]/div[2]/div/div/div").text
+ except:
+ return 1
+
+def GetDate(driver): #显示当前班级的提交时间
+ commit_date = driver.find_element(By.XPATH,"/html/body/div[2]/div/div/div[2]/div[1]/div[2]/span[2]").text
+ year,month,date = re.findall(r"(\d{4})-(\d{2})-(\d{2})",commit_date)[0]
+ return year+month+date
+
+def GetCommitInfo(driver): #获取报告页面的班级及提交日期信息
+ commit_dict = {}
+ classname = SelectNext(driver)
+ sleep(0.5)
+ while not classname in commit_dict.keys():
+ commit_date = GetDate(driver)
+ if not classname == 1:
+ commit_dict[classname] = commit_date
+ classname = SelectNext(driver)
+ else:
+ classname = SelectNext(driver)
+ return commit_dict
+
+def ToIntTime(string):
+ date_obj = datetime.strptime(string, "%Y%m%d")
+ int_time = int(date_obj.timestamp())
+ return int_time
+
+def MedianCommitDate(adict):
+ return datetime.fromtimestamp(np.median([ToIntTime(adict[t]) for t in adict.keys()])).strftime("%Y%m%d")
+
+def gotopagenum(driver,num):
+ pagecontrol = driver.find_element(By.CSS_SELECTOR,"[class*=styles__pagination___]")
+ pageinput = pagecontrol.find_element(By.CSS_SELECTOR,"input[type=text]")
+ pageinput.clear()
+ submitbutton = pagecontrol.find_element(By.CSS_SELECTOR,"[class*=tyles__button___]")
+ pageinput.send_keys(str(1))
+ submitbutton.click()
+ sleep(0.5)
+ pageinput.clear()
+ sleep(0.1)
+ pageinput.send_keys(str(num))
+ submitbutton.click()
+ sleep(0.5)
+
+def ClickBack(driver): # 点击返回按钮
+ backbutton = driver.find_element(By.CSS_SELECTOR,'[class*=styles__back]') # 点击返回
+ backbutton.click()
+ sleep(1)
+
+def GetHomeworkNames(driver,starting_date = "20000101"):
+ hwk_list = []
+ homework_roots = driver.find_elements(By.CSS_SELECTOR,'[class*=styles__reportList]')
+ for homework_root in homework_roots:
+ name = homework_root.find_element(By.CSS_SELECTOR,'[class*=styles__name]').text
+ date_raw = homework_root.find_element(By.CSS_SELECTOR,'[class*=styles__time]').text
+ date = re.findall(r"\d{4}-\d{2}-\d{2}",date_raw)[0].replace("-","")
+ time = re.findall(r"\d{2}:\d{2}:\d{2}",date_raw)[0].replace(":","")
+ grade = homework_root.find_element(By.CSS_SELECTOR,'[class*=styles__grades]').text[3:]
+ if date >= starting_date:
+ hwk_list.append({"name": name, "date": date, "time": time, "grade": grade})
+ return hwk_list
+
+def GetHomeworkRoots(driver): #找到页面上所有作业考试的root
+ homework_roots = driver.find_elements(By.CSS_SELECTOR,'[class*=styles__reportList]')
+ return homework_roots
+
+def ClickDetail(root): #点击查看详情
+ root.find_element(By.XPATH,"div[2]/div[2]").click()
+
+def ClickDownload(root): #点击报表
+ root.find_element(By.XPATH,"div[2]/div[3]").click()
+
+def StartandEndDownload(driver):
+ driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div/div[2]/div[3]/div/button[2]").click()
+ QuitDownload(driver)
+
+def QuitDownload(driver): #退出下载对话框
+ while driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div/div[2]/div[3]/div/button[1]").text == '取 消':
+ try:
+ sleep(0.5)
+ driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div/div[2]/div[3]/div/button[1]").click()
+ except:
+ pass
+
+def GenerateCommitMessages(driver,index): #在班级报告页面逐一读取页面上的指定作业的名称和计算中位提交日期
+ page_info = GetPageInfo(driver)
+ page = page_info[:page_info.index("/")]
+ roots = GetHomeworkRoots(driver)
+ root = roots[index-1]
+ # root_text = root.text
+ # homework_name = root_text.split("\n")[1]
+ ClickDetail(root)
+ adict = GetCommitInfo(driver)
+ # print(homework_name)
+ message = MedianCommitDate(adict)
+ sleep(0.1)
+ ClickBack(driver)
+ sleep(0.5)
+ gotopagenum(driver,page)
+ sleep(0.5)
+ return message
+
+def DownloadZipwithDetail(driver,index): #下载页面上的指定zip文件, 并返回中位上传时间
+ page_info = GetPageInfo(driver)
+ page = page_info[:page_info.index("/")]
+ roots = GetHomeworkRoots(driver)
+ root = roots[index-1]
+ root_text = root.text
+ homework_name = root_text.split("\n")[1]
+ ClickDownload(root)
+ sleep(0.5)
+ StartandEndDownload(driver)
+ print(f"已下载: {homework_name}")
+ sleep(0.5)
+ QuitDownload(driver)
+ ClickDetail(root)
+ adict = GetCommitInfo(driver)
+ message = MedianCommitDate(adict)
+ sleep(0.1)
+ ClickBack(driver)
+ sleep(0.5)
+ gotopagenum(driver,page)
+ sleep(0.5)
+ return message
+
+def RenameRecentzip(folder,message): #将folder中最新的zip文件名加上(message)
+ filelist = os.listdir(folder)
+ filelist.sort(key = lambda x:os.path.getmtime(os.path.join(folder,x)),reverse = True)
+ filepath = os.path.join(folder,filelist[0])
+ renamedfilepath = filepath[:-4]+f"({message}).zip"
+ try:
+ os.rename(filepath,renamedfilepath)
+ print(f"已重命名为: {os.path.split(renamedfilepath)[-1]}")
+ except:
+ return 1
+ return 0
+
+def DownloadZips(driver,alist=[]): #下载页面上的指定zip文件
+ page_info = GetPageInfo(driver)
+ page = page_info[:page_info.index("/")]
+ roots_len = len(GetHomeworkRoots(driver))
+ if alist == []:
+ rangelist = range(roots_len)
+ else:
+ rangelist = alist.copy()
+ for i in rangelist:
+ roots = GetHomeworkRoots(driver)
+ root = roots[i]
+ root_text = root.text
+ homework_name = root_text.split("\n")[1]
+ ClickDownload(root)
+ sleep(0.5)
+ StartandEndDownload(driver)
+ print(f"已下载: {homework_name}")
+ sleep(0.5)
+ gotopagenum(driver,page)
+ sleep(0.5)
+
+def DownloadandRenameZips(driver,message,folder,index): #下载页面上的指定zip文件
+ page_info = GetPageInfo(driver)
+ page = page_info[:page_info.index("/")]
+ roots = GetHomeworkRoots(driver)
+ root = roots[index-1]
+ print(root.text)
+ ClickDownload(root)
+ sleep(0.5)
+ StartandEndDownload(driver)
+ sleep(5)
+ filelist = os.listdir(folder)
+ filelist.sort(key = lambda x:os.path.getmtime(os.path.join(folder,x)),reverse = True)
+ filepath = os.path.join(folder,filelist[0])
+ renamedfilepath = filepath[:-4]+f"({message}).zip"
+ os.rename(filepath,renamedfilepath)
+ print(f"已重命名为: {os.path.split(renamedfilepath)[-1]}")
+ sleep(0.5)
+ gotopagenum(driver,page)
+ sleep(0.5)
+
+def getIndices(string):
+ if string.strip() == "":
+ return []
+ else:
+ string_list = [int(i)-1 for i in string.strip().split(",")]
+ return string_list
+
+def GetValidHomeworks(driver,startdate,enddate,graderegex): #在所有作业中找到在startdate,enddate中符合graderegex年级的作业, 返回(作业信息,页码)的列表
+ validhomeworks = []
+ gotopagenum(driver,1)
+ sleep(2)
+ endflag = False
+ while not endflag:
+ currentpage = int(re.findall("^([\d]+)/",GetPageInfo(driver))[0])
+ homeworkinfo = GetHomeworkNames(driver)
+ for item in homeworkinfo:
+ if re.findall(graderegex,item["grade"])!= [] and startdate <= item["date"] <= enddate:
+ validhomeworks.append((item,currentpage))
+ print(item)
+ if startdate > item["date"]:
+ endflag = True
+ break
+ gotopagenum(driver,currentpage+1)
+ sleep(2)
+ return validhomeworks[-1::-1]
+
+
+def gethmwkIndexonPage(driver,hmwkinfo): #在当前页面寻找作业位置, 返回位置(1-10), 若未找到则返回-1
+ homeworks = GetHomeworkNames(driver)
+ count = 1
+ for h in homeworks:
+ if h == hmwkinfo:
+ return count
+ count += 1
+ return -1
+
+class MyWindow_xxxz(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.pushButton_chrome.clicked.connect(self.getchromePath)
+ self.pushButton_driver.clicked.connect(self.getdriverPath)
+ self.pushButton_outputfolder.clicked.connect(self.getoutputfolder)
+ self.pushButton_openbrowser.clicked.connect(self.openbrowser)
+ self.pushButton_login.clicked.connect(self.login)
+ self.pushButton_getlist.clicked.connect(self.getlist)
+ self.pushButton_exec.clicked.connect(self.exec)
+ self.tableWidget.setColumnWidth(0,350)
+ self.tableWidget.setColumnWidth(1,80)
+ def getchromePath(self):
+ pathlist = QFileDialog.getOpenFileName(self,"选择文件",".","chrome.exe文件(chrome.exe);;所有文件(*)")
+ self.label_chrome.setText(pathlist[0])
+ self.chromepath = pathlist[0]
+ def getdriverPath(self):
+ pathlist = QFileDialog.getOpenFileName(self,"选择文件",".","chromedriver.exe文件(chromedriver.exe);;所有文件(*)")
+ self.label_driver.setText(pathlist[0])
+ self.driverpath = pathlist[0]
+ def getoutputfolder(self):
+ self.outputfolder = QFileDialog.getExistingDirectory(None, "选择文件夹")
+ if sys.platform == "win32":
+ self.outputfolder = self.outputfolder.replace("/","\\")
+ self.label_outputfolder.setText(self.outputfolder)
+ def openbrowser(self):
+ service = Service(executable_path=self.driverpath)
+ options = webdriver.ChromeOptions()
+ options.binary_location = self.chromepath
+ prefs = {"download.default_directory": self.outputfolder}
+ options.add_experimental_option("prefs", prefs)
+ self.driver = webdriver.Chrome(service= service,options = options)
+ self.driver.get("http://ls.xiaoxianai.cn")
+ def login(self):
+ loginform = self.driver.find_element(By.XPATH,'//*[@id="container"]/div/div/form')
+ loginlist = loginform.find_elements(By.TAG_NAME,"div")
+ loginlist[1].find_element(By.TAG_NAME,"input").send_keys("16621337584")
+ loginlist[2].find_element(By.TAG_NAME,"input").send_keys("password")
+ loginform.find_element(By.TAG_NAME,"button").click()
+ def getlist(self):
+ self.homeworklist = GetValidHomeworks(self.driver,self.lineEdit_startdate.text(),self.lineEdit_enddate.text(),self.lineEdit_graderegex.text())
+ for i in range(len(self.homeworklist)):
+ self.tableWidget.insertRow(i)
+ self.tableWidget.setItem(i,0,QTableWidgetItem(f"{self.homeworklist[i][0]['name']} / {self.homeworklist[i][0]['grade']}"))
+ self.tableWidget.setItem(i,1,QTableWidgetItem(""))
+ def exec(self):
+ i = 0
+ for hmwk,page in self.homeworklist:
+ foundhmwk = False
+ downloaded = False
+ for p in range(page+1,0,-1):
+ gotopagenum(self.driver,p)
+ sleep(2)
+ hmwkindex = gethmwkIndexonPage(self.driver,hmwk)
+ if hmwkindex > 0:
+ foundhmwk = True
+ print(f"已找到 {hmwk['name']} 于页码 {p}, 第 {hmwkindex} 项")
+ break
+ if not foundhmwk:
+ print(f"!!!!!!未找到 {hmwk['name']}")
+ else:
+ print(f"正在下载 {hmwk['name']} 的 zip 文件")
+ try:
+ message = DownloadZipwithDetail(self.driver,hmwkindex)
+ renamed = RenameRecentzip(self.outputfolder,message)
+ if renamed == 0:
+ downloaded = True
+ self.tableWidget.item(i,1).setBackground(QColor("green"))
+ QApplication.processEvents()
+ except:
+ print(Exception)
+ if not downloaded:
+ print(f"@@@@@@未能下载并改名 {hmwk['name']}")
+ self.tableWidget.item(i,1).setBackground(QColor("red"))
+ i += 1
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_xxxz()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/下载小闲答题数据.ui b/工具v4/下载小闲答题数据.ui
new file mode 100644
index 00000000..109eaf2c
--- /dev/null
+++ b/工具v4/下载小闲答题数据.ui
@@ -0,0 +1,252 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 下载小闲答题纸
+
+
+
+
+ 130
+ 10
+ 75
+ 24
+
+
+
+ chrome路径
+
+
+
+
+
+ 130
+ 50
+ 75
+ 24
+
+
+
+ driver路径
+
+
+
+
+
+ 130
+ 90
+ 75
+ 24
+
+
+
+ 输出路径
+
+
+
+
+
+ 210
+ 10
+ 221
+ 21
+
+
+
+ 此处显示chrome.exe的路径
+
+
+
+
+
+ 210
+ 50
+ 221
+ 21
+
+
+
+ 此处显示chrome webdriver的路径
+
+
+
+
+
+ 210
+ 90
+ 221
+ 21
+
+
+
+ 此处显示输出的zip文件的路径
+
+
+
+
+
+ 130
+ 130
+ 71
+ 51
+
+
+
+ 开启浏览器
+
+
+
+
+
+ 210
+ 130
+ 71
+ 51
+
+
+
+ 登录
+
+
+
+
+
+ 400
+ 130
+ 201
+ 51
+
+
+
+
+ true
+
+
+
+ 下载答题情况
+
+
+
+
+
+ 450
+ 10
+ 54
+ 16
+
+
+
+ 起始日期
+
+
+
+
+
+ 450
+ 50
+ 54
+ 16
+
+
+
+ 终止日期
+
+
+
+
+
+ 450
+ 90
+ 71
+ 16
+
+
+
+ 年级(regex)
+
+
+
+
+
+ 510
+ 10
+ 91
+ 20
+
+
+
+ yyyymmdd
+
+
+
+
+
+ 510
+ 50
+ 91
+ 20
+
+
+
+ yyyymmdd
+
+
+
+
+
+ 530
+ 90
+ 71
+ 20
+
+
+
+ 高[一二]
+
+
+
+
+
+ 290
+ 130
+ 101
+ 51
+
+
+
+ 获取答题卡列表
+
+
+
+
+
+ 130
+ 190
+ 471
+ 271
+
+
+
+
+ 答题纸名称
+
+
+
+
+ 已下载
+
+
+
+
+
+
+
diff --git a/工具v4/修改metadata.py b/工具v4/修改metadata.py
new file mode 100644
index 00000000..5622a89c
--- /dev/null
+++ b/工具v4/修改metadata.py
@@ -0,0 +1,63 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_修改metadata import Ui_Form
+import os
+from database_tools_2 import *
+
+
+class MyWindow_tjzd(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+
+ def bind(self):
+ self.metadatafilepath = "文本文件/metadata.txt"
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def exec(self):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+
+
+ changedids = ImportMetadata(mydb,self.metadatafilepath)
+ print(changedids)
+
+ if len(changedids) > 0:
+ mycursor = mydb.cursor()
+ configjson = BuildFullScheme
+ latexbody = "\\begin{enumerate}\n\n"
+ for id in sorted(list(set(changedids))):
+ latexbody += generateLaTeXBodyContentfromMariaDB(mycursor,id,configjson) + "\n\n"
+ latexbody += "\\end{enumerate}"
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ if configjson["教师版"] == True:
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学")
+
+ if sys.platform != "win32": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,("试编译",latexbody)) #替换标题和bodystring
+ outputdir = os.path.join(os.getcwd(),"临时文件")
+ outputfilepath = os.path.join(outputdir,"试编译.tex")
+ SaveTextFile(latex_data,outputfilepath)
+ if XeLaTeXCompile(outputdir,"试编译.tex",times=1):
+ print("修改后检验成功, 已导入数据库.")
+ mydb.commit()
+ else:
+ print("修改后检验失败, 已回滚.")
+ mydb.rollback()
+ else:
+ print("未作修改.")
+
+
+
+
+ mydb.commit()
+ mydb.close()
+
+
diff --git a/工具v4/修改metadata.ui b/工具v4/修改metadata.ui
new file mode 100644
index 00000000..971a212d
--- /dev/null
+++ b/工具v4/修改metadata.ui
@@ -0,0 +1,37 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 修改metadata
+
+
+
+
+ 220
+ 190
+ 321
+ 101
+
+
+
+
+ true
+
+
+
+ 修改metadata
+
+
+
+
+
+
diff --git a/工具v4/共享使用记录.py b/工具v4/共享使用记录.py
new file mode 100644
index 00000000..0e76bb52
--- /dev/null
+++ b/工具v4/共享使用记录.py
@@ -0,0 +1,31 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_共享使用记录 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_gxsy(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+ def bind(self):
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def exec(self):
+ same_list = generate_same_list(self.database_name)
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ for id,same_id in tqdm.tqdm(same_list):
+ ShareSameUsagesinDB(id,same_id,mydb)
+ mydb.commit()
+ mydb.close()
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_gxsy("tikutest")
+ windows.show()
+ app.exec()
diff --git a/工具v4/共享使用记录.ui b/工具v4/共享使用记录.ui
new file mode 100644
index 00000000..ab927b4f
--- /dev/null
+++ b/工具v4/共享使用记录.ui
@@ -0,0 +1,37 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 共享使用记录
+
+
+
+
+ 270
+ 170
+ 251
+ 131
+
+
+
+
+ true
+
+
+
+ 相同题目共享使用记录
+
+
+
+
+
+
diff --git a/工具v4/关键字筛选题号.py b/工具v4/关键字筛选题号.py
new file mode 100644
index 00000000..9241b0e4
--- /dev/null
+++ b/工具v4/关键字筛选题号.py
@@ -0,0 +1,148 @@
+from PySide6.QtWidgets import QWidget, QApplication, QLineEdit
+from Ui_题号筛选器 import Ui_Form
+from database_tools_2 import *
+import time,os
+
+class MyWindow_sxth(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.conditions = []
+ self.bind()
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+ def bind(self):
+ self.pushButton_content.clicked.connect(lambda: self.add_content("content"))
+ self.pushButton_obj.clicked.connect(lambda: self.add_content("objs"))
+ self.pushButton_tag.clicked.connect(lambda: self.add_content("tags"))
+ self.pushButton_usage.clicked.connect(lambda: self.add_content("usages"))
+ self.pushButton_origin.clicked.connect(lambda: self.add_content("origin"))
+ self.pushButton_genre.clicked.connect(lambda: self.add_content("genre"))
+ self.pushButton_ans.clicked.connect(lambda: self.add_content("ans"))
+ self.pushButton_solution.clicked.connect(lambda: self.add_content("solution"))
+ self.pushButton_same.clicked.connect(lambda: self.add_content("same"))
+ self.pushButton_related.clicked.connect(lambda: self.add_content("related"))
+ self.pushButton_remark.clicked.connect(lambda: self.add_content("remark"))
+ self.pushButton_undo.clicked.connect(self.undolast)
+ self.pushButton_clearAll.clicked.connect(self.clearConditions)
+ self.pushButton_exec.clicked.connect(self.exec)
+ self.pushButton_savebuild.clicked.connect(self.build)
+
+
+
+ def add_content(self,field):
+ self.conditions.append((field,self.checkBox_not.isChecked(),self.lineEdit_SingleCondition.text()))
+ self.showConditions()
+ def undolast(self):
+ self.conditions = self.conditions[:-1].copy()
+ self.showConditions()
+ def clearConditions(self):
+ self.conditions = []
+ self.showConditions()
+ def showConditions(self):
+ text = ""
+ for cond in self.conditions:
+ field, flag_not, matchexp = cond
+ text += f"字段 {field} 中{'没有' if flag_not else '有 '}{' 或 '.join([t.strip() for t in matchexp.split(',')])}\n"
+ self.label_conditions.setText(text)
+ def exec(self):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT count(*) FROM problems WHERE NOT content REGEXP 'OBS';")
+ self.lcdNumber_resCount.display(mycursor.fetchall()[0][0])
+ self.matchlist = MatchConditioninMariaDB(self.conditions,self.database_name)
+ self.lcdNumber_resCount.display(len(self.matchlist))
+
+ def build(self):
+ idlist = sorted(list(self.matchlist))
+ exp = generate_exp(idlist)
+ try:
+ AppendTextFile(f"\n{time.ctime()}\n{exp}\n","临时文件/题号筛选.txt")
+ except:
+ SaveTextFile(f"{time.ctime()}\n{exp}\n","临时文件/题号筛选.txt")
+ configjson_detailed = {
+ "pdf标题": "筛选题目编译",
+ "教师版": True,
+ "字段显示设置": {
+ "题后空间": True,
+ "课时目标": True,
+ "题目标签": True,
+ "答案": True,
+ "解答与提示": True,
+ "使用记录": [3,-1],
+ "使用记录说明": "[a,b]表示显示最好的a个和最差b个, 有-2表示不显示, 无-2但有-1表示全部显示",
+ "来源": True,
+ "备注": True,
+ "届别": []
+ }
+ }
+ configjson_simple = {
+ "pdf标题": "筛选题目编译",
+ "教师版": False,
+ "字段显示设置": {
+ "题后空间": True,
+ "课时目标": True,
+ "题目标签": True,
+ "答案": True,
+ "解答与提示": True,
+ "使用记录": [3,-1],
+ "使用记录说明": "[a,b]表示显示最好的a个和最差b个, 有-2表示不显示, 无-2但有-1表示全部显示",
+ "来源": True,
+ "备注": True,
+ "届别": []
+ }
+ }
+
+ if self.checkBox_Detailed.isChecked():
+ configjson = configjson_detailed
+ else:
+ configjson = configjson_simple
+
+
+ notetitle = configjson["pdf标题"]
+ outputdir = "临时文件" #输出文件的目录
+ outputfilepath = os.path.join(outputdir,notetitle+".tex")
+ print("输出文件目录: %s\n输出文件名: %s"%(os.path.join(os.getcwd(),outputdir),notetitle+".tex"))
+
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ if configjson["教师版"] == True:
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学")
+
+ if sys.platform != "win32": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+ starttime = time.time()
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ bodystring = "\\begin{enumerate}\n\n"
+ for id in generate_number_set(exp):
+ bodystring += generateLaTeXBodyContentfromMariaDB(mycursor,id,configjson)
+ bodystring += "\\end{enumerate}\n\n"
+
+ midtime = time.time()
+ print(f"生成LaTeX文件所花时间: {midtime-starttime:.3f}秒")
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,bodystring)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if XeLaTeXCompile(outputdir,notetitle+".tex"):
+ print("编译成功")
+ else:
+ print("编译失败")
+
+ endtime = time.time()
+ print(f"生成pdf文件所花时间: {endtime-midtime:.3f}秒")
+ os.system("code 临时文件/题号筛选.txt")
+ startfile("临时文件")
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/单一题号转为图片.py b/工具v4/单一题号转为图片.py
new file mode 100644
index 00000000..5b0923ac
--- /dev/null
+++ b/工具v4/单一题号转为图片.py
@@ -0,0 +1,69 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_单一题号转为图片 import Ui_Form
+from database_tools_2 import *
+import os
+from datetime import datetime
+
+class MyWindow_sctp(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+ def bind(self):
+ self.pushButton_open.setDisabled(True)
+ self.pushButton_convert.clicked.connect(self.convert)
+ self.pushButton_open.clicked.connect(self.open)
+ self.lineEdit_ID.textChanged.connect(self.toggleopen)
+ self.lineEdit_dpi.textChanged.connect(self.toggleopen)
+ def convert(self):
+ self.time = datetime.now().strftime("%Y%m%d%H%M%S")
+ self.id = self.lineEdit_ID.text()
+ self.dpi = self.lineEdit_dpi.text()
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ sql = "SELECT content FROM problems WHERE ID = %s;"
+ val = (self.id.strip().zfill(6),)
+ mycursor.execute(sql,val)
+ content = mycursor.fetchall()[0][0]
+ mydb.close()
+ prefix = self.lineEdit_message.text().strip()
+ latexcontent = f"{prefix} {content}"
+ latex_raw = ReadTextFile("模板文件/独立文件模板.txt")
+ #识别操作系统
+ if sys.platform != "win32":
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+ latexdata = StringSubstitute(r"<<待替换\d*>>",latex_raw,[latexcontent])
+ makedir("临时文件/pics")
+ SaveTextFile(latexdata,f"临时文件/pics/ID{self.id}-{self.dpi}-{self.time}.tex")
+ os.system(f"xelatex -interaction=batchmode -output-directory=临时文件/pics ID{self.id}-{self.dpi}-{self.time}.tex")
+ os.chdir("临时文件/pics")
+ os.system(f"pdftocairo -png -r {self.dpi} ID{self.id}-{self.dpi}-{self.time}.pdf")
+ os.chdir("../..")
+ self.pushButton_open.setEnabled(True)
+ def open(self):
+ startfile(os.path.join("临时文件/pics", f"ID{self.id}-{self.dpi}-{self.time}-1.png"))
+ def toggleopen(self):
+ self.pushButton_open.setDisabled(True)
+
+
+
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/单一题号转为图片.ui b/工具v4/单一题号转为图片.ui
new file mode 100644
index 00000000..6ef466d5
--- /dev/null
+++ b/工具v4/单一题号转为图片.ui
@@ -0,0 +1,117 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 单一题号转为图片
+
+
+
+
+ 240
+ 160
+ 31
+ 16
+
+
+
+ 题号
+
+
+
+
+
+ 270
+ 160
+ 71
+ 20
+
+
+
+
+
+
+ 240
+ 190
+ 31
+ 16
+
+
+
+ DPI
+
+
+
+
+
+ 270
+ 190
+ 71
+ 20
+
+
+
+
+
+
+ 350
+ 160
+ 75
+ 51
+
+
+
+ 转换
+
+
+
+
+
+ 430
+ 160
+ 75
+ 51
+
+
+
+ 打开
+
+
+
+
+
+ 240
+ 220
+ 51
+ 16
+
+
+
+ 附加文字
+
+
+
+
+
+ 300
+ 220
+ 201
+ 20
+
+
+
+ 输入附加文字, 留空表示无附加文字
+
+
+
+
+
+
diff --git a/工具v4/单元挂钩.py b/工具v4/单元挂钩.py
new file mode 100644
index 00000000..fa32847f
--- /dev/null
+++ b/工具v4/单元挂钩.py
@@ -0,0 +1,55 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_单元挂钩 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_dygg(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+ def bind(self):
+ self.radioButton_phase1.setChecked(True)
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def exec(self):
+ # pro_dict = load_dict("../题库0.3/Problems.json")
+ allids = getAllIDsExp(self.database_name)
+ all_same_list = generate_same_list(self.database_name)
+ all_related_list = generate_related_list(self.database_name)
+ if self.radioButton_phase1.isChecked():
+ idexp = self.lineEdit_ids.text()
+ if idexp.strip() == "":
+ idlist = unUnitted(allids,self.database_name)
+ else:
+ idlist = generate_number_set(idexp)
+ latex_body,tagrec = AutoAssignTagNotoLaTeX(idlist,all_same_list,all_related_list,self.database_name)
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,("待检查单元",latex_body))
+ SaveTextFile(tagrec,"临时文件/单元对应.txt")
+ SaveTextFile(latex_data,"临时文件/待检查单元.tex")
+ if XeLaTeXCompile("临时文件","待检查单元.tex") == True:
+ print("处理成功.")
+ os.system("code 临时文件/单元对应.txt 临时文件/待检查单元.pdf")
+ else:
+ print("处理失败.")
+ elif self.radioButton_phase2.isChecked():
+ output = UnitRectoMetadataText(ReadTextFile("临时文件/单元对应.txt"))
+ SaveTextFile(output,"文本文件/metadata.txt")
+ os.system("code 文本文件/metadata.txt")
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_dygg()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/单元挂钩.ui b/工具v4/单元挂钩.ui
new file mode 100644
index 00000000..a2ccf43d
--- /dev/null
+++ b/工具v4/单元挂钩.ui
@@ -0,0 +1,89 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 单元挂钩
+
+
+
+
+ 200
+ 190
+ 54
+ 16
+
+
+
+ 题号
+
+
+
+
+
+ 200
+ 240
+ 191
+ 20
+
+
+
+ 收录单元信息至metadata阶段
+
+
+
+
+
+ 200
+ 210
+ 271
+ 20
+
+
+
+ 输入题号, 留空表示针对所有没有对应单元的题目
+
+
+
+
+
+ 490
+ 160
+ 71
+ 111
+
+
+
+
+ true
+
+
+
+ 运行
+
+
+
+
+
+ 200
+ 170
+ 141
+ 20
+
+
+
+ 自适应赋予单元信息
+
+
+
+
+
+
diff --git a/工具v4/寻找空闲题号.py b/工具v4/寻找空闲题号.py
new file mode 100644
index 00000000..acb310a0
--- /dev/null
+++ b/工具v4/寻找空闲题号.py
@@ -0,0 +1,38 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_寻找空闲题号 import Ui_Form
+from database_tools_2 import *
+
+
+class MyWindow_kxth(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.label_used.setText("")
+ self.label_available.setText("")
+ self.pushButton_exec.clicked.connect(self.exec)
+
+
+ def exec(self):
+ # self.databaes_name = ReadTextFile("临时文件/databasename.txt").strip()
+ self.label_used.setText(usedIDs(self.database_name))
+ self.label_available.setText(spareIDs(self.database_name))
+
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/寻找空闲题号.ui b/工具v4/寻找空闲题号.ui
new file mode 100644
index 00000000..0808b5ee
--- /dev/null
+++ b/工具v4/寻找空闲题号.ui
@@ -0,0 +1,102 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 寻找空闲题号
+
+
+
+
+ 20
+ 50
+ 260
+ 401
+
+
+
+ QFrame::StyledPanel
+
+
+ TextLabel
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+
+ 290
+ 50
+ 260
+ 401
+
+
+
+ QFrame::StyledPanel
+
+
+ TextLabel
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+
+ 30
+ 20
+ 71
+ 16
+
+
+
+ 已使用题号
+
+
+
+
+
+ 290
+ 20
+ 71
+ 16
+
+
+
+ 空闲题号
+
+
+
+
+
+ 570
+ 50
+ 141
+ 401
+
+
+
+
+ true
+
+
+
+ 生成已使用题号列表
+及空闲题号列表
+
+
+
+
+
+
diff --git a/工具v4/手动统计结果导入.py b/工具v4/手动统计结果导入.py
new file mode 100644
index 00000000..d3716a36
--- /dev/null
+++ b/工具v4/手动统计结果导入.py
@@ -0,0 +1,46 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_手动统计结果导入 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_sddr(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.pushButton_exec.clicked.connect(self.exec)
+ def exec(self):
+ handmadeusagedatafilepath = r"临时文件/手动统计结果.txt" #手动统计文件的位置
+ metadatafilepath = r"文本文件/metadata.txt" #输出的metadata文件的位置
+ raw_data = ReadTextFile(handmadeusagedatafilepath)
+ data_list = [d.strip() for d in re.findall(r"\[BEGIN\]([\s\S]*?)\[END\]",raw_data)]
+ output = "usages\n\n"
+ for item in data_list:
+ lines = item.split("\n")
+ for line in lines:
+ if line.startswith("##"):
+ date = line.replace("##","").strip()
+ elif line.startswith("**"):
+ classname = line.replace("**","").strip()
+ else:
+ linedata = re.sub(r"\s+","\t",line)
+ usage = linedata.split("\t")
+ id = usage.pop(0)
+ usagestr = "\t".join([f"{float(u):.3f}" for u in usage])
+ output += "%s\n%s\t%s\t%s\n\n"%(id.zfill(6),date,classname,usagestr)
+ SaveTextFile(output,metadatafilepath)
+ os.system(f"code {metadatafilepath}")
+ self.close()
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_sddr()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/手动统计结果导入.ui b/工具v4/手动统计结果导入.ui
new file mode 100644
index 00000000..41b4e927
--- /dev/null
+++ b/工具v4/手动统计结果导入.ui
@@ -0,0 +1,75 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 手动统计结果导入
+
+
+
+ true
+
+
+
+ 250
+ 150
+ 181
+ 141
+
+
+
+ true
+
+
+ [BEGIN]
+##20240101
+**2026届高一01班
+1 0.875
+10001 0.825 0.630
+[END]
+
+
+
+
+
+ 250
+ 130
+ 101
+ 16
+
+
+
+ 手动统计结果模板
+
+
+
+
+
+ 440
+ 130
+ 75
+ 161
+
+
+
+
+ true
+
+
+
+ 导入为
+metadata
+
+
+
+
+
+
diff --git a/工具v4/批量收录新题.py b/工具v4/批量收录新题.py
new file mode 100644
index 00000000..fb77317c
--- /dev/null
+++ b/工具v4/批量收录新题.py
@@ -0,0 +1,113 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_批量收录新题 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_bdsl(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+ def bind(self):
+ self.suffix_checked = True
+ self.checkBox_suffix.toggled.connect(self.togglesuffix)
+ self.pushButton_step1.clicked.connect(self.execstep1)
+ self.pushButton_step2.clicked.connect(self.execstep2)
+ 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 execstep1(self):
+ colors = ["green","orange","blue"]
+ templatepath = "./模板文件/讲义模板.txt"
+
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+
+ mycursor.execute("SELECT ID,content FROM problems;")
+ ret_list = mycursor.fetchall()
+ pro_dict = {i:j for i,j in ret_list}
+ treated_dict = {i:pre_treating(pro_dict[i]) for i in pro_dict.keys()}
+ mydb.close()
+
+ data_raw = self.plainTextEdit_raw.toPlainText()
+ if data_raw.strip().startswith("\\item"):
+ data_raw = "\\begin{enumerate}\n"+data_raw
+ if data_raw.count("\\begin{enumerate}") == data_raw.count("\\end{enumerate}") + 1:
+ data_raw = data_raw + "\n\\end{enumerate}"
+ problems_raw = re.findall(r"\\begin\{enumerate\}([\s\S]*?)\\end\{enumerate\}",data_raw)
+ data = "\n".join([item.strip() for item in problems_raw])
+ problems = [(d.strip()) for d in data.split("\\item") if not d.strip() == ""]
+ output = "使用说明:\\\\\n rep??????表示使用已有题号??????,\\\\\n s??????表示和??????相同,\\\\\n r??????表示和??????相关\n\n\\begin{enumerate}\n\n"
+
+
+ for p in problems:
+ p_treated = pre_treating(p)
+ t = stringmaxsim(p_treated,treated_dict,5)
+ psrstring = ""
+ for id,simrate in t:
+ if simrate == 1:
+ psrstring = "rep"+id+","
+ else:
+ psrstring += id + ","
+ if psrstring[-1] == ",":
+ psrstring = psrstring[:-1]
+ output += f"\\item [{psrstring}] {p}\n\n"
+ count = 0
+ for id,simrate in t:
+ if simrate > 0.5:
+ colors = get_color(simrate*2-1)
+ output += "\n\\definecolor{mycolor}{rgb}"+colors
+ output += "\n\\begin{tcolorbox}"+f"[colback = mycolor, opacityback = 0.25, colframe = orange!10!white, breakable]\n"
+ output += f"{simrate:.3f} \\ {id}\n\n"
+ content = pro_dict[id]
+ output += f"{content}\n"
+ output += "\\end{tcolorbox}\n"
+ count += 1
+ output += "\n\n"
+ output += "\n\\end{enumerate}\n\n"
+
+ texraw = ReadTextFile(templatepath)
+ texdata = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",texraw,("新题比对"+f"{GetDate()}",output))
+ self.plainTextEdit_compared.setPlainText(texdata)
+
+ def execstep2(self):
+ starting_id = self.lineEdit_startingid.text().strip().zfill(6)
+ raworigin = self.lineEdit_origin.text().strip()
+ data = self.plainTextEdit_compared.toPlainText()
+ editor = self.lineEdit_editor.text().strip()
+ if self.checkBox_suffix.isChecked():
+ Indexed = True
+ else:
+ Indexed = False
+ idlistpath = "文本文件/新题收录列表.txt"
+ problems = GenerateProblemListFromString2024(data)
+ # pro_dict = load_dict("../题库0.3/Problems.json")
+ nextspareid = NextSpareID(starting_id,self.database_name)
+ idlist = AddProblemstoDict2024(nextspareid,raworigin,problems,editor,Indexed,self.database_name)
+ # 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_bdsl("tikutest")
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/批量收录新题.ui b/工具v4/批量收录新题.ui
new file mode 100644
index 00000000..b401cc83
--- /dev/null
+++ b/工具v4/批量收录新题.ui
@@ -0,0 +1,211 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 批量收录新题
+
+
+
+
+ 10
+ 30
+ 281
+ 441
+
+
+
+
+
+
+ 20
+ 10
+ 251
+ 16
+
+
+
+ 要处理的题目(按 item 切分, 可以包含文件头)
+
+
+
+
+
+ 300
+ 50
+ 70
+ 400
+
+
+
+
+ true
+
+
+
+ 新题比对
+
+
+
+
+
+ 360
+ 10
+ 321
+ 16
+
+
+
+ 含有相似题目的LaTeX源代码, 复制到LaTeX编辑器人工标注
+
+
+
+
+
+ 380
+ 30
+ 281
+ 301
+
+
+
+
+
+
+ 670
+ 50
+ 70
+ 400
+
+
+
+
+ true
+
+
+
+ 收录题目
+
+
+
+
+
+ 380
+ 340
+ 281
+ 131
+
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ 起始ID
+
+
+
+ -
+
+
+
+
+
+ false
+
+
+ false
+
+
+ 数字题号(通常是10000n+1)
+
+
+
+
+
+
+
+ -
+
+
+ 编辑者
+
+
+
+ -
+
+
+ 编辑者姓名
+
+
+
+
+
+
+
+ -
+
+
+ 来源
+
+
+
+ -
+
+
+ 自拟题目
+
+
+ 题目来源, 通常为 自拟题目
+
+
+
+
+
+
+
+ -
+
+
+ 后缀
+
+
+
+ -
+
+
+ false
+
+
+ 试题
+
+
+ 通常为 试题
+
+
+
+ -
+
+
+ 使用后缀
+
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/指定题号编译pdf.py b/工具v4/指定题号编译pdf.py
new file mode 100644
index 00000000..03144841
--- /dev/null
+++ b/工具v4/指定题号编译pdf.py
@@ -0,0 +1,166 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_指定题号编译pdf import Ui_Form
+import os
+from database_tools_2 import *
+
+
+
+
+
+class MyWindow_xtby(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+ def bind(self):
+ outputpath = os.path.join(os.getcwd(),"临时文件")
+ self.lineEdit_path.setText(outputpath)
+ self.TitleID_list = []
+ self.TeachersCheckBoxList = [self.checkBox_ans,self.checkBox_objs,self.checkBox_tags,self.checkBox_solution,self.checkBox_usages,self.checkBox_origin,self.checkBox_remark]
+ self.StudentsCheckBoxList = [self.checkBox_space,self.checkBox_ans]
+ self.ContnetCheckBoxList = [self.checkBox_ans,self.checkBox_objs,self.checkBox_tags,self.checkBox_solution,self.checkBox_usages,self.checkBox_origin,self.checkBox_remark,self.checkBox_space]
+ for widget in self.ContnetCheckBoxList:
+ widget.setDisabled(True)
+ self.studentlayout()
+ self.disableusagessetting()
+ self.pushButton_exec.clicked.connect(self.saveandbuild)
+ self.pushButton_add.clicked.connect(self.additems)
+ self.pushButton_delete.clicked.connect(self.deleteitem)
+ self.pushButton_SelectPath.clicked.connect(self.selectpath)
+ self.radioButton_teacher.clicked.connect(self.teacherlayout)
+ self.radioButton_student.clicked.connect(self.studentlayout)
+ self.checkBox_usages.clicked.connect(self.toggleusagessetting)
+ self.radioButton_student.clicked.connect(self.toggleusagessetting)
+ self.radioButton_teacher.clicked.connect(self.toggleusagessetting)
+ def selectpath(self):
+ outputpath = QFileDialog.getExistingDirectory(self,"选择文件夹")
+ self.lineEdit_path.setText(outputpath)
+ def additems(self):
+ toAddString = self.plainTextEdit_newitems.toPlainText()
+ try:
+ toAddList = TitleIDStringtoTupleList(toAddString)
+ self.TitleID_list += toAddList.copy()
+ except:
+ print("待添加字符串格式有误, 请检查")
+ self.plainTextEdit_allitems.setPlainText(TitleIDTupleListtoString(self.TitleID_list))
+ def deleteitem(self):
+ enum = int(self.lineEdit_index.text())-1
+ self.TitleID_list.pop(enum)
+ self.plainTextEdit_allitems.setPlainText(TitleIDTupleListtoString(self.TitleID_list))
+
+ def disableusagessetting(self):
+ self.lineEdit_high.setDisabled(True)
+ self.lineEdit_low.setDisabled(True)
+ self.lineEdit_grades.setDisabled(True)
+ def enableusagessetting(self):
+ self.lineEdit_high.setEnabled(True)
+ self.lineEdit_low.setEnabled(True)
+ self.lineEdit_grades.setEnabled(True)
+ def toggleusagessetting(self):
+ if self.checkBox_usages.isChecked() and self.radioButton_teacher.isChecked():
+ self.enableusagessetting()
+ else:
+ self.disableusagessetting()
+ def studentlayout(self):
+ for widget in self.TeachersCheckBoxList:
+ widget.setDisabled(True)
+ for widget in self.StudentsCheckBoxList:
+ widget.setEnabled(True)
+
+ def teacherlayout(self):
+ for widget in self.StudentsCheckBoxList:
+ widget.setDisabled(True)
+ for widget in self.TeachersCheckBoxList:
+ widget.setEnabled(True)
+ def generate_usages(self):
+ high = self.lineEdit_high.text().strip()
+ low = self.lineEdit_low.text().strip()
+ if self.checkBox_usages.isChecked() == False:
+ return [-2,-2]
+ elif self.checkBox_usages.isChecked() and high == "" and low == "":
+ return [-1,-1]
+ else:
+ if high == "":
+ high = "0"
+ if low == "":
+ low = "0"
+ return [int(high),int(low)]
+ def saveandbuild(self):
+ if self.radioButton_teacher.isChecked() and self.checkBox_usages.isChecked() and not self.lineEdit_grades.text().strip() == "":
+ grades = self.lineEdit_grades.text().strip().split(",")
+ else:
+ grades = []
+ # prodictpath = "../题库0.3/Problems.json"
+ # objdictpath = "../题库0.3/LessonObj.json"
+ # raw_pro_dict = load_dict(prodictpath)
+ # pro_dict = select_grade_from_pro_dict(raw_pro_dict,grades)
+ # obj_dict = load_dict(objdictpath)
+ configjson = {
+ "教师版": self.radioButton_teacher.isChecked(),
+ "字段显示设置": {
+ "题后空间": self.checkBox_space.isChecked(),
+ "课时目标": self.checkBox_objs.isChecked(),
+ "题目标签": self.checkBox_tags.isChecked(),
+ "答案": self.checkBox_ans.isChecked(),
+ "解答与提示": self.checkBox_solution.isChecked(),
+ "使用记录": self.generate_usages(),
+ "来源": self.checkBox_origin.isChecked(),
+ "备注": self.checkBox_remark.isChecked(),
+ "届别": grades
+ }
+ }
+
+ notetitle = "选题编译"
+ outputdir = self.lineEdit_path.text() #输出文件的目录
+ outputfilepath = os.path.join(outputdir,notetitle+".tex")
+ print("输出文件目录: %s\n输出文件名: %s"%(outputdir,notetitle+".tex"))
+
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ if configjson["教师版"] == True:
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学")
+
+ if sys.platform != "win32": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+
+ bodystring = "\\tableofcontents\n\n\\newpage\n\n"
+ bodylist = []
+ # problems_dict = configjson["标题与题号"]
+ starttime = time.time()
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ for p,IDs in self.TitleID_list:
+ currentbodystring = f"\\section{{{p}}}\n\\begin{{enumerate}}\n\n"
+ print(f"正在从数据库获取数据, 生成源代码...")
+ for id in tqdm.tqdm(generate_number_set(IDs)):
+ currentbodystring += generateLaTeXBodyContentfromMariaDB(mycursor,id,configjson)
+ currentbodystring += "\\end{enumerate}"
+ bodylist.append(currentbodystring)
+ bodystring += "\n\n\\newpage\n\n".join(bodylist)
+
+ midtime = time.time()
+ print(f"生成LaTeX文件所花时间: {midtime-starttime:.3f}秒")
+
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,(notetitle,bodystring)) #替换标题和bodystring
+ SaveTextFile(latex_data,outputfilepath) #保存.tex文件
+
+ if XeLaTeXCompile(outputdir,notetitle+".tex"):
+ print("编译成功")
+ else:
+ print("编译失败")
+
+ endtime = time.time()
+ print(f"生成pdf文件所花时间: {endtime-midtime:.3f}秒")
+ startfile(self.lineEdit_path.text())
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/指定题号编译pdf.ui b/工具v4/指定题号编译pdf.ui
new file mode 100644
index 00000000..5afdf2e1
--- /dev/null
+++ b/工具v4/指定题号编译pdf.ui
@@ -0,0 +1,369 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 指定题号编译
+
+
+
+
+ 500
+ 279
+ 221
+ 131
+
+
+
+
+ true
+
+
+
+ 开始生成与编译
+
+
+
+
+
+ 300
+ 79
+ 61
+ 16
+
+
+
+ 文件路径
+
+
+
+
+
+ 300
+ 99
+ 421
+ 20
+
+
+
+
+
+
+ 640
+ 69
+ 81
+ 24
+
+
+
+ 选择路径
+
+
+
+
+
+ 300
+ 139
+ 81
+ 271
+
+
+
+ -
+
+
+ 版式选择
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 学生版
+
+
+ true
+
+
+
+ -
+
+
+ 教师版
+
+
+
+
+
+
+
+
+ 400
+ 139
+ 91
+ 271
+
+
+
+ -
+
+
+ 内容选项
+
+
+
+ -
+
+
+ 题后空间
+
+
+
+ -
+
+
+ 答案
+
+
+
+ -
+
+
+ 课时目标
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+ 题目标签
+
+
+
+ -
+
+
+ 解答与提示
+
+
+
+ -
+
+
+ 使用记录
+
+
+
+ -
+
+
+ 来源
+
+
+
+ -
+
+
+ 备注
+
+
+
+
+
+
+
+
+ 500
+ 139
+ 222
+ 121
+
+
+
+ -
+
+
+ 使用记录选择(都留空为全显示)
+
+
+
+ -
+
+
-
+
+
+ 高
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ 低
+
+
+
+ -
+
+
+
+
+ -
+
+
+ 届别选择(用 "," 分隔, 留空为全显示)
+
+
+
+ -
+
+
+
+
+
+
+
+ 30
+ 219
+ 261
+ 191
+
+
+
+
+
+
+ 30
+ 199
+ 54
+ 16
+
+
+
+ 题号列表
+
+
+
+
+
+ 31
+ 50
+ 60
+ 16
+
+
+
+ 标题与题号
+
+
+
+
+
+ 30
+ 69
+ 261
+ 71
+
+
+
+ 如: 集合: 2:30,32 或 "集合": "2:30", "函数": "5,60,71:75"(可有回车)
+
+
+
+
+
+ 40
+ 149
+ 241
+ 28
+
+
+
+ -
+
+
+ 添加一项或多项
+
+
+
+ -
+
+
-
+
+
+ 删去一项
+
+
+
+ -
+
+
-
+
+
+
+ 20
+ 16777215
+
+
+
+ 第
+
+
+
+ -
+
+
+
+ 20
+ 16777215
+
+
+
+
+ -
+
+
+
+ 20
+ 16777215
+
+
+
+ 项
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/文件或文本框提取答案.py b/工具v4/文件或文本框提取答案.py
new file mode 100644
index 00000000..04294d09
--- /dev/null
+++ b/工具v4/文件或文本框提取答案.py
@@ -0,0 +1,56 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_文件或文本框提取答案 import Ui_Form
+import os
+from database_tools_2 import *
+
+
+class MyWindow_tqda(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.radioButton_text.setChecked(True)
+ self.textlayout()
+ self.radioButton_text.clicked.connect(self.textlayout)
+ self.radioButton_file.clicked.connect(self.filelayout)
+ self.pushButton_selectfilepath.clicked.connect(self.selectfilepath)
+ self.pushButton_exec.clicked.connect(self.exec)
+ def textlayout(self):
+ self.pushButton_selectfilepath.setDisabled(True)
+ self.lineEdit_filepath.setDisabled(True)
+ self.plainTextEdit_rawString.setEnabled(True)
+ def filelayout(self):
+ self.pushButton_selectfilepath.setEnabled(True)
+ self.lineEdit_filepath.setEnabled(True)
+ self.plainTextEdit_rawString.setDisabled(True)
+ def selectfilepath(self):
+ self.filepath = QFileDialog.getOpenFileName(self,"选择.tex文件",os.getcwd(),"tex文件(*.tex);;所有文件(*)")[0]
+ self.lineEdit_filepath.setText(self.filepath)
+ def exec(self):
+ if self.radioButton_text.isChecked():
+ data = self.plainTextEdit_rawString.toPlainText()
+ else:
+ data = ReadTextFile(self.filepath)
+ print(self.lineEdit_2.text())
+ anslist = generateAnswerList(data,self.lineEdit_2.text())
+
+ output = "ans\n\n"
+ for id,ans in anslist:
+ if not ans == "暂无答案":
+ output += f"{id}\n{ans}\n\n"
+
+ self.plainTextEdit_ans.setPlainText(output)
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_tqda()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/文件或文本框提取答案.ui b/工具v4/文件或文本框提取答案.ui
new file mode 100644
index 00000000..0557da07
--- /dev/null
+++ b/工具v4/文件或文本框提取答案.ui
@@ -0,0 +1,174 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 文件或文本框提取答案
+
+
+
+
+ 40
+ 100
+ 311
+ 331
+
+
+
+
+
+
+ 40
+ 50
+ 331
+ 20
+
+
+
+ true
+
+
+
+
+
+ 40
+ 30
+ 54
+ 16
+
+
+
+ 文件路径
+
+
+
+
+
+ 40
+ 80
+ 101
+ 16
+
+
+
+ 含答案的源代码
+
+
+
+
+
+ 492
+ 50
+ 211
+ 20
+
+
+
+ 答案: \textcolor{blue}{
+
+
+
+
+
+ 490
+ 30
+ 54
+ 16
+
+
+
+ 答案前缀
+
+
+
+
+
+ 610
+ 90
+ 95
+ 20
+
+
+
+ 文本框生成
+
+
+
+
+
+ 610
+ 120
+ 95
+ 20
+
+
+
+ 文件生成
+
+
+
+
+
+ 380
+ 50
+ 101
+ 24
+
+
+
+ 选择文件路径
+
+
+
+
+
+ 360
+ 80
+ 91
+ 16
+
+
+
+ 答案metadata
+
+
+
+
+
+ 360
+ 100
+ 241
+ 331
+
+
+
+
+
+
+ 610
+ 150
+ 101
+ 281
+
+
+
+
+ true
+
+
+
+ 提取答案
+
+
+
+
+
+
diff --git a/工具v4/文本文件/新题收录列表.txt b/工具v4/文本文件/新题收录列表.txt
new file mode 100644
index 00000000..c7372d99
--- /dev/null
+++ b/工具v4/文本文件/新题收录列表.txt
@@ -0,0 +1,3 @@
+20240425-215540
+024789,000589,024790:024794
+
diff --git a/工具v4/文本转换处理.py b/工具v4/文本转换处理.py
new file mode 100644
index 00000000..d44ab4ac
--- /dev/null
+++ b/工具v4/文本转换处理.py
@@ -0,0 +1,43 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_文本转换处理 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_wbzh(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.radioButton_mathpix.setChecked(True)
+ self.pushButton_convert.clicked.connect(self.exec)
+ def exec(self):
+ raw_string = self.plainTextEdit_origin.toPlainText()
+ if self.radioButton_mathpix.isChecked():
+ dest_string = RefineMathpix(raw_string)
+ elif self.radioButton_textcircled.isChecked():
+ dest_string = re.sub(r"\((\d)\)",lambda x: "\\textcircled{"+x.group(1)+"}",raw_string) #替换所有的小括号包围的单个数字为圆圈包围的
+ dest_string = re.sub(r"\$\\textcircled\{\\scriptsize\{(\d)\}\}",lambda x: "\\textcircled{"+x.group(1)+"}$",dest_string)
+ dest_string = re.sub(r"\\textcircled\{\\scriptsize\{(\d)\}\}",lambda x: "\\textcircled{"+x.group(1)+"}",dest_string)
+ elif self.radioButton_multiple.isChecked():
+ try:
+ dest_string = MultiplechoicetoBlankFilling(raw_string)
+ except:
+ dest_string = "转换失败"
+ elif self.radioButton_puctuations.isChecked():
+ dest_string = RefinePunctuations(raw_string)
+ elif self.radioButton_answers.isChecked():
+ self.pro_dict = load_dict("../题库0.3/Problems.json")
+ dest_string = PaintRedAnswers(raw_string,self.pro_dict)
+ self.plainTextEdit_dest.setPlainText(dest_string)
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/文本转换处理.ui b/工具v4/文本转换处理.ui
new file mode 100644
index 00000000..a96ec25e
--- /dev/null
+++ b/工具v4/文本转换处理.ui
@@ -0,0 +1,142 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 文本转换处理
+
+
+
+
+ 10
+ 30
+ 310
+ 430
+
+
+
+ 将源文本 粘贴 在这个文本框内
+
+
+
+
+
+ 10
+ 10
+ 54
+ 16
+
+
+
+ 源文本
+
+
+
+
+
+ 430
+ 10
+ 54
+ 16
+
+
+
+ 目标文本
+
+
+
+
+
+ 430
+ 30
+ 310
+ 430
+
+
+
+ false
+
+
+ true
+
+
+ 按 转换 以获得目标文本
+
+
+
+
+
+ 330
+ 280
+ 91
+ 71
+
+
+
+
+ true
+
+
+
+ 转换
+
+
+
+
+
+ 330
+ 100
+ 97
+ 126
+
+
+
+ -
+
+
+ Mathpix
+
+
+
+ -
+
+
+ 圆圈数字
+
+
+
+ -
+
+
+ 多选转填空
+
+
+
+ -
+
+
+ 标点转半角
+
+
+
+ -
+
+
+ 已有答案标红
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/新增基础知识梳理.py b/工具v4/新增基础知识梳理.py
new file mode 100644
index 00000000..f08cb940
--- /dev/null
+++ b/工具v4/新增基础知识梳理.py
@@ -0,0 +1,71 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_新增基础知识梳理 import Ui_Form
+import os
+from database_tools_2 import *
+
+class MyWindow_tjjc(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+ def bind(self):
+ self.pushButton_exec.setDisabled(True)
+ self.pushButton_selectfilepath.clicked.connect(self.selectfilepath)
+ self.pushButton_exec.clicked.connect(self.exec)
+ def selectfilepath(self):
+ self.filepath = QFileDialog.getOpenFileName(self,"选择.tex文件",os.getcwd(),"tex文件(*.tex);;所有文件(*)")[0]
+ self.lineEdit_filepath.setText(self.filepath)
+ self.pushButton_exec.setEnabled(True)
+ def exec(self):
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ mycursor.execute("SELECT bn_id FROM basic_knowledges;")
+ bnids = [ret[0] for ret in mycursor.fetchall()]
+ bkdata_raw = ReadTextFile(self.filepath)
+ bkdatabody = re.findall(r"\\begin\{enumerate\}([\s\S]*?)\\end\{enumerate\}",bkdata_raw)[0].strip().split("\\item")
+ bkdata = [line.strip() for line in bkdatabody if "[" in line and "]" in line]
+ currentid = int(max(bnids)[1:])+1
+
+
+
+ for line in bkdata:
+ if not line.strip() == "":
+ bnid = f"B{str(currentid).zfill(5)}"
+ pos = line.index("]")
+ head = line[1:pos]
+ content = line[pos+1:].strip()
+ lesson,objs = head.split("/")
+ obj_list = objs.split(",")
+ print(f"{bnid} {obj_list} {content}")
+ sql = "INSERT INTO basic_knowledges (bn_id,bn_content) VALUE (%s,%s);"
+ val = (bnid,content)
+ mycursor.execute(sql,val)
+ for objid in obj_list:
+ sql = "INSERT INTO bn_obj_corresp (bn_id,obj_id) VALUE (%s,%s);"
+ val = (bnid,objid)
+ mycursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,db_content) VALUE (%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),"新增基础",f"{bnid} {obj_list} {content}")
+ mycursor.execute(sql,val)
+ currentid += 1
+ mydb.commit()
+ mydb.close()
+ print("已录入完成")
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/新增基础知识梳理.ui b/工具v4/新增基础知识梳理.ui
new file mode 100644
index 00000000..77161e74
--- /dev/null
+++ b/工具v4/新增基础知识梳理.ui
@@ -0,0 +1,91 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 新增基础知识梳理
+
+
+
+
+ 200
+ 160
+ 91
+ 16
+
+
+
+ LaTeX文件位置
+
+
+
+
+
+ 200
+ 180
+ 381
+ 20
+
+
+
+ true
+
+
+
+
+
+ 494
+ 150
+ 91
+ 24
+
+
+
+ 选择文件
+
+
+
+
+
+ 200
+ 210
+ 291
+ 51
+
+
+
+ LaTeX文件中有一个enumerate环境.
+每一个item后面有一个中括号包围的信息, 之后是内容.
+例如: \item [K0101/K0101001B,K0101002B] 内容
+
+
+
+
+
+ 500
+ 210
+ 81
+ 51
+
+
+
+
+ true
+
+
+
+ 添加基础知识
+
+
+
+
+
+
diff --git a/工具v4/根据正确率选择题号.py b/工具v4/根据正确率选择题号.py
new file mode 100644
index 00000000..5df7bf43
--- /dev/null
+++ b/工具v4/根据正确率选择题号.py
@@ -0,0 +1,48 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_根据正确率选择题号 import Ui_widget
+from database_tools_2 import *
+
+class MyWindow_ndsx(QWidget,Ui_widget):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+ def exec(self):
+ startdate = self.lineEdit_startdate.text()
+ enddate = self.lineEdit_enddate.text()
+ interval = [float(self.lineEdit_lowerbound.text()),float(self.lineEdit_upperbound.text())]
+ classregex = self.lineEdit_classregex.text()
+ chosendict,cautionlist,usedlist = ChooseIDsByUsageInterval(startdate,enddate,interval,classregex,self.database_name)
+ self.plainTextEdit_ids.setPlainText(generate_exp(list(chosendict.keys())))
+ self.plainTextEdit_cautionids.setPlainText(generate_exp(cautionlist))
+ subp_output = ""
+ for id in chosendict.keys():
+ if chosendict[id] != []:
+ subp_output += f"题号 {id}: "
+ for s in chosendict[id]:
+ subp_output += f"第 ({s}) 小题 "
+ subp_output += "\n"
+ self.plainTextEdit_subproblems.setPlainText(subp_output)
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/根据正确率选择题号.ui b/工具v4/根据正确率选择题号.ui
new file mode 100644
index 00000000..031c6e08
--- /dev/null
+++ b/工具v4/根据正确率选择题号.ui
@@ -0,0 +1,236 @@
+
+
+ widget
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 根据正确率选择题号
+
+
+
+
+ 180
+ 10
+ 91
+ 92
+
+
+
+ -
+
+
+ 起始日期
+
+
+
+ -
+
+
+ yyyymmdd
+
+
+
+ -
+
+
+ 终止日期
+
+
+
+ -
+
+
+
+
+
+ yyyymmdd
+
+
+
+
+
+
+
+
+ 280
+ 10
+ 91
+ 92
+
+
+
+ -
+
+
+ 正确率下界
+
+
+
+ -
+
+
+ 如: 0.5
+
+
+
+ -
+
+
+ 正确率上界
+
+
+
+ -
+
+
+
+
+
+ 如: 0.73
+
+
+
+
+
+
+
+
+ 380
+ 10
+ 91
+ 92
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+ 班级正则表达式
+
+
+
+ -
+
+
+
+
+
+ 如: 2026
+
+
+
+
+
+
+
+
+ 480
+ 10
+ 91
+ 91
+
+
+
+
+ true
+
+
+
+ 生成列表
+
+
+
+
+
+ 180
+ 110
+ 54
+ 16
+
+
+
+ 题号
+
+
+
+
+
+ 180
+ 130
+ 391
+ 81
+
+
+
+ true
+
+
+
+
+
+ 180
+ 220
+ 141
+ 16
+
+
+
+ 使用记录长度不一致题号
+
+
+
+
+
+ 180
+ 240
+ 391
+ 81
+
+
+
+ true
+
+
+
+
+
+ 180
+ 330
+ 54
+ 16
+
+
+
+ 关注小题
+
+
+
+
+
+ 180
+ 350
+ 391
+ 111
+
+
+
+ true
+
+
+
+
+
+
diff --git a/工具v4/模板文件/合集模板.txt b/工具v4/模板文件/合集模板.txt
new file mode 100644
index 00000000..1fbae70a
--- /dev/null
+++ b/工具v4/模板文件/合集模板.txt
@@ -0,0 +1,70 @@
+\documentclass[10pt,a4paper,twoside]{report}
+\usepackage[UTF8, fontset = none, heading = true]{ctex}
+\setCJKmainfont[BoldFont=黑体,ItalicFont=楷体]{华文中宋}
+\usepackage{amssymb,amsmath,amsfonts,amsthm,mathrsfs,graphicx}
+\usepackage{ifthen,indentfirst,enumerate,color,lastpage}
+\usepackage{tikz}
+\usepackage{multicol}
+\usepackage{multirow}
+\usepackage{makecell}
+\usepackage{longtable}
+\usepackage{diagbox}
+\usepackage{hyperref}
+\usepackage{tcolorbox}
+\usepackage[top=1in, bottom=1in,left=0.8in,right=0.8in]{geometry}
+\usepackage{fancyhdr}
+\fancyhf{}
+\fancyhead[LO]{学号\blank{50} \ 姓名\blank{80}}
+\chead{\leftmark}
+\rhead{--\ \thepage\ of \pageref{LastPage} \ --}
+\pagestyle{fancy}
+\ctexset{section={
+name={},
+number=\chinese{section},
+}}
+\ctexset{chapter = {name = {}, number = {}, format = \center\large\bfseries}}
+\CTEXsetup[format={\bfseries\raggedright}]{section}
+\usetikzlibrary{arrows,calc,intersections,patterns,decorations.pathreplacing,3d,angles,quotes,positioning,shapes.geometric}
+
+\renewcommand{\baselinestretch}{1.5}
+\newtheorem{defi}{定义~}
+\newtheorem{eg}{例~}
+\newtheorem{ex}{~}
+\newtheorem{rem}{注~}
+\newtheorem{thm}{定理~}
+\newtheorem{coro}{推论~}
+\newtheorem{axiom}{公理~}
+\newtheorem{prop}{性质~}
+\newcommand{\blank}[1]{\underline{\hbox to #1pt{}}}
+\newcommand{\bracket}[1]{(\hbox to #1pt{})}
+\newcommand{\onech}[4]{\par\begin{tabular}{p{.9\linewidth}}
+A.~#1\\
+B.~#2\\
+C.~#3\\
+D.~#4
+\end{tabular}}
+\newcommand{\twoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{0\linewidth}p{.46\linewidth}p{0\linewidth}}
+A.~#1& &B.~#2&\\
+C.~#3& &D.~#4&
+\end{tabular}}
+\newcommand{\vartwoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{.46\linewidth}}
+(1)~#1& (2)~#2\\
+(3)~#3& (4)~#4
+\end{tabular}}
+\newcommand{\fourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+A.~#1 &B.~#2& C.~#3& D.~#4
+\end{tabular}}
+\newcommand{\varfourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+(1)~#1 &(2)~#2& (3)~#3& (4)~#4
+\end{tabular}}
+
+\tcbuselibrary{breakable}
+
+
+\begin{document}
+
+<<待替换1>>
+
+
+
+\end{document}
\ No newline at end of file
diff --git a/工具v4/模板文件/手动统计结果模板.txt b/工具v4/模板文件/手动统计结果模板.txt
new file mode 100644
index 00000000..ea863eab
--- /dev/null
+++ b/工具v4/模板文件/手动统计结果模板.txt
@@ -0,0 +1,6 @@
+[BEGIN]
+##20240101
+**2026届高一01班
+1 0.875
+10001 0.825 0.630
+[END]
\ No newline at end of file
diff --git a/工具v4/模板文件/独立文件模板.txt b/工具v4/模板文件/独立文件模板.txt
new file mode 100644
index 00000000..6b22aa53
--- /dev/null
+++ b/工具v4/模板文件/独立文件模板.txt
@@ -0,0 +1,50 @@
+\documentclass[border = {2pt 2pt 2pt 2pt},varwidth=50em,12pt]{standalone}
+\usepackage[UTF8,fontset = none]{ctex}
+\setCJKmainfont[BoldFont=黑体,ItalicFont=楷体]{华文中宋}
+\usepackage{amssymb,amsmath,amsfonts,amsthm,mathrsfs,graphicx}
+\usepackage{ifthen,indentfirst,enumerate,color,titletoc}
+\usepackage{tikz}
+\usepackage{multicol}
+\usepackage{multirow}
+\usepackage{makecell}
+\usepackage{longtable}
+\usepackage{diagbox}
+\usetikzlibrary{arrows,calc,intersections,patterns,decorations.pathreplacing,3d,angles,quotes,positioning,shapes.geometric}
+\usepackage[bf,small,indentafter,pagestyles]{titlesec}
+% \usepackage[top=1in, bottom=1in,left=0.8in,right=0.8in]{geometry}
+\renewcommand{\baselinestretch}{1.65}
+\newtheorem{defi}{定义~}
+\newtheorem{eg}{例~}
+\newtheorem{ex}{~}
+\newtheorem{rem}{注~}
+\newtheorem{thm}{定理~}
+\newtheorem{coro}{推论~}
+\newtheorem{axiom}{公理~}
+\newtheorem{prop}{性质~}
+\newcommand{\blank}[1]{\underline{\hbox to #1pt{}}}
+\newcommand{\bracket}[1]{(\hbox to #1pt{})}
+\newcommand{\onech}[4]{\par\begin{tabular}{p{.9\linewidth}}
+A.~#1\\
+B.~#2\\
+C.~#3\\
+D.~#4
+\end{tabular}}
+\newcommand{\twoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{.0\linewidth}p{.46\linewidth}p{.0\textwidth}}
+A.~#1& &B.~#2&\\
+C.~#3& &D.~#4&
+\end{tabular}}
+\newcommand{\vartwoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{.46\linewidth}}
+(1)~#1& (2)~#2\\
+(3)~#3& (4)~#4
+\end{tabular}}
+\newcommand{\fourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+A.~#1 &B.~#2& C.~#3& D.~#4
+\end{tabular}}
+\newcommand{\varfourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+(1)~#1 &(2)~#2& (3)~#3& (4)~#4
+\end{tabular}}
+\begin{document}
+
+<<待替换1>>
+
+\end{document}
\ No newline at end of file
diff --git a/工具v4/模板文件/讲义模板.txt b/工具v4/模板文件/讲义模板.txt
new file mode 100644
index 00000000..3d807fbf
--- /dev/null
+++ b/工具v4/模板文件/讲义模板.txt
@@ -0,0 +1,72 @@
+\documentclass[10pt,a4paper,twoside]{article}
+\usepackage[UTF8, fontset = none, heading = true]{ctex}
+\setCJKmainfont[BoldFont=黑体,ItalicFont=楷体]{华文中宋}
+\usepackage{amssymb,amsmath,amsfonts,amsthm,mathrsfs,graphicx}
+\usepackage{ifthen,indentfirst,enumerate,color,lastpage}
+\usepackage{tikz}
+\usepackage{multicol}
+\usepackage{multirow}
+\usepackage{makecell}
+\usepackage{longtable}
+\usepackage{diagbox}
+\usepackage{hyperref}
+\usepackage{tcolorbox}
+\usepackage[top=1in, bottom=1in,left=0.8in,right=0.8in]{geometry}
+\usepackage{fancyhdr}
+\fancyhf{}
+\fancyhead[LO]{学号\blank{50} \ 姓名\blank{80}}
+\chead{\papername}
+\rhead{--\ \thepage\ of \pageref{LastPage} \ --}
+\pagestyle{fancy}
+\ctexset{section={
+name={},
+number=\chinese{section},
+}}
+\CTEXsetup[format={\bfseries\raggedright}]{section}
+\usetikzlibrary{arrows,calc,intersections,patterns,decorations.pathreplacing,3d,angles,quotes,positioning,shapes.geometric}
+
+\renewcommand{\baselinestretch}{1.5}
+\newtheorem{defi}{定义~}
+\newtheorem{eg}{例~}
+\newtheorem{ex}{~}
+\newtheorem{rem}{注~}
+\newtheorem{thm}{定理~}
+\newtheorem{coro}{推论~}
+\newtheorem{axiom}{公理~}
+\newtheorem{prop}{性质~}
+\newcommand{\blank}[1]{\underline{\hbox to #1pt{}}}
+\newcommand{\bracket}[1]{(\hbox to #1pt{})}
+\newcommand{\onech}[4]{\par\begin{tabular}{p{.9\linewidth}}
+A.~#1\\
+B.~#2\\
+C.~#3\\
+D.~#4
+\end{tabular}}
+\newcommand{\twoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{0\linewidth}p{.46\linewidth}p{0\linewidth}}
+A.~#1& &B.~#2&\\
+C.~#3& &D.~#4&
+\end{tabular}}
+\newcommand{\vartwoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{.46\linewidth}}
+(1)~#1& (2)~#2\\
+(3)~#3& (4)~#4
+\end{tabular}}
+\newcommand{\fourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+A.~#1 &B.~#2& C.~#3& D.~#4
+\end{tabular}}
+\newcommand{\varfourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+(1)~#1 &(2)~#2& (3)~#3& (4)~#4
+\end{tabular}}
+
+\tcbuselibrary{breakable}
+\newcommand{\papername}{<<待替换1>>}
+
+\begin{document}
+\begin{center}
+{\bf\large \papername}
+\end{center}
+
+<<待替换2>>
+
+
+
+\end{document}
\ No newline at end of file
diff --git a/工具v4/模板文件/题目编辑.txt b/工具v4/模板文件/题目编辑.txt
new file mode 100644
index 00000000..f3eccb46
--- /dev/null
+++ b/工具v4/模板文件/题目编辑.txt
@@ -0,0 +1,50 @@
+\documentclass[10pt,a4paper]{article}
+\usepackage[UTF8,fontset = none]{ctex}
+\setCJKmainfont[BoldFont=黑体,ItalicFont=楷体]{华文中宋}
+\usepackage{amssymb,amsmath,amsfonts,amsthm,mathrsfs,dsfont,graphicx}
+\usepackage{ifthen,indentfirst,enumerate,color,titletoc}
+\usepackage{tikz}
+\usepackage{multicol}
+\usepackage{multirow}
+\usepackage{makecell}
+\usepackage{longtable}
+\usepackage{diagbox}
+\usetikzlibrary{arrows,calc,intersections,patterns,decorations.pathreplacing,3d,angles,quotes,positioning,shapes.geometric}
+\usepackage[bf,small,indentafter,pagestyles]{titlesec}
+\usepackage[top=1in, bottom=1in,left=0.8in,right=0.8in]{geometry}
+\renewcommand{\baselinestretch}{1.65}
+\newtheorem{defi}{定义~}
+\newtheorem{eg}{例~}
+\newtheorem{ex}{~}
+\newtheorem{rem}{注~}
+\newtheorem{thm}{定理~}
+\newtheorem{coro}{推论~}
+\newtheorem{axiom}{公理~}
+\newtheorem{prop}{性质~}
+\newcommand{\blank}[1]{\underline{\hbox to #1pt{}}}
+\newcommand{\bracket}[1]{(\hbox to #1pt{})}
+\newcommand{\onech}[4]{\par\begin{tabular}{p{.9\linewidth}}
+A.~#1\\
+B.~#2\\
+C.~#3\\
+D.~#4
+\end{tabular}}
+\newcommand{\twoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{0\linewidth}p{.46\linewidth}p{0\linewidth}}
+A.~#1& &B.~#2&\\
+C.~#3& &D.~#4&
+\end{tabular}}
+\newcommand{\vartwoch}[4]{\par\begin{tabular}{p{.46\linewidth}p{.46\linewidth}}
+(1)~#1& (2)~#2\\
+(3)~#3& (4)~#4
+\end{tabular}}
+\newcommand{\fourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+A.~#1 &B.~#2& C.~#3& D.~#4
+\end{tabular}}
+\newcommand{\varfourch}[4]{\par\begin{tabular}{p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}p{.23\linewidth}}
+(1)~#1 &(2)~#2& (3)~#3& (4)~#4
+\end{tabular}}
+\begin{document}
+
+<<待替换>>
+
+\end{document}
\ No newline at end of file
diff --git a/工具v4/添加关联题目.py b/工具v4/添加关联题目.py
new file mode 100644
index 00000000..6b277df6
--- /dev/null
+++ b/工具v4/添加关联题目.py
@@ -0,0 +1,54 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_添加关联题目 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_tjgl(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+ def bind(self):
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def exec(self):
+ old_ids = RefinePunctuations(self.lineEdit_oldids.text()).strip() # 需要添加关联题目的id字符串
+ starting_id = self.lineEdit_newid.text().strip() # 目的地的id字符串从这个位置后的首个空闲开始, 其中空闲位置需不少于old_ids中的题目数量 20240124修改
+ editor = self.lineEdit_editor.text().strip() # 修改人姓名
+ new_ids = NextSpareIDBlock(starting_id,self.database_name)
+ links = CreateIDLinks(generate_number_set(old_ids),generate_number_set(new_ids))
+ bodydata = CreateRelatedProblems(links,self.database_name)
+ tempfilepath = "临时文件/problem_edit.tex"
+ latextemplate = "模板文件/题目编辑.txt"
+ latexdata = ReadTextFile(latextemplate)
+ latexdata = StringSubstitute("<<待替换>>",latexdata,(bodydata,))
+ SaveTextFile(latexdata,tempfilepath)
+ print("编辑完毕后, 保存关闭文件继续.")
+ os.system(f"code -w -g {tempfilepath}") #-w表示关闭窗口后继续下一步
+ if XeLaTeXCompile("临时文件",tempfilepath,times=1):
+ problems = GenerateProblemListFromString2024(ReadTextFile(tempfilepath))
+ for problem in problems:
+ id_and_content = problem[0]
+ oid = re.findall(r"^\((\d{6})->",id_and_content)[0]
+ id = re.findall(r"^\(\d{6}->(\d{6})\)",id_and_content)[0]
+ content = re.findall(r"->\d{6}\)([\S\s]*)$",id_and_content)[0].strip()
+ print(id,content)
+ AddRelatedProblemToDB(id,content,oid,editor,self.database_name)
+ print("编译成功, 已汇入题库")
+ else:
+ print("编译失败, 未汇入题库")
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/添加关联题目.ui b/工具v4/添加关联题目.ui
new file mode 100644
index 00000000..66f5e8d0
--- /dev/null
+++ b/工具v4/添加关联题目.ui
@@ -0,0 +1,103 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 添加关联题目
+
+
+
+
+ 460
+ 120
+ 81
+ 81
+
+
+
+
+ true
+
+
+
+ 添加关联题目
+
+
+
+
+
+ 250
+ 120
+ 196
+ 80
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ 旧题号
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ 新起始题号
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ 编辑者
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/生成直方图代码.py b/工具v4/生成直方图代码.py
new file mode 100644
index 00000000..b89035c1
--- /dev/null
+++ b/工具v4/生成直方图代码.py
@@ -0,0 +1,105 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_生成直方图代码 import Ui_Form
+import re
+
+
+class MyWindow_hist(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.pushButton_generatexlist.clicked.connect(self.generateXList)
+ self.pushButton_exec.clicked.connect(self.exec)
+ def generateXList(self):
+ self.xmin = int(self.lineEdit_xmin.text())
+ self.xmax = int(self.lineEdit_xmax.text())
+ self.xwidth = int(self.lineEdit_xwidth.text())
+ self.xlist = list(range(self.xmin,self.xmax,self.xwidth))
+ output = ""
+ for x in self.xlist:
+ output += f"({x},)[]\n"
+ self.plainTextEdit_yinfo.setPlainText(output)
+ def folding(self):
+ if self.xmin <= self.xwidth:
+ foldingstr = ""
+ else:
+ midpoint = 1/2 * (self.xstart + self.xmin)
+ dist = (midpoint - self.xstart) / 6
+ foldingstr = ""
+ height = 0.2/self.yscale
+ foldingstr += f" -- ({(self.xstart+dist*4):.2f},0)"
+ foldingstr += f" -- ({(self.xstart+dist*5):.2f},{height:.4f})"
+ foldingstr += f" -- ({(self.xstart+dist*7):.2f},{-height:.4f})"
+ foldingstr += f" -- ({(self.xstart+dist*8):.2f},0)"
+ return foldingstr
+ def exec(self):
+ xlabel = self.lineEdit_xlabel.text()
+ coordinateinfo = self.plainTextEdit_yinfo.toPlainText()
+ if self.xmin < self.xwidth: #计算x轴的起点坐标
+ self.xstart = 0
+ else:
+ self.xstart = self.xmin - self.xwidth
+ self.xmax = self.xlist[-1] + self.xwidth
+ self.xend = self.xmax + self.xwidth #计算x轴的终点坐标
+ self.xscale = 5/(self.xend-self.xstart) #计算x放缩比例
+ # print(self.xscale)
+ self.coordinatelist = re.findall(r"\(([\d\.]+),([\d\.]+)\)\[([\S]*)\]",coordinateinfo) #计算坐标信息
+ for cindex in range(len(self.coordinatelist)):
+ x,y,label = self.coordinatelist[cindex]
+ if label.strip() == "":
+ self.coordinatelist[cindex] = (x,y,y)
+ self.coordinatestr_raw = []
+ for coord in self.coordinatelist:
+ x,y,label = coord
+ self.coordinatestr_raw.append(f"{x}/{y}")
+ self.coordinatestr = ",".join(self.coordinatestr_raw)
+ # print(self.coordinatelist)
+ self.dasheddict = {} #计算以纵坐标为key的虚线信息, 内容为(x,label)
+ for coord in self.coordinatelist:
+ x,y,label = coord
+ if not y in self.dasheddict:
+ self.dasheddict[y] = (x,label)
+ else:
+ if x > self.dasheddict[y][0] and label == y:
+ self.dasheddict[y] = (x,self.dasheddict[y][1])
+ elif x > self.dasheddict[y][0] and label != y:
+ self.dasheddict[y] = (x,label)
+ self.dashedstr_raw = []
+ for y in self.dasheddict:
+ x,label = self.dasheddict[y]
+ self.dashedstr_raw.append(f"{x}/{y}/{label}")
+ self.dashedstr = ",".join(self.dashedstr_raw)
+ # print(self.dasheddict)
+ ymax = max([float(i) for i in self.dasheddict.keys()])
+ self.yscale = 3/ymax #3是y的长度, 计算y放缩比例
+ self.yend = ymax + 1/float(self.yscale)
+ latexcode = f"""\\begin{{center}}
+ \\begin{{tikzpicture}}[>=latex, xscale = {self.xscale:.4f}, yscale = {self.yscale:.4f}]
+ \\draw [->] ({self.xstart},0) {self.folding()}-- ({self.xend},0) node [below right] {{{xlabel}}};
+ \\draw [->] ({self.xstart},0) -- ({self.xstart},{self.yend:.2f}) node [left] {{$\\dfrac{{\\text{{频率}}}}{{\\text{{组距}}}}$}};
+ \\draw ({self.xstart},0) node [below] {{$0$}};
+ \\foreach \\i/\\j in {{{self.coordinatestr}}}
+ {{\\draw (\\i,0) node [below] {{$\\i$}} --++ (0,\\j) --++ ({self.xwidth},0) --++ (0,-\\j);}};
+ \\draw ({self.xmax},0) node [below] {{${self.xmax}$}};
+ \\foreach \i/\j/\k in {{{self.dashedstr}}}
+ {{\\draw [dashed] (\\i,\\j) -- ({self.xstart},\\j) node [left] {{$\\k$}};}};
+ \\end{{tikzpicture}}
+ \\end{{center}}"""
+ latexcode = re.sub(r"\n\s+","\n",latexcode)
+ self.plainTextEdit_latexcode.setPlainText(latexcode)
+
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/生成直方图代码.ui b/工具v4/生成直方图代码.ui
new file mode 100644
index 00000000..dc15b98e
--- /dev/null
+++ b/工具v4/生成直方图代码.ui
@@ -0,0 +1,193 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 生成直方图代码
+
+
+
+
+ 10
+ 130
+ 121
+ 24
+
+
+
+ 生成横坐标序列
+
+
+
+
+ false
+
+
+
+ 10
+ 160
+ 121
+ 211
+
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: "\2610"; }
+li.checked::marker { content: "\2612"; }
+</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">不改变横坐标的值, 在逗号后输入纵坐标的值</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">如果标签和纵坐标的值不同, 在后面的方括号内输入标签</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">如:</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(10,0.02)[]</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(20,0.04)[a]</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(30,0.04][]</p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
+
+
+
+
+
+ 140
+ 10
+ 121
+ 16
+
+
+
+ 输入纵坐标信息
+
+
+
+
+
+ 140
+ 30
+ 521
+ 181
+
+
+
+
+
+
+ 670
+ 30
+ 71
+ 181
+
+
+
+
+ true
+
+
+
+ 生成代码
+
+
+
+
+
+ 140
+ 220
+ 71
+ 16
+
+
+
+ 直方图代码
+
+
+
+
+
+ 140
+ 240
+ 601
+ 221
+
+
+
+
+
+
+ 10
+ 10
+ 121
+ 108
+
+
+
+ -
+
+
-
+
+
+ x最小值
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ x最大值
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ x宽度
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ x轴label
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/答题纸对应.py b/工具v4/答题纸对应.py
new file mode 100644
index 00000000..b4789853
--- /dev/null
+++ b/工具v4/答题纸对应.py
@@ -0,0 +1,132 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_答题纸对应 import Ui_Form
+from database_tools_2 import *
+
+def getindices(string):
+ indices_list = []
+ notesjsonpath = f"../备课组/{string[3:5]}届/校本材料.json"
+ try:
+ notesjson = load_dict(notesjsonpath)
+ for id in notesjson["notes"]:
+ if id.startswith(string):
+ indices_list.append(id[-2:])
+ return indices_list
+ except:
+ return []
+
+
+
+
+class MyWindow_dtlr(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.label_next.setVisible(False)
+ self.comboBox_grade.activated.connect(self.setindex)
+ self.comboBox_semester.activated.connect(self.setindex)
+ self.lineEdit_structure.textChanged.connect(self.setindex)
+ self.comboBox_index.activated.connect(self.activateexec)
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def setindex(self):
+ self.comboBox_index.clear()
+ self.structure = self.lineEdit_structure.text().upper()
+ self.grade = self.comboBox_grade.currentText()
+ self.semester = self.comboBox_semester.currentText()
+ self.initialstring = f"{self.structure}{self.grade}{self.semester}"
+ self.indices = getindices(self.initialstring)
+ self.comboBox_index.addItems(self.indices)
+ if self.indices == []:
+ self.pushButton_exec.setDisabled(True)
+ else:
+ self.pushButton_exec.setEnabled(True)
+ def activateexec(self):
+ self.pushButton_exec.setEnabled(True)
+ def unvisible(self):
+ self.label_next.setVisible(False)
+ def exec(self):
+ pid = f"{self.structure}{self.grade}{self.semester}{self.comboBox_index.currentText()}"
+ answersheetjson = f"../备课组/{self.grade[-2:]}届/答题纸对应.json"
+ notesjson = f"../备课组/{self.grade[-2:]}届/校本材料.json"
+ marks_list = re.findall(r"(?:(?:^)|(?:\n))(\d+)分",self.plainTextEdit_marks.toPlainText())
+ marks_list = [int(mark) for mark in marks_list]
+
+ answersheet_dict = load_dict(answersheetjson)
+ notes_dict = load_dict(notesjson)
+
+ new_dict = {}
+
+ if not pid in notes_dict["notes"]:
+ print("讲义编号有误.")
+ else:
+ self.label_next.setVisible(True)
+ self.repaint()
+ new_dict["id"] = pid
+ corresponding_method = input("用何种方式对应? 题号(I)/章节标题(P):")
+ if corresponding_method[0].upper() == "I":
+ new_dict["idlist"] = []
+ count = 1
+ id = input(f"输入第 {count} 题的题号(S表示跳过, E表示结束):")
+ while len(re.findall(r"[Ss\d,:]",id)) == len(id):
+ if "S" in id.upper():
+ new_dict["idlist"].append("999999")
+ count += 1
+ else:
+ new_id_list = generate_number_set(id)
+ new_dict["idlist"] += [i.zfill(6) for i in new_id_list]
+ count += len(new_id_list)
+ id = input(f"输入第 {count} 题的题号(S表示跳过, E表示结束):")
+
+
+ elif corresponding_method[0].upper() == "P":
+ structure = notes_dict["structures"][pid[0]]["structure"]
+ count = 1
+ partslist = []
+ for key in structure:
+ print(f"{count}. {key}: {structure[key]['name']}")
+ count += 1
+ partslist.append(key)
+ parts_selected_string = input("使用哪些部分(输入数字编号, 用';'分隔):")
+ parts_selected_index = parts_selected_string.strip().split(";")
+ parts_selected = []
+ for i in parts_selected_index:
+ parts_selected.append(partslist[int(i)-1])
+ new_dict["parts"] = parts_selected.copy()
+ if marks_list == []:
+ marksflag = input("是否为每个结果赋分(Y/N):")
+ if marksflag[0].upper() == "Y":
+ new_dict["marks"] = []
+ count = 1
+ mark = input(f"依次输入分数, 非自然数作为结束(第 {count} 个位置):")
+ while len(re.findall(r"\d",mark)) == len(mark):
+ new_dict["marks"].append(int(mark))
+ count += 1
+ mark = input(f"依次输入分数, 非自然数作为结束(第 {count} 个位置):")
+ else:
+ new_dict["marks"] = marks_list.copy()
+
+
+ answersheet_dict[self.lineEdit_xiaoxianid.text()] = new_dict.copy()
+ save_dict(answersheet_dict,answersheetjson)
+ self.label_next.setVisible(False)
+ print("设置完成")
+
+
+
+
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/答题纸对应.ui b/工具v4/答题纸对应.ui
new file mode 100644
index 00000000..f05f1afc
--- /dev/null
+++ b/工具v4/答题纸对应.ui
@@ -0,0 +1,250 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 答题纸对应
+
+
+
+
+ 180
+ 90
+ 81
+ 16
+
+
+
+ 对应讲义代码
+
+
+
+
+ false
+
+
+
+ 440
+ 140
+ 111
+ 191
+
+
+
+
+ true
+
+
+
+ 添加答题纸对应
+
+
+
+
+
+ 180
+ 60
+ 371
+ 22
+
+
+
+ -
+
+
+ 小闲试卷ID
+
+
+
+ -
+
+
+
+
+
+
+
+ 180
+ 110
+ 371
+ 23
+
+
+
+ -
+
+
+ 类型
+
+
+
+ -
+
+
+
+ 20
+ 16777215
+
+
+
+
+ -
+
+
+ 届别
+
+
+
+ -
+
+
-
+
+ 2022
+
+
+ -
+
+ 2023
+
+
+ -
+
+ 2024
+
+
+ -
+
+ 2025
+
+
+ -
+
+ 2026
+
+
+ -
+
+ 2027
+
+
+ -
+
+ 2028
+
+
+ -
+
+ 2029
+
+
+
+
+ -
+
+
+ 学期
+
+
+
+ -
+
+
-
+
+ 00
+
+
+ -
+
+ 01
+
+
+ -
+
+ 02
+
+
+ -
+
+ 03
+
+
+ -
+
+ 04
+
+
+ -
+
+ 05
+
+
+ -
+
+ 06
+
+
+
+
+ -
+
+
+ 序号
+
+
+
+ -
+
+
+
+
+
+
+
+ 180
+ 160
+ 251
+ 171
+
+
+
+ 将小闲平台答题纸 修改 界面中间靠右的所有分数复制到该文本框, 留空表示均为1分
+
+
+
+
+
+ 180
+ 140
+ 111
+ 16
+
+
+
+ 小闲平台各题满分
+
+
+
+
+
+ 180
+ 340
+ 161
+ 16
+
+
+
+ 请到终端完成之后的操作
+
+
+
+
+
+
diff --git a/工具v4/系列讲义生成.py b/工具v4/系列讲义生成.py
new file mode 100644
index 00000000..bc8d6228
--- /dev/null
+++ b/工具v4/系列讲义生成.py
@@ -0,0 +1,172 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_系列讲义生成 import Ui_Form
+from database_tools_2 import *
+import os
+
+class MyWindow_jysc(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.outputpath = os.path.join(os.getcwd(),"临时文件")
+ self.lineEdit_path.setText(self.outputpath)
+ self.TeachersCheckBoxList = [self.checkBox_ans,self.checkBox_objs,self.checkBox_tags,self.checkBox_solution,self.checkBox_usages,self.checkBox_origin,self.checkBox_remark]
+ self.StudentsCheckBoxList = [self.checkBox_space,self.checkBox_ans]
+ self.ContnetCheckBoxList = [self.checkBox_ans,self.checkBox_objs,self.checkBox_tags,self.checkBox_solution,self.checkBox_usages,self.checkBox_origin,self.checkBox_remark,self.checkBox_space]
+ for widget in self.ContnetCheckBoxList:
+ widget.setDisabled(True)
+ self.studentlayout()
+ self.disableusagessetting()
+ self.radioButton_teacher.clicked.connect(self.teacherlayout)
+ self.radioButton_student.clicked.connect(self.studentlayout)
+ self.checkBox_usages.clicked.connect(self.toggleusagessetting)
+ self.radioButton_student.clicked.connect(self.toggleusagessetting)
+ self.radioButton_teacher.clicked.connect(self.toggleusagessetting)
+ self.pushButton_exec.clicked.connect(self.saveandbuild)
+ self.pushButton_SelectPath.clicked.connect(self.selectpath)
+
+
+
+ def disableusagessetting(self):
+ self.lineEdit_high.setDisabled(True)
+ self.lineEdit_low.setDisabled(True)
+ self.lineEdit_grades.setDisabled(True)
+ def enableusagessetting(self):
+ self.lineEdit_high.setEnabled(True)
+ self.lineEdit_low.setEnabled(True)
+ self.lineEdit_grades.setEnabled(True)
+ def toggleusagessetting(self):
+ if self.checkBox_usages.isChecked() and self.radioButton_teacher.isChecked():
+ self.enableusagessetting()
+ else:
+ self.disableusagessetting()
+
+ def studentlayout(self):
+ for widget in self.TeachersCheckBoxList:
+ widget.setDisabled(True)
+ for widget in self.StudentsCheckBoxList:
+ widget.setEnabled(True)
+
+ def selectpath(self):
+ self.outputpath = QFileDialog.getExistingDirectory(None, "选择文件夹")
+ self.lineEdit_path.setText(self.outputpath)
+
+ def teacherlayout(self):
+ for widget in self.StudentsCheckBoxList:
+ widget.setDisabled(True)
+ for widget in self.TeachersCheckBoxList:
+ widget.setEnabled(True)
+ def generate_usages(self):
+ high = self.lineEdit_high.text().strip()
+ low = self.lineEdit_low.text().strip()
+ if self.checkBox_usages.isChecked() == False:
+ return [-2,-2]
+ elif self.checkBox_usages.isChecked() and high == "" and low == "":
+ return [-1,-1]
+ else:
+ if high == "":
+ high = "0"
+ if low == "":
+ low = "0"
+ return [int(high),int(low)]
+
+ def setdbname(self,string):
+ self.database_name = string
+ # print(self.database_name)
+
+ def saveandbuild(self):
+ patterns = self.lineEdit_regex.text().strip().split(",")
+ jsonpath = "../备课组" #有json文件的根目录, 文件名需为"校本材料.json"
+ jsondicts = []
+ for loc,dirs,files in os.walk(jsonpath):
+ if "校本材料.json" in files:
+ jsondicts.append(load_dict(os.path.join(loc,"校本材料.json")))
+
+ mydb = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ mycursor = mydb.cursor()
+ # raw_pro_dict = load_dict("../题库0.3/Problems.json")
+ # obj_dict = load_dict("../题库0.3/LessonObj.json")
+ # basicknowledge_dict = load_dict("../题库0.3/BasicKnowledge.json")
+ if self.radioButton_teacher.isChecked() and self.checkBox_usages.isChecked() and not self.lineEdit_grades.text().strip() == "":
+ grades = self.lineEdit_grades.text().strip().split(",")
+ else:
+ grades = []
+ # pro_dict = select_grade_from_pro_dict(raw_pro_dict,grades)
+ # dictionaries = {} #合并字典
+ # for t in (obj_dict,basicknowledge_dict,pro_dict):
+ # dictionaries.update(t)
+ configjson = {
+ "教师版": self.radioButton_teacher.isChecked(),
+ "字段显示设置": {
+ "题后空间": self.checkBox_space.isChecked(),
+ "课时目标": self.checkBox_objs.isChecked(),
+ "题目标签": self.checkBox_tags.isChecked(),
+ "答案": self.checkBox_ans.isChecked(),
+ "解答与提示": self.checkBox_solution.isChecked(),
+ "使用记录": self.generate_usages(),
+ "来源": self.checkBox_origin.isChecked(),
+ "备注": self.checkBox_remark.isChecked(),
+ "届别": grades
+ },
+ "编译单个文件": self.checkBox_singlenote.isChecked(),
+ "编译合集": self.checkBox_seriesnote.isChecked()
+ }
+ papernames = []
+ multitexdata = []
+ for notes_dict in jsondicts:
+ for lessonid in notes_dict["notes"]:
+ coincideflag = False
+ for lessonpattern in patterns:
+ if re.findall(lessonpattern,lessonid) != []:
+ coincideflag = True
+ break
+ if coincideflag:
+ print(f"正在生成 {lessonid} {notes_dict['notes'][lessonid]['filename']} 的 .tex 文件")
+ filename = notes_dict["notes"][lessonid]["id"]+notes_dict["notes"][lessonid]["filename"]+".tex"
+ papertype = lessonid[0]
+ consecutivenumbering = notes_dict["structures"][papertype]["consecutivenumbering"]
+ texdata = GenerateSingleLessonNotefromMariaDB(cursor = mycursor, id = lessonid,notesdict=notes_dict,templatepath="./模板文件/讲义模板.txt",outputfilepath = os.path.join(self.outputpath,filename),misc=configjson,consecutivenumbering = consecutivenumbering)
+ papernames.append(notes_dict["notes"][lessonid]["id"]+" \\ "+notes_dict["notes"][lessonid]["name"])
+ print(f"已生成 {papernames[-1]} 文件")
+ multitexdata.append(re.findall(r"\\begin{center}\n{\\bf\\large \\papername}\n\\end{center}([\s\S]*\\end\{enumerate\})",texdata)[0])
+ # print(lessonid)
+ # print(configjson)
+ # print("\n".join(patterns))
+ mydb.close()
+ merged = ""
+ for i in range(len(papernames)):
+ merged += "\n\n\\chapter{"+papernames[i]+"}\n\n\n"
+ merged += multitexdata[i]
+
+ print("已生成合集的 .tex 文件")
+
+ mergedtext = StringSubstitute(r"<<待替换[\d]+>>",ReadTextFile("./模板文件/合集模板.txt"),[merged])
+ if sys.platform != "win32":
+ mergedtext = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",mergedtext)
+ mergedtext = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",mergedtext)
+ SaveTextFile(mergedtext,os.path.join(self.outputpath,f"合集{GetDate()}.tex"))
+
+ # if not "编译合集" in configjson or configjson["编译合集"] == False:
+ # tocompile = input("需要编译合集吗?(Y/[N]):")
+ if configjson["编译合集"] == True:
+ XeLaTeXCompile(self.outputpath,f"合集{GetDate()}.tex")
+ print("合集编译完成")
+ print("执行完毕.")
+ startfile(self.outputpath)
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/系列讲义生成.ui b/工具v4/系列讲义生成.ui
new file mode 100644
index 00000000..b0b67dda
--- /dev/null
+++ b/工具v4/系列讲义生成.ui
@@ -0,0 +1,322 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 系列讲义生成
+
+
+
+
+ 120
+ 60
+ 431
+ 20
+
+
+
+
+
+
+ 30
+ 60
+ 91
+ 16
+
+
+
+ 讲义编号表达式
+
+
+
+
+
+ 570
+ 60
+ 171
+ 16
+
+
+
+ 用 "," 分隔的一系列正则表达式
+
+
+
+
+
+ 510
+ 310
+ 221
+ 131
+
+
+
+
+ true
+
+
+
+ 开始生成与编译
+
+
+
+
+
+ 30
+ 90
+ 61
+ 16
+
+
+
+ 文件路径
+
+
+
+
+
+ 30
+ 110
+ 701
+ 20
+
+
+
+
+
+
+ 660
+ 80
+ 75
+ 24
+
+
+
+ 选择路径
+
+
+
+
+
+ 30
+ 170
+ 81
+ 111
+
+
+
+ -
+
+
+ 版式选择
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 学生版
+
+
+ true
+
+
+
+ -
+
+
+ 教师版
+
+
+
+
+
+
+
+
+ 250
+ 170
+ 151
+ 271
+
+
+
+ -
+
+
+ 内容选项
+
+
+
+ -
+
+
+ 题后空间
+
+
+
+ -
+
+
+ 答案
+
+
+
+ -
+
+
+ 课时目标
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+ 题目标签
+
+
+
+ -
+
+
+ 解答与提示
+
+
+
+ -
+
+
+ 使用记录
+
+
+
+ -
+
+
+ 来源
+
+
+
+ -
+
+
+ 备注
+
+
+
+
+
+
+
+
+ 510
+ 170
+ 222
+ 121
+
+
+
+ -
+
+
+ 使用记录选择(都留空为全显示)
+
+
+
+ -
+
+
-
+
+
+ 高
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ 低
+
+
+
+ -
+
+
+
+
+ -
+
+
+ 届别选择(用 "," 分隔, 留空为全显示)
+
+
+
+ -
+
+
+
+
+
+
+
+ 30
+ 300
+ 81
+ 141
+
+
+
+ -
+
+
+ 编译选项
+
+
+
+ -
+
+
+ 单张编译
+
+
+
+ -
+
+
+ 合集编译
+
+
+ true
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/统考数据导入.py b/工具v4/统考数据导入.py
new file mode 100644
index 00000000..3192f106
--- /dev/null
+++ b/工具v4/统考数据导入.py
@@ -0,0 +1,96 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_统考数据导入 import Ui_Form
+import os
+from database_tools_2 import *
+
+
+class MyWindow_tkdr(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+
+ def bind(self):
+ self.pushButton_paper.clicked.connect(self.selectpaper)
+ self.pushButton_excel.clicked.connect(self.selectexcel)
+ self.pushButton_exec.clicked.connect(self.exec)
+
+ def selectpaper(self):
+ self.texfile = QFileDialog.getOpenFileName(self,"选择试卷文件",os.getcwd(),"试卷文件(*.tex *.pdf);;所有文件(*)")[0]
+ self.lineEdit_paperfile.setText(self.texfile)
+ def selectexcel(self):
+ self.excelfile = QFileDialog.getOpenFileName(self,"选择统计文件",os.getcwd(),"Excel文件(*.xlsx);;所有文件(*)")[0]
+ self.lineEdit_excelfile.setText(self.excelfile)
+ def exec(self):
+ sheetname = "难度统计" # excel中难度数据所在的工作表名
+ date = self.lineEdit_date.text().strip() # 考试日期
+ grade = self.lineEdit_grade.text().strip() # 考试年级
+ max_classnum = int(self.lineEdit_classnum.text().strip()) # 年级参加考试的最大班级班号
+ outputfilepath = r"文本文件/metadata.txt" # 输出的用于导入的metadata.txt文件位置
+ checkingfilepath = r"临时文件/手动统计结果.txt" # 用于检查结构的文件所在位置
+
+
+
+ df = pd.read_excel(self.excelfile,sheet_name = sheetname)
+ problems_list = generate_number_set(extractIDs(self.texfile))
+
+ #生成题号(1~n)与题库id对应
+ id_dict = {}
+ for i in range(len(problems_list)):
+ id_dict[i+1] = problems_list[i]
+
+ output = "usages\n\n"
+ checkoutput = ""
+ #生成题号(1~n)与表格中数据列的对应
+ idcol_dict = {}
+ for i in id_dict:
+ idcol_dict[i] = []
+ mincol = -1
+ for col_index in range(mincol+1,len(df.columns)):
+ col = df.columns[col_index]
+ if str(col).startswith(str(i)):
+ idcol_dict[i].append(col_index)
+ mincol = col_index
+ if str(i) == str(col):
+ break
+
+ #生成行号与班级的对应列表
+ classrows_dict = {}
+ for row in df.iloc[:,0]:
+ if type(row) == int or type(row) == float:
+ if 1<=row<=max_classnum:
+ row = int(row)
+ classrows_dict[list(df.iloc[:,0]).index(row)] = str(row).zfill(2) + "班"
+
+ #生成手动统计列表
+ for cl in classrows_dict:
+ classname = grade + classrows_dict[cl]
+ checkoutput += "[BEGIN]\n"
+ checkoutput += "## "+date+"\n"
+ checkoutput += "** "+classname+"\n"
+ for i in range(len(problems_list)):
+ id = id_dict[i+1]
+ results_cols = idcol_dict[i+1]
+ results = []
+ for col in results_cols:
+ results.append("%.3f"%df.iloc[cl,col])
+ checkoutput += id+"\t"+"\t".join(results) + "\n"
+ output += id + "\n" + "\t".join([date,classname]+results) + "\n\n"
+ checkoutput += "[END]\n\n\n"
+
+ SaveTextFile(output,outputfilepath)
+ print("可以在 %s 文件中检查结构"%(SaveTextFile(checkoutput,checkingfilepath)))
+ os.system("code %s"%checkingfilepath)
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_tkdr()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/统考数据导入.ui b/工具v4/统考数据导入.ui
new file mode 100644
index 00000000..0a75fbc5
--- /dev/null
+++ b/工具v4/统考数据导入.ui
@@ -0,0 +1,273 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 统考数据导入
+
+
+
+
+ 120
+ 170
+ 381
+ 54
+
+
+
+ -
+
+
-
+
+
+
+ 180
+ 0
+
+
+
+
+ 180
+ 16777215
+
+
+
+ 统计文件(.xlsx)
+
+
+
+ -
+
+
+
+ 60
+ 0
+
+
+
+ 文件选择
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+
+
+ 120
+ 260
+ 194
+ 22
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ 年级
+
+
+
+ -
+
+
+ 如 2026届高一
+
+
+
+
+
+
+
+
+ 120
+ 290
+ 194
+ 22
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ 班级数
+
+
+
+ -
+
+
+ 如 12
+
+
+
+
+
+
+
+
+ 510
+ 110
+ 141
+ 201
+
+
+
+
+
+
+ true
+
+
+ 说明:
+.xlsx文件需要有一个名为"难度统计"的数据表.
+第一列从第二行起为班级号, 第一行从第二列起为题号.
+例如: 1,2,3,4,5,61,62,71,72,73,8,9,101,102,...
+
+
+
+
+
+
+ 320
+ 230
+ 181
+ 81
+
+
+
+
+ true
+
+
+
+ 统考数据导入
+metadata.txt
+
+
+
+
+
+ 120
+ 110
+ 381
+ 54
+
+
+
+ -
+
+
-
+
+
+
+ 180
+ 0
+
+
+
+
+ 180
+ 16777215
+
+
+
+ 试卷文件(.tex或.pdf)
+
+
+
+ -
+
+
+
+ 60
+ 0
+
+
+
+ 文件选择
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+
+
+ 120
+ 230
+ 194
+ 22
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ 日期(yyyymmdd)
+
+
+
+ -
+
+
+
+ 100
+ 16777215
+
+
+
+ 如 20240101
+
+
+
+
+
+
+
+
+
diff --git a/工具v4/获取小闲平台使用数据.py b/工具v4/获取小闲平台使用数据.py
new file mode 100644
index 00000000..da37e028
--- /dev/null
+++ b/工具v4/获取小闲平台使用数据.py
@@ -0,0 +1,163 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog, QTableWidgetItem
+from PySide6.QtGui import QColor
+from Ui_获取小闲平台使用数据 import Ui_Form
+from database_tools_2 import *
+import shutil,zipfile
+
+
+def getFilename(string):
+ filename = re.findall(r"\d{21}_([\s\S]*?)_高[一二三]_数学",string)[0]
+ return filename
+
+def getDate(string):
+ date = re.findall(r"\((\d{8})\).zip",string)
+ if len(date) > 0:
+ return date[0]
+ else:
+ return ""
+
+class MyWindow_xxdr(QWidget,Ui_Form):
+
+
+
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.tempdir = "临时文件/zips"
+ self.statsfilename = "小题分_按学号(数学).xlsx"
+ self.answersheetseekingpath = "../备课组"
+ self.lineEdit_threshold.setText("0.75")
+ self.tableWidget.setColumnWidth(0,200)
+ self.tableWidget.setColumnWidth(1,280)
+ self.tableWidget.setColumnWidth(2,80)
+ self.pushButton_file.clicked.connect(self.getFilePath)
+ self.pushButton_folder.clicked.connect(self.getDirPath)
+ self.pushButton_validcheck.clicked.connect(self.checkpathanddate)
+ self.pushButton_exec.clicked.connect(self.exec)
+ self.tablevalid = True
+
+
+ def getFilePath(self): #选取文件并放置在tableWidget的第一行
+ self.tableWidget.setRowCount(1)
+ pathlist = QFileDialog.getOpenFileName(self,"选择文件",".","zip文件(*.zip);;所有文件(*)")
+ self.zipfilepath = pathlist[0]
+ self.tableWidget.setItem(0,0,QTableWidgetItem(pathlist[0]))
+ # self.singlefile = True
+ filename = getFilename(pathlist[0])
+ self.tableWidget.setItem(0,1,QTableWidgetItem(filename))
+ self.tableWidget.setItem(0,2,QTableWidgetItem(getDate(pathlist[0])))
+
+
+
+
+
+ def getDirPath(self): #选取文件夹中的所有符合答题纸特征的.zip文件并放置在tableWidget中
+ dirpath = QFileDialog.getExistingDirectory(self,"选择文件夹")
+ self.filelist = [os.path.join(dirpath,f) for f in os.listdir(dirpath) if ".zip" in f and re.findall(r"\d{21}",f)!=[]]
+ # self.singlefile = False
+ count = 0
+ for f in self.filelist:
+ if count + 1 > self.tableWidget.rowCount():
+ self.tableWidget.insertRow(self.tableWidget.rowCount())
+ self.tableWidget.setItem(count,0,QTableWidgetItem(f))
+ self.tableWidget.setItem(count,1,QTableWidgetItem(getFilename(f)))
+ self.tableWidget.setItem(count,2,QTableWidgetItem(getDate(f)))
+ count += 1
+
+
+ def checkpathanddate(self): #检查文件名和日期是否有效, 文件名无效标黄色, 日期无效标红色
+ if not float(self.lineEdit_threshold.text()) >= 0 or not float(self.lineEdit_threshold.text()) <= 1:
+ self.lineEdit_threshold.setStyleSheet("background-color: red;")
+ self.tablevalid = False
+ self.using_rows = []
+ self.tableWidget.clearSelection()
+ for t in range(self.tableWidget.rowCount()):
+ for i in range(3):
+ self.tableWidget.item(t,i).setBackground(QColor("transparent"))
+ for t in range(self.tableWidget.rowCount()):
+ if re.findall(r"\d{21}",self.tableWidget.item(t,0).text()) == [] or not len(self.tableWidget.item(t,2).text().strip()) == 8:
+ for i in range(3):
+ self.tableWidget.item(t,i).setBackground(QColor("yellow"))
+ else:
+ self.using_rows.append(t)
+ for row in range(self.tableWidget.rowCount()):
+ date = self.tableWidget.item(row,2).text()
+ if not len(date.strip()) == 8:
+ self.tableWidget.item(row,2).setBackground(QColor("red"))
+
+
+
+
+
+
+
+
+
+ def exec(self): #执行操作并将使用数据汇入metadata.txt
+ self.checkpathanddate()
+ if len(self.using_rows) >= 2:
+ SaveTextFile("","文本文件/metadata.txt")
+ for row in self.using_rows:
+ date = self.tableWidget.item(row,2).text()
+ threshold = float(self.lineEdit_threshold.text())
+ output = self.generateUsingInfo(zipfilepath=self.tableWidget.item(row,0).text(),threshold=threshold,date=date)
+ if output == 1 or CheckUsagesValidity(output) != 0:
+ for i in range(3):
+ self.tableWidget.item(row,i).setBackground(QColor("red"))
+ if output == 1:
+ print(f"{self.tableWidget.item(row,1).text()} 有问题, 未能生成使用数据")
+ else:
+ print(f"{self.tableWidget.item(row,1).text()} 满分设置有误, 请检查")
+ else:
+ for i in range(3):
+ self.tableWidget.item(row,i).setBackground(QColor("green"))
+ print(f"{self.tableWidget.item(row,1).text()} 使用数据生成成功")
+ if not self.checkBox.isChecked() and len(self.using_rows) == 1:
+ SaveTextFile(output,"文本文件/metadata.txt")
+ else:
+ AppendTextFile(output,"文本文件/metadata.txt")
+ os.system("code 文本文件/metadata.txt")
+
+
+
+ def generateUsingInfo(self,zipfilepath,threshold,date): #根据zipfilepath文件路径, threshold最低提交比例阈值, date日期产生对应一张答题纸的使用记录
+ try:
+ shutil.rmtree(self.tempdir)
+ except:
+ pass
+ makedir(self.tempdir)
+ try:
+ xiaoxianpid = ParseZipname(zipfilepath)
+ paperinfo = FindPaper(xiaoxianpid, self.answersheetseekingpath)
+ gradename = paperinfo[1]
+ idlist = paperinfo[2]
+ excludejson = paperinfo[4]
+ print(paperinfo)
+ # print(paperinfo)
+ zf = zipfile.ZipFile(zipfilepath)
+ zf.extractall(self.tempdir) #解压zip文件中的所有内容到tempdir
+ # # papertype = CheckPaperType(tempdir,statsfilename)
+ statsfilepathlist = FindFile(self.tempdir,self.statsfilename)
+ validcols,marks = generateColIndexandMarks(statsfilepathlist,self.statsfilename,paperinfo)
+ dfcurrent = pd.read_excel(os.path.join(statsfilepathlist[0],self.statsfilename))
+ correspondence_dict = generateIDtoUsageCorrespondence(idlist,validcols,dfcurrent.iloc[1,validcols])
+ output = CalculateUsages(statsfilepathlist,self.statsfilename,gradename,threshold,marks,correspondence_dict,validcols,date,exclude=RefineExclude(excludejson))
+ return output #返回由usages开始的使用记录文本
+ except:
+ return 1 #无法生成就返回1
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_xxdr()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/获取小闲平台使用数据.ui b/工具v4/获取小闲平台使用数据.ui
new file mode 100644
index 00000000..7e6f60b9
--- /dev/null
+++ b/工具v4/获取小闲平台使用数据.ui
@@ -0,0 +1,147 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ Form
+
+
+
+
+ 620
+ 70
+ 91
+ 24
+
+
+
+ 选择文件
+
+
+
+
+
+ 620
+ 30
+ 91
+ 20
+
+
+
+
+
+
+ 623
+ 10
+ 61
+ 20
+
+
+
+ 设定阈值
+
+
+
+
+
+ 620
+ 110
+ 91
+ 24
+
+
+
+ 选择文件夹
+
+
+
+
+
+ 620
+ 270
+ 91
+ 75
+
+
+
+
+ true
+
+
+
+ 开始分析
+
+
+
+
+
+ 10
+ 20
+ 600
+ 451
+
+
+
+ 1
+
+
+ 3
+
+
+ 130
+
+
+
+
+ 文件名
+
+
+
+
+ 答题纸名称
+
+
+
+
+ 中位扫描日期
+
+
+
+
+
+
+ 620
+ 230
+ 91
+ 20
+
+
+
+ Append模式
+
+
+
+
+
+ 620
+ 160
+ 91
+ 51
+
+
+
+ 检查有效性
+
+
+
+
+
+
diff --git a/工具v4/获取题号.py b/工具v4/获取题号.py
new file mode 100644
index 00000000..c21fbc29
--- /dev/null
+++ b/工具v4/获取题号.py
@@ -0,0 +1,33 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_获取题号 import Ui_Form
+import os
+from database_tools_2 import *
+
+class MyWindow_hqth(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.pushButton_exec.setDisabled(True)
+ self.pushButton_selectfilepath.clicked.connect(self.selectfilepath)
+ self.pushButton_exec.clicked.connect(self.exec)
+ def selectfilepath(self):
+ self.filepath = QFileDialog.getOpenFileName(self,"选择.tex文件或.pdf文件",os.getcwd(),"tex或pdf文件(*.tex *.pdf);;所有文件(*)")[0]
+ self.lineEdit.setText(self.filepath)
+ self.pushButton_exec.setEnabled(True)
+ def exec(self):
+ self.plainTextEdit.setPlainText(ExportIDList(self.filepath))
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/获取题号.ui b/工具v4/获取题号.ui
new file mode 100644
index 00000000..90ec8dd5
--- /dev/null
+++ b/工具v4/获取题号.ui
@@ -0,0 +1,102 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 获取题号
+
+
+
+
+ 10
+ 10
+ 54
+ 16
+
+
+
+ 文件路径
+
+
+
+
+
+ 670
+ 10
+ 75
+ 24
+
+
+
+ 选择文件
+
+
+
+
+
+ 10
+ 100
+ 741
+ 361
+
+
+
+ true
+
+
+
+
+
+ 10
+ 40
+ 741
+ 20
+
+
+
+ true
+
+
+
+
+
+ 10
+ 80
+ 54
+ 16
+
+
+
+ 题号
+
+
+
+
+
+ 670
+ 70
+ 75
+ 24
+
+
+
+
+ true
+
+
+
+ 获取题号
+
+
+
+
+
+
diff --git a/工具v4/讲义结构与内容录入.py b/工具v4/讲义结构与内容录入.py
new file mode 100644
index 00000000..978535ae
--- /dev/null
+++ b/工具v4/讲义结构与内容录入.py
@@ -0,0 +1,111 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_讲义结构与内容录入 import Ui_Form
+from database_tools_2 import *
+
+
+class MyWindow_jglr(QWidget,Ui_Form):
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+ self.bind()
+ def bind(self):
+ self.label_next.setVisible(False)
+ self.pushButton_exec.clicked.connect(self.exec)
+ self.lineEdit_structure.textChanged.connect(self.unvisible)
+ self.comboBox_grade.currentIndexChanged.connect(self.unvisible)
+ self.comboBox_semester.currentIndexChanged.connect(self.unvisible)
+
+ def unvisible(self):
+ self.label_next.setVisible(False)
+ def exec(self):
+ if not len(self.lineEdit_structure.text()) == 1:
+ print("首字母有误")
+ else:
+ self.label_next.setVisible(True)
+ self.repaint()
+ self.prefix = self.lineEdit_structure.text().upper()+self.comboBox_grade.currentText()+self.comboBox_semester.currentText()
+ jsonfile = f"../备课组/{self.prefix[3:5]}届/校本材料.json"
+ notes_dict = load_dict(jsonfile)
+
+ # print(len(notes_dict))
+ if not self.prefix[0] in notes_dict["structures"]:
+ AddNew = input("此类型不在列表中, 新增(A)/退出(Q):")
+ if AddNew[0].upper() == "A":
+ descr = input("类型描述:")
+ cn = input("编号是否连续(T/F):")
+ if cn[0].upper() == "T":
+ cn = True
+ else:
+ cn = False
+ partscount = int(input("分为多少个部分:"))
+ new_struct_dict = {
+ "description": descr,
+ "consecutivenumbering": cn,
+ "structure": {}
+ }
+ for i in range(partscount):
+ partid = input(f"第 {i+1} 部分的代号:")
+ partname = input(f"部分 {partid} 的名称:")
+ spaceflag = input(f"部分 {partid}: {partname} 中是否要在题目后留空格(T/F):")
+ if spaceflag[0].upper() == "T":
+ spaceflag = True
+ else:
+ spaceflag = False
+ new_struct_dict["structure"][partid] = {
+ "name": partname,
+ "spaceflag": spaceflag
+ }
+ notes_dict["structures"][self.prefix[0]] = new_struct_dict.copy()
+ save_dict(notes_dict,jsonfile)
+ else:
+ pass
+ else:
+ numberlist = []
+ for id in notes_dict["notes"]:
+ if self.prefix in id:
+ numberlist.append(id[-2:])
+ print("该分类下已有材料编号: "+generate_exp(numberlist))
+ pid = self.prefix + input("请输入新材料编号(两位数):")
+ while pid in notes_dict["notes"]:
+ print("编号重复, 请重新输入.")
+ pid = self.prefix + input("请输入新材料编号(两位数):")
+ name = input("请输入材料名称:")
+ filenameraw = input("生成的文件名和材料名称是否一致?([Y]/如果不一致请输入文件名):")
+ if filenameraw.upper() == "Y":
+ filename = name
+ else:
+ filename = filenameraw
+ new_note_dict = {
+ "id": pid,
+ "name": name,
+ "filename": filename
+ }
+ structure = notes_dict['structures'][self.prefix[0]]['structure']
+ print(f"此类材料共有 {len(structure)} 个部分, 分别是:")
+ for p in structure:
+ print(f"{p}: {structure[p]['name']}")
+ new_note_dict[p] = []
+ for p in structure:
+ rawdata = input(f"现在输入 {p}: {structure[p]['name']} 部分的内容编号:")
+ rawdata = re.sub(r"[^BXK\d:,]","",rawdata)
+ if re.findall(r"\d",rawdata) == []:
+ new_note_dict[p] = []
+ else:
+ new_note_dict[p] = generate_id_set(rawdata)
+ print(f"{p}: {new_note_dict[p]}")
+ notes_dict["notes"][pid] = new_note_dict.copy()
+ notes_dict["notes"] = SortDict(notes_dict["notes"])
+ save_dict(notes_dict,jsonfile)
+ print("处理完成")
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/讲义结构与内容录入.ui b/工具v4/讲义结构与内容录入.ui
new file mode 100644
index 00000000..81941216
--- /dev/null
+++ b/工具v4/讲义结构与内容录入.ui
@@ -0,0 +1,171 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ Form
+
+
+
+
+ 460
+ 200
+ 75
+ 24
+
+
+
+ 执行
+
+
+
+
+
+ 220
+ 200
+ 221
+ 23
+
+
+
+ -
+
+
+ 种类
+
+
+
+ -
+
+
+
+ 20
+ 16777215
+
+
+
+
+ -
+
+
+ 届别
+
+
+
+ -
+
+
-
+
+ 2022
+
+
+ -
+
+ 2023
+
+
+ -
+
+ 2024
+
+
+ -
+
+ 2025
+
+
+ -
+
+ 2026
+
+
+ -
+
+ 2027
+
+
+ -
+
+ 2028
+
+
+ -
+
+ 2029
+
+
+
+
+ -
+
+
+ 学期
+
+
+
+ -
+
+
-
+
+ 00
+
+
+ -
+
+ 01
+
+
+ -
+
+ 02
+
+
+ -
+
+ 03
+
+
+ -
+
+ 04
+
+
+ -
+
+ 05
+
+
+ -
+
+ 06
+
+
+
+
+
+
+
+
+
+ 400
+ 240
+ 141
+ 16
+
+
+
+ 请到终端执行下一步操作
+
+
+
+
+
+
diff --git a/工具v4/题号筛选器.ui b/工具v4/题号筛选器.ui
new file mode 100644
index 00000000..ae0d78a0
--- /dev/null
+++ b/工具v4/题号筛选器.ui
@@ -0,0 +1,340 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 题号筛选器
+
+
+ 1.000000000000000
+
+
+
+
+ 120
+ 30
+ 75
+ 24
+
+
+
+ 内容
+
+
+
+
+
+ 120
+ 60
+ 75
+ 24
+
+
+
+ 目标编号
+
+
+
+
+
+ 120
+ 90
+ 75
+ 24
+
+
+
+ 标签
+
+
+
+
+
+ 120
+ 120
+ 75
+ 24
+
+
+
+ 使用记录
+
+
+
+
+
+ 120
+ 150
+ 75
+ 24
+
+
+
+ 出处
+
+
+
+
+
+ 120
+ 180
+ 75
+ 24
+
+
+
+ 题目类型
+
+
+
+
+
+ 120
+ 210
+ 75
+ 24
+
+
+
+ 答案
+
+
+
+
+
+ 120
+ 240
+ 75
+ 24
+
+
+
+ 解答与提示
+
+
+
+
+
+ 120
+ 270
+ 75
+ 24
+
+
+
+ 相同题号
+
+
+
+
+
+ 120
+ 300
+ 75
+ 24
+
+
+
+ 相关题号
+
+
+
+
+
+ 120
+ 330
+ 75
+ 24
+
+
+
+ 备注
+
+
+
+
+
+ 490
+ 110
+ 161
+ 91
+
+
+
+
+ true
+
+
+
+ 运行
+
+
+
+
+
+ 120
+ 380
+ 75
+ 24
+
+
+
+ 撤销上一个
+
+
+
+
+
+ 120
+ 410
+ 75
+ 24
+
+
+
+ 清除
+
+
+
+
+
+ 220
+ 110
+ 261
+ 331
+
+
+
+ QFrame::StyledPanel
+
+
+
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ false
+
+
+
+
+
+ 220
+ 90
+ 71
+ 16
+
+
+
+ 当前条件:
+
+
+
+
+
+ 380
+ 30
+ 111
+ 20
+
+
+
+ 选中表示不包含
+
+
+
+
+
+ 220
+ 60
+ 261
+ 20
+
+
+
+
+
+
+ 220
+ 30
+ 151
+ 16
+
+
+
+ 输入条件, 用","分隔表示"或"
+
+
+
+
+
+ 490
+ 30
+ 91
+ 16
+
+
+
+ 筛选结果数目:
+
+
+
+
+
+ 490
+ 50
+ 161
+ 51
+
+
+
+ 6
+
+
+ 0.000000000000000
+
+
+
+
+
+ 490
+ 240
+ 161
+ 201
+
+
+
+
+ true
+
+
+
+ 保存和编译
+
+
+
+
+
+ 490
+ 210
+ 79
+ 20
+
+
+
+ 详细信息
+
+
+
+
+
+
diff --git a/工具v4/题目内容直接编辑.py b/工具v4/题目内容直接编辑.py
new file mode 100644
index 00000000..de6ca843
--- /dev/null
+++ b/工具v4/题目内容直接编辑.py
@@ -0,0 +1,206 @@
+from PySide6.QtWidgets import QWidget, QApplication, QFileDialog
+from Ui_题目内容直接编辑 import Ui_Form
+from database_tools_2 import *
+
+class MyWindow_bjtm(QWidget,Ui_Form):
+ def __init__(self,database_name):
+ super().__init__()
+ self.database_name = database_name
+ self.setupUi(self)
+ self.bind()
+
+ def setdbname(self,string):
+ self.database_name = string
+ try:
+ self.db.close()
+ except:
+ pass
+ self.db = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ self.cursor = self.db.cursor()
+ self.changedids = []
+ # print(self.database_name)
+
+ def bind(self):
+ self.lineEdit_ID.textChanged.connect(self.getID)
+ self.pushButton_content.clicked.connect(self.showcontent)
+ self.pushButton_genre.clicked.connect(self.showgenre)
+ self.pushButton_ans.clicked.connect(self.showans)
+ self.pushButton_solution.clicked.connect(self.showsolution)
+ self.pushButton_origin.clicked.connect(self.showorigin)
+ self.pushButton_remarks.clicked.connect(self.showremarks)
+ self.pushButton_exec.clicked.connect(self.exec)
+ self.pushButton_tocommit.clicked.connect(self.tocommit)
+ self.db = connect(hostname = db_host, port = db_port, username=db_user, pwd=db_pwd, db = self.database_name)
+ self.cursor = self.db.cursor()
+ self.changedids = []
+ def getID(self):
+ self.ID = self.lineEdit_ID.text().zfill(6)
+ def showcontent(self):
+ self.field = "题目内容"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT content FROM problems WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret = self.cursor.fetchall()
+ if ret[0][0] is None:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = ret[0][0].strip()
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def showgenre(self):
+ self.field = "类型"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT genre FROM problems WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret = self.cursor.fetchall()
+ if ret[0][0] is None:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = ret[0][0].strip()
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def showans(self):
+ self.field = "答案"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT ans FROM problems WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret = self.cursor.fetchall()
+ if ret[0][0] is None:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = ret[0][0].strip()
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def showsolution(self):
+ self.field = "解答"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT solution FROM problems WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret = self.cursor.fetchall()
+ if ret[0][0] is None:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = ret[0][0].strip()
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def showorigin(self):
+ self.field = "来源"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT origin FROM problems WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret = self.cursor.fetchall()
+ if ret[0][0] is None:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = ret[0][0].strip()
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def showremarks(self):
+ self.field = "备注"
+ self.label_2.setText(f"题号 {self.lineEdit_ID.text()} 的 {self.field} 字段")
+ sql = "SELECT date,remark_content FROM remarks WHERE ID = (%s);"
+ val = (self.lineEdit_ID.text().zfill(6),)
+ self.cursor.execute(sql,val)
+ ret_list = self.cursor.fetchall()
+ if len(ret_list) == 0:
+ self.originalcontent = ""
+ else:
+ self.originalcontent = "\n\n----------\n\n".join([ret[0]+"\t"+ret[1] for ret in ret_list])
+ self.plainTextEdit_toedit.setPlainText(self.originalcontent)
+ def exec(self):
+ self.currentcontent = self.plainTextEdit_toedit.toPlainText().strip()
+ if self.currentcontent == self.originalcontent:
+ print("未改变, 不作处理.")
+ elif self.field in ["题目内容","答案","解答","来源"]:
+ corresp = {"题目内容":"content","答案":"ans","解答":"solution","来源":"origin"}
+ sql = f"UPDATE problems SET {corresp[self.field]} = %s WHERE ID = %s;"
+ val = (self.currentcontent,self.ID)
+ self.cursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,id,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),f"修改{self.field}",self.ID,f"{self.originalcontent} -> {self.currentcontent}")
+ self.cursor.execute(sql,val)
+ if self.field == "题目内容":
+ sql = f"INSERT INTO edit_history (ID,date,editor) VALUE (%s,%s,%s);"
+ val = (self.ID,GetDate(),get_git_username())
+ self.cursor.execute(sql,val)
+ print(f"{self.ID} {self.field} 修改已完成")
+ self.changedids.append(self.ID)
+ elif self.field == "类型":
+ sql = "UPDATE problems SET genre = %s WHERE ID = %s;"
+ val = (self.currentcontent,self.ID)
+ if self.currentcontent == "解答题":
+ self.cursor.execute("UPDATE problems SET space = %s WHERE ID = %s;",("4em",self.ID))
+ else:
+ self.cursor.execute("UPDATE problems SET space = %s WHERE ID = %s;",("",self.ID))
+ self.cursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,id,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),f"修改类型",self.ID,f"{self.originalcontent} -> {self.currentcontent}")
+ self.cursor.execute(sql,val)
+ print(f"{self.ID} {self.field} 修改已完成")
+ self.changedids.append(self.ID)
+ elif self.field == "备注":
+ self.original_list = [item.strip() for item in self.originalcontent.split("-"*10)]
+ self.current_list = [item.strip() for item in self.currentcontent.split("-"*10)]
+ if len(self.original_list) != len(self.current_list):
+ print("有问题, 需保持备注的数量一致")
+ else:
+ for i in range(len(self.original_list)):
+ if self.original_list[i] != self.current_list[i]:
+ rm_date_origin,rm_content_origin = parseRemark(self.original_list[i])
+ rm_date_current,rm_content_current = parseRemark(self.current_list[i])
+ sql = "UPDATE remarks SET date = %s, remark_content = %s WHERE ID = %s AND date = %s AND remark_content = %s;"
+ val = (rm_date_current,rm_content_current,self.ID,rm_date_origin,rm_content_origin)
+ self.cursor.execute(sql,val)
+ sql = "INSERT INTO logs (DATE,TIME,username,action,id,db_content) VALUE (%s,%s,%s,%s,%s,%s);"
+ val = (GetDate(),GetTime(),get_git_username(),f"修改备注",self.ID,f"{self.original_list[i]} -> {self.current_list[i]}")
+ self.cursor.execute(sql,val)
+ print(f"{self.ID} {self.field} 修改已完成")
+ self.changedids.append(self.ID)
+
+
+
+
+
+
+
+ def tocommit(self):
+ if len(self.changedids) > 0:
+ configjson = BuildFullScheme
+ latexbody = "\\begin{enumerate}\n\n"
+ for id in sorted(list(set(self.changedids))):
+ latexbody += generateLaTeXBodyContentfromMariaDB(self.cursor,id,configjson) + "\n\n"
+ latexbody += "\\end{enumerate}"
+ latex_raw = ReadTextFile("模板文件/讲义模板.txt")
+ if configjson["教师版"] == True:
+ latex_raw = latex_raw.replace(r"学号\blank{50} \ 姓名\blank{80}","上海市控江中学")
+
+ if sys.platform != "win32": #非win系统用默认字体
+ latex_raw = re.sub(r"fontset[\s]*=[\s]*none","fontset = fandol",latex_raw)
+ latex_raw = re.sub(r"\\setCJKmainfont",r"% \\setCJKmainfont",latex_raw)
+ latex_data = StringSubstitute(r"<<[\s\S]*?待替换[\s\S]*?>>",latex_raw,("试编译",latexbody)) #替换标题和bodystring
+ outputdir = os.path.join(os.getcwd(),"临时文件")
+ outputfilepath = os.path.join(outputdir,"试编译.tex")
+ SaveTextFile(latex_data,outputfilepath)
+ if XeLaTeXCompile(outputdir,"试编译.tex",times=1):
+ print("修改后检验成功, 已导入数据库.")
+ self.db.commit()
+ else:
+ print("修改后检验失败, 已回滚.")
+ self.db.rollback()
+ self.changedids = []
+ else:
+ print("未作修改.")
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication([])
+ windows = MyWindow_bjtm()
+ windows.show()
+ app.exec()
+
diff --git a/工具v4/题目内容直接编辑.ui b/工具v4/题目内容直接编辑.ui
new file mode 100644
index 00000000..04da229d
--- /dev/null
+++ b/工具v4/题目内容直接编辑.ui
@@ -0,0 +1,174 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 760
+ 490
+
+
+
+ 题库内容直接编辑
+
+
+
+
+ 60
+ 110
+ 54
+ 16
+
+
+
+ 题号
+
+
+
+
+
+ 60
+ 130
+ 71
+ 20
+
+
+
+
+
+
+ 60
+ 160
+ 75
+ 24
+
+
+
+ 题目内容
+
+
+
+
+
+ 60
+ 190
+ 75
+ 24
+
+
+
+ 类型
+
+
+
+
+
+ 60
+ 220
+ 75
+ 24
+
+
+
+ 答案
+
+
+
+
+
+ 60
+ 250
+ 75
+ 24
+
+
+
+ 解答
+
+
+
+
+
+ 60
+ 280
+ 75
+ 24
+
+
+
+ 来源
+
+
+
+
+
+ 60
+ 310
+ 75
+ 24
+
+
+
+ 备注列表
+
+
+
+
+
+ 140
+ 110
+ 191
+ 16
+
+
+
+ 题号为 的 字段
+
+
+
+
+
+ 140
+ 130
+ 471
+ 201
+
+
+
+
+
+
+ 620
+ 130
+ 61
+ 91
+
+
+
+ 修改
+
+
+
+
+
+ 620
+ 230
+ 61
+ 101
+
+
+
+
+ true
+
+
+
+ 提交
+
+
+
+
+
+