Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 142d30347b | |||
| e57cea7219 | |||
| 98ec6504a4 | |||
| 243eea864b | |||
| cfb1385f4d | |||
| a1462206a2 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,8 +11,6 @@ electron/test.toml
|
|||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
old/
|
old/
|
||||||
|
|
||||||
# Project specific directories
|
|
||||||
# config/
|
# config/
|
||||||
data/cache/
|
data/cache/
|
||||||
data/electron/
|
data/electron/
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ python -m heurams.interface
|
|||||||
|
|
||||||
## 配置
|
## 配置
|
||||||
|
|
||||||
配置文件位于 `config/config.toml`(相对于工作目录). 如果不存在, 会使用内置的默认配置.
|
配置文件位于 `config/config.toml`(相对于工作目录). 如果不存在, 会使用内置的默认配置.
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
prompt = """HeurAMS 已经被成功地安装在系统中.
|
prompt = """HeurAMS 已经被成功地安装在系统中.
|
||||||
但 HeurAMS 被设计为一个带有辅助记忆调度器功能的软件包, 无法直接被执行, 但可被其他 Python 程序调用.
|
但 HeurAMS 被设计为一个带有辅助记忆调度器功能的软件包, 无法直接被执行, 但可被其他 Python 程序调用.
|
||||||
若您想启动内置的基本用户界面,
|
若您想启动内置的基本用户界面,
|
||||||
请运行 python -m heurams.interface,
|
请运行 python -m heurams.interface,
|
||||||
或者 python -m heurams.interface.__main__
|
或者 python -m heurams.interface.__main__
|
||||||
注意: 一个常见的误区是, 执行 interface 下的 __main__.py 运行基本用户界面, 这会导致 Python 上下文环境异常, 请不要这样做."""
|
注意: 一个常见的误区是, 执行 interface 下的 __main__.py 运行基本用户界面, 这会导致 Python 上下文环境异常, 请不要这样做."""
|
||||||
print(prompt)
|
print(prompt)
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
以及基准路径
|
以及基准路径
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from contextvars import ContextVar
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
from contextvars import ContextVar
|
||||||
|
|
||||||
from heurams.services.config import ConfigFile
|
from heurams.services.config import ConfigFile
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ daystamp_override = -1
|
|||||||
timestamp_override = -1
|
timestamp_override = -1
|
||||||
|
|
||||||
# [调试] 一键通过
|
# [调试] 一键通过
|
||||||
quick_pass = 0
|
quick_pass = 1
|
||||||
|
|
||||||
# 对于每个项目的默认新记忆原子数量
|
# 对于每个项目的默认新记忆原子数量
|
||||||
tasked_number = 8
|
scheduled_num = 8
|
||||||
|
|
||||||
# UTC 时间戳修正 仅用于 UNIX 日时间戳的生成修正, 单位为秒
|
# UTC 时间戳修正 仅用于 UNIX 日时间戳的生成修正, 单位为秒
|
||||||
timezone_offset = +28800 # 中国标准时间 (UTC+8)
|
timezone_offset = +28800 # 中国标准时间 (UTC+8)
|
||||||
@@ -22,8 +22,18 @@ max_riddles_num = 2
|
|||||||
[puzzles.cloze]
|
[puzzles.cloze]
|
||||||
min_denominator = 3
|
min_denominator = 3
|
||||||
|
|
||||||
[paths] # 相对于工作目录而言 或绝对路径
|
[paths] # 相对于配置文件的 ".." (即工作目录) 而言 或绝对路径
|
||||||
nucleon_dir = "./data/nucleon"
|
nucleon_dir = "./data/nucleon"
|
||||||
electron_dir = "./data/electron"
|
electron_dir = "./data/electron"
|
||||||
orbital_dir = "./data/orbital"
|
orbital_dir = "./data/orbital"
|
||||||
cache_dir = "./data/cache"
|
cache_dir = "./data/cache"
|
||||||
|
template_dir = "./data/template"
|
||||||
|
|
||||||
|
[services] # 定义服务到提供者的映射
|
||||||
|
audio = "playsound" # 可选项: playsound(通用), termux(仅用于支持 Android Termux), mpg123(TODO)
|
||||||
|
tts = "edgetts" # 可选项: edgetts
|
||||||
|
llm = "openai" # 可选项: openai
|
||||||
|
|
||||||
|
[providers.llm.openai] # 与 OpenAI 相容的语言模型接口服务设置
|
||||||
|
url = ""
|
||||||
|
key = ""
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from textual.app import App
|
from textual.app import App
|
||||||
from textual.widgets import Button
|
from textual.widgets import Button
|
||||||
|
|
||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .screens.about import AboutScreen
|
||||||
from .screens.dashboard import DashboardScreen
|
from .screens.dashboard import DashboardScreen
|
||||||
from .screens.nucreator import NucleonCreatorScreen
|
from .screens.nucreator import NucleonCreatorScreen
|
||||||
from .screens.precache import PrecachingScreen
|
from .screens.precache import PrecachingScreen
|
||||||
from .screens.about import AboutScreen
|
|
||||||
from heurams.services.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -66,11 +68,11 @@ def is_subdir(parent, child):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# 开发模式
|
|
||||||
from heurams.context import rootdir, workdir, config_var
|
|
||||||
from pathlib import Path
|
|
||||||
from heurams.context import rootdir
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# 开发模式
|
||||||
|
from heurams.context import config_var, rootdir, workdir
|
||||||
|
|
||||||
if is_subdir(Path(rootdir), Path(os.getcwd())):
|
if is_subdir(Path(rootdir), Path(os.getcwd())):
|
||||||
os.chdir(Path(rootdir) / ".." / "..")
|
os.chdir(Path(rootdir) / ".." / "..")
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.widgets import (
|
from textual.containers import ScrollableContainer
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
Static,
|
|
||||||
Button,
|
|
||||||
Markdown,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer, ScrollableContainer
|
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Button, Footer, Header, Label, Markdown, Static
|
||||||
|
|
||||||
import heurams.services.version as version
|
import heurams.services.version as version
|
||||||
from heurams.context import *
|
from heurams.context import *
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import pathlib
|
||||||
|
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.widgets import (
|
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
ListView,
|
|
||||||
ListItem,
|
|
||||||
Button,
|
|
||||||
Static,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer
|
from textual.containers import ScrollableContainer
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import (Button, Footer, Header, Label, ListItem, ListView,
|
||||||
|
Static)
|
||||||
|
|
||||||
from heurams.kernel.particles import *
|
|
||||||
from heurams.context import *
|
|
||||||
import heurams.services.version as version
|
|
||||||
import heurams.services.timer as timer
|
import heurams.services.timer as timer
|
||||||
from .preparation import PreparationScreen
|
import heurams.services.version as version
|
||||||
from .about import AboutScreen
|
from heurams.context import *
|
||||||
|
from heurams.kernel.particles import *
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
import pathlib
|
from .about import AboutScreen
|
||||||
|
from .preparation import PreparationScreen
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -51,8 +45,8 @@ class DashboardScreen(Screen):
|
|||||||
res = dict()
|
res = dict()
|
||||||
filestem = pathlib.Path(filename).stem
|
filestem = pathlib.Path(filename).stem
|
||||||
res[0] = f"{filename}\0"
|
res[0] = f"{filename}\0"
|
||||||
from heurams.kernel.particles.loader import load_electron
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
|
from heurams.kernel.particles.loader import load_electron
|
||||||
|
|
||||||
electron_file_path = pathlib.Path(config_var.get()["paths"]["electron_dir"]) / (
|
electron_file_path = pathlib.Path(config_var.get()["paths"]["electron_dir"]) / (
|
||||||
filestem + ".json"
|
filestem + ".json"
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from textual.app import ComposeResult
|
|
||||||
from textual.widgets import Header, Footer, Label, Static, Button
|
|
||||||
from textual.containers import Center, ScrollableContainer
|
|
||||||
from textual.screen import Screen
|
|
||||||
from textual.reactive import reactive
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from textual.app import ComposeResult
|
||||||
from heurams.context import config_var
|
from textual.containers import Center, ScrollableContainer
|
||||||
from heurams.kernel.reactor import *
|
from textual.reactive import reactive
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Button, Footer, Header, Label, Static
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.kernel.puzzles as pz
|
import heurams.kernel.puzzles as pz
|
||||||
|
from heurams.context import config_var
|
||||||
|
from heurams.kernel.reactor import *
|
||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
from .. import shim
|
from .. import shim
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import toml
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.widgets import (
|
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Button,
|
|
||||||
Markdown,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer
|
from textual.containers import ScrollableContainer
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import (Button, Footer, Header, Input, Label, Markdown,
|
||||||
|
Select)
|
||||||
|
|
||||||
from heurams.services.version import ver
|
|
||||||
import toml
|
|
||||||
from pathlib import Path
|
|
||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
|
from heurams.services.version import ver
|
||||||
|
|
||||||
|
|
||||||
class NucleonCreatorScreen(Screen):
|
class NucleonCreatorScreen(Screen):
|
||||||
@@ -27,6 +21,7 @@ class NucleonCreatorScreen(Screen):
|
|||||||
|
|
||||||
def search_templates(self):
|
def search_templates(self):
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
|
|
||||||
template_dir = Path(config_var.get()["paths"]["template_dir"])
|
template_dir = Path(config_var.get()["paths"]["template_dir"])
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from textual.app import ComposeResult
|
|
||||||
from textual.widgets import (
|
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
Static,
|
|
||||||
ProgressBar,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer, Horizontal
|
|
||||||
from textual.containers import ScrollableContainer
|
|
||||||
from textual.screen import Screen
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Horizontal, ScrollableContainer
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Button, Footer, Header, Label, ProgressBar, Static
|
||||||
|
from textual.worker import get_current_worker
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.services.hasher as hasher
|
import heurams.services.hasher as hasher
|
||||||
from heurams.context import *
|
from heurams.context import *
|
||||||
from textual.worker import get_current_worker
|
|
||||||
|
|
||||||
|
|
||||||
class PrecachingScreen(Screen):
|
class PrecachingScreen(Screen):
|
||||||
@@ -96,7 +89,7 @@ class PrecachingScreen(Screen):
|
|||||||
|
|
||||||
def precache_by_text(self, text: str):
|
def precache_by_text(self, text: str):
|
||||||
"""预缓存单段文本的音频"""
|
"""预缓存单段文本的音频"""
|
||||||
from heurams.context import rootdir, workdir, config_var
|
from heurams.context import config_var, rootdir, workdir
|
||||||
|
|
||||||
cache_dir = pathlib.Path(config_var.get()["paths"]["cache_dir"])
|
cache_dir = pathlib.Path(config_var.get()["paths"]["cache_dir"])
|
||||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -166,7 +159,7 @@ class PrecachingScreen(Screen):
|
|||||||
|
|
||||||
def precache_all_files(self):
|
def precache_all_files(self):
|
||||||
"""预缓存所有文件"""
|
"""预缓存所有文件"""
|
||||||
from heurams.context import rootdir, workdir, config_var
|
from heurams.context import config_var, rootdir, workdir
|
||||||
|
|
||||||
nucleon_path = pathlib.Path(config_var.get()["paths"]["nucleon_dir"])
|
nucleon_path = pathlib.Path(config_var.get()["paths"]["nucleon_dir"])
|
||||||
nucleon_files = [
|
nucleon_files = [
|
||||||
@@ -220,7 +213,8 @@ class PrecachingScreen(Screen):
|
|||||||
# 清空缓存
|
# 清空缓存
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
from heurams.context import rootdir, workdir, config_var
|
|
||||||
|
from heurams.context import config_var, rootdir, workdir
|
||||||
|
|
||||||
shutil.rmtree(
|
shutil.rmtree(
|
||||||
f"{config_var.get()["paths"]["cache_dir"]}", ignore_errors=True
|
f"{config_var.get()["paths"]["cache_dir"]}", ignore_errors=True
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.widgets import (
|
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
Static,
|
|
||||||
Button,
|
|
||||||
Markdown,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer
|
from textual.containers import ScrollableContainer
|
||||||
|
from textual.reactive import reactive
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from heurams.context import config_var
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Footer, Header, Label, Markdown, Static
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.services.hasher as hasher
|
import heurams.services.hasher as hasher
|
||||||
from heurams.context import *
|
from heurams.context import *
|
||||||
from textual.reactive import reactive
|
from heurams.context import config_var
|
||||||
from textual.widget import Widget
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from textual.app import ComposeResult
|
|
||||||
from textual.widgets import (
|
|
||||||
Header,
|
|
||||||
Footer,
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
Static,
|
|
||||||
ProgressBar,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer, Horizontal
|
|
||||||
from textual.containers import ScrollableContainer
|
|
||||||
from textual.screen import Screen
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.containers import Horizontal, ScrollableContainer
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Button, Footer, Header, Label, ProgressBar, Static
|
||||||
|
from textual.worker import get_current_worker
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.services.hasher as hasher
|
import heurams.services.hasher as hasher
|
||||||
from heurams.context import *
|
from heurams.context import *
|
||||||
from textual.worker import get_current_worker
|
|
||||||
|
|
||||||
|
|
||||||
class SyncScreen(Screen):
|
class SyncScreen(Screen):
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
"""Kernel 操作辅助函数库"""
|
"""Kernel 操作辅助函数库"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
import heurams.interface.widgets as pzw
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.kernel.puzzles as pz
|
import heurams.kernel.puzzles as pz
|
||||||
import heurams.interface.widgets as pzw
|
|
||||||
from typing import TypedDict
|
|
||||||
|
|
||||||
staging = {} # 细粒度缓存区, 是 ident -> quality 的封装
|
staging = {} # 细粒度缓存区, 是 ident -> quality 的封装
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from textual.app import ComposeResult
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
from textual.widgets import (
|
from textual.containers import Horizontal, ScrollableContainer
|
||||||
Label,
|
|
||||||
Static,
|
|
||||||
Button,
|
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer, Horizontal
|
|
||||||
from textual.widget import Widget
|
|
||||||
import heurams.kernel.particles as pt
|
|
||||||
from .base_puzzle_widget import BasePuzzleWidget
|
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label, Static
|
||||||
|
|
||||||
|
import heurams.kernel.particles as pt
|
||||||
|
|
||||||
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
|
|
||||||
|
|
||||||
class BasicEvaluation(BasePuzzleWidget):
|
class BasicEvaluation(BasePuzzleWidget):
|
||||||
@@ -51,7 +49,7 @@ class BasicEvaluation(BasePuzzleWidget):
|
|||||||
# 显示主要内容
|
# 显示主要内容
|
||||||
yield Label(self.atom.registry["nucleon"]["content"], id="main")
|
yield Label(self.atom.registry["nucleon"]["content"], id="main")
|
||||||
|
|
||||||
# 显示评估说明(可选)
|
# 显示评估说明(可选)
|
||||||
yield Static("请评估你对这个内容的记忆程度: ", classes="instruction")
|
yield Static("请评估你对这个内容的记忆程度: ", classes="instruction")
|
||||||
|
|
||||||
# 按钮容器
|
# 按钮容器
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
from textual.widgets import (
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
)
|
|
||||||
from textual.widget import Widget
|
|
||||||
import heurams.kernel.particles as pt
|
|
||||||
import heurams.kernel.puzzles as pz
|
|
||||||
from .base_puzzle_widget import BasePuzzleWidget
|
|
||||||
import copy
|
import copy
|
||||||
import random
|
import random
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
from textual.containers import Container
|
from textual.containers import Container
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label
|
||||||
|
|
||||||
|
import heurams.kernel.particles as pt
|
||||||
|
import heurams.kernel.puzzles as pz
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
from typing import TypedDict
|
|
||||||
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
from textual.widgets import (
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
)
|
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label
|
||||||
|
|
||||||
|
|
||||||
class Finished(Widget):
|
class Finished(Widget):
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# 单项选择题
|
# 单项选择题
|
||||||
from textual.widgets import (
|
from typing import TypedDict
|
||||||
Label,
|
|
||||||
Button,
|
from textual.containers import Container, ScrollableContainer
|
||||||
)
|
|
||||||
from textual.containers import ScrollableContainer, Container
|
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.kernel.puzzles as pz
|
import heurams.kernel.puzzles as pz
|
||||||
from .base_puzzle_widget import BasePuzzleWidget
|
|
||||||
from typing import TypedDict
|
|
||||||
from heurams.services.hasher import hash
|
from heurams.services.hasher import hash
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
|
|
||||||
self.screen.rating = rating # type: ignore
|
self.screen.rating = rating # type: ignore
|
||||||
self.handler(rating)
|
self.handler(rating)
|
||||||
# 重置输入(如果回答错误)
|
# 重置输入(如果回答错误)
|
||||||
if not is_correct:
|
if not is_correct:
|
||||||
self.inputlist = []
|
self.inputlist = []
|
||||||
self.refresh_buttons()
|
self.refresh_buttons()
|
||||||
@@ -127,7 +127,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.update_display()
|
self.update_display()
|
||||||
|
|
||||||
def refresh_buttons(self):
|
def refresh_buttons(self):
|
||||||
"""刷新按钮显示(用于题目切换)"""
|
"""刷新按钮显示(用于题目切换)"""
|
||||||
# 移除所有选项按钮
|
# 移除所有选项按钮
|
||||||
logger.debug("刷新按钮")
|
logger.debug("刷新按钮")
|
||||||
self.cursor += 1
|
self.cursor += 1
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
from textual.widgets import (
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
)
|
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label
|
||||||
|
|
||||||
|
|
||||||
class Placeholder(Widget):
|
class Placeholder(Widget):
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
from textual.reactive import reactive
|
|
||||||
from textual.widgets import (
|
|
||||||
Markdown,
|
|
||||||
Label,
|
|
||||||
Static,
|
|
||||||
Button,
|
|
||||||
)
|
|
||||||
from textual.containers import Center
|
|
||||||
from textual.widget import Widget
|
|
||||||
from typing import Dict
|
|
||||||
import heurams.kernel.particles as pt
|
|
||||||
import re
|
import re
|
||||||
from .base_puzzle_widget import BasePuzzleWidget
|
from typing import Dict, List, TypedDict
|
||||||
from typing import TypedDict, List
|
|
||||||
|
from textual.containers import Center
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
|
from textual.reactive import reactive
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Button, Label, Markdown, Static
|
||||||
|
|
||||||
|
import heurams.kernel.particles as pt
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from .sm2 import SM2Algorithm
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .sm2 import SM2Algorithm
|
||||||
|
from .sm15m import SM15MAlgorithm
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -9,7 +11,8 @@ __all__ = [
|
|||||||
|
|
||||||
algorithms = {
|
algorithms = {
|
||||||
"SM-2": SM2Algorithm,
|
"SM-2": SM2Algorithm,
|
||||||
"supermemo2": SM2Algorithm,
|
"SM-15M": SM15MAlgorithm,
|
||||||
|
# "SM-15M": SM15MAlgorithm,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("算法模块初始化完成, 注册的算法: %s", list(algorithms.keys()))
|
logger.debug("算法模块初始化完成, 注册的算法: %s", list(algorithms.keys()))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import heurams.services.timer as timer
|
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
|
import heurams.services.timer as timer
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
281
src/heurams/kernel/algorithms/sm15m.py
Normal file
281
src/heurams/kernel/algorithms/sm15m.py
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
"""
|
||||||
|
SM-15 接口兼容实现, 基于 SM-15 算法的逆向工程
|
||||||
|
全局状态保存在文件中, 项目状态通过 algodata 字典传递
|
||||||
|
|
||||||
|
基于: https://github.com/kazuaki/sm.js
|
||||||
|
原始 CoffeeScript 代码: (c) 2014 Kazuaki Tanida (MIT 许可证)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
from heurams.kernel.algorithms.sm15m_calc import (MAX_AF, MIN_AF, NOTCH_AF,
|
||||||
|
RANGE_AF, RANGE_REPETITION,
|
||||||
|
SM, THRESHOLD_RECALL, Item)
|
||||||
|
|
||||||
|
# 全局状态文件路径
|
||||||
|
_GLOBAL_STATE_FILE = os.path.expanduser("~/.sm15_global_state.json")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_global_sm():
|
||||||
|
"""获取全局 SM 实例, 从文件加载或创建新的"""
|
||||||
|
if os.path.exists(_GLOBAL_STATE_FILE):
|
||||||
|
try:
|
||||||
|
with open(_GLOBAL_STATE_FILE, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
sm_instance = SM.load(data)
|
||||||
|
return sm_instance
|
||||||
|
except Exception:
|
||||||
|
# 如果加载失败, 创建新的实例
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 创建新的 SM 实例
|
||||||
|
sm_instance = SM()
|
||||||
|
# 保存初始状态
|
||||||
|
_save_global_sm(sm_instance)
|
||||||
|
return sm_instance
|
||||||
|
|
||||||
|
|
||||||
|
def _save_global_sm(sm_instance):
|
||||||
|
"""保存全局 SM 实例到文件"""
|
||||||
|
try:
|
||||||
|
data = sm_instance.data()
|
||||||
|
with open(_GLOBAL_STATE_FILE, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
except Exception:
|
||||||
|
# 忽略保存错误
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SM15MAlgorithm:
|
||||||
|
algo_name = "SM-15M"
|
||||||
|
|
||||||
|
class AlgodataDict(TypedDict):
|
||||||
|
efactor: float
|
||||||
|
real_rept: int
|
||||||
|
rept: int
|
||||||
|
interval: int
|
||||||
|
last_date: int
|
||||||
|
next_date: int
|
||||||
|
is_activated: int
|
||||||
|
last_modify: float
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
"efactor": 2.5,
|
||||||
|
"real_rept": 0,
|
||||||
|
"rept": 0,
|
||||||
|
"interval": 0,
|
||||||
|
"last_date": 0,
|
||||||
|
"next_date": 0,
|
||||||
|
"is_activated": 0,
|
||||||
|
"last_modify": 0.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_timestamp(cls):
|
||||||
|
"""获取当前时间戳(秒)"""
|
||||||
|
return datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_daystamp(cls):
|
||||||
|
"""获取当前天数戳(从某个纪元开始的天数)"""
|
||||||
|
# 使用与原始 SM-2 相同的纪元:1970-01-01
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
|
delta = now - epoch
|
||||||
|
return delta.days
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _algodata_to_item(cls, algodata, sm_instance):
|
||||||
|
"""将 algodata 转换为 Item 实例"""
|
||||||
|
# 从 algodata 获取 SM-2 数据
|
||||||
|
sm15_data = algodata.get(cls.algo_name, cls.defaults.copy())
|
||||||
|
|
||||||
|
# 创建 Item 实例
|
||||||
|
item = Item(sm_instance)
|
||||||
|
|
||||||
|
# 映射字段
|
||||||
|
# efactor -> A-Factor (需要转换)
|
||||||
|
efactor = sm15_data.get("efactor", 2.5)
|
||||||
|
# SM-2 的 efactor 范围 [1.3, 2.5+], SM-15 的 A-Factor 范围 [1.2, 6.9]
|
||||||
|
# 简单线性映射:af = (efactor - 1.3) * (MAX_AF - MIN_AF) / (2.5 - 1.3) + MIN_AF
|
||||||
|
# 但 efactor 可能大于 2.5, 所以需要限制
|
||||||
|
af = max(MIN_AF, min(MAX_AF, efactor * 2.0)) # 粗略映射
|
||||||
|
# 调试
|
||||||
|
# print(f"DEBUG: efactor={efactor}, af before set={af}")
|
||||||
|
item.af(af)
|
||||||
|
# print(f"DEBUG: item.af() after set={item.af()}")
|
||||||
|
|
||||||
|
# rept -> repetition (成功回忆次数)
|
||||||
|
rept = sm15_data.get("rept", 0)
|
||||||
|
item.repetition = (
|
||||||
|
rept - 1 if rept > 0 else -1
|
||||||
|
) # SM-15 中 repetition=-1 表示新项目
|
||||||
|
|
||||||
|
# real_rept -> lapse? 或者忽略
|
||||||
|
real_rept = sm15_data.get("real_rept", 0)
|
||||||
|
# 可以存储在 value 中或忽略
|
||||||
|
|
||||||
|
# interval -> optimum_interval (需要从天数转换为毫秒)
|
||||||
|
interval_days = sm15_data.get("interval", 0)
|
||||||
|
if interval_days == 0:
|
||||||
|
item.optimum_interval = sm_instance.interval_base
|
||||||
|
else:
|
||||||
|
item.optimum_interval = interval_days * 24 * 60 * 60 * 1000 # 天转毫秒
|
||||||
|
|
||||||
|
# last_date -> previous_date
|
||||||
|
last_date_days = sm15_data.get("last_date", 0)
|
||||||
|
if last_date_days > 0:
|
||||||
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
|
item.previous_date = epoch + datetime.timedelta(days=last_date_days)
|
||||||
|
|
||||||
|
# next_date -> due_date
|
||||||
|
next_date_days = sm15_data.get("next_date", 0)
|
||||||
|
if next_date_days > 0:
|
||||||
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
|
item.due_date = epoch + datetime.timedelta(days=next_date_days)
|
||||||
|
|
||||||
|
# is_activated 和 last_modify 忽略
|
||||||
|
|
||||||
|
# 将原始 algodata 保存在 value 中以便恢复
|
||||||
|
item.value = {
|
||||||
|
"front": "SM-15 item",
|
||||||
|
"back": "SM-15 item",
|
||||||
|
"_sm15_data": sm15_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _item_to_algodata(cls, item, algodata):
|
||||||
|
"""将 Item 实例状态写回 algodata"""
|
||||||
|
if cls.algo_name not in algodata:
|
||||||
|
algodata[cls.algo_name] = cls.defaults.copy()
|
||||||
|
|
||||||
|
sm15_data = algodata[cls.algo_name]
|
||||||
|
|
||||||
|
# A-Factor -> efactor (反向映射)
|
||||||
|
af = item.af()
|
||||||
|
if af is None:
|
||||||
|
af = MIN_AF
|
||||||
|
# 反向粗略映射
|
||||||
|
efactor = max(1.3, min(af / 2.0, 10.0)) # 限制范围
|
||||||
|
# 调试
|
||||||
|
# print(f"DEBUG: item.af()={af}, computed efactor={efactor}")
|
||||||
|
sm15_data["efactor"] = efactor
|
||||||
|
|
||||||
|
# repetition -> rept
|
||||||
|
rept = item.repetition + 1 if item.repetition >= 0 else 0
|
||||||
|
sm15_data["rept"] = rept
|
||||||
|
|
||||||
|
# real_rept: 递增在 revisor 中处理, 这里保持不变
|
||||||
|
# 但如果没有 real_rept 字段, 则初始化为0
|
||||||
|
if "real_rept" not in sm15_data:
|
||||||
|
sm15_data["real_rept"] = 0
|
||||||
|
|
||||||
|
# optimum_interval -> interval (毫秒转天)
|
||||||
|
interval_ms = item.optimum_interval
|
||||||
|
if interval_ms == item.sm.interval_base:
|
||||||
|
sm15_data["interval"] = 0
|
||||||
|
else:
|
||||||
|
interval_days = max(0, round(interval_ms / (24 * 60 * 60 * 1000)))
|
||||||
|
sm15_data["interval"] = interval_days
|
||||||
|
|
||||||
|
# previous_date -> last_date
|
||||||
|
if item.previous_date:
|
||||||
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
|
last_date_days = (item.previous_date - epoch).days
|
||||||
|
sm15_data["last_date"] = last_date_days
|
||||||
|
else:
|
||||||
|
sm15_data["last_date"] = 0
|
||||||
|
|
||||||
|
# due_date -> next_date
|
||||||
|
if item.due_date:
|
||||||
|
epoch = datetime.datetime(1970, 1, 1)
|
||||||
|
next_date_days = (item.due_date - epoch).days
|
||||||
|
sm15_data["next_date"] = next_date_days
|
||||||
|
else:
|
||||||
|
sm15_data["next_date"] = 0
|
||||||
|
|
||||||
|
# is_activated: 保持不变或设为1
|
||||||
|
if "is_activated" not in sm15_data:
|
||||||
|
sm15_data["is_activated"] = 1
|
||||||
|
|
||||||
|
# last_modify: 更新时间戳
|
||||||
|
sm15_data["last_modify"] = cls._get_timestamp()
|
||||||
|
|
||||||
|
return algodata
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def revisor(
|
||||||
|
cls, algodata: dict, feedback: int = 5, is_new_activation: bool = False
|
||||||
|
):
|
||||||
|
"""SM-15 算法迭代决策机制实现"""
|
||||||
|
# 获取全局 SM 实例
|
||||||
|
sm_instance = _get_global_sm()
|
||||||
|
|
||||||
|
# 将 algodata 转换为 Item
|
||||||
|
item = cls._algodata_to_item(algodata, sm_instance)
|
||||||
|
|
||||||
|
# 处理 is_new_activation
|
||||||
|
if is_new_activation:
|
||||||
|
# 重置为初始状态
|
||||||
|
item.repetition = -1
|
||||||
|
item.lapse = 0
|
||||||
|
item.optimum_interval = sm_instance.interval_base
|
||||||
|
item.previous_date = None
|
||||||
|
item.due_date = datetime.datetime.fromtimestamp(0)
|
||||||
|
item.af(2.5) # 重置 efactor
|
||||||
|
|
||||||
|
# 将项目临时添加到 SM 实例(以便 answer 更新共享状态)
|
||||||
|
sm_instance.q.append(item)
|
||||||
|
|
||||||
|
# 处理反馈(评分)
|
||||||
|
# SM-2 的 feedback 是 0-5, SM-15 的 grade 也是 0-5
|
||||||
|
grade = feedback
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
# 调用 answer 方法
|
||||||
|
item.answer(grade, now)
|
||||||
|
|
||||||
|
# 更新共享状态(FI-Graph, ForgettingCurves, OFM)
|
||||||
|
if item.repetition >= 0:
|
||||||
|
sm_instance.forgetting_curves.register_point(grade, item, now)
|
||||||
|
sm_instance.ofm.update()
|
||||||
|
sm_instance.fi_g.update(grade, item, now)
|
||||||
|
|
||||||
|
# 从队列中移除项目
|
||||||
|
sm_instance.q.remove(item)
|
||||||
|
|
||||||
|
# 保存全局状态
|
||||||
|
_save_global_sm(sm_instance)
|
||||||
|
|
||||||
|
# 将更新后的 Item 状态写回 algodata
|
||||||
|
cls._item_to_algodata(item, algodata)
|
||||||
|
|
||||||
|
# 更新 real_rept(总复习次数)
|
||||||
|
algodata[cls.algo_name]["real_rept"] += 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_due(cls, algodata):
|
||||||
|
"""检查项目是否到期"""
|
||||||
|
sm15_data = algodata.get(cls.algo_name, cls.defaults.copy())
|
||||||
|
next_date_days = sm15_data.get("next_date", 0)
|
||||||
|
current_daystamp = cls._get_daystamp()
|
||||||
|
return next_date_days <= current_daystamp
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def rate(cls, algodata):
|
||||||
|
"""获取项目的评分(返回 efactor 字符串)"""
|
||||||
|
sm15_data = algodata.get(cls.algo_name, cls.defaults.copy())
|
||||||
|
efactor = sm15_data.get("efactor", 2.5)
|
||||||
|
return str(efactor)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def nextdate(cls, algodata) -> int:
|
||||||
|
"""获取下次复习日期(天数戳)"""
|
||||||
|
sm15_data = algodata.get(cls.algo_name, cls.defaults.copy())
|
||||||
|
next_date_days = sm15_data.get("next_date", 0)
|
||||||
|
return next_date_days
|
||||||
1573
src/heurams/kernel/algorithms/sm15m_calc.py
Normal file
1573
src/heurams/kernel/algorithms/sm15m_calc.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
|||||||
from .base import BaseAlgorithm
|
|
||||||
import heurams.services.timer as timer
|
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
|
import heurams.services.timer as timer
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base import BaseAlgorithm
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ from heurams.services.logger import get_logger
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
logger.debug("粒子模块已加载")
|
logger.debug("粒子模块已加载")
|
||||||
|
|
||||||
|
from .atom import Atom, atom_registry
|
||||||
from .electron import Electron
|
from .electron import Electron
|
||||||
|
from .loader import load_electron, load_nucleon
|
||||||
from .nucleon import Nucleon
|
from .nucleon import Nucleon
|
||||||
from .orbital import Orbital
|
from .orbital import Orbital
|
||||||
from .atom import Atom, atom_registry
|
|
||||||
from .probe import probe_all, probe_by_filename
|
from .probe import probe_all, probe_by_filename
|
||||||
from .loader import load_nucleon, load_electron
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Electron",
|
"Electron",
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import typing
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
import bidict
|
||||||
|
import toml
|
||||||
|
|
||||||
|
from heurams.context import config_var
|
||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
from .electron import Electron
|
from .electron import Electron
|
||||||
from .nucleon import Nucleon
|
from .nucleon import Nucleon
|
||||||
from .orbital import Orbital
|
from .orbital import Orbital
|
||||||
from typing import TypedDict
|
|
||||||
import pathlib
|
|
||||||
import typing
|
|
||||||
import toml
|
|
||||||
import json
|
|
||||||
import bidict
|
|
||||||
from heurams.context import config_var
|
|
||||||
from heurams.services.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -16,7 +19,7 @@ logger = get_logger(__name__)
|
|||||||
class AtomRegister_runtime(TypedDict):
|
class AtomRegister_runtime(TypedDict):
|
||||||
locked: bool # 只读锁定标识符
|
locked: bool # 只读锁定标识符
|
||||||
min_rate: int # 最低评分
|
min_rate: int # 最低评分
|
||||||
newact: bool # 新激活
|
newact: bool # 新激活
|
||||||
|
|
||||||
|
|
||||||
class AtomRegister(TypedDict):
|
class AtomRegister(TypedDict):
|
||||||
@@ -68,9 +71,9 @@ class Atom:
|
|||||||
self.registry[key] = value
|
self.registry[key] = value
|
||||||
logger.debug("键 '%s' 已链接, 触发 do_eval", key)
|
logger.debug("键 '%s' 已链接, 触发 do_eval", key)
|
||||||
self.do_eval()
|
self.do_eval()
|
||||||
if key == 'electron':
|
if key == "electron":
|
||||||
if self.registry['electron'].is_activated() == 0:
|
if self.registry["electron"].is_activated() == 0:
|
||||||
self.registry['runtime']['newact'] = True
|
self.registry["runtime"]["newact"] = True
|
||||||
else:
|
else:
|
||||||
logger.error("尝试链接不受支持的键: '%s'", key)
|
logger.error("尝试链接不受支持的键: '%s'", key)
|
||||||
raise ValueError("不受支持的原子元数据链接操作")
|
raise ValueError("不受支持的原子元数据链接操作")
|
||||||
@@ -104,7 +107,10 @@ class Atom:
|
|||||||
"""
|
"""
|
||||||
if self.registry["runtime"]["locked"]:
|
if self.registry["runtime"]["locked"]:
|
||||||
logger.debug(f"允许总评分: {self.registry['runtime']['min_rate']}")
|
logger.debug(f"允许总评分: {self.registry['runtime']['min_rate']}")
|
||||||
self.registry["electron"].revisor(self.registry["runtime"]["min_rate"], is_new_activation=self.registry["runtime"]["newact"])
|
self.registry["electron"].revisor(
|
||||||
|
self.registry["runtime"]["min_rate"],
|
||||||
|
is_new_activation=self.registry["runtime"]["newact"],
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug("禁止总评分")
|
logger.debug("禁止总评分")
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ logger = get_logger(__name__)
|
|||||||
class Electron:
|
class Electron:
|
||||||
"""电子: 记忆分析元数据及算法"""
|
"""电子: 记忆分析元数据及算法"""
|
||||||
|
|
||||||
def __init__(self, ident: str, algodata: dict = {}, algo_name: str = "supermemo2"):
|
def __init__(self, ident: str, algodata: dict = {}, algo_name: str = "SM-2"):
|
||||||
"""初始化电子对象 (记忆数据)
|
"""初始化电子对象 (记忆数据)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
from .nucleon import Nucleon
|
|
||||||
from .electron import Electron
|
|
||||||
import heurams.services.hasher as hasher
|
|
||||||
import pathlib
|
|
||||||
import toml
|
|
||||||
import json
|
import json
|
||||||
|
import pathlib
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
import heurams.services.hasher as hasher
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .electron import Electron
|
||||||
|
from .nucleon import Nucleon
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from heurams.context import config_var
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
from heurams.context import config_var
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class BasePuzzle:
|
|||||||
"""谜题基类"""
|
"""谜题基类"""
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
logger.debug("BasePuzzle.refresh 被调用(未实现)")
|
logger.debug("BasePuzzle.refresh 被调用(未实现)")
|
||||||
raise NotImplementedError("谜题对象未实现 refresh 方法")
|
raise NotImplementedError("谜题对象未实现 refresh 方法")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from .base import BasePuzzle
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base import BasePuzzle
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# mcq.py
|
# mcq.py
|
||||||
from .base import BasePuzzle
|
|
||||||
import random
|
import random
|
||||||
from typing import List, Dict, Optional, Union
|
from typing import Dict, List, Optional, Union
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base import BasePuzzle
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# mcq.py
|
# mcq.py
|
||||||
from .base import BasePuzzle
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base import BasePuzzle
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -14,5 +16,5 @@ class RecognitionPuzzle(BasePuzzle):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
logger.debug("RecognitionPuzzle.refresh(空实现)")
|
logger.debug("RecognitionPuzzle.refresh(空实现)")
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from .states import PhaserState, ProcessionState
|
from heurams.services.logger import get_logger
|
||||||
from .procession import Procession
|
|
||||||
from .fission import Fission
|
from .fission import Fission
|
||||||
from .phaser import Phaser
|
from .phaser import Phaser
|
||||||
from heurams.services.logger import get_logger
|
from .procession import Procession
|
||||||
|
from .states import PhaserState, ProcessionState
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.kernel.puzzles as puz
|
import heurams.kernel.puzzles as puz
|
||||||
import random
|
|
||||||
from .states import PhaserState
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .states import PhaserState
|
||||||
|
|
||||||
|
|
||||||
class Fission:
|
class Fission:
|
||||||
"""裂变器: 单原子调度展开器"""
|
"""裂变器: 单原子调度展开器"""
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# 移相器类定义
|
# 移相器类定义
|
||||||
|
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
from .states import PhaserState, ProcessionState
|
|
||||||
from .procession import Procession
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .procession import Procession
|
||||||
|
from .states import PhaserState, ProcessionState
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
from .states import PhaserState, ProcessionState
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .states import PhaserState, ProcessionState
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ class Procession:
|
|||||||
self.queue.append(atom)
|
self.queue.append(atom)
|
||||||
logger.debug("原子已追加到队列, 新队列长度=%d", len(self.queue))
|
logger.debug("原子已追加到队列, 新队列长度=%d", len(self.queue))
|
||||||
else:
|
else:
|
||||||
logger.debug("原子未追加(重复或队列长度<=1)")
|
logger.debug("原子未追加(重复或队列长度<=1)")
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
length = len(self.queue) - self.cursor
|
length = len(self.queue) - self.cursor
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# 音频播放器, 必须基于文件操作
|
# 音频播放器, 必须基于文件操作
|
||||||
from . import termux_audio
|
|
||||||
from . import playsound_audio
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from . import playsound_audio, termux_audio
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import playsound
|
import playsound
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Protocol
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
logger.debug("OpenAI provider 模块已加载(未实现)")
|
logger.debug("OpenAI provider 模块已加载(未实现)")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
from .base import BaseTTS
|
from .base import BaseTTS
|
||||||
from .edge_tts import EdgeTTS
|
from .edge_tts import EdgeTTS
|
||||||
from heurams.services.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from .base import BaseTTS
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import edge_tts
|
import edge_tts
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .base import BaseTTS
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# 音频服务
|
# 音频服务
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
from heurams.providers.audio import providers as prov
|
from heurams.providers.audio import providers as prov
|
||||||
from typing import Callable
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# 配置文件服务
|
# 配置文件服务
|
||||||
import pathlib
|
import pathlib
|
||||||
import toml
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# 哈希服务
|
# 哈希服务
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def setup_logging(
|
|||||||
# 创建formatter
|
# 创建formatter
|
||||||
formatter = logging.Formatter(log_format, date_format)
|
formatter = logging.Formatter(log_format, date_format)
|
||||||
|
|
||||||
# 创建文件handler(使用RotatingFileHandler防止日志过大)
|
# 创建文件handler(使用RotatingFileHandler防止日志过大)
|
||||||
file_handler = logging.handlers.RotatingFileHandler(
|
file_handler = logging.handlers.RotatingFileHandler(
|
||||||
filename=log_path,
|
filename=log_path,
|
||||||
maxBytes=max_bytes,
|
maxBytes=max_bytes,
|
||||||
@@ -55,7 +55,7 @@ def setup_logging(
|
|||||||
file_handler.setFormatter(formatter)
|
file_handler.setFormatter(formatter)
|
||||||
file_handler.setLevel(log_level)
|
file_handler.setLevel(log_level)
|
||||||
|
|
||||||
# 配置root logger - 设置为 WARNING 级别(只记录重要信息)
|
# 配置root logger - 设置为 WARNING 级别(只记录重要信息)
|
||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
root_logger.setLevel(logging.WARNING) # 这里改为 WARNING
|
root_logger.setLevel(logging.WARNING) # 这里改为 WARNING
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ def setup_logging(
|
|||||||
for handler in root_logger.handlers[:]:
|
for handler in root_logger.handlers[:]:
|
||||||
root_logger.removeHandler(handler)
|
root_logger.removeHandler(handler)
|
||||||
|
|
||||||
# 创建自己的应用logger(单独设置DEBUG级别)
|
# 创建自己的应用logger(单独设置DEBUG级别)
|
||||||
app_logger = logging.getLogger("heurams")
|
app_logger = logging.getLogger("heurams")
|
||||||
app_logger.setLevel(log_level) # 保持DEBUG级别
|
app_logger.setLevel(log_level) # 保持DEBUG级别
|
||||||
app_logger.addHandler(file_handler)
|
app_logger.addHandler(file_handler)
|
||||||
@@ -146,7 +146,7 @@ def exception(msg: str, *args, **kwargs) -> None:
|
|||||||
get_logger().exception(msg, *args, **kwargs)
|
get_logger().exception(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# 初始化日志系统(硬编码配置)
|
# 初始化日志系统(硬编码配置)
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# 时间服务
|
# 时间服务
|
||||||
from heurams.context import config_var
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from heurams.context import config_var
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# 文本转语音服务
|
# 文本转语音服务
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
from heurams.providers.tts import TTSs
|
from heurams.providers.tts import TTSs
|
||||||
from typing import Callable
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
ver = "0.4.0"
|
ver = "0.4.1"
|
||||||
stage = "prototype"
|
stage = "prototype"
|
||||||
codename = "fledge" # 雏鸟, 0.4.x 版本
|
codename = "fledge" # 雏鸟, 0.4.x 版本
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
DashboardScreen 的测试, 包括单元测试和 pilot 测试.
|
DashboardScreen 的测试, 包括单元测试和 pilot 测试.
|
||||||
"""
|
"""
|
||||||
import unittest
|
|
||||||
import tempfile
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
from unittest.mock import patch, MagicMock
|
import unittest
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from textual.pilot import Pilot
|
from textual.pilot import Pilot
|
||||||
|
|
||||||
from heurams.context import ConfigContext
|
from heurams.context import ConfigContext
|
||||||
from heurams.services.config import ConfigFile
|
|
||||||
from heurams.interface.__main__ import HeurAMSApp
|
from heurams.interface.__main__ import HeurAMSApp
|
||||||
from heurams.interface.screens.dashboard import DashboardScreen
|
from heurams.interface.screens.dashboard import DashboardScreen
|
||||||
|
from heurams.services.config import ConfigFile
|
||||||
|
|
||||||
|
|
||||||
class TestDashboardScreenUnit(unittest.TestCase):
|
class TestDashboardScreenUnit(unittest.TestCase):
|
||||||
"""DashboardScreen 的单元测试(不启动完整应用). """
|
"""DashboardScreen 的单元测试(不启动完整应用)."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""在每个测试之前运行, 设置临时目录和配置. """
|
"""在每个测试之前运行, 设置临时目录和配置."""
|
||||||
# 创建临时目录用于测试数据
|
# 创建临时目录用于测试数据
|
||||||
self.temp_dir = tempfile.TemporaryDirectory()
|
self.temp_dir = tempfile.TemporaryDirectory()
|
||||||
self.temp_path = pathlib.Path(self.temp_dir.name)
|
self.temp_path = pathlib.Path(self.temp_dir.name)
|
||||||
@@ -53,12 +54,12 @@ class TestDashboardScreenUnit(unittest.TestCase):
|
|||||||
self.config_ctx.__enter__()
|
self.config_ctx.__enter__()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""在每个测试之后清理. """
|
"""在每个测试之后清理."""
|
||||||
self.config_ctx.__exit__(None, None, None)
|
self.config_ctx.__exit__(None, None, None)
|
||||||
self.temp_dir.cleanup()
|
self.temp_dir.cleanup()
|
||||||
|
|
||||||
def test_compose(self):
|
def test_compose(self):
|
||||||
"""测试 compose 方法返回正确的部件. """
|
"""测试 compose 方法返回正确的部件."""
|
||||||
screen = DashboardScreen()
|
screen = DashboardScreen()
|
||||||
# 手动调用 compose 并收集部件
|
# 手动调用 compose 并收集部件
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
@@ -66,7 +67,7 @@ class TestDashboardScreenUnit(unittest.TestCase):
|
|||||||
result = screen.compose()
|
result = screen.compose()
|
||||||
widgets = list(result)
|
widgets = list(result)
|
||||||
# 检查是否包含 Header 和 Footer
|
# 检查是否包含 Header 和 Footer
|
||||||
from textual.widgets import Header, Footer
|
from textual.widgets import Footer, Header
|
||||||
|
|
||||||
header_present = any(isinstance(w, Header) for w in widgets)
|
header_present = any(isinstance(w, Header) for w in widgets)
|
||||||
footer_present = any(isinstance(w, Footer) for w in widgets)
|
footer_present = any(isinstance(w, Footer) for w in widgets)
|
||||||
@@ -84,7 +85,7 @@ class TestDashboardScreenUnit(unittest.TestCase):
|
|||||||
self.assertEqual(list_view.__class__.__name__, "ListView")
|
self.assertEqual(list_view.__class__.__name__, "ListView")
|
||||||
|
|
||||||
def test_item_desc_generator(self):
|
def test_item_desc_generator(self):
|
||||||
"""测试 item_desc_generator 函数. """
|
"""测试 item_desc_generator 函数."""
|
||||||
screen = DashboardScreen()
|
screen = DashboardScreen()
|
||||||
# 模拟一个文件名
|
# 模拟一个文件名
|
||||||
filename = "test.toml"
|
filename = "test.toml"
|
||||||
@@ -100,10 +101,10 @@ class TestDashboardScreenUnit(unittest.TestCase):
|
|||||||
|
|
||||||
@unittest.skip("Pilot 测试需要进一步配置, 暂不运行")
|
@unittest.skip("Pilot 测试需要进一步配置, 暂不运行")
|
||||||
class TestDashboardScreenPilot(unittest.TestCase):
|
class TestDashboardScreenPilot(unittest.TestCase):
|
||||||
"""使用 Textual Pilot 的集成测试. """
|
"""使用 Textual Pilot 的集成测试."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""配置临时目录和配置. """
|
"""配置临时目录和配置."""
|
||||||
self.temp_dir = tempfile.TemporaryDirectory()
|
self.temp_dir = tempfile.TemporaryDirectory()
|
||||||
self.temp_path = pathlib.Path(self.temp_dir.name)
|
self.temp_path = pathlib.Path(self.temp_dir.name)
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ class TestDashboardScreenPilot(unittest.TestCase):
|
|||||||
self.temp_dir.cleanup()
|
self.temp_dir.cleanup()
|
||||||
|
|
||||||
def test_dashboard_loads_with_pilot(self):
|
def test_dashboard_loads_with_pilot(self):
|
||||||
"""使用 Pilot 测试 DashboardScreen 加载. """
|
"""使用 Pilot 测试 DashboardScreen 加载."""
|
||||||
with patch("heurams.interface.__main__.environment_check"):
|
with patch("heurams.interface.__main__.environment_check"):
|
||||||
app = HeurAMSApp()
|
app = HeurAMSApp()
|
||||||
# 注意: Pilot 在 Textual 6.9.0 中的用法可能不同
|
# 注意: Pilot 在 Textual 6.9.0 中的用法可能不同
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from heurams.kernel.algorithms.sm2 import SM2Algorithm
|
from heurams.kernel.algorithms.sm2 import SM2Algorithm
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ class TestSM2Algorithm(unittest.TestCase):
|
|||||||
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)
|
||||||
# interval 应为 1(因为 rept=0)
|
# interval 应为 1(因为 rept=0)
|
||||||
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 1)
|
self.assertEqual(algodata[SM2Algorithm.algo_name]["interval"], 1)
|
||||||
|
|
||||||
def test_revisor_efactor_calculation(self):
|
def test_revisor_efactor_calculation(self):
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import unittest
|
import json
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
import toml
|
import unittest
|
||||||
import json
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
from heurams.context import ConfigContext
|
||||||
from heurams.kernel.particles.atom import Atom, atom_registry
|
from heurams.kernel.particles.atom import Atom, atom_registry
|
||||||
from heurams.kernel.particles.electron import Electron
|
from heurams.kernel.particles.electron import Electron
|
||||||
from heurams.kernel.particles.nucleon import Nucleon
|
from heurams.kernel.particles.nucleon import Nucleon
|
||||||
from heurams.kernel.particles.orbital import Orbital
|
from heurams.kernel.particles.orbital import Orbital
|
||||||
from heurams.context import ConfigContext
|
|
||||||
from heurams.services.config import ConfigFile
|
from heurams.services.config import ConfigFile
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import unittest
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from heurams.kernel.particles.electron import Electron
|
|
||||||
from heurams.kernel.algorithms import algorithms
|
from heurams.kernel.algorithms import algorithms
|
||||||
|
from heurams.kernel.particles.electron import Electron
|
||||||
|
|
||||||
|
|
||||||
class TestElectron(unittest.TestCase):
|
class TestElectron(unittest.TestCase):
|
||||||
@@ -27,7 +27,7 @@ class TestElectron(unittest.TestCase):
|
|||||||
self.assertEqual(electron.algo, algorithms["supermemo2"])
|
self.assertEqual(electron.algo, algorithms["supermemo2"])
|
||||||
self.assertIn(electron.algo, electron.algodata)
|
self.assertIn(electron.algo, electron.algodata)
|
||||||
self.assertIsInstance(electron.algodata[electron.algo], dict)
|
self.assertIsInstance(electron.algodata[electron.algo], dict)
|
||||||
# 检查默认值(排除动态字段)
|
# 检查默认值(排除动态字段)
|
||||||
defaults = electron.algo.defaults
|
defaults = electron.algo.defaults
|
||||||
for key, value in defaults.items():
|
for key, value in defaults.items():
|
||||||
if key == "last_modify":
|
if key == "last_modify":
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from heurams.kernel.particles.nucleon import Nucleon
|
from heurams.kernel.particles.nucleon import Nucleon
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from heurams.kernel.puzzles.cloze import ClozePuzzle
|
from heurams.kernel.puzzles.cloze import ClozePuzzle
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch, MagicMock, call
|
from unittest.mock import MagicMock, call, patch
|
||||||
|
|
||||||
from heurams.kernel.puzzles.mcq import MCQPuzzle
|
from heurams.kernel.puzzles.mcq import MCQPuzzle
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ class TestMCQPuzzle(unittest.TestCase):
|
|||||||
# 模拟 random.sample 返回前两个映射项
|
# 模拟 random.sample 返回前两个映射项
|
||||||
mock_sample.side_effect = [
|
mock_sample.side_effect = [
|
||||||
[("q1", "a1"), ("q2", "a2")], # 选择问题
|
[("q1", "a1"), ("q2", "a2")], # 选择问题
|
||||||
["j1", "j2", "j3"], # 为每个问题选择干扰项(实际调用两次)
|
["j1", "j2", "j3"], # 为每个问题选择干扰项(实际调用两次)
|
||||||
]
|
]
|
||||||
puzzle.refresh()
|
puzzle.refresh()
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ class TestMCQPuzzle(unittest.TestCase):
|
|||||||
self.assertEqual(puzzle.answer, ["a1", "a2"])
|
self.assertEqual(puzzle.answer, ["a1", "a2"])
|
||||||
# 检查 options 列表
|
# 检查 options 列表
|
||||||
self.assertEqual(len(puzzle.options), 2)
|
self.assertEqual(len(puzzle.options), 2)
|
||||||
# 每个选项列表应包含 4 个选项(正确答案 + 3 个干扰项)
|
# 每个选项列表应包含 4 个选项(正确答案 + 3 个干扰项)
|
||||||
self.assertEqual(len(puzzle.options[0]), 4)
|
self.assertEqual(len(puzzle.options[0]), 4)
|
||||||
self.assertEqual(len(puzzle.options[1]), 4)
|
self.assertEqual(len(puzzle.options[1]), 4)
|
||||||
# random.shuffle 应被调用
|
# random.shuffle 应被调用
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import Mock, patch, MagicMock
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
from heurams.kernel.reactor.phaser import Phaser
|
|
||||||
from heurams.kernel.reactor.states import PhaserState, ProcessionState
|
|
||||||
from heurams.kernel.particles.atom import Atom
|
from heurams.kernel.particles.atom import Atom
|
||||||
from heurams.kernel.particles.electron import Electron
|
from heurams.kernel.particles.electron import Electron
|
||||||
|
from heurams.kernel.reactor.phaser import Phaser
|
||||||
|
from heurams.kernel.reactor.states import PhaserState, ProcessionState
|
||||||
|
|
||||||
|
|
||||||
class TestPhaser(unittest.TestCase):
|
class TestPhaser(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user