mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 15:33:03 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 842fb434f4 | |||
| 6cabddf0cd | |||
| 0322558339 | |||
| 703ee527ae | |||
| 9a925fecb6 | |||
| 189fddfb6a | |||
| c2d53a8b78 | |||
| b99431476a | |||
| 977c0835b7 | |||
| cd565ec57d |
+6
-6
@@ -8,10 +8,10 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
model/*.onnx
|
model/*.onnx
|
||||||
driver/*.exe
|
driver/*.exe
|
||||||
gui/configs/*.json
|
src/gui/configs/*.json
|
||||||
gui/translators/qtbase_zh_CN.qm
|
src/gui/translators/qtbase_zh_CN.qm
|
||||||
gui/AutoLibraryResources.py
|
src/gui/AutoLibraryResources.py
|
||||||
gui/AutoLibraryResource.py
|
src/gui/AutoLibraryResource.py
|
||||||
gui/Ui_ALMainWindow.py
|
src/gui/Ui_ALMainWindow.py
|
||||||
gui/Ui_ALConfigWidget.py
|
src/gui/Ui_ALConfigWidget.py
|
||||||
Main.spec
|
Main.spec
|
||||||
|
|||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Copyright (c) 2025 KenanZhu.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
|
||||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
|
||||||
See the LICENSE file for details.
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import queue
|
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
|
||||||
|
|
||||||
|
|
||||||
class LibRenew(LibOperator):
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
input_queue: queue.Queue,
|
|
||||||
output_queue: queue.Queue,
|
|
||||||
driver
|
|
||||||
):
|
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
|
||||||
|
|
||||||
self.__driver = driver
|
|
||||||
|
|
||||||
|
|
||||||
def _waitResponseLoad(
|
|
||||||
self
|
|
||||||
) -> bool:
|
|
||||||
|
|
||||||
pass
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
# AutoLibrary
|
# AutoLibrary
|
||||||
|
|
||||||
请访问[AutoLibrary 网站](http://autolibrary.cv)
|
请访问[AutoLibrary 网站](http://autolibrary.cv)\
|
||||||
|
|
||||||
Please access the [AutoLibrary Website](http://autolibrary.cv)
|
Please access the [AutoLibrary Website](http://autolibrary.cv)
|
||||||
|
|||||||
Binary file not shown.
@@ -9,7 +9,7 @@ See the LICENSE file for details.
|
|||||||
"""
|
"""
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
from MsgBase import MsgBase
|
from base.MsgBase import MsgBase
|
||||||
|
|
||||||
|
|
||||||
class LibOperator(MsgBase):
|
class LibOperator(MsgBase):
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
"""
|
||||||
|
Base module for the AutoLibrary project.
|
||||||
|
|
||||||
|
Here are the classes and modules in this package:
|
||||||
|
- MsgBase: Base class for messages.\
|
||||||
|
- LibOperator: Base class for library operators.
|
||||||
|
"""
|
||||||
@@ -18,10 +18,12 @@ from PySide6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
from PySide6.QtGui import QCloseEvent
|
from PySide6.QtGui import QCloseEvent
|
||||||
|
|
||||||
from .Ui_ALConfigWidget import Ui_ALConfigWidget
|
from gui.Ui_ALConfigWidget import Ui_ALConfigWidget
|
||||||
|
from gui.SeatMapWidget import SeatMapWidget
|
||||||
|
|
||||||
from ConfigReader import ConfigReader
|
from gui.SeatMapTable import seats_maps
|
||||||
from ConfigWriter import ConfigWriter
|
from utils.ConfigReader import ConfigReader
|
||||||
|
from utils.ConfigWriter import ConfigWriter
|
||||||
|
|
||||||
|
|
||||||
class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||||
@@ -66,6 +68,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
|
|
||||||
self.ShowPasswordCheckBox.clicked.connect(self.onShowPasswordCheckBoxChecked)
|
self.ShowPasswordCheckBox.clicked.connect(self.onShowPasswordCheckBoxChecked)
|
||||||
self.FloorComboBox.currentIndexChanged.connect(self.onFloorComboBoxCurrentIndexChanged)
|
self.FloorComboBox.currentIndexChanged.connect(self.onFloorComboBoxCurrentIndexChanged)
|
||||||
|
self.SelectSeatsButton.clicked.connect(self.onSelectSeatsButtonClicked)
|
||||||
self.UserListWidget.currentItemChanged.connect(self.onUserListWidgetCurrentItemChanged)
|
self.UserListWidget.currentItemChanged.connect(self.onUserListWidgetCurrentItemChanged)
|
||||||
self.AddUserButton.clicked.connect(self.onAddUserButtonClicked)
|
self.AddUserButton.clicked.connect(self.onAddUserButtonClicked)
|
||||||
self.DelUserButton.clicked.connect(self.onDelUserButtonClicked)
|
self.DelUserButton.clicked.connect(self.onDelUserButtonClicked)
|
||||||
@@ -303,6 +306,9 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.MaxEndTimeDiffSpinBox.setValue(30)
|
self.MaxEndTimeDiffSpinBox.setValue(30)
|
||||||
self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600)
|
self.ExpectDurationSpinBox.setValue(self.BeginTimeEdit.time().secsTo(self.EndTimeEdit.time())/3600)
|
||||||
self.SatisfyDurationCheckBox.setChecked(False)
|
self.SatisfyDurationCheckBox.setChecked(False)
|
||||||
|
self.ExpectRenewDurationSpinBox.setValue(1.0)
|
||||||
|
self.MaxRenewTimeDiffSpinBox.setValue(30)
|
||||||
|
self.PreferLateRenewTimeCheckBox.setChecked(False)
|
||||||
|
|
||||||
|
|
||||||
def collectUserConfigFromUserInfoWidget(
|
def collectUserConfigFromUserInfoWidget(
|
||||||
@@ -314,7 +320,8 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
"password": self.PasswordEdit.text(),
|
"password": self.PasswordEdit.text(),
|
||||||
"reserve_info": {
|
"reserve_info": {
|
||||||
"begin_time":{},
|
"begin_time":{},
|
||||||
"end_time": {}
|
"end_time": {},
|
||||||
|
"renew_time": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user_config["reserve_info"]["date"] = self.DateEdit.dateTime().toString("yyyy-MM-dd")
|
user_config["reserve_info"]["date"] = self.DateEdit.dateTime().toString("yyyy-MM-dd")
|
||||||
@@ -330,6 +337,9 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
user_config["reserve_info"]["end_time"]["prefer_early"] = not self.PreferLateEndTimeCheckBox.isChecked()
|
user_config["reserve_info"]["end_time"]["prefer_early"] = not self.PreferLateEndTimeCheckBox.isChecked()
|
||||||
user_config["reserve_info"]["expect_duration"] = self.ExpectDurationSpinBox.value()
|
user_config["reserve_info"]["expect_duration"] = self.ExpectDurationSpinBox.value()
|
||||||
user_config["reserve_info"]["satisfy_duration"] = self.SatisfyDurationCheckBox.isChecked()
|
user_config["reserve_info"]["satisfy_duration"] = self.SatisfyDurationCheckBox.isChecked()
|
||||||
|
user_config["reserve_info"]["renew_time"]["expect_duration"] = self.ExpectRenewDurationSpinBox.value()
|
||||||
|
user_config["reserve_info"]["renew_time"]["max_diff"] = self.MaxRenewTimeDiffSpinBox.value()
|
||||||
|
user_config["reserve_info"]["renew_time"]["prefer_early"] = not self.PreferLateRenewTimeCheckBox.isChecked()
|
||||||
return user_config
|
return user_config
|
||||||
|
|
||||||
|
|
||||||
@@ -368,6 +378,9 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.PreferLateEndTimeCheckBox.setChecked(not user_config["reserve_info"]["end_time"]["prefer_early"])
|
self.PreferLateEndTimeCheckBox.setChecked(not user_config["reserve_info"]["end_time"]["prefer_early"])
|
||||||
self.ExpectDurationSpinBox.setValue(user_config["reserve_info"]["expect_duration"])
|
self.ExpectDurationSpinBox.setValue(user_config["reserve_info"]["expect_duration"])
|
||||||
self.SatisfyDurationCheckBox.setChecked(user_config["reserve_info"]["satisfy_duration"])
|
self.SatisfyDurationCheckBox.setChecked(user_config["reserve_info"]["satisfy_duration"])
|
||||||
|
self.ExpectRenewDurationSpinBox.setValue(user_config["reserve_info"]["renew_time"]["expect_duration"])
|
||||||
|
self.MaxRenewTimeDiffSpinBox.setValue(user_config["reserve_info"]["renew_time"]["max_diff"])
|
||||||
|
self.PreferLateRenewTimeCheckBox.setChecked(not user_config["reserve_info"]["renew_time"]["prefer_early"])
|
||||||
except:
|
except:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
@@ -562,7 +575,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
"prefer_early": True
|
"prefer_early": True
|
||||||
},
|
},
|
||||||
"expect_duration": 2.0,
|
"expect_duration": 2.0,
|
||||||
"satisfy_duration": False
|
"satisfy_duration": False,
|
||||||
|
"renew_time": {
|
||||||
|
"expect_duration": 1.0,
|
||||||
|
"max_diff": 30,
|
||||||
|
"prefer_early": True
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user_item = QListWidgetItem(new_user["username"])
|
user_item = QListWidgetItem(new_user["username"])
|
||||||
@@ -602,6 +620,41 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.RoomComboBox.addItems(self.__floor_room_map[floor])
|
self.RoomComboBox.addItems(self.__floor_room_map[floor])
|
||||||
self.RoomComboBox.setCurrentIndex(0)
|
self.RoomComboBox.setCurrentIndex(0)
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def onSeatMapWidgetClosed(
|
||||||
|
self,
|
||||||
|
selected_seats: list[str]
|
||||||
|
):
|
||||||
|
|
||||||
|
self.__seat_map_widget.seatMapWidgetClosed.disconnect(self.onSeatMapWidgetClosed)
|
||||||
|
self.__seat_map_widget.deleteLater()
|
||||||
|
self.__seat_map_widget = None
|
||||||
|
if len(selected_seats) == 0:
|
||||||
|
return
|
||||||
|
self.SeatIDEdit.setText(",".join(selected_seats))
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def onSelectSeatsButtonClicked(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
floor = self.FloorComboBox.currentText()
|
||||||
|
room = self.RoomComboBox.currentText()
|
||||||
|
floor_idx = self.__floor_rmap[floor]
|
||||||
|
room_idx = self.__room_rmap[room]
|
||||||
|
if self.__seat_map_widget is None:
|
||||||
|
self.__seat_map_widget = SeatMapWidget(
|
||||||
|
self,
|
||||||
|
floor,
|
||||||
|
room,
|
||||||
|
seats_maps[floor_idx][room_idx]
|
||||||
|
)
|
||||||
|
self.__seat_map_widget.seatMapWidgetClosed.connect(self.onSeatMapWidgetClosed)
|
||||||
|
self.__seat_map_widget.show()
|
||||||
|
self.__seat_map_widget.raise_()
|
||||||
|
self.__seat_map_widget.activateWindow()
|
||||||
|
self.__seat_map_widget.selectSeats(self.SeatIDEdit.text().split(","))
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def onUserListWidgetCurrentItemChanged(
|
def onUserListWidgetCurrentItemChanged(
|
||||||
self,
|
self,
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ import time
|
|||||||
import queue
|
import queue
|
||||||
|
|
||||||
from PySide6.QtCore import (
|
from PySide6.QtCore import (
|
||||||
Qt, Signal, Slot, QTimer, QThread
|
Qt, Signal, Slot, QDir, QFileInfo, QTimer, QThread
|
||||||
)
|
)
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QMainWindow, QMenu
|
QMainWindow, QMenu
|
||||||
@@ -27,8 +27,8 @@ from .ALConfigWidget import ALConfigWidget
|
|||||||
|
|
||||||
from . import AutoLibraryResource
|
from . import AutoLibraryResource
|
||||||
|
|
||||||
from AutoLib import AutoLib
|
from operators.AutoLib import AutoLib
|
||||||
from ConfigReader import ConfigReader
|
from utils.ConfigReader import ConfigReader
|
||||||
|
|
||||||
|
|
||||||
class AutoLibWorker(QThread):
|
class AutoLibWorker(QThread):
|
||||||
@@ -129,9 +129,11 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.__input_queue = queue.Queue()
|
self.__input_queue = queue.Queue()
|
||||||
self.__output_queue = queue.Queue()
|
self.__output_queue = queue.Queue()
|
||||||
|
script_path = sys.executable
|
||||||
|
script_dir = QFileInfo(script_path).absoluteDir()
|
||||||
self.__config_paths = {
|
self.__config_paths = {
|
||||||
"system": "",
|
"system": QDir.toNativeSeparators(script_dir.absoluteFilePath("system.json")),
|
||||||
"users": "",
|
"users": QDir.toNativeSeparators(script_dir.absoluteFilePath("users.json")),
|
||||||
}
|
}
|
||||||
self.__alConfigWidget = None
|
self.__alConfigWidget = None
|
||||||
self.__auto_lib_thread = None
|
self.__auto_lib_thread = None
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2025 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
from PySide6.QtCore import (
|
||||||
|
Qt, Signal
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QFrame, QLabel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SeatFrame(QFrame):
|
||||||
|
|
||||||
|
clicked = Signal(str)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
seat_number,
|
||||||
|
parent=None
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(parent)
|
||||||
|
self.__seat_number = seat_number
|
||||||
|
self.__is_selected = False
|
||||||
|
self.setUpUi()
|
||||||
|
|
||||||
|
def setUpUi(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.setFixedSize(60, 40)
|
||||||
|
self.setFrameStyle(QFrame.Box | QFrame.Plain)
|
||||||
|
self.setLineWidth(2)
|
||||||
|
self.setStyleSheet("""
|
||||||
|
QFrame {
|
||||||
|
background-color: #4196EB;
|
||||||
|
border: 2px solid #4196EB;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
color: #F0F0F0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.label = QLabel(self.__seat_number, self)
|
||||||
|
self.label.setAlignment(Qt.AlignCenter)
|
||||||
|
self.label.setGeometry(0, 0, 60, 40)
|
||||||
|
|
||||||
|
def mousePressEvent(
|
||||||
|
self,
|
||||||
|
event
|
||||||
|
):
|
||||||
|
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
self.toggleSelection()
|
||||||
|
self.clicked.emit(self.__seat_number)
|
||||||
|
|
||||||
|
|
||||||
|
def isSelected(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
return self.__is_selected
|
||||||
|
|
||||||
|
|
||||||
|
def toggleSelection(self):
|
||||||
|
|
||||||
|
self.__is_selected = not self.__is_selected
|
||||||
|
if self.__is_selected:
|
||||||
|
self.setStyleSheet("""
|
||||||
|
QFrame {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
border: 2px solid #388E3C;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
color: #F0F0F0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
self.setStyleSheet("""
|
||||||
|
QFrame {
|
||||||
|
background-color: #4196EB;
|
||||||
|
border: 2px solid #4196EB;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
color: #F0F0F0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
""")
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
seats_maps = {
|
||||||
|
"2": {
|
||||||
|
"1": """
|
||||||
|
,,,,,,,,,,,039A,039B,,040A,040B,,041A,041B,,042A,042B,,043A,043B,,044A,044B,,,,,,,,,
|
||||||
|
,,,,,,,,,,,039C,039D,,040C,040D,,041C,041D,,042C,042D,,043C,043D,,044C,044D,,,,,,,,,
|
||||||
|
038B,038D,,037B,037D,,036B,036D,,,,,,,,,,,,,,,,,,,,,,045C,045A,,046C,046A,,047C,047A
|
||||||
|
038A,038C,,037A,037C,,036A,036C,,,,,,,,,,,,,,,,,,,,,,045D,045B,,046D,046B,,047D,047B
|
||||||
|
035B,035D,,034B,034D,,033B,033D,,,,,,,,,,,,,,,,,,,,,,048C,048A,,049C,049A,,050C,050A
|
||||||
|
035A,035C,,034A,034C,,033A,033C,,,,,,,,,,,,,,,,,,,,,,048D,048B,,049D,049B,,050D,050B
|
||||||
|
032B,032D,,031B,031D,,030B,030D,,,,,,,,,,,,,,,,,,,,,,051C,051A,,052C,052A,,053C,053A
|
||||||
|
032A,032C,,031A,031C,,030A,030C,,,,,,,,,,,,,,,,,,,,,,051D,051B,,052D,052B,,053D,053B
|
||||||
|
029B,029D,,028B,028D,,027B,027D,,,,,,,,,,,,,,,,,,,,,,054C,054A,,055C,055A,,056C,056A
|
||||||
|
029A,029C,,028A,028C,,027A,027C,,,,,,,,,,,,,,,,,,,,,,054D,054B,,055D,055B,,056D,056B
|
||||||
|
026B,026D,,025B,025D,,024B,024D,,,,,,,,,,,,,,,,,,,,,,057C,057A,,058C,058A,,059C,059A
|
||||||
|
026A,026C,,025A,025C,,024A,024C,,,,,,,,,,,,,,,,,,,,,,057D,057B,,058D,058B,,059D,059B
|
||||||
|
023B,023D,,022B,022D,,021B,021D,,,,,,,,,,,,,,,,,,,,,,060C,060A,,061C,061A,,062C,062A
|
||||||
|
023A,023C,,022A,022C,,021A,021C,,,,,,,,,,,,,,,,,,,,,,060D,060B,,061D,061B,,062D,062B
|
||||||
|
020B,020D,,019B,019D,,018B,018D,,,,,,,,,,,,,,,,,,,,,,063C,063A,,064C,064A,,065C,065A
|
||||||
|
020A,020C,,019A,019C,,018A,018C,,,,,,,,,,,,,,,,,,,,,,063D,063B,,064D,064B,,065D,065B
|
||||||
|
,,,,,,,,,,,017D,017C,,014D,014C,,011D,011C,,008D,008C,,005D,005C,,002D,002C,001D,001C,,,,,,,
|
||||||
|
,,,,,,,,,,,017B,017A,,014B,014A,,011B,011A,,008B,008A,,005B,005A,,002B,002A,001B,001A,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,073D,073C,,015D,015C,,012D,012C,,,,,006D,006C,,003D,003C,,,,,,,,,
|
||||||
|
,,,,,,,,,,,073B,073A,,015B,015A,,012B,012A,,,,,006B,006A,,003B,003A,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,072D,072C,,016D,016C,,013D,013C,,,,,007D,007C,,004D,004C,,,,,,,,,
|
||||||
|
,,,,,,,,,,,072B,072A,,016B,016A,,013B,013A,,,,,007B,007A,,004B,004A,,,,,,,,,
|
||||||
|
,,,,,,,,,,,071D,071C,,070D,070C,,069D,069C,,068D,068C,,067D,067C,,066D,066C,,,,,,,,,
|
||||||
|
,,,,,,,,,,,071B,071A,,070B,070A,,069B,069A,,068B,068A,,067B,067A,,066B,066A,,,,,,,,,
|
||||||
|
""",
|
||||||
|
"2": """
|
||||||
|
023B,023D,024B,024D,,,,,,,,,,,,,,,
|
||||||
|
023A,023C,024A,024C,,,,,,,,,,,,,,,
|
||||||
|
022B,022D,032D,032C,,,,,,,,,,,,,,,
|
||||||
|
022A,022C,032B,032A,,,,,,,,,,,,,,,
|
||||||
|
021B,021D,,,,,,,,,,,,,,,,,
|
||||||
|
021A,021C,,,,,,,,,,,,,,,,,
|
||||||
|
020B,020D,,,,,,,,,,,,,,,,,
|
||||||
|
020A,020C,,,,,,,,,,,,,,,,,
|
||||||
|
019B,019D,,,,,,,,,,,,,,,,,
|
||||||
|
019A,019C,,,,,,,,,,,,,,,,,
|
||||||
|
018B,018D,,,,,,,,,,,,,,,,,
|
||||||
|
018A,018C,,,,,,,,,,,,,,,,,
|
||||||
|
017B,017D,,,,,,,,,,,,,,,,,
|
||||||
|
017A,017C,,,,,,,,,,,,,,,,,
|
||||||
|
016B,016D,,,,,,,,,,,,,,,,,
|
||||||
|
016A,016C,,,,,031A,031C,,,,,,,,,,,
|
||||||
|
015B,015D,,,,,030B,030D,,,,,,,,,,,
|
||||||
|
015A,015C,,,,,030A,030C,,,,,,,,,,,
|
||||||
|
014B,014D,,,,,029B,029D,,,,,,,,,,,
|
||||||
|
014A,014C,,,,,029A,029C,,,,,,,,,,,
|
||||||
|
013B,013D,,,,,028B,028D,,,,,,,,,,,
|
||||||
|
013A,013C,,,,,028A,028C,,,,,,,,,,,
|
||||||
|
012B,012D,,,,,027B,027D,,,,,,,,,,,
|
||||||
|
012A,012C,,,,,027A,027C,,,,,,,,,,,
|
||||||
|
011B,011D,,,,,026B,026D,,,,,,,,,,,
|
||||||
|
011A,011C,,,,,026A,026C,,,,,,,,,,,
|
||||||
|
010B,010D,,,,,025B,025D,,,,,,,,,,,
|
||||||
|
010A,010C,,,,,,,,,,,,,,,,,
|
||||||
|
009B,009D,,,,,,,,,,,,,,,,,
|
||||||
|
009A,009C,,,,,,,,,,,,,,,,,
|
||||||
|
008B,008D,,,,,,,,,,,,,,,,,
|
||||||
|
008A,008C,,,,,,,,,,,,,,,,,
|
||||||
|
007B,007D,,,,,,,,,,,,,,,,,
|
||||||
|
007A,007C,,,,,,,,,,,,,,,,,
|
||||||
|
006B,006D,,,,,,,,,,,,,,,,,
|
||||||
|
006A,006C,,,,,,,,,,,,,,,,,
|
||||||
|
005B,005D,,,,,,,,,,,,,,,,,
|
||||||
|
005A,005C,,,,,,,,,,,,,,,,,
|
||||||
|
004D,004C,003D,003C,002D,002C,001D,001C,,,,,,,,,,,
|
||||||
|
004B,004A,003B,003A,002B,002A,001B,001A,,,,,,,,,,,
|
||||||
|
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"3": """
|
||||||
|
,,007B,007D,,,,,,,,008C,008A,,
|
||||||
|
,,007A,007C,,,,,,,,008D,008B,,
|
||||||
|
,,006B,006D,,,,,,,,009C,009A,,
|
||||||
|
,,006A,006C,,,,,,,,009D,009B,,
|
||||||
|
,,005B,005D,,,,,,,,010C,010a,,
|
||||||
|
,,005A,005C,,,,,,,,010D,010B,,
|
||||||
|
,,004B,004D,,,,,,,,011C,011A,,
|
||||||
|
,,004A,004C,,,,,,,,011D,011B,,
|
||||||
|
,,003B,003D,,,,,,,,012C,012A,,
|
||||||
|
,,003A,003C,,,,,,,,012D,012B,,
|
||||||
|
,,002B,002D,,,,,,,,013C,013A,,
|
||||||
|
,,002A,002C,,,,,,,,013D,013B,,
|
||||||
|
,,001B,001D,,,,,,,,014C,014A,,
|
||||||
|
,,001A,001C,,,,,,,,014D,014B,,
|
||||||
|
""",
|
||||||
|
"4": """
|
||||||
|
,,037D,037C,038D,038C,039D,039C,040D,040C,041D,041C,042D,042C,043D,043C,044D,044C,045D,045C,,,046D,046C,047D,047C,048D,048C,049D,049C,050D,050C,051D,051C,052D,052C,053D,053C,054D,054C,055D,055C,056D,056C,057D,057C,,
|
||||||
|
,,037B,037A,038B,038A,039B,039A,040B,040A,041B,041A,042B,042A,043B,043A,044B,044A,045B,045A,,,046B,046A,047B,047A,048B,048A,049B,049A,050B,050A,051B,051A,052B,052A,053B,053A,054B,054A,055B,055A,056B,056A,057B,057A,,
|
||||||
|
036B,036D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,058C,058A,,060C,060A
|
||||||
|
036A,036C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,058D,058B,,060D,060B
|
||||||
|
035B,035D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,059C,059A,,061C,061A
|
||||||
|
035A,035C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,059D,059B,,061D,061B
|
||||||
|
034B,034D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,062C,062A
|
||||||
|
034A,034C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,062D,062B
|
||||||
|
033B,033D,,,,,,,,,,,,080B,080D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,063C,063A
|
||||||
|
033A,033C,,,,,,,,,,,,080A,080C,,081A,081B,082A,082B,083A,083B,084A,084B,085A,085B,086A,086B,087A,,,,,,,,,,,,,,,,,,063D,063B
|
||||||
|
032B,032D,,,,,,,,,,,,079B,079D,,081C,081D,082C,082D,083C,083D,084C,084D,085C,085D,086C,086D,087C,,,,,,,,,,,,,,,,,,064C,064A
|
||||||
|
032A,032C,,,,,,,,,,,,079A,079C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,064D,064B
|
||||||
|
031B,031D,,,,,,,,,,,,078B,078D,,,,,,,,,,,,,,088A,088C,,,,,,,,,,,,,,,,,065C,065A
|
||||||
|
031A,031C,,,,,,,,,,,,078A,078C,,,,,,,,,,,,,,088B,088D,,,,,,,,,,,,,,,,,065D,065B
|
||||||
|
030B,030D,,,,,,,,,,,,077B,077D,,,,,,,,,,,,,,089A,089C,,,,,,,,,,,,,,,,,066C,066A
|
||||||
|
030A,030C,,,,,,,,,,,,077A,077C,,,,,,,,,,,,,,089B,089D,,,,,,,,,,,,,,,,,066D,066B
|
||||||
|
029B,029D,,,,,,,,,,,,076B,076D,,,,,,,,,,,,,,090A,090C,,,,,,,,,,,,,,,,,,
|
||||||
|
029A,029C,,,,,,,,,,,,076A,076C,,,,,,,,,,,,,,090B,090D,,,,,,,,,,,,,,,,,,
|
||||||
|
028B,028D,,,,,,,,,,,,075B,075D,,,,,,,,,,,,,,091A,091C,,,,,,,,,,,,,,,,,,
|
||||||
|
028A,028C,,,,,,,,,,,,075A,075C,,,,,,,,,,,,,,091B,091D,,,,,,,,,,,,,,,,,,
|
||||||
|
027B,027D,,,,,,,,,,,,074B,074D,,,,,,,,,,,,,,092A,092C,,,,,,,,,,,,,,,,,,
|
||||||
|
027A,027C,,,,,,,,,,,,,,,,,,,,,,,,,,,092B,092D,,,,,,,,,,,,,,,,,,
|
||||||
|
026B,026D,,,,,,,,,,,,,,,073D,073C,072D,072C,071D,071C,070D,070C,069D,069C,068D,068C,,,,,,,,,,,,,,,,,,,,
|
||||||
|
026A,026C,,,,,,,,,,,,,,,073B,073A,072B,072A,071B,071A,070B,070A,069B,069A,068B,068A,,,,,,,,,,,,,,,,,,,,
|
||||||
|
025B,025D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
025A,025C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
024B,024D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
024A,024C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
023B,023D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
023A,023C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,067C,,
|
||||||
|
022B,022D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,067B,,
|
||||||
|
022A,022C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,067A,,
|
||||||
|
,,021D,021C,020D,020C,019D,019C,018D,018C,017D,017C,016D,016C,015D,015C,014D,014C,013D,013C,012D,012C,011D,011C,010D,010C,009D,009C,008D,008C,007D,007C,006D,006C,005D,005C,004D,004C,003D,003C,002D,002C,001D,001C,,,,
|
||||||
|
,,021B,021A,020B,020A,019B,019A,018B,018A,017B,017A,016B,016A,015B,015A,014B,014A,013B,013A,012B,012A,011B,011A,010B,010A,009B,009A,008B,008A,007B,007a,006B,006A,005B,005A,004B,004A,003b,003A,002B,002A,001B,001A,,,,
|
||||||
|
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"5": """
|
||||||
|
,,,,,,,,042A,042B,045A,045B,048A,048B,051A,051B,054A,054B,057A,057B,060A,060B,,,,,,
|
||||||
|
,,,,,,,,042C,042D,045C,045D,048C,048D,051C,051D,054C,054D,057C,057D,060C,060D,,,,,,
|
||||||
|
,,,,,,,,041A,041B,044A,044B,047A,047B,050A,050B,053A,053B,056A,056B,059A,059B,,,,,,
|
||||||
|
,,,,,,,,041C,041D,044C,044D,047C,047D,050C,050D,053C,053D,056C,056D,059C,059D,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,040A,040B,043A,043B,046A,046B,049A,049B,052A,052B,055A,055B,058A,058B,,,,,,
|
||||||
|
,,,,,,,,040C,040D,043C,043D,046C,046D,049C,049D,052C,052D,055C,055D,058C,058D,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,039B,039D,038B,038D,,037B,037D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,039A,039C,038A,038C,,037A,037C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,036B,036D,035B,035D,,034B,034D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,036A,036C,035A,035C,,034A,034C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,033B,033D,032B,032D,,031B,031D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,033A,033C,032A,032C,,031A,031C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,030B,030D,029B,029D,,028B,028D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,030A,030C,029A,029C,,028A,028C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,027B,027D,026B,026D,,025B,025D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,027A,027C,026A,026C,,025A,025C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,024B,024D,023B,023D,,022B,022D,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,024A,024C,023A,023C,,022A,022C,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,019D,019C,016D,016C,013D,013C,010D,010C,007D,007C,004D,004C,001D,001C,,,,,,
|
||||||
|
,,,,,,,,019B,019A,016B,016A,013B,013A,010B,010A,007B,007A,004B,004A,001B,001A,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,020D,020C,017D,017C,014D,014C,011D,011C,008D,008C,005D,005C,002D,002C,,,,,,
|
||||||
|
,,,,,,,,020B,020A,017B,017A,014B,014A,011B,011A,008B,008A,005B,005A,002B,002A,,,,,,
|
||||||
|
,,,,,,,,021D,021C,018D,018C,015D,015C,012D,012C,009D,009C,006D,006C,003D,003C,,,,,,
|
||||||
|
,,,,,,,,021B,021A,018B,018A,015B,015A,012B,012A,009B,009A,006B,006A,003B,003A,,,,,,
|
||||||
|
|
||||||
|
""",
|
||||||
|
"6": """
|
||||||
|
,,,026C,026D,027D,027C,028D,028C,029D,029C,030D,030C,031D,031C,032D,032C,033D,033C,035D,035C,036D,036C,037D,037C,038D,038C,039D,039C,040D,040C,041D,041C,042D,042C,043D,043C,044D,044C,045D,045C,046D,046C
|
||||||
|
,,,026A,026B,027B,027A,028B,028A,029B,029A,030B,030A,031B,031A,032B,032A,033B,033A,035B,035A,036B,036A,037B,037A,038B,038A,039B,039A,040B,040A,041B,041A,042B,042A,043B,043A,044B,044A,045B,045A,046B,046A
|
||||||
|
025D,025C,,,,,,,,,,,,,,,,034D,034C,,,,,,,,,,,,,,,,,,,,,,,047C,047A
|
||||||
|
025B,025A,,,,,,,,,,,,,,,,034B,034A,,,,,,,,,,,,,,,,,,,,,,,047D,047B
|
||||||
|
024D,024C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,048C,048A
|
||||||
|
024B,024A,,,,,,,,,,,,,,050D,050C,052D,052C,054D,054C,056D,056C,058D,058C,060D,060C,,,,,,,,,,,,,,,048D,048B
|
||||||
|
023D,023C,,,,,,,,,,,,,,050B,050A,052B,052A,054B,054A,056B,056A,058B,058A,060B,060A,,,,,,,,,,,,,,,,
|
||||||
|
023B,023A,,,,,,,,,,,,,,049D,049C,051D,051C,053D,053C,055D,055C,057D,057C,059D,059C,,,,,,,,,,,,,,,,
|
||||||
|
022D,022C,,,,,,,,,,,,,,049B,049A,051B,051A,053B,053A,055B,055A,057B,057A,059B,059A,,,,,,,,,,,,,,,,
|
||||||
|
022B,022A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
021D,021C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
021B,021A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
020D,020C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
020B,020A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
019D,019C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
019B,019A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
015D,015C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
015B,015A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
014D,014C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
014B,014A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
013D,013C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
013B,013A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
012D,012C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
012B,012A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
011D,011C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
011B,011A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
010D,010C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
010B,010A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
009D,009C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
009B,009A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
008D,008C,,007D,007C,006D,006C,005D,005C,004D,004C,003D,003C,002D,002C,001D,001C,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
008B,008A,,007B,007A,006B,006A,005B,005A,004B,004A,003B,003A,002B,002A,001B,001A,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
|
||||||
|
""",
|
||||||
|
"7": """
|
||||||
|
,,,,,,,,022D,022C,021D,021C,020D,020C,019D,019C,018D,018C,017D,017C,,,,,,,,,,,,
|
||||||
|
,,,,,,,,022B,022A,021B,021A,020B,020A,019B,019A,018B,018A,017B,017A,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
016D,016C,015D,015C,014D,014C,013D,013C,012D,012C,011D,011C,010D,010C,009D,009C,008D,008C,007D,007C,006D,006C,005D,005C,004D,004C,003D,003C,002D,002C,001D,001C
|
||||||
|
016B,016A,015B,015A,014B,014A,013B,013A,012B,012A,011B,011A,010B,010A,009B,009A,008B,008A,007B,007A,006B,006A,005B,005A,004B,004A,003B,003A,002B,002A,001B,001A
|
||||||
|
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"8": """
|
||||||
|
,,,046D,046C,047D,047C,048D,048C,049D,049C,050D,050C,051D,051C,052D,052C,053D,053C,054D,054C,055D,055C,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,046B,046A,047B,047A,048B,048A,049B,049A,050B,050A,051B,051A,052B,052A,053B,053A,054B,054A,055B,055A,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,,,,,,,,,,,,,,,,,,,,,056C,056A,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
045B,045D,,,,,,,,,,,,,,,,,,,,,,056D,056B,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
045A,045C,,,,,,,,,,,,,,,,,,,,,,057C,057A,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
044B,044D,,,,,,,,,,,,,,,,,,,,,,057D,057B,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
044A,044C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
043B,043D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
043A,043C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
042B,042D,,,,,,,,,,,,,,,,,070B,070D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
042A,042C,,,,,,,,,,,,,,,,,070A,070C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
041B,041D,,,,,,,,,,,,,,,,,069B,069D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
041A,041C,,,,,,,,,,,,,,,,,069A,069C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
040B,040D,,,,,,,,,,,,,,,,,068B,068D,,071A,071B,072A,072B,073A,073B,074A,074B,075A,075B,076A,076B,077A,077B,,,,,,,,,,,,,,,,
|
||||||
|
040A,040C,,,,,,,,,,,,,,,,,068A,068C,,071C,071D,072C,072D,073C,073D,074C,074D,075C,075D,076C,076D,077C,077D,,,,,,,,,,,,,,,,
|
||||||
|
039B,039D,,,,,,,,,,,,,,,,,067B,067D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
039A,039C,,,,,,,,,,,,,,,,,067A,067C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
038B,038D,,,,,,,,,,,,,,,,,066B,066D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
038A,038C,,,,,,,,,,,,,,,,,066A,066C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
037B,037D,,,,,,,,,,,,,,,,,065B,065D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
037A,037C,,,,,,,,,,,,,,,,,065A,065C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
036B,036D,,,,,,,,,,,,,,,,,064B,064D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
036A,036C,,,,,,,,,,,,,,,,,064A,064C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
035B,035D,,,,,,,,,,,,,,,,,063B,063D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
035A,035C,,,,,,,,,,,,,,,,,063A,063C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
034B,034D,,,,,,,,,,,,,,,,,062B,062D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
034A,034C,,,,,,,,,,,,,,,,,062A,062C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
033B,033D,,,,,,,,,,,,,,,,,,,061D,061C,,060D,060C,,059D,059C,,058D,058C,,,,,,,,,,,,,,,,,,,,
|
||||||
|
033A,033C,,,,,,,,,,,,,,,,,,,061B,061A,,060B,060A,,059B,059A,,058B,058A,,,,,,,,,,,,,,,,,,,,
|
||||||
|
032B,032D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
032A,032C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
031B,031D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
031A,031C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
030B,030D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
030A,030C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
029B,029D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
029A,029C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
028B,028D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
028A,028C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
027B,027D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
027A,027C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
026B,026D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
026A,026C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
025B,025D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
025A,025C,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||||
|
,,,024D,024C,023D,023C,022D,022C,021D,021C,020D,020C,019D,019C,018D,018C,017D,017C,016D,016C,015D,015C,014D,014C,013D,013C,012D,012C,011D,011C,010D,010C,009D,009C,008D,008C,007D,007C,006D,006C,005D,005C,004D,004C,003D,003C,002D,002C,001D,001C
|
||||||
|
,,,024B,024A,023B,023A,022B,022A,021B,021A,020B,020A,019B,019A,018B,018A,017B,017A,016B,016A,015B,015A,014B,014A,013B,013A,012B,012A,011B,011A,010B,010A,009B,009A,008B,008A,007B,007A,006B,006A,005B,005A,004B,004A,003B,003A,002B,002A,001B,001A
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2025 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
from PySide6.QtCore import (
|
||||||
|
Qt, Slot, Signal, QEvent
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QFrame, QWidget, QLabel, QHBoxLayout, QVBoxLayout,
|
||||||
|
QGridLayout, QGraphicsView, QGraphicsScene, QGraphicsItem,
|
||||||
|
QPushButton,
|
||||||
|
)
|
||||||
|
from PySide6.QtGui import (
|
||||||
|
QPainter, QWheelEvent, QCloseEvent
|
||||||
|
)
|
||||||
|
from gui.SeatFrame import SeatFrame
|
||||||
|
|
||||||
|
|
||||||
|
class SeatMapWidget(QWidget):
|
||||||
|
|
||||||
|
seatMapWidgetClosed = Signal(list)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: QWidget = None,
|
||||||
|
floor: str = "",
|
||||||
|
room: str = "",
|
||||||
|
seats_data: dict = {},
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.__floor = floor
|
||||||
|
self.__room = room
|
||||||
|
self.__seats_data = seats_data
|
||||||
|
self.__selected_seats = []
|
||||||
|
self.__seat_frames = {}
|
||||||
|
self.setUpUi()
|
||||||
|
self.connectSignals()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def formatSeatNumber(
|
||||||
|
seat_number: str
|
||||||
|
) -> str:
|
||||||
|
|
||||||
|
if seat_number and not seat_number[-1].isdigit():
|
||||||
|
digits = seat_number[:-1]
|
||||||
|
letter = seat_number[-1]
|
||||||
|
return digits.zfill(3) + letter
|
||||||
|
return seat_number.zfill(3)
|
||||||
|
|
||||||
|
|
||||||
|
def setUpUi(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.setWindowFlags(Qt.WindowType.Window)
|
||||||
|
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||||
|
self.setMinimumSize(800, 600)
|
||||||
|
self.resize(800, 600)
|
||||||
|
self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
|
||||||
|
|
||||||
|
self.SeatMapWidgetMainLayout = QVBoxLayout(self)
|
||||||
|
self.TitleLabel = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}")
|
||||||
|
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||||
|
self.SeatMapWidgetMainLayout.addWidget(self.TitleLabel)
|
||||||
|
|
||||||
|
self.SeatMapGraphicsView = QGraphicsView(self)
|
||||||
|
self.SeatMapGraphicsScene = QGraphicsScene(self)
|
||||||
|
self.SeatMapGraphicsView.setScene(self.SeatMapGraphicsScene)
|
||||||
|
self.SeatMapGraphicsView.setRenderHint(QPainter.RenderHint.LosslessImageRendering)
|
||||||
|
self.SeatMapGraphicsView.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
|
||||||
|
self.SeatMapGraphicsView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||||
|
self.SeatMapGraphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||||
|
self.SeatMapGraphicsView.viewport().installEventFilter(self)
|
||||||
|
|
||||||
|
self.SeatsContainerWidget = QWidget()
|
||||||
|
self.SeatsContainerLayout = QGridLayout(self.SeatsContainerWidget)
|
||||||
|
self.createSeatMap()
|
||||||
|
|
||||||
|
self.ContainerProxy = self.SeatMapGraphicsScene.addWidget(self.SeatsContainerWidget)
|
||||||
|
self.ContainerProxy.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, False)
|
||||||
|
self.SeatMapWidgetMainLayout.addWidget(self.SeatMapGraphicsView)
|
||||||
|
|
||||||
|
self.TipsLabel = QLabel(
|
||||||
|
" 点击座位进行选择/取消选择, 最多选择1个座位 \n"
|
||||||
|
" [操作方法: Ctrl+鼠标滚轮缩放 | 滚轮/拖拽/方向键 移动]"
|
||||||
|
)
|
||||||
|
self.TipsLabel.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||||
|
self.TipsLabel.setStyleSheet("color: #666; margin: 5px;")
|
||||||
|
self.SeatMapWidgetMainLayout.addWidget(self.TipsLabel)
|
||||||
|
|
||||||
|
self.ConfirmButton = QPushButton("确认")
|
||||||
|
self.ConfirmButton.setFixedSize(80, 25)
|
||||||
|
self.CancelButton = QPushButton("取消")
|
||||||
|
self.CancelButton.setFixedSize(80, 25)
|
||||||
|
self.SeatMapWidgetControlLayout = QHBoxLayout()
|
||||||
|
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||||
|
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
|
||||||
|
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
|
||||||
|
self.SeatMapWidgetMainLayout.addLayout(self.SeatMapWidgetControlLayout)
|
||||||
|
|
||||||
|
|
||||||
|
def connectSignals(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked)
|
||||||
|
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
|
||||||
|
|
||||||
|
|
||||||
|
def closeEvent(
|
||||||
|
self,
|
||||||
|
event: QCloseEvent
|
||||||
|
):
|
||||||
|
|
||||||
|
self.seatMapWidgetClosed.emit(self.__selected_seats)
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
def eventFilter(
|
||||||
|
self,
|
||||||
|
watched,
|
||||||
|
event
|
||||||
|
):
|
||||||
|
|
||||||
|
if (watched is self.SeatMapGraphicsView.viewport() and
|
||||||
|
event.type() == QEvent.Type.Wheel and
|
||||||
|
event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||||
|
):
|
||||||
|
self.zoomGraphicsView(event)
|
||||||
|
return True
|
||||||
|
return super().eventFilter(watched, event)
|
||||||
|
|
||||||
|
|
||||||
|
def zoomGraphicsView(
|
||||||
|
self,
|
||||||
|
event: QWheelEvent
|
||||||
|
):
|
||||||
|
|
||||||
|
delta = event.angleDelta().y()
|
||||||
|
zoom_factor = 1.2 if delta > 0 else 1/1.2
|
||||||
|
self.SeatMapGraphicsView.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
||||||
|
self.SeatMapGraphicsView.scale(zoom_factor, zoom_factor)
|
||||||
|
|
||||||
|
|
||||||
|
def createSeatMap(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
rows = self.__seats_data.strip().split("\n")
|
||||||
|
for row_idx, row in enumerate(rows):
|
||||||
|
col_idx = 0
|
||||||
|
seats_number = [seat.strip() for seat in row.split(",")]
|
||||||
|
for seat_number in seats_number:
|
||||||
|
if seat_number:
|
||||||
|
seat_widget = SeatFrame(seat_number)
|
||||||
|
seat_widget.clicked.connect(self.onSeatClicked)
|
||||||
|
self.SeatsContainerLayout.addWidget(seat_widget, row_idx, col_idx)
|
||||||
|
self.__seat_frames[seat_number] = seat_widget
|
||||||
|
else:
|
||||||
|
spacer = QFrame()
|
||||||
|
spacer.setFixedSize(20, 30)
|
||||||
|
spacer.setStyleSheet("background-color: transparent; border: none;")
|
||||||
|
self.SeatsContainerLayout.addWidget(spacer, row_idx, col_idx)
|
||||||
|
col_idx += 1
|
||||||
|
self.SeatsContainerLayout.setSpacing(20)
|
||||||
|
self.SeatsContainerLayout.setContentsMargins(20, 20, 20, 20)
|
||||||
|
self.SeatsContainerWidget.adjustSize()
|
||||||
|
|
||||||
|
|
||||||
|
def selectSeat(
|
||||||
|
self,
|
||||||
|
seat_number: str
|
||||||
|
):
|
||||||
|
|
||||||
|
if len(self.__selected_seats) >= 1:
|
||||||
|
return
|
||||||
|
seat_number = self.formatSeatNumber(seat_number)
|
||||||
|
if seat_number not in self.__seat_frames:
|
||||||
|
return
|
||||||
|
widget = self.__seat_frames[seat_number]
|
||||||
|
if widget.isSelected():
|
||||||
|
return
|
||||||
|
widget.toggleSelection()
|
||||||
|
self.__selected_seats.append(seat_number)
|
||||||
|
|
||||||
|
|
||||||
|
def selectSeats(
|
||||||
|
self,
|
||||||
|
selected_seats: list
|
||||||
|
):
|
||||||
|
|
||||||
|
self.clearSelections()
|
||||||
|
for seat_number in selected_seats:
|
||||||
|
self.selectSeat(seat_number)
|
||||||
|
|
||||||
|
|
||||||
|
def getSelectedSeats(
|
||||||
|
self
|
||||||
|
) -> list[str]:
|
||||||
|
|
||||||
|
return self.__selected_seats
|
||||||
|
|
||||||
|
|
||||||
|
def clearSelections(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
seats_to_clear = self.__selected_seats.copy()
|
||||||
|
for seat_number in seats_to_clear:
|
||||||
|
if seat_number not in self.__seat_frames:
|
||||||
|
continue
|
||||||
|
widget = self.__seat_frames[seat_number]
|
||||||
|
if widget.isSelected():
|
||||||
|
widget.toggleSelection()
|
||||||
|
self.__selected_seats = []
|
||||||
|
|
||||||
|
@Slot(str)
|
||||||
|
def onSeatClicked(
|
||||||
|
self,
|
||||||
|
seat_number: str
|
||||||
|
):
|
||||||
|
|
||||||
|
if seat_number in self.__selected_seats:
|
||||||
|
self.__selected_seats.remove(seat_number)
|
||||||
|
else:
|
||||||
|
if len(self.__selected_seats) < 1:
|
||||||
|
self.__selected_seats.append(seat_number)
|
||||||
|
else:
|
||||||
|
self.__seat_frames[seat_number].toggleSelection()
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def onConfirmButtonClicked(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def onCancelButtonClicked(
|
||||||
|
self
|
||||||
|
):
|
||||||
|
|
||||||
|
self.clearSelections()
|
||||||
|
self.close()
|
||||||
|
Before Width: | Height: | Size: 785 KiB After Width: | Height: | Size: 785 KiB |
@@ -16,14 +16,15 @@ from selenium.webdriver.support.ui import WebDriverWait
|
|||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.webdriver.edge.service import Service
|
from selenium.webdriver.edge.service import Service
|
||||||
|
|
||||||
from MsgBase import MsgBase
|
from base.MsgBase import MsgBase
|
||||||
from LibChecker import LibChecker
|
from operators.LibChecker import LibChecker
|
||||||
from LibLogin import LibLogin
|
from operators.LibLogin import LibLogin
|
||||||
from LibLogout import LibLogout
|
from operators.LibLogout import LibLogout
|
||||||
from LibReserve import LibReserve
|
from operators.LibReserve import LibReserve
|
||||||
from LibCheckin import LibCheckin
|
from operators.LibCheckin import LibCheckin
|
||||||
|
from operators.LibRenew import LibRenew
|
||||||
|
|
||||||
from ConfigReader import ConfigReader
|
from utils.ConfigReader import ConfigReader
|
||||||
|
|
||||||
|
|
||||||
class AutoLib(MsgBase):
|
class AutoLib(MsgBase):
|
||||||
@@ -114,6 +115,7 @@ class AutoLib(MsgBase):
|
|||||||
self.__lib_logout = LibLogout(self._input_queue, self._output_queue, self.__driver)
|
self.__lib_logout = LibLogout(self._input_queue, self._output_queue, self.__driver)
|
||||||
self.__lib_reserve = LibReserve(self._input_queue, self._output_queue, self.__driver)
|
self.__lib_reserve = LibReserve(self._input_queue, self._output_queue, self.__driver)
|
||||||
self.__lib_checkin = LibCheckin(self._input_queue, self._output_queue, self.__driver)
|
self.__lib_checkin = LibCheckin(self._input_queue, self._output_queue, self.__driver)
|
||||||
|
self.__lib_renew = LibRenew(self._input_queue, self._output_queue, self.__driver)
|
||||||
|
|
||||||
|
|
||||||
def __waitResponseLoad(
|
def __waitResponseLoad(
|
||||||
@@ -186,30 +188,29 @@ class AutoLib(MsgBase):
|
|||||||
if run_mode["auto_reserve"]:
|
if run_mode["auto_reserve"]:
|
||||||
if self.__lib_checker.canReserve(reserve_info.get("date")):
|
if self.__lib_checker.canReserve(reserve_info.get("date")):
|
||||||
if self.__lib_reserve.reserve(reserve_info):
|
if self.__lib_reserve.reserve(reserve_info):
|
||||||
self._showTrace(f"用户 {username} 预约成功 !")
|
|
||||||
result = 0
|
result = 0
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"用户 {username} 预约失败 !")
|
|
||||||
result = 1
|
result = 1
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"用户 {username} 无法预约,已跳过")
|
self._showTrace(f"用户 {username} 无法预约,已跳过")
|
||||||
result = 2
|
result = 2
|
||||||
# checkin
|
# checkin
|
||||||
if run_mode["auto_checkin"] and result == 2:
|
if run_mode["auto_checkin"] and result == 2:
|
||||||
if self.__lib_checker.canCheckin(reserve_info.get("date")):
|
if self.__lib_checker.canCheckin():
|
||||||
if self.__lib_checkin.checkin(username):
|
if self.__lib_checkin.checkin(username):
|
||||||
self._showTrace(f"用户 {username} 签到成功 !")
|
|
||||||
result = 0
|
result = 0
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"用户 {username} 签到失败 !")
|
|
||||||
result = 1
|
result = 1
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"用户 {username} 无法签到,已跳过")
|
self._showTrace(f"用户 {username} 无法签到,已跳过")
|
||||||
result = 2
|
result = 2
|
||||||
# renewal
|
# renewal
|
||||||
if run_mode["auto_renewal"] and result == 2:
|
if run_mode["auto_renewal"] and result == 2:
|
||||||
if self.__lib_checker.canRenew(reserve_info.get("date")):
|
if record := self.__lib_checker.canRenew():
|
||||||
pass
|
if self.__lib_renew.renew(username, record, reserve_info):
|
||||||
|
result = 0
|
||||||
|
else:
|
||||||
|
result = 1
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"用户 {username} 无法续约,已跳过")
|
self._showTrace(f"用户 {username} 无法续约,已跳过")
|
||||||
result = 2
|
result = 2
|
||||||
@@ -16,7 +16,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibChecker(LibOperator):
|
class LibChecker(LibOperator):
|
||||||
@@ -25,7 +25,7 @@ class LibChecker(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -160,23 +160,6 @@ class LibChecker(LibOperator):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def __decodeReserveRecords(
|
|
||||||
self,
|
|
||||||
reservations
|
|
||||||
) -> list:
|
|
||||||
|
|
||||||
records = []
|
|
||||||
|
|
||||||
for reservation in reservations:
|
|
||||||
record = self.__decodeReserveRecord(reservation)
|
|
||||||
if record["date"] == "":
|
|
||||||
record = None
|
|
||||||
if record["time"] == {"begin": "", "end": ""}:
|
|
||||||
record = None
|
|
||||||
records.append(record)
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
def __loadReserveRecords(
|
def __loadReserveRecords(
|
||||||
self
|
self
|
||||||
) -> list:
|
) -> list:
|
||||||
@@ -240,11 +223,15 @@ class LibChecker(LibOperator):
|
|||||||
reservations = self.__loadReserveRecords()
|
reservations = self.__loadReserveRecords()
|
||||||
if reservations is None:
|
if reservations is None:
|
||||||
return None
|
return None
|
||||||
records = self.__decodeReserveRecords(reservations[checked_count:])
|
for reservation in reservations[checked_count:]:
|
||||||
for record in records:
|
record = self.__decodeReserveRecord(reservation)
|
||||||
checked_count += 1
|
checked_count += 1
|
||||||
if record is None:
|
if record is None:
|
||||||
continue
|
continue
|
||||||
|
if record["date"] == "":
|
||||||
|
continue
|
||||||
|
if record["time"] == {"begin": "", "end": ""}:
|
||||||
|
continue
|
||||||
# record date is later than the given date, check the next one
|
# record date is later than the given date, check the next one
|
||||||
if datetime.strptime(record["date"], "%Y-%m-%d").date() >\
|
if datetime.strptime(record["date"], "%Y-%m-%d").date() >\
|
||||||
datetime.strptime(wanted_date, "%Y-%m-%d").date():
|
datetime.strptime(wanted_date, "%Y-%m-%d").date():
|
||||||
@@ -283,11 +270,11 @@ class LibChecker(LibOperator):
|
|||||||
|
|
||||||
|
|
||||||
def canCheckin(
|
def canCheckin(
|
||||||
self,
|
self
|
||||||
date: str
|
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
# have a reserved record in the given date
|
# only check the current date
|
||||||
|
date = time.strftime("%Y-%m-%d", time.localtime())
|
||||||
record = self.__getReserveRecord(date, "已预约")
|
record = self.__getReserveRecord(date, "已预约")
|
||||||
if record is not None:
|
if record is not None:
|
||||||
begin_time = record["time"]["begin"]
|
begin_time = record["time"]["begin"]
|
||||||
@@ -320,11 +307,11 @@ class LibChecker(LibOperator):
|
|||||||
|
|
||||||
|
|
||||||
def canRenew(
|
def canRenew(
|
||||||
self,
|
self
|
||||||
date: str
|
):
|
||||||
) -> bool:
|
|
||||||
|
|
||||||
# have a using record in the given date
|
# only check the current date
|
||||||
|
date = time.strftime("%Y-%m-%d", time.localtime())
|
||||||
record = self.__getReserveRecord(date, "使用中")
|
record = self.__getReserveRecord(date, "使用中")
|
||||||
if record is not None:
|
if record is not None:
|
||||||
end_time = record["time"]["end"]
|
end_time = record["time"]["end"]
|
||||||
@@ -338,9 +325,9 @@ class LibChecker(LibOperator):
|
|||||||
)
|
)
|
||||||
if abs(time_diff_seconds) < 120*60:
|
if abs(time_diff_seconds) < 120*60:
|
||||||
self._showTrace(f"{trace_msg}, 可以续约")
|
self._showTrace(f"{trace_msg}, 可以续约")
|
||||||
return True
|
return record
|
||||||
else:
|
else:
|
||||||
self._showTrace(f"{trace_msg}, 无法续约")
|
self._showTrace(f"{trace_msg}, 无法续约")
|
||||||
return False
|
return None
|
||||||
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约")
|
self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约")
|
||||||
return False
|
return None
|
||||||
@@ -16,7 +16,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibCheckin(LibOperator):
|
class LibCheckin(LibOperator):
|
||||||
@@ -25,7 +25,7 @@ class LibCheckin(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -54,7 +54,6 @@ class LibCheckin(LibOperator):
|
|||||||
except:
|
except:
|
||||||
self._showTrace("签到时发生未知错误 !")
|
self._showTrace("签到时发生未知错误 !")
|
||||||
return False
|
return False
|
||||||
print(result_message_element)
|
|
||||||
result_message = result_message_element.text
|
result_message = result_message_element.text
|
||||||
if "签到成功" in result_message:
|
if "签到成功" in result_message:
|
||||||
try:
|
try:
|
||||||
@@ -73,14 +72,16 @@ class LibCheckin(LibOperator):
|
|||||||
f" {details[3]}\n"\
|
f" {details[3]}\n"\
|
||||||
f" {details[4]}")
|
f" {details[4]}")
|
||||||
else:
|
else:
|
||||||
self._showTrace(
|
self._showTrace(f"\n"\
|
||||||
" 签到成功 !\n"\
|
" 签到成功 !\n"\
|
||||||
" 未获取到签到详情 !")
|
" 未获取到签到详情 !")
|
||||||
ok_btn.click()
|
ok_btn.click()
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
failure_reason = result_message.replace("签到失败", "").strip()
|
failure_reason = result_message.replace("签到失败", "").strip()
|
||||||
self._showTrace(f"签到失败: {failure_reason}")
|
self._showTrace(f"\n"\
|
||||||
|
" 签到失败 !\n"\
|
||||||
|
f" {failure_reason}")
|
||||||
ok_btn.click()
|
ok_btn.click()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -104,4 +105,9 @@ class LibCheckin(LibOperator):
|
|||||||
self._showTrace("签到按钮不可用, 可能不在场馆内, 请连接图书馆网络后重试")
|
self._showTrace("签到按钮不可用, 可能不在场馆内, 请连接图书馆网络后重试")
|
||||||
return False
|
return False
|
||||||
checkin_btn.click()
|
checkin_btn.click()
|
||||||
return self._waitResponseLoad()
|
if self._waitResponseLoad():
|
||||||
|
self._showTrace(f"用户 {username} 签到成功 !")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self._showTrace(f"用户 {username} 签到失败 !")
|
||||||
|
return False
|
||||||
@@ -16,7 +16,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibCheckout(LibOperator):
|
class LibCheckout(LibOperator):
|
||||||
@@ -25,7 +25,7 @@ class LibCheckout(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -17,7 +17,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibLogin(LibOperator):
|
class LibLogin(LibOperator):
|
||||||
@@ -26,7 +26,7 @@ class LibLogin(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -13,7 +13,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibLogout(LibOperator):
|
class LibLogout(LibOperator):
|
||||||
@@ -22,7 +22,7 @@ class LibLogout(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2025 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import queue
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
|
class LibRenew(LibOperator):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
input_queue: queue.Queue,
|
||||||
|
output_queue: queue.Queue,
|
||||||
|
driver: any
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(input_queue, output_queue)
|
||||||
|
|
||||||
|
self.__driver = driver
|
||||||
|
|
||||||
|
|
||||||
|
def _waitResponseLoad(
|
||||||
|
self
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
try:
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.presence_of_element_located((By.CLASS_NAME, "ui_dialog"))
|
||||||
|
)
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.presence_of_element_located((By.CLASS_NAME, "resultMessage"))
|
||||||
|
)
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.element_to_be_clickable((By.CLASS_NAME, "btnOK"))
|
||||||
|
)
|
||||||
|
result_message_element = self.__driver.find_element(
|
||||||
|
By.CLASS_NAME, "resultMessage"
|
||||||
|
)
|
||||||
|
ok_btn = self.__driver.find_element(By.CLASS_NAME, "btnOK")
|
||||||
|
except:
|
||||||
|
self._showTrace("续约时发生未知错误 !")
|
||||||
|
return False
|
||||||
|
result_message = result_message_element.text
|
||||||
|
if "续约成功" in result_message:
|
||||||
|
try:
|
||||||
|
detail_elements = self.__driver.find_elements(
|
||||||
|
By.CSS_SELECTOR, ".resultMessage dd"
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if detail_elements:
|
||||||
|
details = [element.text for element in detail_elements if element.text.strip()]
|
||||||
|
if len(details) >= 5:
|
||||||
|
self._showTrace(f"\n"\
|
||||||
|
f" 续约成功 !\n"\
|
||||||
|
f" {details[1]}\n"\
|
||||||
|
f" {details[2]}\n"\
|
||||||
|
f" {details[3]}\n"\
|
||||||
|
f" {details[4]}")
|
||||||
|
else:
|
||||||
|
self._showTrace(f"\n"\
|
||||||
|
" 续约成功 !\n"\
|
||||||
|
" 未获取到续约详情 !")
|
||||||
|
ok_btn.click()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
failure_reason = result_message.replace("续约失败", "").strip()
|
||||||
|
self._showTrace(f"\n"\
|
||||||
|
" 续约失败 !\n"\
|
||||||
|
f" {failure_reason}"
|
||||||
|
)
|
||||||
|
ok_btn.click()
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __timeToMins(
|
||||||
|
time_str: str
|
||||||
|
) -> int:
|
||||||
|
|
||||||
|
hour, minute = map(int, time_str.split(":"))
|
||||||
|
return hour*60 + minute
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __minsToTime(
|
||||||
|
mins: int
|
||||||
|
) -> str:
|
||||||
|
|
||||||
|
hour, minute = divmod(mins, 60)
|
||||||
|
return f"{hour:02d}:{minute:02d}"
|
||||||
|
|
||||||
|
|
||||||
|
def __selectNearstRecord(
|
||||||
|
self,
|
||||||
|
record: dict,
|
||||||
|
reserve_info: dict
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
end_time = record["time"]["end"]
|
||||||
|
renew_info = reserve_info["renew_time"]
|
||||||
|
max_diff = renew_info["max_diff"]
|
||||||
|
prefer_earlier = renew_info["prefer_early"]
|
||||||
|
target_renew_mins = self.__timeToMins(end_time) + renew_info["expect_duration"]*60
|
||||||
|
try:
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.visibility_of_element_located((By.ID, "extendDiv"))
|
||||||
|
)
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.presence_of_all_elements_located(
|
||||||
|
(By.CSS_SELECTOR, "#extendDiv .renewal_List li")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
renew_ok_btn = WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.presence_of_element_located((By.CSS_SELECTOR, "#extendDiv .btnOK"))
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
self._showTrace("续约时间选择界面加载失败 !")
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
renew_time_opts = self.__driver.find_elements(
|
||||||
|
By.CSS_SELECTOR, "#extendDiv .renewal_List li"
|
||||||
|
)
|
||||||
|
free_times = []
|
||||||
|
best_time_diff = max_diff
|
||||||
|
best_actual_diff = None
|
||||||
|
best_time_opt = None
|
||||||
|
|
||||||
|
if not renew_time_opts:
|
||||||
|
self._showTrace("当前未查询到可用续约时间 !")
|
||||||
|
return False
|
||||||
|
for time_opt in renew_time_opts:
|
||||||
|
time_attr = time_opt.get_attribute("id")
|
||||||
|
if time_attr and time_attr.isdigit():
|
||||||
|
time_val = int(time_attr)
|
||||||
|
free_times.append(time_opt.text.strip())
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
actual_diff = time_val - target_renew_mins
|
||||||
|
abs_diff = abs(actual_diff)
|
||||||
|
if abs_diff < best_time_diff or (
|
||||||
|
abs_diff == best_time_diff and (
|
||||||
|
# 优先选择更早的时间
|
||||||
|
(prefer_earlier and actual_diff <= 0) or
|
||||||
|
# 优先选择更晚的时间
|
||||||
|
(not prefer_earlier and actual_diff >= 0)
|
||||||
|
)
|
||||||
|
):
|
||||||
|
best_time_diff = abs_diff
|
||||||
|
best_actual_diff = actual_diff
|
||||||
|
best_time_opt = time_opt
|
||||||
|
|
||||||
|
if best_time_opt is not None:
|
||||||
|
best_time_opt.click()
|
||||||
|
abs_time_diff = abs(best_actual_diff)
|
||||||
|
if best_actual_diff < 0:
|
||||||
|
time_relation = f"早了 {abs_time_diff} 分钟"
|
||||||
|
elif best_actual_diff > 0:
|
||||||
|
time_relation = f"晚了 {abs_time_diff} 分钟"
|
||||||
|
else:
|
||||||
|
time_relation = f"正好等于续约时间"
|
||||||
|
self._showTrace(
|
||||||
|
f"选择距离期望续约时间最近的 {best_time_opt.text}, "\
|
||||||
|
f"与期望续约时间相比 {time_relation}"
|
||||||
|
)
|
||||||
|
renew_ok_btn.click()
|
||||||
|
return True
|
||||||
|
self._showTrace(
|
||||||
|
"无法选择最近的可用续约时间 !" \
|
||||||
|
f"所有可选时间与目标时间相差都超过了 {max_diff} 分钟 !"
|
||||||
|
)
|
||||||
|
self._showTrace(
|
||||||
|
f"当前可供续约的时间有: {free_times}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
self._showTrace("查询可用续约时间时发生未知错误 !")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def renew(
|
||||||
|
self,
|
||||||
|
username: str,
|
||||||
|
record: dict,
|
||||||
|
reserve_info: dict
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
if self.__driver is None:
|
||||||
|
self._showTrace("未提供有效 WebDriver 实例 !")
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
renew_btn = WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.element_to_be_clickable((By.ID, "btnExtend"))
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
self._showTrace(f"用户 {username} 续约界面加载失败 !")
|
||||||
|
return False
|
||||||
|
if "disabled" in renew_btn.get_attribute("class"):
|
||||||
|
self._showTrace(f"用户 {username} 续约按钮不可用, 可能不在场馆内")
|
||||||
|
return False
|
||||||
|
renew_btn.click()
|
||||||
|
if not self.__selectNearstRecord(record, reserve_info):
|
||||||
|
return False
|
||||||
|
# renew_ok_btn.click()
|
||||||
|
if self._waitResponseLoad():
|
||||||
|
self._showTrace(f"用户 {username} 续约成功 !")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self._showTrace(f"用户 {username} 续约失败 !")
|
||||||
|
return False
|
||||||
@@ -16,7 +16,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
from LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
|
|
||||||
class LibReserve(LibOperator):
|
class LibReserve(LibOperator):
|
||||||
@@ -25,7 +25,7 @@ class LibReserve(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver
|
driver: any
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -187,12 +187,13 @@ class LibReserve(LibOperator):
|
|||||||
reserve_info: dict
|
reserve_info: dict
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
if reserve_info.get("expect_duration") is None:
|
|
||||||
reserve_info["expect_duration"] = 4
|
|
||||||
self._showTrace("预约持续时间未指定, 使用默认时长为 4 小时")
|
|
||||||
if reserve_info.get("satisfy_duration") is None:
|
if reserve_info.get("satisfy_duration") is None:
|
||||||
reserve_info["satisfy_duration"] = True
|
reserve_info["satisfy_duration"] = True
|
||||||
self._showTrace("预约满足时长要求未指定, 默认满足")
|
self._showTrace("预约满足时长要求未指定, 默认满足")
|
||||||
|
if reserve_info["satisfy_duration"]:
|
||||||
|
if reserve_info.get("expect_duration") is None:
|
||||||
|
reserve_info["expect_duration"] = 4
|
||||||
|
self._showTrace("需要满足预约持续时间, 但未指定, 使用默认时长为 4 小时")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ class LibReserve(LibOperator):
|
|||||||
# if end time is earlier than begin_time, exchange them
|
# if end time is earlier than begin_time, exchange them
|
||||||
if end_mins < begin_mins:
|
if end_mins < begin_mins:
|
||||||
self._showTrace(
|
self._showTrace(
|
||||||
f"结束时间 {end_time['time']} 早于开始时间 {begin_time['time']}, 自动交换"
|
f"结束时间 {end_time['time']} 早于开始时间 {begin_time['time']}, 尝试交换时间"
|
||||||
)
|
)
|
||||||
reserve_info["end_time"] = begin_time
|
reserve_info["end_time"] = begin_time
|
||||||
reserve_info["begin_time"] = end_time
|
reserve_info["begin_time"] = end_time
|
||||||
@@ -261,10 +262,9 @@ class LibReserve(LibOperator):
|
|||||||
if end_mins - begin_mins > 8*60:
|
if end_mins - begin_mins > 8*60:
|
||||||
self._showTrace(
|
self._showTrace(
|
||||||
f"该用户未设置优先满足时长要求, 但是检查到预约持续时间 "
|
f"该用户未设置优先满足时长要求, 但是检查到预约持续时间 "
|
||||||
f"{int((end_mins - begin_mins)/60)} 小时 "
|
f"{float((end_mins - begin_mins)/60)} 小时 "
|
||||||
f"超出最大时长 8 小时, 自动设置为 8 小时"
|
f"超出最大时长 8 小时, 自动设置为 8 小时"
|
||||||
)
|
)
|
||||||
reserve_info["expect_duration"] = 8
|
|
||||||
reserve_info["end_time"]["time"] = self.__minsToTime(begin_mins + 8*60)
|
reserve_info["end_time"]["time"] = self.__minsToTime(begin_mins + 8*60)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -324,11 +324,52 @@ class LibReserve(LibOperator):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def __clickElementByJS(
|
||||||
|
self,
|
||||||
|
trigger_locator_id: str,
|
||||||
|
option_query_selector: str,
|
||||||
|
fail_msg: str,
|
||||||
|
success_msg: str,
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
script = f"""
|
||||||
|
try {{
|
||||||
|
var trigger = document.getElementById('{trigger_locator_id}');
|
||||||
|
if (trigger) {{
|
||||||
|
trigger.click();
|
||||||
|
var option = document.querySelector("{option_query_selector}");
|
||||||
|
if (option) {{
|
||||||
|
option.click();
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
return false;
|
||||||
|
}} catch (e) {{
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
result = self.__driver.execute_script(script)
|
||||||
|
time.sleep(0.1)
|
||||||
|
if result:
|
||||||
|
self._showTrace(success_msg)
|
||||||
|
else:
|
||||||
|
self._showTrace(fail_msg)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def __selectDate(
|
def __selectDate(
|
||||||
self,
|
self,
|
||||||
date_str: str
|
date_str: str
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
|
if self.__clickElementByJS(
|
||||||
|
trigger_locator_id="onDate_select",
|
||||||
|
option_query_selector=f"p#options_onDate a[value='{date_str}']",
|
||||||
|
success_msg=f"日期 {date_str} 选择成功 !",
|
||||||
|
fail_msg=f"选择日期失败 ! : {date_str} 不可用"
|
||||||
|
):
|
||||||
|
return True
|
||||||
return self.__clickElement(
|
return self.__clickElement(
|
||||||
trigger_locator=(By.ID, "onDate_select"),
|
trigger_locator=(By.ID, "onDate_select"),
|
||||||
option_locator=(By.XPATH, f"//p[@id='options_onDate']/a[@value='{date_str}']"),
|
option_locator=(By.XPATH, f"//p[@id='options_onDate']/a[@value='{date_str}']"),
|
||||||
@@ -342,12 +383,20 @@ class LibReserve(LibOperator):
|
|||||||
place: str
|
place: str
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
actual_place = "1" if place == "图书馆" else "1"
|
place = "1" # the library only have this place :)
|
||||||
|
display_place = "图书馆"
|
||||||
|
if self.__clickElementByJS(
|
||||||
|
trigger_locator_id="display_building",
|
||||||
|
option_query_selector=f"p#options_building a[value='{place}']",
|
||||||
|
success_msg=f"预约场所 {display_place} 选择成功 !",
|
||||||
|
fail_msg=f"选择预约场所失败 ! : {display_place} 不可用"
|
||||||
|
):
|
||||||
|
return True
|
||||||
return self.__clickElement(
|
return self.__clickElement(
|
||||||
trigger_locator=(By.ID, "display_building"),
|
trigger_locator=(By.ID, "display_building"),
|
||||||
option_locator=(By.XPATH, f"//p[@id='options_building']/a[@value='{actual_place}']"),
|
option_locator=(By.XPATH, f"//p[@id='options_building']/a[@value='{place}']"),
|
||||||
success_msg=f"预约场所 {place} 选择成功 !",
|
success_msg=f"预约场所 {display_place} 选择成功 !",
|
||||||
fail_msg=f"选择预约场所失败 ! : {place} 不可用"
|
fail_msg=f"选择预约场所失败 ! : {display_place} 不可用"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -357,6 +406,13 @@ class LibReserve(LibOperator):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
display_floor = self.__floor_map.get(floor)
|
display_floor = self.__floor_map.get(floor)
|
||||||
|
if self.__clickElementByJS(
|
||||||
|
trigger_locator_id="floor_select",
|
||||||
|
option_query_selector=f"p#options_floor a[value='{floor}']",
|
||||||
|
success_msg=f"楼层 {display_floor} 选择成功 !",
|
||||||
|
fail_msg=f"选择楼层失败 ! : {display_floor} 不可用"
|
||||||
|
):
|
||||||
|
return True
|
||||||
return self.__clickElement(
|
return self.__clickElement(
|
||||||
trigger_locator=(By.ID, "floor_select"),
|
trigger_locator=(By.ID, "floor_select"),
|
||||||
option_locator=(By.XPATH, f"//p[@id='options_floor']/a[@value='{floor}']"),
|
option_locator=(By.XPATH, f"//p[@id='options_floor']/a[@value='{floor}']"),
|
||||||
@@ -371,12 +427,24 @@ class LibReserve(LibOperator):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
display_room = self.__room_map.get(room)
|
display_room = self.__room_map.get(room)
|
||||||
return self.__clickElement(
|
# find room
|
||||||
trigger_locator=(By.ID, f"room_{room}"),
|
try:
|
||||||
option_locator=None,
|
WebDriverWait(self.__driver, 2).until(
|
||||||
success_msg=f"房间 {display_room} 选择成功 !",
|
EC.element_to_be_clickable((By.ID, "findRoom"))
|
||||||
fail_msg=f"选择房间失败 ! : {display_room} 不可用"
|
).click()
|
||||||
)
|
except:
|
||||||
|
self._showTrace("加载房间/区域失败 !")
|
||||||
|
return False
|
||||||
|
# select room
|
||||||
|
try:
|
||||||
|
WebDriverWait(self.__driver, 2).until(
|
||||||
|
EC.element_to_be_clickable((By.ID, f"room_{room}"))
|
||||||
|
).click()
|
||||||
|
self._showTrace(f"房间 {display_room} 选择成功 !")
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
self._showTrace(f"选择房间失败 ! : {display_room} 不可用")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __selectSeat(
|
def __selectSeat(
|
||||||
@@ -509,6 +577,7 @@ class LibReserve(LibOperator):
|
|||||||
expect_begin_time = actual_begin_time = begin_time["time"]
|
expect_begin_time = actual_begin_time = begin_time["time"]
|
||||||
expect_end_time = actual_end_time = end_time["time"]
|
expect_end_time = actual_end_time = end_time["time"]
|
||||||
expect_begin_mins = self.__timeToMins(expect_begin_time)
|
expect_begin_mins = self.__timeToMins(expect_begin_time)
|
||||||
|
actual_begin_mins = expect_begin_mins
|
||||||
expect_end_mins = self.__timeToMins(expect_end_time)
|
expect_end_mins = self.__timeToMins(expect_end_time)
|
||||||
|
|
||||||
# select the begin time
|
# select the begin time
|
||||||
@@ -522,11 +591,18 @@ class LibReserve(LibOperator):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
actual_begin_time = self.__minsToTime(expect_begin_mins)
|
actual_begin_time = self.__minsToTime(expect_begin_mins)
|
||||||
|
actual_begin_mins = self.__timeToMins(actual_begin_time)
|
||||||
# if 'satisfy_duration' is True.
|
# if 'satisfy_duration' is True.
|
||||||
# select the end time based on the begin time
|
# select the end time based on the begin time
|
||||||
# (because it may be changed under the 'max time diff' strategy) and expect duration.
|
# (because it may be changed under the 'max time diff' strategy) and expect duration.
|
||||||
if satisfy_duration:
|
if satisfy_duration:
|
||||||
expect_end_mins = int(expect_begin_mins + expct_duration*60)
|
expect_end_mins = int(actual_begin_mins + expct_duration*60)
|
||||||
|
if expect_end_mins > self.__timeToMins("23:30"):
|
||||||
|
expect_end_mins = self.__timeToMins("23:30")
|
||||||
|
self._showTrace(
|
||||||
|
f"预约持续时间 {expct_duration} 小时, 超过最大预约时间 23:30, 自动调整为 23:30"
|
||||||
|
)
|
||||||
|
expect_end_time = self.__minsToTime(expect_end_mins)
|
||||||
self._showTrace(
|
self._showTrace(
|
||||||
f"需要满足期望预约持续时间: {expct_duration} 小时, "\
|
f"需要满足期望预约持续时间: {expct_duration} 小时, "\
|
||||||
f"根据开始时间 {actual_begin_time} 计算结束时间: {self.__minsToTime(expect_end_mins)}"
|
f"根据开始时间 {actual_begin_time} 计算结束时间: {self.__minsToTime(expect_end_mins)}"
|
||||||
@@ -572,22 +648,13 @@ class LibReserve(LibOperator):
|
|||||||
except:
|
except:
|
||||||
self._showTrace(f"加载预约选座页面失败 !")
|
self._showTrace(f"加载预约选座页面失败 !")
|
||||||
return False
|
return False
|
||||||
# date, place, floor
|
# date, place, floor, room
|
||||||
if not self.__selectDate(reserve_info["date"]):
|
if not self.__selectDate(reserve_info["date"]):
|
||||||
return False
|
return False
|
||||||
if not self.__selectPlace(reserve_info["place"]):
|
if not self.__selectPlace(reserve_info["place"]):
|
||||||
return False
|
return False
|
||||||
if not self.__selectFloor(reserve_info["floor"]):
|
if not self.__selectFloor(reserve_info["floor"]):
|
||||||
return False
|
return False
|
||||||
# room find
|
|
||||||
try:
|
|
||||||
WebDriverWait(self.__driver, 2).until(
|
|
||||||
EC.element_to_be_clickable((By.ID, "findRoom"))
|
|
||||||
).click()
|
|
||||||
except:
|
|
||||||
self._showTrace("加载房间/区域失败 !")
|
|
||||||
return False
|
|
||||||
# room
|
|
||||||
if not self.__selectRoom(reserve_info["room"]):
|
if not self.__selectRoom(reserve_info["room"]):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -615,4 +682,8 @@ class LibReserve(LibOperator):
|
|||||||
self._showTrace(f"预约提交失败 !")
|
self._showTrace(f"预约提交失败 !")
|
||||||
if not submit_reserve and have_hover_on_page:
|
if not submit_reserve and have_hover_on_page:
|
||||||
self.__driver.refresh()
|
self.__driver.refresh()
|
||||||
|
if reserve_success:
|
||||||
|
self._showTrace(f"用户 {reserve_info['username']} 预约成功 !")
|
||||||
|
else:
|
||||||
|
self._showTrace(f"用户 {reserve_info['username']} 预约失败 !")
|
||||||
return reserve_success
|
return reserve_success
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
Operators module for the AutoLibrary project.
|
||||||
|
|
||||||
|
Here are the classes and modules in this package:
|
||||||
|
- AutoLib: AutoLibrary operator.
|
||||||
|
- LibLogin: Library operator for logging in.
|
||||||
|
- LibLogout: Library operator for logging out.
|
||||||
|
- LibReserve: Library operator for reserving seat.
|
||||||
|
- LibCheckin: Library operator for checking in seat.
|
||||||
|
- LibCheckout: Library operator for checking out seat.
|
||||||
|
- LibRenew: Library operator for renewing seat.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Utils module for the AutoLibrary project.
|
||||||
|
|
||||||
|
Here are the classes and modules in this package:
|
||||||
|
- ConfigReader: Configuration reader class for the AutoLibrary project.
|
||||||
|
- ConfigWriter: Configuration writer class for the AutoLibrary project.
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user