feat: 改进状态机
This commit is contained in:
17
README.md
17
README.md
@@ -4,15 +4,16 @@
|
||||
"潜进" (HeurAMS: Heuristic Auxiliary Memorizing Scheduler, 启发式记忆辅助调度器) 是为习题册, 古诗词, 及其他问答/记忆/理解型知识设计的多用途辅助记忆软件, 提供动态规划的优化记忆方案
|
||||
|
||||
## 关于此仓库
|
||||
"潜进" 软件组项目包含多个子项目
|
||||
此仓库包含了 "潜进" 项目的核心和基于 Textual 的基本用户界面的实现
|
||||
本仓库为 "潜进" 软件组项目的核心部分, 包含核心功能模块以及基于 Textual 框架的基础用户界面(heurams.interface)实现
|
||||
除了通过用户界面进行学习外, 你也可以在 Python 中导入 `heurams` 库, 使用其中实现的状态机, 算法迭代器和数据模型构建辅助记忆功能
|
||||
|
||||
## 开发进程
|
||||
## 版本日志
|
||||
- 0.0.x: 简易调度器实现与最小原型.
|
||||
- 0.1.x: 命令行操作的调度器.
|
||||
- 0.2.x: 使用 Textual 构建富文本终端用户界面, 项目可行性验证, 采用 SM-2 原始算法, 评估方式为用户自评估的原型.
|
||||
- 0.3.x: 简单的多文件项目, 创建了记忆内容/算法数据结构, 基于 SM-2 改进算法的自动复习测评评估. 重点设计古诗文记忆理解功能, 以及 TUI 界面实现, 简单的 TTS 集成.
|
||||
- 0.4.x: 使用模块管理解耦设计, 增加文档与类型标注, 采用上下文设计模式的隐式依赖注入与遵从 IoC, 注册器设计的算法与功能实现, 支持其他调度算法模块 (SM-2, FSRS) 与谜题模块, 采用日志调试, 更新文件格式, 引入动态数据模式(宏驱动的动态内容生成), 与基于文件的策略调控, 更佳的用户数据处理, 加入模块化扩展集成, 将算法数据格式换为 json 提高性能, 采用 provider-service 抽象架构, 支持切换服务提供者, 整体兼容性改进.
|
||||
- 0.2.x: 使用 Textual 构建富文本终端用户界面; 项目可行性验证; 采用 SM-2 原始算法, 评估方式为用户自评估原型.
|
||||
- 0.3.x: 简单的多文件项目; 创建了记忆内容/算法数据结构; 基于 SM-2 改进算法的自动复习测评评估; 重点设计古诗文记忆理解功能; TUI 界面改进; 简单的 TTS 集成.
|
||||
- 0.4.x: 使用模块管理解耦设计; 增加文档与类型标注; 采用上下文设计模式的隐式依赖注入与遵从 IoC, 注册器设计的算法与功能实现; 支持其他调度算法模块 (SM-2, FSRS) 与谜题模块; 采用日志调试; 更新文件格式; 引入动态数据模式(宏驱动的动态内容生成), 与基于文件的策略调控; 更佳的用户数据处理; 加入模块化扩展集成; 将算法数据格式换为 json 提高性能; 采用 provider-service 抽象架构, 支持切换服务提供者; 整体兼容性改进.
|
||||
- 0.5.x: 以仓库(repo)对象作为文件系统与运行时对象间的桥梁, 提高解耦性与性能; 使用具有列表 - 字典 API 同步特性的 "Lict" 对象作为 Repo 数据的内部存储; 将粒子对象作为纯运行时对象, 数据通过引用自动同步至 Repo, 减少负担; 实现声音形式回顾 "电台" 功能; 改进数据存储结构, 实现选择性持久化; 增强可配置性; 使用 Transitions 状态机库重新实现 reactor 状态机, 增强可维护性; 实现整体回顾记忆功能, 与队列式记忆功能并列.
|
||||
> 下一步?
|
||||
> 使用 Flutter 构建酷酷的现代化前端, 增加云同步/文档源服务...
|
||||
|
||||
@@ -103,7 +104,7 @@ verify_ssl = true # SSL 证书验证
|
||||
|
||||
## 项目结构
|
||||
|
||||
### 架构图
|
||||
### 架构图(待更新 0.5.0)
|
||||
|
||||
以下 Mermaid 图展示了 HeurAMS 的主要组件及其关系:
|
||||
|
||||
@@ -164,7 +165,7 @@ graph TB
|
||||
Algorithms --> Files
|
||||
```
|
||||
|
||||
### 目录结构
|
||||
### 目录结构(待更新 0.5.0)
|
||||
```
|
||||
src/heurams/
|
||||
├── __init__.py # 包入口点
|
||||
|
||||
@@ -12,7 +12,7 @@ tts_text = "文本转语音文本"
|
||||
delimiter = "/"
|
||||
tts_text = "eval:payload['content'].replace('/', '')"
|
||||
|
||||
["puzzles"] # 谜题定义
|
||||
["common.puzzles"] # 谜题定义, 也可以单独定义到 payload, common 不会对 payload 已有内容进行覆盖, 参见 nucleon.py 第16行
|
||||
# 我们称 "Recognition" 为 recognition 谜题的 alia
|
||||
"Recognition" = { __origin__ = "recognition", __hint__ = "", primary = "eval:payload['content']", secondary = ["eval:payload['keyword_note']", "eval:payload['note']"], top_dim = ["eval:payload['translation']"] }
|
||||
"SelectMeaning" = { __origin__ = "mcq", __hint__ = "eval:payload['content']", primary = "eval:payload['content']", mapping = "eval:payload['keyword_note']", jammer = "eval:list(payload['keyword_note'].values())", max_riddles_num = "eval:default['mcq']['max_riddles_num']", prefix = "选择正确项: " }
|
||||
|
||||
@@ -103,7 +103,9 @@
|
||||
"source": [
|
||||
"import heurams.kernel.repolib as repolib # 这是 RepoLib 子模块, 用于管理和结构化 repo(中文含义: 仓库) 数据结构与本地文件间的联系\n",
|
||||
"import heurams.kernel.particles as pt # 这是 Particles(中文含义: 粒子) 子模块, 用于运行时的记忆管理操作\n",
|
||||
"from pathlib import Path # 这是 Python 的 Pathlib 模块, 用于表示文件路径, 在整个项目中, 都使用此模块表示路径"
|
||||
"from pathlib import (\n",
|
||||
" Path,\n",
|
||||
") # 这是 Python 的 Pathlib 模块, 用于表示文件路径, 在整个项目中, 都使用此模块表示路径"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -231,6 +233,7 @@
|
||||
"source": [
|
||||
"test_repo_dic = test_repo.export_to_single_dict()\n",
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"pprint(test_repo_dic)"
|
||||
]
|
||||
},
|
||||
@@ -287,7 +290,10 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"test_repo.persist_to_repodir(save_list=[\"schedule\", \"payload\", \"manifest\", \"typedef\", \"algodata\"], source=Path(\"test_new_repo\"))\n",
|
||||
"test_repo.persist_to_repodir(\n",
|
||||
" save_list=[\"schedule\", \"payload\", \"manifest\", \"typedef\", \"algodata\"],\n",
|
||||
" source=Path(\"test_new_repo\"),\n",
|
||||
")\n",
|
||||
"!tree"
|
||||
]
|
||||
},
|
||||
@@ -336,11 +342,12 @@
|
||||
],
|
||||
"source": [
|
||||
"from heurams.utils.lict import Lict\n",
|
||||
"\n",
|
||||
"lct = Lict() # 空的\n",
|
||||
"lct = Lict(initlist=[(\"name\", \"tom\"), (\"age\", 12), (\"enemy\", \"jerry\")]) # 基于列表\n",
|
||||
"print(lct)\n",
|
||||
"lct = Lict(initdict={\"name\": \"tom\", \"age\": 12, \"enemy\": \"jerry\"}) # 基于字典\n",
|
||||
"print(lct)\n"
|
||||
"print(lct)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -412,7 +419,7 @@
|
||||
"print(lct)\n",
|
||||
"\n",
|
||||
"# 推荐方式\n",
|
||||
"lct['is_human'] = False\n",
|
||||
"lct[\"is_human\"] = False\n",
|
||||
"print(lct)"
|
||||
]
|
||||
},
|
||||
@@ -446,7 +453,7 @@
|
||||
"# 由于 jupyter 的环境处理, 请不要重复运行此单元格, 如果想再看一遍, 请重启 jupyter 后再全部运行\n",
|
||||
"\n",
|
||||
"# 唯一推荐方式\n",
|
||||
"lct.append(('enemy_2', 'spike'))\n",
|
||||
"lct.append((\"enemy_2\", \"spike\"))\n",
|
||||
"print(lct.dicted_data)"
|
||||
]
|
||||
},
|
||||
@@ -507,7 +514,16 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"lct = Lict(initdict={'age': 12, 'enemy': 'jerry', 'is_human': False, 'name': 'tom', 'type': 'cat', 'enemy_2': 'spike'})\n",
|
||||
"lct = Lict(\n",
|
||||
" initdict={\n",
|
||||
" \"age\": 12,\n",
|
||||
" \"enemy\": \"jerry\",\n",
|
||||
" \"is_human\": False,\n",
|
||||
" \"name\": \"tom\",\n",
|
||||
" \"type\": \"cat\",\n",
|
||||
" \"enemy_2\": \"spike\",\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"print(lct)\n",
|
||||
"print(lct.dicted_data)\n",
|
||||
"print(\"------\")\n",
|
||||
@@ -517,7 +533,16 @@
|
||||
"while len(lct) > 0:\n",
|
||||
" print(lct.pop())\n",
|
||||
" print(lct)\n",
|
||||
"lct = Lict(initdict={'age': 12, 'enemy': 'jerry', 'is_human': False, 'name': 'tom', 'type': 'cat', 'enemy_2': 'spike'})\n",
|
||||
"lct = Lict(\n",
|
||||
" initdict={\n",
|
||||
" \"age\": 12,\n",
|
||||
" \"enemy\": \"jerry\",\n",
|
||||
" \"is_human\": False,\n",
|
||||
" \"name\": \"tom\",\n",
|
||||
" \"type\": \"cat\",\n",
|
||||
" \"enemy_2\": \"spike\",\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"..."
|
||||
]
|
||||
},
|
||||
@@ -654,10 +679,14 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"repo = repolib.Repo.create_from_repodir(Path('./test_repo'))\n",
|
||||
"repo = repolib.Repo.create_from_repodir(Path(\"./test_repo\"))\n",
|
||||
"for i in repo.ident_index:\n",
|
||||
" n = pt.Nucleon.create_on_nucleonic_data(nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i))\n",
|
||||
" e = pt.Electron.create_on_electonic_data(electronic_data=repo.electronic_data_lict.get_itemic_unit(i))\n",
|
||||
" n = pt.Nucleon.create_on_nucleonic_data(\n",
|
||||
" nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i)\n",
|
||||
" )\n",
|
||||
" e = pt.Electron.create_on_electonic_data(\n",
|
||||
" electronic_data=repo.electronic_data_lict.get_itemic_unit(i)\n",
|
||||
" )\n",
|
||||
" e.activate()\n",
|
||||
" e.revisor(5, True)\n",
|
||||
" print(repr(n))\n",
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import heurams.kernel.repolib as repolib
|
||||
import heurams.kernel.particles as pt
|
||||
from pathlib import Path
|
||||
repo = repolib.Repo.create_from_repodir(Path('./test_repo'))
|
||||
|
||||
repo = repolib.Repo.create_from_repodir(Path("./test_repo"))
|
||||
for i in repo.ident_index:
|
||||
n = pt.Nucleon.create_on_nucleonic_data(nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i))
|
||||
e = pt.Electron.create_on_electonic_data(electronic_data=repo.electronic_data_lict.get_itemic_unit(i))
|
||||
n = pt.Nucleon.create_on_nucleonic_data(
|
||||
nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
e = pt.Electron.create_on_electonic_data(
|
||||
electronic_data=repo.electronic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
a = pt.Atom(n, e, repo.orbitic_data)
|
||||
e.activate()
|
||||
e.revisor(5, True)
|
||||
|
||||
@@ -26,23 +26,30 @@ if pathlib.Path(workdir / "data" / "config" / "config_dev.toml").exists():
|
||||
print("使用开发设置")
|
||||
logger.debug("使用开发设置")
|
||||
config_var: ContextVar[ConfigFile] = ContextVar(
|
||||
"config_var", default=ConfigFile(workdir / "data" / "config" / "config_dev.toml")
|
||||
"config_var",
|
||||
default=ConfigFile(workdir / "data" / "config" / "config_dev.toml"),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
config_var: ContextVar[ConfigFile] = ContextVar(
|
||||
"config_var", default=ConfigFile(workdir / "data" / "config" / "config.toml")
|
||||
"config_var",
|
||||
default=ConfigFile(workdir / "data" / "config" / "config.toml"),
|
||||
) # 配置文件
|
||||
except Exception as e:
|
||||
input("按下回车以创建新的配置文件, 或按下 Ctrl + C 以终止程序 ")
|
||||
(workdir / "data" / 'config').mkdir(parents=True, exist_ok=True)
|
||||
(workdir / "data" / 'config' / 'config').unlink(missing_ok=True)
|
||||
shutil.copy((rootdir / 'default' / 'config' / 'config.toml'), workdir / "data" / "config" / "config.toml")
|
||||
(workdir / "data" / "config").mkdir(parents=True, exist_ok=True)
|
||||
(workdir / "data" / "config" / "config").unlink(missing_ok=True)
|
||||
shutil.copy(
|
||||
(rootdir / "default" / "config" / "config.toml"),
|
||||
workdir / "data" / "config" / "config.toml",
|
||||
)
|
||||
finally:
|
||||
config_var: ContextVar[ConfigFile] = ContextVar(
|
||||
"config_var", default=ConfigFile(workdir / "data" / "config" / "config.toml")
|
||||
"config_var",
|
||||
default=ConfigFile(workdir / "data" / "config" / "config.toml"),
|
||||
) # 配置文件
|
||||
|
||||
|
||||
class ConfigContext:
|
||||
"""
|
||||
功能完备的上下文管理器
|
||||
|
||||
@@ -12,6 +12,7 @@ from .screens.synctool import SyncScreen
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def environment_check():
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import pathlib
|
||||
from textual.app import ComposeResult
|
||||
from textual.containers import ScrollableContainer
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import (Button, Footer, Header, Label, ListItem, ListView,
|
||||
Static)
|
||||
from textual.widgets import Button, Footer, Header, Label, ListItem, ListView, Static
|
||||
|
||||
import heurams.services.timer as timer
|
||||
import heurams.services.version as version
|
||||
@@ -48,14 +47,14 @@ class DashboardScreen(Screen):
|
||||
Label(f"使用算法: {config_var.get()['algorithm']['default']}"),
|
||||
Label("选择待学习或待修改的仓库:", classes="title-label"),
|
||||
ListView(id="repo-list", classes="repo-list-view"),
|
||||
Label(
|
||||
f'"潜进" 启发式辅助记忆调度器 | 版本 {version.ver} '
|
||||
),
|
||||
Label(f'"潜进" 启发式辅助记忆调度器 | 版本 {version.ver} '),
|
||||
)
|
||||
yield Footer()
|
||||
|
||||
def _load_data(self):
|
||||
self.repo_dirs = Repo.probe_vaild_repos_in_dir(Path(config_var.get()['paths']['data']) / 'repo')
|
||||
self.repo_dirs = Repo.probe_vaild_repos_in_dir(
|
||||
Path(config_var.get()["paths"]["data"]) / "repo"
|
||||
)
|
||||
for repo_dir in self.repo_dirs:
|
||||
repo = Repo.create_from_repodir(repo_dir)
|
||||
self._analyse_repo(repo)
|
||||
@@ -66,11 +65,15 @@ class DashboardScreen(Screen):
|
||||
is_due = 0
|
||||
unit_sum = len(repo)
|
||||
activated_sum = 0
|
||||
nextdate = 0x3f3f3f3f
|
||||
is_unfinished = (unit_sum > activated_sum)
|
||||
nextdate = 0x3F3F3F3F
|
||||
is_unfinished = unit_sum > activated_sum
|
||||
for i in repo.ident_index:
|
||||
nucleon = pt.Nucleon.create_on_nucleonic_data(nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i))
|
||||
electron = pt.Electron.create_on_electonic_data(electronic_data=repo.electronic_data_lict.get_itemic_unit(i))
|
||||
nucleon = pt.Nucleon.create_on_nucleonic_data(
|
||||
nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
electron = pt.Electron.create_on_electonic_data(
|
||||
electronic_data=repo.electronic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
if electron.is_activated():
|
||||
activated_sum += 1
|
||||
if electron.is_due():
|
||||
@@ -102,10 +105,10 @@ class DashboardScreen(Screen):
|
||||
# 按下次复习时间排序
|
||||
repodirs = sorted(
|
||||
self.repo_dirs,
|
||||
key=lambda f: self.repostat[f.name]['nextdate'],
|
||||
key=lambda f: self.repostat[f.name]["nextdate"],
|
||||
reverse=True,
|
||||
)
|
||||
repotitles = map(lambda f: self.repostat[f.name]['title'], repodirs)
|
||||
repotitles = map(lambda f: self.repostat[f.name]["title"], repodirs)
|
||||
# 填充列表
|
||||
if not repodirs:
|
||||
repo_list_widget.append(
|
||||
@@ -120,7 +123,7 @@ class DashboardScreen(Screen):
|
||||
return
|
||||
|
||||
for repotitle in repotitles:
|
||||
prompt = self.repostat[self.title2dirname[repotitle]]['prompt']
|
||||
prompt = self.repostat[self.title2dirname[repotitle]]["prompt"]
|
||||
list_item = ListItem(Label(prompt))
|
||||
repo_list_widget.append(list_item)
|
||||
|
||||
@@ -142,9 +145,12 @@ class DashboardScreen(Screen):
|
||||
selected_repotitle = label_text.partition("\0")[0].replace("*", "")
|
||||
selected_repo = self.title2repo[label_text.partition("\0")[0].replace("*", "")]
|
||||
|
||||
|
||||
# 跳转到准备屏幕
|
||||
self.app.push_screen(PreparationScreen(selected_repo, self.repostat[self.title2dirname[selected_repotitle]]))
|
||||
self.app.push_screen(
|
||||
PreparationScreen(
|
||||
selected_repo, self.repostat[self.title2dirname[selected_repotitle]]
|
||||
)
|
||||
)
|
||||
|
||||
def action_quit_app(self) -> None:
|
||||
"""退出应用程序"""
|
||||
|
||||
@@ -88,7 +88,7 @@ class PrecachingScreen(Screen):
|
||||
"""预缓存单段文本的音频"""
|
||||
from heurams.context import config_var, rootdir, workdir
|
||||
|
||||
cache_dir = pathlib.Path(config_var.get()["paths"]["data"]) / 'cache'
|
||||
cache_dir = pathlib.Path(config_var.get()["paths"]["data"]) / "cache"
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
cache_file = cache_dir / f"{hasher.get_md5(text)}.wav"
|
||||
if not cache_file.exists():
|
||||
@@ -149,7 +149,7 @@ class PrecachingScreen(Screen):
|
||||
from heurams.context import config_var, rootdir, workdir
|
||||
from heurams.kernel.repolib import Repo
|
||||
|
||||
repo_path = pathlib.Path(config_var.get()["paths"]["data"]) / 'repo'
|
||||
repo_path = pathlib.Path(config_var.get()["paths"]["data"]) / "repo"
|
||||
repo_dirs = Repo.probe_vaild_repos_in_dir(repo_path)
|
||||
repos = map(Repo.create_from_repodir, repo_dirs)
|
||||
|
||||
@@ -159,7 +159,11 @@ class PrecachingScreen(Screen):
|
||||
for repo in repos:
|
||||
try:
|
||||
for i in repo.ident_index:
|
||||
nucleon_list.append(pt.Nucleon.create_on_nucleonic_data(repo.nucleonic_data_lict.get_itemic_unit(i)))
|
||||
nucleon_list.append(
|
||||
pt.Nucleon.create_on_nucleonic_data(
|
||||
repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
)
|
||||
except:
|
||||
continue
|
||||
self.total = len(nucleon_list)
|
||||
|
||||
@@ -73,7 +73,9 @@ class PreparationScreen(Screen):
|
||||
def _get_full_content(self):
|
||||
content = ""
|
||||
for i in self.repo.ident_index:
|
||||
n = pt.Nucleon.create_on_nucleonic_data(nucleonic_data=self.repo.nucleonic_data_lict.get_itemic_unit(i))
|
||||
n = pt.Nucleon.create_on_nucleonic_data(
|
||||
nucleonic_data=self.repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
content += f"- {n['content']} \n"
|
||||
return content
|
||||
|
||||
@@ -85,8 +87,14 @@ class PreparationScreen(Screen):
|
||||
|
||||
lst = list()
|
||||
for i in self.repo.ident_index:
|
||||
lst.append(pt.Nucleon.create_on_nucleonic_data(self.repo.nucleonic_data_lict.get_itemic_unit(i)))
|
||||
precache_screen = PrecachingScreen(nucleons=lst, desc=self.repo.manifest["title"])
|
||||
lst.append(
|
||||
pt.Nucleon.create_on_nucleonic_data(
|
||||
self.repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
)
|
||||
precache_screen = PrecachingScreen(
|
||||
nucleons=lst, desc=self.repo.manifest["title"]
|
||||
)
|
||||
self.app.push_screen(precache_screen)
|
||||
|
||||
def action_quit_app(self):
|
||||
@@ -98,8 +106,12 @@ class PreparationScreen(Screen):
|
||||
if event.button.id == "start_memorizing_button":
|
||||
atoms = list()
|
||||
for i in self.repo.ident_index:
|
||||
n = pt.Nucleon.create_on_nucleonic_data(nucleonic_data=self.repo.nucleonic_data_lict.get_itemic_unit(i))
|
||||
e = pt.Electron.create_on_electonic_data(electronic_data=self.repo.electronic_data_lict.get_itemic_unit(i))
|
||||
n = pt.Nucleon.create_on_nucleonic_data(
|
||||
nucleonic_data=self.repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
e = pt.Electron.create_on_electonic_data(
|
||||
electronic_data=self.repo.electronic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
a = pt.Atom(n, e, self.repo.orbitic_data)
|
||||
atoms.append(a)
|
||||
|
||||
@@ -107,7 +119,7 @@ class PreparationScreen(Screen):
|
||||
left_new = self.scheduled_num
|
||||
for i in atoms:
|
||||
i: pt.Atom
|
||||
if i.registry['electron'].is_activated():
|
||||
if i.registry["electron"].is_activated():
|
||||
if i.registry["electron"].is_due():
|
||||
atoms_to_provide.append(i)
|
||||
else:
|
||||
|
||||
@@ -6,8 +6,7 @@ import toml
|
||||
from textual.app import ComposeResult
|
||||
from textual.containers import ScrollableContainer
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import (Button, Footer, Header, Input, Label, Markdown,
|
||||
Select)
|
||||
from textual.widgets import Button, Footer, Header, Input, Label, Markdown, Select
|
||||
|
||||
from heurams.context import config_var
|
||||
from heurams.services.version import ver
|
||||
|
||||
@@ -14,13 +14,22 @@ import pathlib
|
||||
from typing import TypedDict
|
||||
|
||||
from heurams.context import config_var
|
||||
from heurams.kernel.algorithms.sm15m_calc import (MAX_AF, MIN_AF, NOTCH_AF,
|
||||
RANGE_AF, RANGE_REPETITION,
|
||||
SM, THRESHOLD_RECALL, Item)
|
||||
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(
|
||||
pathlib.Path(config_var.get()["paths"]["data"]) / 'global' / "sm15m_global_state.json"
|
||||
pathlib.Path(config_var.get()["paths"]["data"])
|
||||
/ "global"
|
||||
/ "sm15m_global_state.json"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -99,5 +99,6 @@ class Atom:
|
||||
|
||||
def __repr__(self):
|
||||
from pprint import pformat
|
||||
|
||||
s = pformat(self.registry, indent=4)
|
||||
return s
|
||||
|
||||
@@ -31,6 +31,7 @@ class Electron:
|
||||
|
||||
def __repr__(self):
|
||||
from pprint import pformat
|
||||
|
||||
s = pformat(self.algodata, indent=4)
|
||||
return s
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ class Nucleon:
|
||||
|
||||
def __repr__(self):
|
||||
from pprint import pformat
|
||||
|
||||
s = pformat(self.data, indent=4)
|
||||
return s
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
from heurams.utils.evalizor import Evalizer
|
||||
from heurams.utils.lict import Lict
|
||||
"""轨道对象"""
|
||||
|
||||
# 似乎没有实现这个类的必要...
|
||||
# 那不妨在这儿写点文档
|
||||
|
||||
class Orbital:
|
||||
@classmethod
|
||||
def create_orbital(cls, schedule: list, phase_def: dict):
|
||||
phase_def = Lict(initdict=phase_def) # type: ignore
|
||||
orbital = Lict()
|
||||
for i in schedule:
|
||||
orbital[i] = Lict(phase_def[i])
|
||||
return orbital
|
||||
"""
|
||||
orbital, 即轨道, 是定义队列式复习阶段流程的数据结构, 其实就是个字典, 至于为何不用typeddict, 因为懒.
|
||||
|
||||
@classmethod
|
||||
def create_orbital_on_orbitic_data(cls, orbitic_data):
|
||||
return cls.create_orbital(orbitic_data["schedule"], orbitic_data["phases"])
|
||||
orbital_example = {
|
||||
"schedule": [列表 存储阶段(phases)名称]
|
||||
"phases":{
|
||||
阶段名称 = [["谜题(puzzle 现称 evaluator 评估器)名称", "概率系数 可大于1(整数部分为重复次数) 注意使用字符串包裹(toml 规范)"], ...],
|
||||
...
|
||||
}
|
||||
}
|
||||
至于谜题定义 放在 nucleon['puzzles'], 这样设计是为了兼容多种不同谜题实现的记忆单元, 尽管如此, 你也可见其谜题调度方式必须是相同的.
|
||||
"""
|
||||
|
||||
@@ -8,21 +8,23 @@ from .states import PhaserState
|
||||
|
||||
|
||||
class Fission:
|
||||
"""裂变器: 单原子调度展开器"""
|
||||
"""单原子调度展开器"""
|
||||
|
||||
def __init__(self, atom: pt.Atom, phase_state=PhaserState.RECOGNITION):
|
||||
self.logger = get_logger(__name__)
|
||||
self.atom = atom
|
||||
|
||||
# NOTE: phase 为 PhaserState 枚举实例,需要获取其value
|
||||
phase_value = phase_state.value if isinstance(phase_state, PhaserState) else phase_state
|
||||
phase_value = (
|
||||
phase_state.value if isinstance(phase_state, PhaserState) else phase_state
|
||||
)
|
||||
|
||||
self.orbital_schedule = atom.registry["orbital"]["schedule"][phase_value] # type: ignore
|
||||
self.orbital_puzzles = atom.registry["orbital"]["puzzles"]
|
||||
self.orbital_schedule = atom.registry["phases"][phase_value] # type: ignore
|
||||
self.orbital_puzzles = atom.registry["nucleon"]["puzzles"]
|
||||
|
||||
self.puzzles = list()
|
||||
for item, possibility in self.orbital_schedule: # type: ignore
|
||||
self.logger.debug(f"开始处理 orbital 项: {item}")
|
||||
self.logger.debug(f"开始处理: {item}")
|
||||
if not isinstance(possibility, float):
|
||||
possibility = float(possibility)
|
||||
|
||||
@@ -45,5 +47,5 @@ class Fission:
|
||||
|
||||
self.logger.debug(f"orbital 项处理完成: {item}")
|
||||
|
||||
def generate(self):
|
||||
def get_puzzles_list(self):
|
||||
yield from self.puzzles
|
||||
@@ -45,23 +45,43 @@ class Phaser(Machine):
|
||||
|
||||
# 设置transitions状态机
|
||||
states = [
|
||||
{'name': PhaserState.UNSURE.value, 'on_enter': 'on_unsure'},
|
||||
{'name': PhaserState.QUICK_REVIEW.value, 'on_enter': 'on_quick_review'},
|
||||
{'name': PhaserState.RECOGNITION.value, 'on_enter': 'on_recognition'},
|
||||
{'name': PhaserState.FINAL_REVIEW.value, 'on_enter': 'on_final_review'},
|
||||
{'name': PhaserState.FINISHED.value, 'on_enter': 'on_finished'}
|
||||
{"name": PhaserState.UNSURE.value, "on_enter": "on_unsure"},
|
||||
{"name": PhaserState.QUICK_REVIEW.value, "on_enter": "on_quick_review"},
|
||||
{"name": PhaserState.RECOGNITION.value, "on_enter": "on_recognition"},
|
||||
{"name": PhaserState.FINAL_REVIEW.value, "on_enter": "on_final_review"},
|
||||
{"name": PhaserState.FINISHED.value, "on_enter": "on_finished"},
|
||||
]
|
||||
|
||||
transitions = [
|
||||
{'trigger': 'to_unsure', 'source': '*', 'dest': PhaserState.UNSURE.value},
|
||||
{'trigger': 'to_quick_review', 'source': '*', 'dest': PhaserState.QUICK_REVIEW.value},
|
||||
{'trigger': 'to_recognition', 'source': '*', 'dest': PhaserState.RECOGNITION.value},
|
||||
{'trigger': 'to_final_review', 'source': '*', 'dest': PhaserState.FINAL_REVIEW.value},
|
||||
{'trigger': 'to_finished', 'source': '*', 'dest': PhaserState.FINISHED.value}
|
||||
{"trigger": "to_unsure", "source": "*", "dest": PhaserState.UNSURE.value},
|
||||
{
|
||||
"trigger": "to_quick_review",
|
||||
"source": "*",
|
||||
"dest": PhaserState.QUICK_REVIEW.value,
|
||||
},
|
||||
{
|
||||
"trigger": "to_recognition",
|
||||
"source": "*",
|
||||
"dest": PhaserState.RECOGNITION.value,
|
||||
},
|
||||
{
|
||||
"trigger": "to_final_review",
|
||||
"source": "*",
|
||||
"dest": PhaserState.FINAL_REVIEW.value,
|
||||
},
|
||||
{
|
||||
"trigger": "to_finished",
|
||||
"source": "*",
|
||||
"dest": PhaserState.FINISHED.value,
|
||||
},
|
||||
]
|
||||
|
||||
Machine.__init__(self, states=states, transitions=transitions,
|
||||
initial=PhaserState.UNSURE.value)
|
||||
Machine.__init__(
|
||||
self,
|
||||
states=states,
|
||||
transitions=transitions,
|
||||
initial=PhaserState.UNSURE.value,
|
||||
)
|
||||
|
||||
self.to_unsure()
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ class Procession(Machine):
|
||||
name,
|
||||
)
|
||||
|
||||
# 初始化原子队列
|
||||
self.atoms = atoms
|
||||
self.queue = atoms.copy()
|
||||
self.current_atom = atoms[0] if atoms else None
|
||||
@@ -26,17 +25,30 @@ class Procession(Machine):
|
||||
self.name = name
|
||||
self.phase = phase_state
|
||||
|
||||
# 设置transitions状态机
|
||||
states = [{'name': ProcessionState.RUNNING.value, 'on_enter': 'on_running'},
|
||||
{'name': ProcessionState.FINISHED.value, 'on_enter': 'on_finished'}]
|
||||
|
||||
transitions = [
|
||||
{'trigger': 'finish', 'source': ProcessionState.RUNNING.value, 'dest': ProcessionState.FINISHED.value},
|
||||
{'trigger': 'restart', 'source': ProcessionState.FINISHED.value, 'dest': ProcessionState.RUNNING.value}
|
||||
states = [
|
||||
{"name": ProcessionState.RUNNING.value, "on_enter": "on_running"},
|
||||
{"name": ProcessionState.FINISHED.value, "on_enter": "on_finished"},
|
||||
]
|
||||
|
||||
Machine.__init__(self, states=states, transitions=transitions,
|
||||
initial=ProcessionState.RUNNING.value)
|
||||
transitions = [
|
||||
{
|
||||
"trigger": "finish",
|
||||
"source": ProcessionState.RUNNING.value,
|
||||
"dest": ProcessionState.FINISHED.value,
|
||||
},
|
||||
{
|
||||
"trigger": "restart",
|
||||
"source": ProcessionState.FINISHED.value,
|
||||
"dest": ProcessionState.RUNNING.value,
|
||||
},
|
||||
]
|
||||
|
||||
Machine.__init__(
|
||||
self,
|
||||
states=states,
|
||||
transitions=transitions,
|
||||
initial=ProcessionState.RUNNING.value,
|
||||
)
|
||||
|
||||
logger.debug("Procession 初始化完成, 队列长度=%d", len(self.queue))
|
||||
|
||||
@@ -61,7 +73,10 @@ class Procession(Machine):
|
||||
self.restart() # 确保在RUNNING状态
|
||||
self.current_atom = self.queue[self.cursor]
|
||||
logger.debug("cursor 更新为: %d", self.cursor)
|
||||
logger.debug("当前原子更新为: %s", self.current_atom.ident if self.current_atom else "None")
|
||||
logger.debug(
|
||||
"当前原子更新为: %s",
|
||||
self.current_atom.ident if self.current_atom else "None",
|
||||
)
|
||||
return 1 # 成功
|
||||
|
||||
return 0
|
||||
|
||||
@@ -9,11 +9,13 @@ import heurams.kernel.particles as pt
|
||||
|
||||
from ...utils.lict import Lict
|
||||
|
||||
|
||||
class RepoManifest(TypedDict):
|
||||
title: str
|
||||
author: str
|
||||
desc: str
|
||||
|
||||
|
||||
class Repo:
|
||||
file_mapping = {
|
||||
"schedule": "schedule.toml",
|
||||
@@ -61,9 +63,7 @@ class Repo:
|
||||
def generate_particles_data(self):
|
||||
|
||||
self.nucleonic_data_lict = Lict(
|
||||
initlist=list(map(
|
||||
self._nucleonic_proc,
|
||||
self.payload))
|
||||
initlist=list(map(self._nucleonic_proc, self.payload))
|
||||
)
|
||||
self.orbitic_data = self.schedule
|
||||
self.ident_index = self.nucleonic_data_lict.keys()
|
||||
@@ -88,6 +88,7 @@ class Repo:
|
||||
|
||||
def __repr__(self):
|
||||
from pprint import pformat
|
||||
|
||||
s = pformat(self.database, indent=4)
|
||||
return s
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class Evalizer():
|
||||
class Evalizer:
|
||||
"""几乎无副作用的模板系统
|
||||
|
||||
接受环境信息并创建一个模板解析工具, 工具传入参数支持list, dict及其嵌套
|
||||
|
||||
@@ -10,8 +10,12 @@ from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
from heurams.context import ConfigContext
|
||||
from heurams.services.config import ConfigFile
|
||||
from heurams.services.sync_service import (ConflictStrategy, SyncConfig,
|
||||
SyncMode, SyncService)
|
||||
from heurams.services.sync_service import (
|
||||
ConflictStrategy,
|
||||
SyncConfig,
|
||||
SyncMode,
|
||||
SyncService,
|
||||
)
|
||||
|
||||
|
||||
class TestSyncServiceUnit(unittest.TestCase):
|
||||
@@ -202,8 +206,7 @@ class TestSyncServiceUnit(unittest.TestCase):
|
||||
mock_config.data = config_data
|
||||
mock_config_var.get.return_value = mock_config
|
||||
|
||||
from heurams.services.sync_service import \
|
||||
create_sync_service_from_config
|
||||
from heurams.services.sync_service import create_sync_service_from_config
|
||||
|
||||
service = create_sync_service_from_config()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user