You've already forked HeurAMS-legacy
fix
This commit is contained in:
@@ -2,5 +2,6 @@ print("欢迎使用 HeurAMS 及其组件!")
|
|||||||
|
|
||||||
# 补充日志记录
|
# 补充日志记录
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
logger.info("欢迎使用 HeurAMS 及其组件!")
|
logger.info("欢迎使用 HeurAMS 及其组件!")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HeurAMSApp(App):
|
class HeurAMSApp(App):
|
||||||
TITLE = "潜进"
|
TITLE = "潜进"
|
||||||
CSS_PATH = "css/main.tcss"
|
CSS_PATH = "css/main.tcss"
|
||||||
@@ -37,8 +38,10 @@ class HeurAMSApp(App):
|
|||||||
print("DO NOTHING")
|
print("DO NOTHING")
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
def environment_check():
|
def environment_check():
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
logger.debug("检查环境路径")
|
logger.debug("检查环境路径")
|
||||||
|
|
||||||
for i in config_var.get()["paths"].values():
|
for i in config_var.get()["paths"].values():
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class AboutScreen(Screen):
|
|||||||
|
|
||||||
开发人员:
|
开发人员:
|
||||||
|
|
||||||
- [@pluvium27](https://github.com/pluvium27) (Wang Zhiyu)
|
- Wang Zhiyu([@pluvium27](https://github.com/pluvium27)): 项目作者
|
||||||
|
|
||||||
特别感谢:
|
特别感谢:
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ class AboutScreen(Screen):
|
|||||||
|
|
||||||
通过我们协力开发的软件为所有人谋取福祉.
|
通过我们协力开发的软件为所有人谋取福祉.
|
||||||
|
|
||||||
此项目不是 KDE 软件, 但上述工作不可避免地让我们确立了和 KDE 宣言相同的下列价值观:
|
上述工作不可避免地让我们确立了下列价值观 (取自 KDE 宣言):
|
||||||
|
|
||||||
- 开放治理 确保更多人能参与我们的领导和决策进程;
|
- 开放治理 确保更多人能参与我们的领导和决策进程;
|
||||||
|
|
||||||
@@ -68,8 +68,13 @@ class AboutScreen(Screen):
|
|||||||
我们的共同目标是为人人带来高品质的辅助记忆 & 学习软件.
|
我们的共同目标是为人人带来高品质的辅助记忆 & 学习软件.
|
||||||
|
|
||||||
不管您来自何方, 我们都欢迎您加入社区并做出贡献.
|
不管您来自何方, 我们都欢迎您加入社区并做出贡献.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# """
|
||||||
|
# 学术数据
|
||||||
|
|
||||||
|
# "潜进" 的用户数据可用于科学方面的研究, 我们将在未来版本添加学术数据的收集和展示平台
|
||||||
|
# """
|
||||||
yield Markdown(about_text, classes="about-markdown")
|
yield Markdown(about_text, classes="about-markdown")
|
||||||
|
|
||||||
yield Button(
|
yield Button(
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ import pathlib
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DashboardScreen(Screen):
|
class DashboardScreen(Screen):
|
||||||
SUB_TITLE = "仪表盘"
|
SUB_TITLE = "仪表盘"
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Header(show_clock=True)
|
yield Header(show_clock=True)
|
||||||
yield ScrollableContainer(
|
yield ScrollableContainer(
|
||||||
@@ -71,6 +73,7 @@ class DashboardScreen(Screen):
|
|||||||
nextdate = 0x3F3F3F3F
|
nextdate = 0x3F3F3F3F
|
||||||
for i in electron_dict.values():
|
for i in electron_dict.values():
|
||||||
i: pt.Electron
|
i: pt.Electron
|
||||||
|
logger.debug(i, i.is_due())
|
||||||
if i.is_due():
|
if i.is_due():
|
||||||
is_due = 1
|
is_due = 1
|
||||||
if i.is_activated():
|
if i.is_activated():
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ class AtomState(Enum):
|
|||||||
FAILED = auto()
|
FAILED = auto()
|
||||||
NORMAL = auto()
|
NORMAL = auto()
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MemScreen(Screen):
|
class MemScreen(Screen):
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
("q", "pop_screen", "返回"),
|
("q", "pop_screen", "返回"),
|
||||||
@@ -45,6 +47,7 @@ class MemScreen(Screen):
|
|||||||
self.phaser = Phaser(atoms)
|
self.phaser = Phaser(atoms)
|
||||||
# logger.debug(self.phaser.state)
|
# logger.debug(self.phaser.state)
|
||||||
self.procession: Procession = self.phaser.current_procession() # type: ignore
|
self.procession: Procession = self.phaser.current_procession() # type: ignore
|
||||||
|
self.atom: pt.Atom = self.procession.current_atom
|
||||||
# logger.debug(self.phaser.state)
|
# logger.debug(self.phaser.state)
|
||||||
# self.procession.forward(1)
|
# self.procession.forward(1)
|
||||||
for i in atoms:
|
for i in atoms:
|
||||||
@@ -58,12 +61,12 @@ class MemScreen(Screen):
|
|||||||
try:
|
try:
|
||||||
logger.debug(self.phaser.state)
|
logger.debug(self.phaser.state)
|
||||||
logger.debug(self.procession.cursor)
|
logger.debug(self.procession.cursor)
|
||||||
logger.debug(self.procession.current_atom)
|
logger.debug(self.atom)
|
||||||
self.fission = Fission(self.procession.current_atom, self.phaser.state)
|
self.fission = Fission(self.atom, self.phaser.state)
|
||||||
puzzle_debug = next(self.fission.generate())
|
puzzle_debug = next(self.fission.generate())
|
||||||
# logger.debug(puzzle_debug)
|
# logger.debug(puzzle_debug)
|
||||||
return shim.puzzle2widget[puzzle_debug["puzzle"]](
|
return shim.puzzle2widget[puzzle_debug["puzzle"]](
|
||||||
atom=self.procession.current_atom, alia=puzzle_debug["alia"]
|
atom=self.atom, alia=puzzle_debug["alia"]
|
||||||
)
|
)
|
||||||
except (KeyError, StopIteration, AttributeError) as e:
|
except (KeyError, StopIteration, AttributeError) as e:
|
||||||
logger.debug(f"调度展开出错: {e}")
|
logger.debug(f"调度展开出错: {e}")
|
||||||
@@ -98,6 +101,7 @@ class MemScreen(Screen):
|
|||||||
for i in container.children:
|
for i in container.children:
|
||||||
i.remove()
|
i.remove()
|
||||||
from heurams.interface.widgets.finished import Finished
|
from heurams.interface.widgets.finished import Finished
|
||||||
|
|
||||||
container.mount(Finished())
|
container.mount(Finished())
|
||||||
|
|
||||||
def on_button_pressed(self, event):
|
def on_button_pressed(self, event):
|
||||||
@@ -108,11 +112,12 @@ class MemScreen(Screen):
|
|||||||
return
|
return
|
||||||
forwards = 1 if new_rating >= 4 else 0
|
forwards = 1 if new_rating >= 4 else 0
|
||||||
self.rating = -1
|
self.rating = -1
|
||||||
|
logger.debug(f"试图前进: {"允许" if forwards else "禁止"}")
|
||||||
if forwards:
|
if forwards:
|
||||||
ret = self.procession.forward(1)
|
ret = self.procession.forward(1)
|
||||||
if ret == 0:
|
if ret == 0: # 若结束了此次队列
|
||||||
self.procession = self.phaser.current_procession() # type: ignore
|
self.procession = self.phaser.current_procession() # type: ignore
|
||||||
if self.procession == 0:
|
if self.procession == 0: # 若所有队列都结束了
|
||||||
logger.debug(f"记忆进程结束")
|
logger.debug(f"记忆进程结束")
|
||||||
for i in self.atoms:
|
for i in self.atoms:
|
||||||
i: pt.Atom
|
i: pt.Atom
|
||||||
@@ -121,10 +126,10 @@ class MemScreen(Screen):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.debug(f"建立新队列 {self.procession.phase}")
|
logger.debug(f"建立新队列 {self.procession.phase}")
|
||||||
else:
|
self.load_puzzle()
|
||||||
|
else: # 若不通过
|
||||||
self.procession.append()
|
self.procession.append()
|
||||||
self.update_display()
|
self.update_display()
|
||||||
self.load_puzzle()
|
|
||||||
|
|
||||||
def action_play_voice(self):
|
def action_play_voice(self):
|
||||||
"""朗读当前内容"""
|
"""朗读当前内容"""
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ class NucleonCreatorScreen(Screen):
|
|||||||
# selected 是描述字符串,格式如 "描述 (filename.toml)"
|
# selected 是描述字符串,格式如 "描述 (filename.toml)"
|
||||||
# 提取文件名
|
# 提取文件名
|
||||||
import re
|
import re
|
||||||
match = re.search(r'\(([^)]+)\)$', selected)
|
|
||||||
|
match = re.search(r"\(([^)]+)\)$", selected)
|
||||||
if not match:
|
if not match:
|
||||||
self.notify("模板选择格式无效", severity="error")
|
self.notify("模板选择格式无效", severity="error")
|
||||||
return
|
return
|
||||||
@@ -135,7 +136,7 @@ class NucleonCreatorScreen(Screen):
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
with open(template_path, 'r', encoding='utf-8') as f:
|
with open(template_path, "r", encoding="utf-8") as f:
|
||||||
template_data = toml.load(f)
|
template_data = toml.load(f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.notify(f"加载模板失败: {e}", severity="error")
|
self.notify(f"加载模板失败: {e}", severity="error")
|
||||||
@@ -159,7 +160,7 @@ class NucleonCreatorScreen(Screen):
|
|||||||
|
|
||||||
# 写入新文件
|
# 写入新文件
|
||||||
try:
|
try:
|
||||||
with open(nucleon_path, 'w', encoding='utf-8') as f:
|
with open(nucleon_path, "w", encoding="utf-8") as f:
|
||||||
toml.dump(template_data, f)
|
toml.dump(template_data, f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.notify(f"保存单元集失败: {e}", severity="error")
|
self.notify(f"保存单元集失败: {e}", severity="error")
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class PreparationScreen(Screen):
|
|||||||
def action_go_back(self):
|
def action_go_back(self):
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
|
||||||
def action_precache(self):
|
def action_precache(self):
|
||||||
from ..screens.precache import PrecachingScreen
|
from ..screens.precache import PrecachingScreen
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Iterable
|
||||||
|
from textual.app import ComposeResult
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
|
|
||||||
@@ -22,3 +24,9 @@ class BasePuzzleWidget(Widget):
|
|||||||
markup=markup
|
markup=markup
|
||||||
)
|
)
|
||||||
self.atom = atom
|
self.atom = atom
|
||||||
|
|
||||||
|
def compose(self) -> Iterable[Widget]:
|
||||||
|
return super().compose()
|
||||||
|
|
||||||
|
def handler(self, rating) -> None:
|
||||||
|
pass
|
||||||
@@ -15,6 +15,7 @@ from typing import TypedDict
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Setting(TypedDict):
|
class Setting(TypedDict):
|
||||||
__origin__: str
|
__origin__: str
|
||||||
__hint__: str
|
__hint__: str
|
||||||
@@ -22,6 +23,7 @@ class Setting(TypedDict):
|
|||||||
delimiter: str
|
delimiter: str
|
||||||
min_denominator: str
|
min_denominator: str
|
||||||
|
|
||||||
|
|
||||||
class ClozePuzzle(BasePuzzleWidget):
|
class ClozePuzzle(BasePuzzleWidget):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -78,7 +80,6 @@ class ClozePuzzle(BasePuzzleWidget):
|
|||||||
preview = self.query_one("#inputpreview")
|
preview = self.query_one("#inputpreview")
|
||||||
preview.update(f"当前输入: {self.inputlist}") # type: ignore
|
preview.update(f"当前输入: {self.inputlist}") # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
button_id = event.button.id
|
button_id = event.button.id
|
||||||
|
|
||||||
@@ -94,9 +95,15 @@ class ClozePuzzle(BasePuzzleWidget):
|
|||||||
if len(self.inputlist) >= len(self.puzzle.answer):
|
if len(self.inputlist) >= len(self.puzzle.answer):
|
||||||
is_correct = self.inputlist == self.puzzle.answer
|
is_correct = self.inputlist == self.puzzle.answer
|
||||||
rating = 4 if is_correct else 2
|
rating = 4 if is_correct else 2
|
||||||
|
self.handler(rating)
|
||||||
self.screen.rating = rating # type: ignore
|
self.screen.rating = rating # type: ignore
|
||||||
|
|
||||||
if not is_correct:
|
if not is_correct:
|
||||||
self.inputlist = []
|
self.inputlist = []
|
||||||
self.update_display()
|
self.update_display()
|
||||||
|
|
||||||
|
def handler(self, rating):
|
||||||
|
if self.atom.lock():
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.atom.minimize(rating)
|
||||||
@@ -14,6 +14,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Setting(TypedDict):
|
class Setting(TypedDict):
|
||||||
__origin__: str
|
__origin__: str
|
||||||
__hint__: str
|
__hint__: str
|
||||||
@@ -49,6 +50,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.alia = alia
|
self.alia = alia
|
||||||
self.hashmap = dict()
|
self.hashmap = dict()
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
|
self.atom = atom
|
||||||
self._load()
|
self._load()
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
@@ -113,7 +115,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
rating = 4 if is_correct else 2
|
rating = 4 if is_correct else 2
|
||||||
|
|
||||||
self.screen.rating = rating # type: ignore
|
self.screen.rating = rating # type: ignore
|
||||||
|
self.handler(rating)
|
||||||
# 重置输入(如果回答错误)
|
# 重置输入(如果回答错误)
|
||||||
if not is_correct:
|
if not is_correct:
|
||||||
self.inputlist = []
|
self.inputlist = []
|
||||||
@@ -150,3 +152,10 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.hashmap[button_id] = option
|
self.hashmap[button_id] = option
|
||||||
new_button = Button(option, id=button_id)
|
new_button = Button(option, id=button_id)
|
||||||
container.mount(new_button)
|
container.mount(new_button)
|
||||||
|
|
||||||
|
def handler(self, rating):
|
||||||
|
if self.atom.lock():
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.atom.minimize(rating)
|
||||||
|
|
||||||
@@ -13,7 +13,9 @@ import re
|
|||||||
from .base_puzzle_widget import BasePuzzleWidget
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
from typing import TypedDict, List
|
from typing import TypedDict, List
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
class RecognitionConfig(TypedDict):
|
class RecognitionConfig(TypedDict):
|
||||||
__origin__: str
|
__origin__: str
|
||||||
@@ -100,3 +102,11 @@ class Recognition(BasePuzzleWidget):
|
|||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
if event.button.id == "ok":
|
if event.button.id == "ok":
|
||||||
self.screen.rating = 5 # type: ignore
|
self.screen.rating = 5 # type: ignore
|
||||||
|
self.handler(5)
|
||||||
|
|
||||||
|
def handler(self, rating):
|
||||||
|
if not self.atom.registry["electron"].is_activated() and not self.atom.registry["runtime"]["locked"]:
|
||||||
|
self.atom.registry["electron"].activate()
|
||||||
|
logger.debug(f"激活原子 {self.atom}")
|
||||||
|
self.atom.lock(1)
|
||||||
|
self.atom.minimize(5)
|
||||||
|
|||||||
@@ -33,27 +33,37 @@ class BaseAlgorithm:
|
|||||||
cls, algodata: dict, feedback: int = 5, is_new_activation: bool = False
|
cls, algodata: dict, feedback: int = 5, is_new_activation: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
"""迭代记忆数据"""
|
"""迭代记忆数据"""
|
||||||
logger.debug("BaseAlgorithm.revisor 被调用,algodata keys: %s, feedback: %d, is_new_activation: %s",
|
logger.debug(
|
||||||
list(algodata.keys()) if algodata else [], feedback, is_new_activation)
|
"BaseAlgorithm.revisor 被调用,algodata keys: %s, feedback: %d, is_new_activation: %s",
|
||||||
|
list(algodata.keys()) if algodata else [],
|
||||||
|
feedback,
|
||||||
|
is_new_activation,
|
||||||
|
)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_due(cls, algodata) -> int:
|
def is_due(cls, algodata) -> int:
|
||||||
"""是否应该复习"""
|
"""是否应该复习"""
|
||||||
logger.debug("BaseAlgorithm.is_due 被调用,algodata keys: %s",
|
logger.debug(
|
||||||
list(algodata.keys()) if algodata else [])
|
"BaseAlgorithm.is_due 被调用,algodata keys: %s",
|
||||||
|
list(algodata.keys()) if algodata else [],
|
||||||
|
)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def rate(cls, algodata) -> str:
|
def rate(cls, algodata) -> str:
|
||||||
"""获取评分信息"""
|
"""获取评分信息"""
|
||||||
logger.debug("BaseAlgorithm.rate 被调用,algodata keys: %s",
|
logger.debug(
|
||||||
list(algodata.keys()) if algodata else [])
|
"BaseAlgorithm.rate 被调用,algodata keys: %s",
|
||||||
|
list(algodata.keys()) if algodata else [],
|
||||||
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def nextdate(cls, algodata) -> int:
|
def nextdate(cls, algodata) -> int:
|
||||||
"""获取下一次记忆时间戳"""
|
"""获取下一次记忆时间戳"""
|
||||||
logger.debug("BaseAlgorithm.nextdate 被调用,algodata keys: %s",
|
logger.debug(
|
||||||
list(algodata.keys()) if algodata else [])
|
"BaseAlgorithm.nextdate 被调用,algodata keys: %s",
|
||||||
|
list(algodata.keys()) if algodata else [],
|
||||||
|
)
|
||||||
return -1
|
return -1
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ class SM2Algorithm(BaseAlgorithm):
|
|||||||
Args:
|
Args:
|
||||||
quality (int): 记忆保留率量化参数
|
quality (int): 记忆保留率量化参数
|
||||||
"""
|
"""
|
||||||
logger.debug("SM2.revisor 开始,feedback: %d, is_new_activation: %s", feedback, is_new_activation)
|
logger.debug(
|
||||||
|
"SM2.revisor 开始,feedback: %d, is_new_activation: %s",
|
||||||
|
feedback,
|
||||||
|
is_new_activation,
|
||||||
|
)
|
||||||
|
|
||||||
if feedback == -1:
|
if feedback == -1:
|
||||||
logger.debug("feedback 为 -1,跳过更新")
|
logger.debug("feedback 为 -1,跳过更新")
|
||||||
@@ -81,7 +85,9 @@ class SM2Algorithm(BaseAlgorithm):
|
|||||||
algodata[cls.algo_name]["interval"] = round(
|
algodata[cls.algo_name]["interval"] = round(
|
||||||
algodata[cls.algo_name]["interval"] * algodata[cls.algo_name]["efactor"]
|
algodata[cls.algo_name]["interval"] * algodata[cls.algo_name]["efactor"]
|
||||||
)
|
)
|
||||||
logger.debug("rept>1,计算 interval: %d", algodata[cls.algo_name]["interval"])
|
logger.debug(
|
||||||
|
"rept>1,计算 interval: %d", algodata[cls.algo_name]["interval"]
|
||||||
|
)
|
||||||
|
|
||||||
algodata[cls.algo_name]["last_date"] = timer.get_daystamp()
|
algodata[cls.algo_name]["last_date"] = timer.get_daystamp()
|
||||||
algodata[cls.algo_name]["next_date"] = (
|
algodata[cls.algo_name]["next_date"] = (
|
||||||
@@ -89,16 +95,22 @@ class SM2Algorithm(BaseAlgorithm):
|
|||||||
)
|
)
|
||||||
algodata[cls.algo_name]["last_modify"] = timer.get_timestamp()
|
algodata[cls.algo_name]["last_modify"] = timer.get_timestamp()
|
||||||
|
|
||||||
logger.debug("更新日期: last_date=%d, next_date=%d, last_modify=%f",
|
logger.debug(
|
||||||
|
"更新日期: last_date=%d, next_date=%d, last_modify=%f",
|
||||||
algodata[cls.algo_name]["last_date"],
|
algodata[cls.algo_name]["last_date"],
|
||||||
algodata[cls.algo_name]["next_date"],
|
algodata[cls.algo_name]["next_date"],
|
||||||
algodata[cls.algo_name]["last_modify"])
|
algodata[cls.algo_name]["last_modify"],
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_due(cls, algodata):
|
def is_due(cls, algodata):
|
||||||
result = algodata[cls.algo_name]["next_date"] <= timer.get_daystamp()
|
result = algodata[cls.algo_name]["next_date"] <= timer.get_daystamp()
|
||||||
logger.debug("SM2.is_due: next_date=%d, current_daystamp=%d, result=%s",
|
logger.debug(
|
||||||
algodata[cls.algo_name]["next_date"], timer.get_daystamp(), result)
|
"SM2.is_due: next_date=%d, current_daystamp=%d, result=%s",
|
||||||
|
algodata[cls.algo_name]["next_date"],
|
||||||
|
timer.get_daystamp(),
|
||||||
|
result,
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Particle 模块 - 粒子对象系统
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
logger.debug("粒子模块已加载")
|
logger.debug("粒子模块已加载")
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
class AtomRegister_runtime(TypedDict):
|
||||||
|
locked: bool # 只读锁定标识符
|
||||||
|
min_rate: int # 最低评分
|
||||||
|
|
||||||
class AtomRegister(TypedDict):
|
class AtomRegister(TypedDict):
|
||||||
nucleon: Nucleon
|
nucleon: Nucleon
|
||||||
@@ -23,7 +26,7 @@ class AtomRegister(TypedDict):
|
|||||||
orbital: Orbital
|
orbital: Orbital
|
||||||
orbital_path: pathlib.Path
|
orbital_path: pathlib.Path
|
||||||
orbital_fmt: str
|
orbital_fmt: str
|
||||||
runtime: dict
|
runtime: AtomRegister_runtime
|
||||||
|
|
||||||
|
|
||||||
class Atom:
|
class Atom:
|
||||||
@@ -51,6 +54,7 @@ class Atom:
|
|||||||
"orbital": None,
|
"orbital": None,
|
||||||
"orbital_path": None, # 允许设置为 None, 此时使用 nucleon 文件内的推荐配置
|
"orbital_path": None, # 允许设置为 None, 此时使用 nucleon 文件内的推荐配置
|
||||||
"orbital_fmt": "toml",
|
"orbital_fmt": "toml",
|
||||||
|
"runtime": {"locked": False, "min_rate": 0x3f3f3f3f}
|
||||||
}
|
}
|
||||||
self.do_eval()
|
self.do_eval()
|
||||||
logger.debug("Atom 初始化完成")
|
logger.debug("Atom 初始化完成")
|
||||||
@@ -65,6 +69,38 @@ class Atom:
|
|||||||
logger.error("尝试链接不受支持的键: '%s'", key)
|
logger.error("尝试链接不受支持的键: '%s'", key)
|
||||||
raise ValueError("不受支持的原子元数据链接操作")
|
raise ValueError("不受支持的原子元数据链接操作")
|
||||||
|
|
||||||
|
def minimize(self, rating):
|
||||||
|
"""效果等同于 self.registry['runtime']['min_rate'] = min(rating, self.registry['runtime']['min_rate'])
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rating (int): 评分
|
||||||
|
"""
|
||||||
|
self.registry['runtime']['min_rate'] = min(rating, self.registry['runtime']['min_rate'])
|
||||||
|
|
||||||
|
def lock(self, locked = -1):
|
||||||
|
"""锁定, 效果等同于 self.registry['runtime']['locked'] = locked 或者返回是否锁定
|
||||||
|
"""
|
||||||
|
if locked == 1:
|
||||||
|
self.registry['runtime']['locked'] = True
|
||||||
|
return 1
|
||||||
|
elif locked == 0:
|
||||||
|
self.registry['runtime']['locked'] = False
|
||||||
|
return 1
|
||||||
|
elif locked == -1:
|
||||||
|
return self.registry['runtime']["locked"]
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def revise(self):
|
||||||
|
"""执行最终评分
|
||||||
|
PuzzleWidget 的 handler 除了测试, 严禁直接执行 Electron 的 revisor 函数, 否则造成逻辑混乱
|
||||||
|
"""
|
||||||
|
if self.registry["runtime"]["locked"]:
|
||||||
|
logger.debug(f"允许总评分: {self.registry['runtime']['min_rate']}")
|
||||||
|
self.registry["electron"].revisor(self.registry['runtime']["min_rate"])
|
||||||
|
else:
|
||||||
|
logger.debug("禁止总评分")
|
||||||
|
|
||||||
|
|
||||||
def do_eval(self):
|
def do_eval(self):
|
||||||
"""
|
"""
|
||||||
执行并以结果替换当前单元的所有 eval 语句
|
执行并以结果替换当前单元的所有 eval 语句
|
||||||
@@ -91,7 +127,11 @@ class Atom:
|
|||||||
ret = eval_value
|
ret = eval_value
|
||||||
else:
|
else:
|
||||||
ret = str(eval_value)
|
ret = str(eval_value)
|
||||||
logger.debug("eval 执行成功: '%s' -> '%s'", s, str(ret)[:50] + '...' if len(ret) > 50 else ret)
|
logger.debug(
|
||||||
|
"eval 执行成功: '%s' -> '%s'",
|
||||||
|
s,
|
||||||
|
str(ret)[:50] + "..." if len(ret) > 50 else ret,
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ret = f"此 eval 实例发生错误: {e}"
|
ret = f"此 eval 实例发生错误: {e}"
|
||||||
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
||||||
@@ -117,13 +157,13 @@ class Atom:
|
|||||||
|
|
||||||
# 如果 nucleon 存在且有 do_eval 方法,调用它
|
# 如果 nucleon 存在且有 do_eval 方法,调用它
|
||||||
nucleon = self.registry["nucleon"]
|
nucleon = self.registry["nucleon"]
|
||||||
if nucleon is not None and hasattr(nucleon, 'do_eval'):
|
if nucleon is not None and hasattr(nucleon, "do_eval"):
|
||||||
nucleon.do_eval()
|
nucleon.do_eval()
|
||||||
logger.debug("已调用 nucleon.do_eval")
|
logger.debug("已调用 nucleon.do_eval")
|
||||||
|
|
||||||
# 如果 electron 存在且其 algodata 包含 eval 字符串,遍历它
|
# 如果 electron 存在且其 algodata 包含 eval 字符串,遍历它
|
||||||
electron = self.registry["electron"]
|
electron = self.registry["electron"]
|
||||||
if electron is not None and hasattr(electron, 'algodata'):
|
if electron is not None and hasattr(electron, "algodata"):
|
||||||
traverse(electron.algodata, eval_with_env)
|
traverse(electron.algodata, eval_with_env)
|
||||||
logger.debug("已处理 electron algodata eval")
|
logger.debug("已处理 electron algodata eval")
|
||||||
|
|
||||||
@@ -173,7 +213,9 @@ class Atom:
|
|||||||
raise KeyError(f"不支持的键: {key}")
|
raise KeyError(f"不支持的键: {key}")
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
logger.debug("Atom.__setitem__: key='%s', value type: %s", key, type(value).__name__)
|
logger.debug(
|
||||||
|
"Atom.__setitem__: key='%s', value type: %s", key, type(value).__name__
|
||||||
|
)
|
||||||
if key in self.registry:
|
if key in self.registry:
|
||||||
self.registry[key] = value
|
self.registry[key] = value
|
||||||
logger.debug("键 '%s' 已设置", key)
|
logger.debug("键 '%s' 已设置", key)
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ class Electron:
|
|||||||
algodata: 算法数据字典, 包含算法的各项参数和设置
|
algodata: 算法数据字典, 包含算法的各项参数和设置
|
||||||
algo: 使用的算法模块标识
|
algo: 使用的算法模块标识
|
||||||
"""
|
"""
|
||||||
logger.debug("创建 Electron 实例,ident: '%s', algo_name: '%s'", ident, algo_name)
|
logger.debug(
|
||||||
|
"创建 Electron 实例,ident: '%s', algo_name: '%s'", ident, algo_name
|
||||||
|
)
|
||||||
self.algodata = algodata
|
self.algodata = algodata
|
||||||
self.ident = ident
|
self.ident = ident
|
||||||
self.algo = algorithms[algo_name]
|
self.algo = algorithms[algo_name]
|
||||||
@@ -31,11 +33,15 @@ class Electron:
|
|||||||
self._default_init(self.algo.defaults)
|
self._default_init(self.algo.defaults)
|
||||||
else:
|
else:
|
||||||
logger.debug("算法数据已存在,跳过默认初始化")
|
logger.debug("算法数据已存在,跳过默认初始化")
|
||||||
logger.debug("Electron 初始化完成,algodata keys: %s", list(self.algodata.keys()))
|
logger.debug(
|
||||||
|
"Electron 初始化完成,algodata keys: %s", list(self.algodata.keys())
|
||||||
|
)
|
||||||
|
|
||||||
def _default_init(self, defaults: dict):
|
def _default_init(self, defaults: dict):
|
||||||
"""默认初始化包装"""
|
"""默认初始化包装"""
|
||||||
logger.debug("Electron._default_init: 使用默认值,keys: %s", list(defaults.keys()))
|
logger.debug(
|
||||||
|
"Electron._default_init: 使用默认值,keys: %s", list(defaults.keys())
|
||||||
|
)
|
||||||
self.algodata[self.algo.algo_name] = defaults.copy()
|
self.algodata[self.algo.algo_name] = defaults.copy()
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
@@ -88,10 +94,16 @@ class Electron:
|
|||||||
quality (int): 记忆保留率量化参数 (0-5)
|
quality (int): 记忆保留率量化参数 (0-5)
|
||||||
is_new_activation (bool): 是否为初次激活
|
is_new_activation (bool): 是否为初次激活
|
||||||
"""
|
"""
|
||||||
logger.debug("Electron.revisor: ident='%s', quality=%d, is_new_activation=%s",
|
logger.debug(
|
||||||
self.ident, quality, is_new_activation)
|
"Electron.revisor: ident='%s', quality=%d, is_new_activation=%s",
|
||||||
|
self.ident,
|
||||||
|
quality,
|
||||||
|
is_new_activation,
|
||||||
|
)
|
||||||
self.algo.revisor(self.algodata, quality, is_new_activation)
|
self.algo.revisor(self.algodata, quality, is_new_activation)
|
||||||
logger.debug("revisor 完成,更新后的 algodata: %s", self.algodata.get(self.algo, {}))
|
logger.debug(
|
||||||
|
"revisor 完成,更新后的 algodata: %s", self.algodata.get(self.algo, {})
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ def load_nucleon(path: pathlib.Path, fmt="toml"):
|
|||||||
logger.debug("处理项目: %s", item)
|
logger.debug("处理项目: %s", item)
|
||||||
lst.append(
|
lst.append(
|
||||||
(
|
(
|
||||||
Nucleon(
|
Nucleon(item, attr, deepcopy(nested_data["__metadata__"])),
|
||||||
item, attr, deepcopy(nested_data["__metadata__"])
|
|
||||||
),
|
|
||||||
deepcopy(nested_data["__metadata__"]["orbital"]),
|
deepcopy(nested_data["__metadata__"]["orbital"]),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ class Nucleon:
|
|||||||
payload: 记忆内容信息
|
payload: 记忆内容信息
|
||||||
metadata: 可选元数据信息
|
metadata: 可选元数据信息
|
||||||
"""
|
"""
|
||||||
logger.debug("创建 Nucleon 实例,ident: '%s', payload keys: %s, metadata keys: %s",
|
logger.debug(
|
||||||
ident, list(payload.keys()) if payload else [], list(metadata.keys()) if metadata else [])
|
"创建 Nucleon 实例,ident: '%s', payload keys: %s, metadata keys: %s",
|
||||||
|
ident,
|
||||||
|
list(payload.keys()) if payload else [],
|
||||||
|
list(metadata.keys()) if metadata else [],
|
||||||
|
)
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.payload = payload
|
self.payload = payload
|
||||||
self.ident = ident
|
self.ident = ident
|
||||||
@@ -28,7 +32,9 @@ class Nucleon:
|
|||||||
return self.ident
|
return self.ident
|
||||||
if key in self.payload:
|
if key in self.payload:
|
||||||
value = self.payload[key]
|
value = self.payload[key]
|
||||||
logger.debug("返回 payload['%s'], value type: %s", key, type(value).__name__)
|
logger.debug(
|
||||||
|
"返回 payload['%s'], value type: %s", key, type(value).__name__
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
logger.error("键 '%s' 未在 payload 中找到", key)
|
logger.error("键 '%s' 未在 payload 中找到", key)
|
||||||
@@ -59,7 +65,11 @@ class Nucleon:
|
|||||||
ret = str(eval_value)
|
ret = str(eval_value)
|
||||||
else:
|
else:
|
||||||
ret = eval_value
|
ret = eval_value
|
||||||
logger.debug("eval 执行成功: '%s' -> '%s'", s, str(ret)[:50] + '...' if len(ret) > 50 else ret)
|
logger.debug(
|
||||||
|
"eval 执行成功: '%s' -> '%s'",
|
||||||
|
s,
|
||||||
|
str(ret)[:50] + "..." if len(ret) > 50 else ret,
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ret = f"此 eval 实例发生错误: {e}"
|
ret = f"此 eval 实例发生错误: {e}"
|
||||||
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Puzzle 模块 - 谜题生成系统
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
from .base import BasePuzzle
|
from .base import BasePuzzle
|
||||||
@@ -41,7 +42,9 @@ def create_by_dict(config_dict: dict) -> BasePuzzle:
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: 当配置无效时抛出
|
ValueError: 当配置无效时抛出
|
||||||
"""
|
"""
|
||||||
logger.debug("puzzles.create_by_dict: config_dict keys=%s", list(config_dict.keys()))
|
logger.debug(
|
||||||
|
"puzzles.create_by_dict: config_dict keys=%s", list(config_dict.keys())
|
||||||
|
)
|
||||||
puzzle_type = config_dict.get("type")
|
puzzle_type = config_dict.get("type")
|
||||||
|
|
||||||
if puzzle_type == "cloze":
|
if puzzle_type == "cloze":
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ class ClozePuzzle(BasePuzzle):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text: str, min_denominator: int, delimiter: str = "/"):
|
def __init__(self, text: str, min_denominator: int, delimiter: str = "/"):
|
||||||
logger.debug("ClozePuzzle.__init__: text length=%d, min_denominator=%d, delimiter='%s'",
|
logger.debug(
|
||||||
len(text), min_denominator, delimiter)
|
"ClozePuzzle.__init__: text length=%d, min_denominator=%d, delimiter='%s'",
|
||||||
|
len(text),
|
||||||
|
min_denominator,
|
||||||
|
delimiter,
|
||||||
|
)
|
||||||
self.text = text
|
self.text = text
|
||||||
self.min_denominator = min_denominator
|
self.min_denominator = min_denominator
|
||||||
self.wording = "填空题 - 尚未刷新谜题"
|
self.wording = "填空题 - 尚未刷新谜题"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MCQPuzzle(BasePuzzle):
|
class MCQPuzzle(BasePuzzle):
|
||||||
"""选择题谜题生成器
|
"""选择题谜题生成器
|
||||||
|
|
||||||
@@ -37,7 +38,12 @@ class MCQPuzzle(BasePuzzle):
|
|||||||
max_riddles_num: 每次生成的最大题目数量, 范围限制在1-5之间
|
max_riddles_num: 每次生成的最大题目数量, 范围限制在1-5之间
|
||||||
prefix: 题目前缀文本, 会显示在每个题目之前
|
prefix: 题目前缀文本, 会显示在每个题目之前
|
||||||
"""
|
"""
|
||||||
logger.debug("MCQPuzzle.__init__: mapping size=%d, jammer size=%d, max_riddles_num=%d", len(mapping), len(jammer), max_riddles_num)
|
logger.debug(
|
||||||
|
"MCQPuzzle.__init__: mapping size=%d, jammer size=%d, max_riddles_num=%d",
|
||||||
|
len(mapping),
|
||||||
|
len(jammer),
|
||||||
|
max_riddles_num,
|
||||||
|
)
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.mapping = mapping
|
self.mapping = mapping
|
||||||
self.max_riddles_num = max(1, min(max_riddles_num, 5))
|
self.max_riddles_num = max(1, min(max_riddles_num, 5))
|
||||||
|
|||||||
@@ -39,6 +39,5 @@ class Fission:
|
|||||||
print(f"ok:{item}")
|
print(f"ok:{item}")
|
||||||
self.logger.debug(f"orbital 项处理完成: {item}")
|
self.logger.debug(f"orbital 项处理完成: {item}")
|
||||||
|
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
yield from self.puzzles
|
yield from self.puzzles
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ class Procession:
|
|||||||
"""队列: 标识单次记忆流程"""
|
"""队列: 标识单次记忆流程"""
|
||||||
|
|
||||||
def __init__(self, atoms: list, phase: PhaserState, name: str = ""):
|
def __init__(self, atoms: list, phase: PhaserState, name: str = ""):
|
||||||
logger.debug("Procession.__init__: 原子数量=%d, phase=%s, name='%s'",
|
logger.debug(
|
||||||
len(atoms), phase.value, name)
|
"Procession.__init__: 原子数量=%d, phase=%s, name='%s'",
|
||||||
|
len(atoms),
|
||||||
|
phase.value,
|
||||||
|
name,
|
||||||
|
)
|
||||||
self.atoms = atoms
|
self.atoms = atoms
|
||||||
self.queue = atoms.copy()
|
self.queue = atoms.copy()
|
||||||
self.current_atom = atoms[0]
|
self.current_atom = atoms[0]
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ class ProcessionState(Enum):
|
|||||||
RUNNING = auto()
|
RUNNING = auto()
|
||||||
FINISHED = auto()
|
FINISHED = auto()
|
||||||
|
|
||||||
|
|
||||||
logger.debug("状态枚举定义已加载")
|
logger.debug("状态枚举定义已加载")
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ from heurams.services.logger import get_logger
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
play_by_path: Callable = prov[config_var.get()["services"]["audio"]].play_by_path
|
play_by_path: Callable = prov[config_var.get()["services"]["audio"]].play_by_path
|
||||||
logger.debug("音频服务初始化完成,使用 provider: %s", config_var.get()["services"]["audio"])
|
logger.debug(
|
||||||
|
"音频服务初始化完成,使用 provider: %s", config_var.get()["services"]["audio"]
|
||||||
|
)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def setup_logging(
|
|||||||
filename=log_path,
|
filename=log_path,
|
||||||
maxBytes=max_bytes,
|
maxBytes=max_bytes,
|
||||||
backupCount=backup_count,
|
backupCount=backup_count,
|
||||||
encoding='utf-8'
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
file_handler.setFormatter(formatter)
|
file_handler.setFormatter(formatter)
|
||||||
file_handler.setLevel(log_level)
|
file_handler.setLevel(log_level)
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ from heurams.services.logger import get_logger
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
convert: Callable = TTSs[config_var.get().get("tts_provider")]
|
convert: Callable = TTSs[config_var.get().get("tts_provider")]
|
||||||
logger.debug("TTS服务初始化完成,使用 provider: %s", config_var.get().get("tts_provider"))
|
logger.debug(
|
||||||
|
"TTS服务初始化完成,使用 provider: %s", config_var.get().get("tts_provider")
|
||||||
|
)
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# 模拟 timer 函数
|
# 模拟 timer 函数
|
||||||
self.timestamp_patcher = patch('heurams.kernel.algorithms.sm2.timer.get_timestamp')
|
self.timestamp_patcher = patch(
|
||||||
self.daystamp_patcher = patch('heurams.kernel.algorithms.sm2.timer.get_daystamp')
|
"heurams.kernel.algorithms.sm2.timer.get_timestamp"
|
||||||
|
)
|
||||||
|
self.daystamp_patcher = patch(
|
||||||
|
"heurams.kernel.algorithms.sm2.timer.get_daystamp"
|
||||||
|
)
|
||||||
self.mock_get_timestamp = self.timestamp_patcher.start()
|
self.mock_get_timestamp = self.timestamp_patcher.start()
|
||||||
self.mock_get_daystamp = self.daystamp_patcher.start()
|
self.mock_get_daystamp = self.daystamp_patcher.start()
|
||||||
|
|
||||||
@@ -46,7 +50,14 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_revisor_feedback_less_than_3(self):
|
def test_revisor_feedback_less_than_3(self):
|
||||||
"""测试 feedback < 3 重置 rept 和 interval"""
|
"""测试 feedback < 3 重置 rept 和 interval"""
|
||||||
algodata = {SM2Algorithm.algo_name: {"efactor": 2.5, "rept": 5, "interval": 10, "real_rept": 3}}
|
algodata = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"rept": 5,
|
||||||
|
"interval": 10,
|
||||||
|
"real_rept": 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata, feedback=2)
|
SM2Algorithm.revisor(algodata, feedback=2)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 0)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 0)
|
||||||
# rept=0 导致 interval 被设置为 1
|
# rept=0 导致 interval 被设置为 1
|
||||||
@@ -55,7 +66,14 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_revisor_feedback_greater_equal_3(self):
|
def test_revisor_feedback_greater_equal_3(self):
|
||||||
"""测试 feedback >= 3 递增 rept"""
|
"""测试 feedback >= 3 递增 rept"""
|
||||||
algodata = {SM2Algorithm.algo_name: {"efactor": 2.5, "rept": 2, "interval": 6, "real_rept": 2}}
|
algodata = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"rept": 2,
|
||||||
|
"interval": 6,
|
||||||
|
"real_rept": 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata, feedback=4)
|
SM2Algorithm.revisor(algodata, feedback=4)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 3)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 3)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["real_rept"], 3)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["real_rept"], 3)
|
||||||
@@ -65,7 +83,14 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_revisor_new_activation(self):
|
def test_revisor_new_activation(self):
|
||||||
"""测试 is_new_activation 重置 rept 和 efactor"""
|
"""测试 is_new_activation 重置 rept 和 efactor"""
|
||||||
algodata = {SM2Algorithm.algo_name: {"efactor": 3.0, "rept": 5, "interval": 20, "real_rept": 5}}
|
algodata = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 3.0,
|
||||||
|
"rept": 5,
|
||||||
|
"interval": 20,
|
||||||
|
"real_rept": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata, feedback=5, is_new_activation=True)
|
SM2Algorithm.revisor(algodata, feedback=5, is_new_activation=True)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 0)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["rept"], 0)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["efactor"], 2.5)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["efactor"], 2.5)
|
||||||
@@ -74,10 +99,19 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_revisor_efactor_calculation(self):
|
def test_revisor_efactor_calculation(self):
|
||||||
"""测试 efactor 计算"""
|
"""测试 efactor 计算"""
|
||||||
algodata = {SM2Algorithm.algo_name: {"efactor": 2.5, "rept": 1, "interval": 6, "real_rept": 1}}
|
algodata = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"rept": 1,
|
||||||
|
"interval": 6,
|
||||||
|
"real_rept": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata, feedback=5)
|
SM2Algorithm.revisor(algodata, feedback=5)
|
||||||
# efactor = 2.5 + (0.1 - (5-5)*(0.08 + (5-5)*0.02)) = 2.5 + 0.1 = 2.6
|
# efactor = 2.5 + (0.1 - (5-5)*(0.08 + (5-5)*0.02)) = 2.5 + 0.1 = 2.6
|
||||||
self.assertAlmostEqual(algodata[SM2Algorithm.algo_name]["efactor"], 2.6, places=6)
|
self.assertAlmostEqual(
|
||||||
|
algodata[SM2Algorithm.algo_name]["efactor"], 2.6, places=6
|
||||||
|
)
|
||||||
|
|
||||||
# 测试 efactor 下限为 1.3
|
# 测试 efactor 下限为 1.3
|
||||||
algodata[SM2Algorithm.algo_name]["efactor"] = 1.2
|
algodata[SM2Algorithm.algo_name]["efactor"] = 1.2
|
||||||
@@ -86,7 +120,14 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_revisor_interval_calculation(self):
|
def test_revisor_interval_calculation(self):
|
||||||
"""测试 interval 计算规则"""
|
"""测试 interval 计算规则"""
|
||||||
algodata = {SM2Algorithm.algo_name: {"efactor": 2.5, "rept": 0, "interval": 0, "real_rept": 0}}
|
algodata = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"rept": 0,
|
||||||
|
"interval": 0,
|
||||||
|
"real_rept": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata, feedback=4)
|
SM2Algorithm.revisor(algodata, feedback=4)
|
||||||
# rept 从 0 递增到 1,因此 interval 应为 6
|
# rept 从 0 递增到 1,因此 interval 应为 6
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 6)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 6)
|
||||||
@@ -97,7 +138,14 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 15)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 15)
|
||||||
|
|
||||||
# 单独测试 rept=1 的情况
|
# 单独测试 rept=1 的情况
|
||||||
algodata2 = {SM2Algorithm.algo_name: {"efactor": 2.5, "rept": 1, "interval": 0, "real_rept": 0}}
|
algodata2 = {
|
||||||
|
SM2Algorithm.algo_name: {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"rept": 1,
|
||||||
|
"interval": 0,
|
||||||
|
"real_rept": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
SM2Algorithm.revisor(algodata2, feedback=4)
|
SM2Algorithm.revisor(algodata2, feedback=4)
|
||||||
# rept 递增到 2,interval = round(0 * 2.5) = 0
|
# rept 递增到 2,interval = round(0 * 2.5) = 0
|
||||||
self.assertEqual(algodata2[SM2Algorithm.algo_name]["interval"], 0)
|
self.assertEqual(algodata2[SM2Algorithm.algo_name]["interval"], 0)
|
||||||
@@ -108,7 +156,10 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
self.mock_get_daystamp.return_value = 200
|
self.mock_get_daystamp.return_value = 200
|
||||||
SM2Algorithm.revisor(algodata, feedback=5)
|
SM2Algorithm.revisor(algodata, feedback=5)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["last_date"], 200)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["last_date"], 200)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["next_date"], 200 + algodata[SM2Algorithm.algo_name]["interval"])
|
self.assertEqual(
|
||||||
|
algodata[SM2Algorithm.algo_name]["next_date"],
|
||||||
|
200 + algodata[SM2Algorithm.algo_name]["interval"],
|
||||||
|
)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["last_modify"], 1000.0)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["last_modify"], 1000.0)
|
||||||
|
|
||||||
def test_is_due(self):
|
def test_is_due(self):
|
||||||
@@ -131,5 +182,5 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
self.assertEqual(SM2Algorithm.nextdate(algodata), 12345)
|
self.assertEqual(SM2Algorithm.nextdate(algodata), 12345)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -23,8 +23,10 @@ class TestAtom(unittest.TestCase):
|
|||||||
self.temp_path = pathlib.Path(self.temp_dir.name)
|
self.temp_path = pathlib.Path(self.temp_dir.name)
|
||||||
|
|
||||||
# 创建默认配置
|
# 创建默认配置
|
||||||
self.config = ConfigFile(pathlib.Path(__file__).parent.parent.parent.parent /
|
self.config = ConfigFile(
|
||||||
"src/heurams/default/config/config.toml")
|
pathlib.Path(__file__).parent.parent.parent.parent
|
||||||
|
/ "src/heurams/default/config/config.toml"
|
||||||
|
)
|
||||||
|
|
||||||
# 使用 ConfigContext 设置配置
|
# 使用 ConfigContext 设置配置
|
||||||
self.config_ctx = ConfigContext(self.config)
|
self.config_ctx = ConfigContext(self.config)
|
||||||
@@ -71,7 +73,7 @@ class TestAtom(unittest.TestCase):
|
|||||||
atom = Atom("test_eval_trigger")
|
atom = Atom("test_eval_trigger")
|
||||||
nucleon = Nucleon("test_nucleon", {"content": "eval:1+1"})
|
nucleon = Nucleon("test_nucleon", {"content": "eval:1+1"})
|
||||||
|
|
||||||
with patch.object(atom, 'do_eval') as mock_do_eval:
|
with patch.object(atom, "do_eval") as mock_do_eval:
|
||||||
atom.link("nucleon", nucleon)
|
atom.link("nucleon", nucleon)
|
||||||
mock_do_eval.assert_called_once()
|
mock_do_eval.assert_called_once()
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ class TestAtom(unittest.TestCase):
|
|||||||
|
|
||||||
# 验证文件存在且内容正确
|
# 验证文件存在且内容正确
|
||||||
self.assertTrue(test_path.exists())
|
self.assertTrue(test_path.exists())
|
||||||
with open(test_path, 'r') as f:
|
with open(test_path, "r") as f:
|
||||||
data = toml.load(f)
|
data = toml.load(f)
|
||||||
self.assertEqual(data["ident"], "test_nucleon")
|
self.assertEqual(data["ident"], "test_nucleon")
|
||||||
self.assertEqual(data["payload"]["content"], "test")
|
self.assertEqual(data["payload"]["content"], "test")
|
||||||
@@ -106,7 +108,7 @@ class TestAtom(unittest.TestCase):
|
|||||||
atom.persist("electron")
|
atom.persist("electron")
|
||||||
|
|
||||||
self.assertTrue(test_path.exists())
|
self.assertTrue(test_path.exists())
|
||||||
with open(test_path, 'r') as f:
|
with open(test_path, "r") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self.assertIn("supermemo2", data)
|
self.assertIn("supermemo2", data)
|
||||||
|
|
||||||
@@ -149,10 +151,10 @@ class TestAtom(unittest.TestCase):
|
|||||||
def test_do_eval_with_eval_string(self):
|
def test_do_eval_with_eval_string(self):
|
||||||
"""测试 do_eval 处理 eval: 字符串"""
|
"""测试 do_eval 处理 eval: 字符串"""
|
||||||
atom = Atom("test_do_eval")
|
atom = Atom("test_do_eval")
|
||||||
nucleon = Nucleon("test_nucleon", {
|
nucleon = Nucleon(
|
||||||
"content": "eval:'hello' + ' world'",
|
"test_nucleon",
|
||||||
"number": "eval:2 + 3"
|
{"content": "eval:'hello' + ' world'", "number": "eval:2 + 3"},
|
||||||
})
|
)
|
||||||
atom.link("nucleon", nucleon)
|
atom.link("nucleon", nucleon)
|
||||||
|
|
||||||
# do_eval 应该在链接时自动调用
|
# do_eval 应该在链接时自动调用
|
||||||
@@ -163,9 +165,9 @@ class TestAtom(unittest.TestCase):
|
|||||||
def test_do_eval_with_config_access(self):
|
def test_do_eval_with_config_access(self):
|
||||||
"""测试 do_eval 访问配置"""
|
"""测试 do_eval 访问配置"""
|
||||||
atom = Atom("test_eval_config")
|
atom = Atom("test_eval_config")
|
||||||
nucleon = Nucleon("test_nucleon", {
|
nucleon = Nucleon(
|
||||||
"max_riddles": "eval:default['mcq']['max_riddles_num']"
|
"test_nucleon", {"max_riddles": "eval:default['mcq']['max_riddles_num']"}
|
||||||
})
|
)
|
||||||
atom.link("nucleon", nucleon)
|
atom.link("nucleon", nucleon)
|
||||||
|
|
||||||
# 配置中 puzzles.mcq.max_riddles_num = 2
|
# 配置中 puzzles.mcq.max_riddles_num = 2
|
||||||
@@ -195,5 +197,5 @@ class TestAtom(unittest.TestCase):
|
|||||||
self.assertEqual(atom_registry.inverse[atom2], "atom2")
|
self.assertEqual(atom_registry.inverse[atom2], "atom2")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -11,7 +11,9 @@ class TestElectron(unittest.TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# 模拟 timer.get_timestamp 返回固定值
|
# 模拟 timer.get_timestamp 返回固定值
|
||||||
self.timestamp_patcher = patch('heurams.kernel.particles.electron.timer.get_timestamp')
|
self.timestamp_patcher = patch(
|
||||||
|
"heurams.kernel.particles.electron.timer.get_timestamp"
|
||||||
|
)
|
||||||
self.mock_get_timestamp = self.timestamp_patcher.start()
|
self.mock_get_timestamp = self.timestamp_patcher.start()
|
||||||
self.mock_get_timestamp.return_value = 1234567890.0
|
self.mock_get_timestamp.return_value = 1234567890.0
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
self.assertEqual(electron.algodata[electron.algo]["last_modify"], 1234567890.0)
|
self.assertEqual(electron.algodata[electron.algo]["last_modify"], 1234567890.0)
|
||||||
|
|
||||||
# 修改不存在的字段应记录警告但不引发异常
|
# 修改不存在的字段应记录警告但不引发异常
|
||||||
with patch('heurams.kernel.particles.electron.logger.warning') as mock_warning:
|
with patch("heurams.kernel.particles.electron.logger.warning") as mock_warning:
|
||||||
electron.modify("unknown_field", 99)
|
electron.modify("unknown_field", 99)
|
||||||
mock_warning.assert_called_once()
|
mock_warning.assert_called_once()
|
||||||
|
|
||||||
@@ -85,7 +87,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
def test_is_due(self):
|
def test_is_due(self):
|
||||||
"""测试 is_due 方法"""
|
"""测试 is_due 方法"""
|
||||||
electron = Electron("test_electron")
|
electron = Electron("test_electron")
|
||||||
with patch.object(electron.algo, 'is_due') as mock_is_due:
|
with patch.object(electron.algo, "is_due") as mock_is_due:
|
||||||
mock_is_due.return_value = 1
|
mock_is_due.return_value = 1
|
||||||
result = electron.is_due()
|
result = electron.is_due()
|
||||||
mock_is_due.assert_called_once_with(electron.algodata)
|
mock_is_due.assert_called_once_with(electron.algodata)
|
||||||
@@ -94,7 +96,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
def test_rate(self):
|
def test_rate(self):
|
||||||
"""测试 rate 方法"""
|
"""测试 rate 方法"""
|
||||||
electron = Electron("test_electron")
|
electron = Electron("test_electron")
|
||||||
with patch.object(electron.algo, 'rate') as mock_rate:
|
with patch.object(electron.algo, "rate") as mock_rate:
|
||||||
mock_rate.return_value = "good"
|
mock_rate.return_value = "good"
|
||||||
result = electron.rate()
|
result = electron.rate()
|
||||||
mock_rate.assert_called_once_with(electron.algodata)
|
mock_rate.assert_called_once_with(electron.algodata)
|
||||||
@@ -103,7 +105,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
def test_nextdate(self):
|
def test_nextdate(self):
|
||||||
"""测试 nextdate 方法"""
|
"""测试 nextdate 方法"""
|
||||||
electron = Electron("test_electron")
|
electron = Electron("test_electron")
|
||||||
with patch.object(electron.algo, 'nextdate') as mock_nextdate:
|
with patch.object(electron.algo, "nextdate") as mock_nextdate:
|
||||||
mock_nextdate.return_value = 1234568000
|
mock_nextdate.return_value = 1234568000
|
||||||
result = electron.nextdate()
|
result = electron.nextdate()
|
||||||
mock_nextdate.assert_called_once_with(electron.algodata)
|
mock_nextdate.assert_called_once_with(electron.algodata)
|
||||||
@@ -112,7 +114,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
def test_revisor(self):
|
def test_revisor(self):
|
||||||
"""测试 revisor 方法"""
|
"""测试 revisor 方法"""
|
||||||
electron = Electron("test_electron")
|
electron = Electron("test_electron")
|
||||||
with patch.object(electron.algo, 'revisor') as mock_revisor:
|
with patch.object(electron.algo, "revisor") as mock_revisor:
|
||||||
electron.revisor(quality=3, is_new_activation=True)
|
electron.revisor(quality=3, is_new_activation=True)
|
||||||
mock_revisor.assert_called_once_with(electron.algodata, 3, True)
|
mock_revisor.assert_called_once_with(electron.algodata, 3, True)
|
||||||
|
|
||||||
@@ -173,5 +175,5 @@ class TestElectron(unittest.TestCase):
|
|||||||
self.assertEqual(placeholder.algo, algorithms["supermemo2"])
|
self.assertEqual(placeholder.algo, algorithms["supermemo2"])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -9,7 +9,9 @@ class TestNucleon(unittest.TestCase):
|
|||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
"""测试初始化"""
|
"""测试初始化"""
|
||||||
nucleon = Nucleon("test_id", {"content": "hello", "note": "world"}, {"author": "test"})
|
nucleon = Nucleon(
|
||||||
|
"test_id", {"content": "hello", "note": "world"}, {"author": "test"}
|
||||||
|
)
|
||||||
self.assertEqual(nucleon.ident, "test_id")
|
self.assertEqual(nucleon.ident, "test_id")
|
||||||
self.assertEqual(nucleon.payload, {"content": "hello", "note": "world"})
|
self.assertEqual(nucleon.payload, {"content": "hello", "note": "world"})
|
||||||
self.assertEqual(nucleon.metadata, {"author": "test"})
|
self.assertEqual(nucleon.metadata, {"author": "test"})
|
||||||
@@ -58,16 +60,23 @@ class TestNucleon(unittest.TestCase):
|
|||||||
|
|
||||||
def test_do_eval_with_metadata_access(self):
|
def test_do_eval_with_metadata_access(self):
|
||||||
"""测试 do_eval 访问元数据"""
|
"""测试 do_eval 访问元数据"""
|
||||||
nucleon = Nucleon("test_id", {"result": "eval:nucleon.metadata.get('value', 0)"}, {"value": 42})
|
nucleon = Nucleon(
|
||||||
|
"test_id",
|
||||||
|
{"result": "eval:nucleon.metadata.get('value', 0)"},
|
||||||
|
{"value": 42},
|
||||||
|
)
|
||||||
nucleon.do_eval()
|
nucleon.do_eval()
|
||||||
self.assertEqual(nucleon.payload["result"], "42")
|
self.assertEqual(nucleon.payload["result"], "42")
|
||||||
|
|
||||||
def test_do_eval_nested(self):
|
def test_do_eval_nested(self):
|
||||||
"""测试 do_eval 处理嵌套结构"""
|
"""测试 do_eval 处理嵌套结构"""
|
||||||
nucleon = Nucleon("test_id", {
|
nucleon = Nucleon(
|
||||||
|
"test_id",
|
||||||
|
{
|
||||||
"list": ["eval:2*3", "normal"],
|
"list": ["eval:2*3", "normal"],
|
||||||
"dict": {"key": "eval:'hello' + ' world'"}
|
"dict": {"key": "eval:'hello' + ' world'"},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
nucleon.do_eval()
|
nucleon.do_eval()
|
||||||
self.assertEqual(nucleon.payload["list"][0], "6")
|
self.assertEqual(nucleon.payload["list"][0], "6")
|
||||||
self.assertEqual(nucleon.payload["list"][1], "normal")
|
self.assertEqual(nucleon.payload["list"][1], "normal")
|
||||||
@@ -95,5 +104,5 @@ class TestNucleon(unittest.TestCase):
|
|||||||
self.assertEqual(placeholder.metadata, {})
|
self.assertEqual(placeholder.metadata, {})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -19,5 +19,5 @@ class TestBasePuzzle(unittest.TestCase):
|
|||||||
self.assertEqual(str(puzzle), "谜题: BasePuzzle")
|
self.assertEqual(str(puzzle), "谜题: BasePuzzle")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -16,7 +16,7 @@ class TestClozePuzzle(unittest.TestCase):
|
|||||||
self.assertEqual(puzzle.wording, "填空题 - 尚未刷新谜题")
|
self.assertEqual(puzzle.wording, "填空题 - 尚未刷新谜题")
|
||||||
self.assertEqual(puzzle.answer, ["填空题 - 尚未刷新谜题"])
|
self.assertEqual(puzzle.answer, ["填空题 - 尚未刷新谜题"])
|
||||||
|
|
||||||
@patch('random.sample')
|
@patch("random.sample")
|
||||||
def test_refresh(self, mock_sample):
|
def test_refresh(self, mock_sample):
|
||||||
"""测试 refresh 方法"""
|
"""测试 refresh 方法"""
|
||||||
mock_sample.return_value = [0, 2] # 选择索引 0 和 2
|
mock_sample.return_value = [0, 2] # 选择索引 0 和 2
|
||||||
@@ -47,5 +47,5 @@ class TestClozePuzzle(unittest.TestCase):
|
|||||||
self.assertIn("填空题 - 尚未刷新谜题", str_repr)
|
self.assertIn("填空题 - 尚未刷新谜题", str_repr)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -38,8 +38,8 @@ class TestMCQPuzzle(unittest.TestCase):
|
|||||||
self.assertEqual(len(puzzle.jammer), 4)
|
self.assertEqual(len(puzzle.jammer), 4)
|
||||||
self.assertEqual(set(puzzle.jammer), {" "}) # 三个空格?实际上循环填充空格
|
self.assertEqual(set(puzzle.jammer), {" "}) # 三个空格?实际上循环填充空格
|
||||||
|
|
||||||
@patch('random.sample')
|
@patch("random.sample")
|
||||||
@patch('random.shuffle')
|
@patch("random.shuffle")
|
||||||
def test_refresh(self, mock_shuffle, mock_sample):
|
def test_refresh(self, mock_shuffle, mock_sample):
|
||||||
"""测试 refresh 方法生成题目"""
|
"""测试 refresh 方法生成题目"""
|
||||||
mapping = {"q1": "a1", "q2": "a2", "q3": "a3"}
|
mapping = {"q1": "a1", "q2": "a2", "q3": "a3"}
|
||||||
@@ -118,5 +118,5 @@ class TestMCQPuzzle(unittest.TestCase):
|
|||||||
self.assertIn("A1, A2", str_repr)
|
self.assertIn("A1, A2", str_repr)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -21,7 +21,7 @@ class TestPhaser(unittest.TestCase):
|
|||||||
self.atom_old.registry["electron"].is_activated.return_value = True
|
self.atom_old.registry["electron"].is_activated.return_value = True
|
||||||
|
|
||||||
# 模拟 Procession 类以避免复杂依赖
|
# 模拟 Procession 类以避免复杂依赖
|
||||||
self.procession_patcher = patch('heurams.kernel.reactor.phaser.Procession')
|
self.procession_patcher = patch("heurams.kernel.reactor.phaser.Procession")
|
||||||
self.mock_procession_class = self.procession_patcher.start()
|
self.mock_procession_class = self.procession_patcher.start()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -110,5 +110,5 @@ class TestPhaser(unittest.TestCase):
|
|||||||
self.assertEqual(phaser.state, PhaserState.FINISHED)
|
self.assertEqual(phaser.state, PhaserState.FINISHED)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in New Issue
Block a user