使用 NucleonUnion & ElectronUnion 取代繁琐的 AtomicFiles

This commit is contained in:
2025-07-30 01:11:03 +08:00
parent b91176241b
commit 36562323b7
9 changed files with 166 additions and 354 deletions

View File

@@ -34,7 +34,8 @@
![scrshot1](readme_src/image_1.png) ![scrshot1](readme_src/image_1.png)
## 技术架构 ## 技术架构
> 有关技术与实现的细节, 请参阅 CONTRIBUTING.md
> 提交拉取请求以参与到此开放源代码项目
``` mermaid ``` mermaid
graph TD graph TD
subgraph 后端 subgraph 后端

View File

@@ -27,7 +27,7 @@ def get_daystamp() -> int:
time_override = config.get("time_override", -1) time_override = config.get("time_override", -1)
if time_override is not None and time_override != -1: if time_override is not None and time_override != -1:
print(f"TIME OVERRIDEED TO {time_override}") #print(f"TIME OVERRIDEED TO {time_override}")
return int(time_override) return int(time_override)
return int(time.time() // (24 * 3600)) return int(time.time() // (24 * 3600))

View File

@@ -1,7 +1,7 @@
# [调试] 将更改保存到文件 # [调试] 将更改保存到文件
save = 1 save = 1
# [调试] 覆写时间 # [调试] 覆写时间
time_override = 11 time_override = 12
# 对于每个项目的新记忆核子数量 # 对于每个项目的新记忆核子数量
tasked_number = 12 tasked_number = 12
# 竖屏适配 # 竖屏适配

View File

@@ -1,251 +0,0 @@
["臣密言:臣以险衅, 夙遭闵凶."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["生孩六月, 慈父见背;行年四岁, 舅夺母志."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["祖母刘愍臣孤弱, 躬亲抚养."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["臣少多疾病, 九岁不行, 零丁孤苦, 至于成立."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["既无伯叔, 终鲜兄弟, 门衰祚薄, 晚有儿息."]
efactor = 2.5
real_rept = 11
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["外无期功强近之亲, 内无应门五尺之僮, 茕茕孑立, 形影相吊."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["而刘夙婴疾病, 常在床蓐, 臣侍汤药, 未曾废离."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["逮奉圣朝, 沐浴清化."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["前太守臣逵察臣孝廉;后刺史臣荣举臣秀才."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["臣以供养无主, 辞不赴命."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["诏书特下, 拜臣郎中, 寻蒙国恩, 除臣洗马."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["猥以微贱, 当侍东宫, 非臣陨首所能上报."]
efactor = 2.5
real_rept = 2
rept = 0
interval = 1
last_date = 11
next_date = 12
is_activated = 1
["臣具以表闻, 辞不就职."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["诏书切峻, 责臣逋慢;郡县逼迫, 催臣上道;州司临门, 急于星火."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣欲奉诏奔驰, 则刘病日笃, 欲苟顺私情, 则告诉不许."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣之进退, 实为狼狈."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["伏惟圣朝以孝治天下, 凡在故老, 犹蒙矜育, 况臣孤苦, 特为尤甚."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["且臣少仕伪朝, 历职郎署, 本图宦达, 不矜名节."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["今臣亡国贱俘, 至微至陋, 过蒙拔擢, 宠命优渥, 岂敢盘桓, 有所希冀!"]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["但以刘日薄西山, 气息奄奄, 人命危浅, 朝不虑夕."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣无祖母, 无以至今日, 祖母无臣, 无以终余年."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["母孙二人, 更相为命, 是以区区不能废远."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣密今年四十有四, 祖母今年九十有六, 是臣尽节于陛下之日长, 报养刘之日短."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["乌鸟私情, 愿乞终养."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣之辛苦, 非独蜀之人士及二州牧伯所见明知, 皇天后土, 实所共鉴."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["愿陛下矜悯愚诚, 听臣微志, 庶刘侥幸, 保卒余年."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣生当陨首, 死当结草."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0
["臣不胜犬马怖惧之情, 谨拜表以闻."]
efactor = 2.5
real_rept = 0
rept = 0
interval = 0
last_date = 0
next_date = 0
is_activated = 0

20
main.py
View File

@@ -37,8 +37,8 @@ class MemScreen(Screen):
btn = dict() btn = dict()
def __init__( def __init__(
self, self,
nucleon_file: pt.AtomicFile, nucleon_file: pt.NucleonUnion,
electron_file: pt.AtomicFile, electron_file: pt.ElectronUnion,
tasked_num tasked_num
): ):
super().__init__(name=None, id=None, classes=None) super().__init__(name=None, id=None, classes=None)
@@ -147,7 +147,7 @@ class PreparationScreen(Screen):
("escape", "quit_app", "退出") ("escape", "quit_app", "退出")
] ]
def __init__(self, nucleon_file: pt.AtomicFile, electron_file: pt.AtomicFile) -> None: def __init__(self, nucleon_file: pt.NucleonUnion, electron_file: pt.ElectronUnion) -> None:
super().__init__(name=None, id=None, classes=None) super().__init__(name=None, id=None, classes=None)
self.nucleon_file = nucleon_file self.nucleon_file = nucleon_file
self.electron_file = electron_file self.electron_file = electron_file
@@ -158,12 +158,18 @@ class PreparationScreen(Screen):
yield Label(f"记忆项目: [b]{self.nucleon_file.name}[/b]\n") yield Label(f"记忆项目: [b]{self.nucleon_file.name}[/b]\n")
yield Label(f"核子文件对象: ./nucleon/[b]{self.nucleon_file.name}[/b].toml") yield Label(f"核子文件对象: ./nucleon/[b]{self.nucleon_file.name}[/b].toml")
yield Label(f"电子文件对象: ./electron/[b]{self.electron_file.name}[/b].toml") yield Label(f"电子文件对象: ./electron/[b]{self.electron_file.name}[/b].toml")
yield Label(f"核子数量:{self.nucleon_file.get_len()}") yield Label(f"核子数量:{len(self.nucleon_file)}")
yield Button("开始记忆", id="start_memorizing_button", variant="primary", classes="start-button") yield Button("开始记忆", id="start_memorizing_button", variant="primary", classes="start-button")
yield Static(f"\n全文如下:\n") yield Static(f"\n全文如下:\n")
yield Static(self.nucleon_file.get_full_content(), classes="full") yield Static(self._get_full_content(), classes="full")
yield Footer() yield Footer()
def _get_full_content(self):
content = ""
for i in self.nucleon_file.nucleons:
content += i['content']
return content
def action_go_back(self): def action_go_back(self):
self.app.pop_screen() self.app.pop_screen()
@@ -216,13 +222,13 @@ class FileSelectorScreen(Screen):
return return
selected_filename = str(selected_label.renderable) selected_filename = str(selected_label.renderable)
nucleon_file = pt.AtomicFile(pathlib.Path("./nucleon") / selected_filename, "nucleon") nucleon_file = pt.NucleonUnion(pathlib.Path("./nucleon") / selected_filename)
electron_file_path = pathlib.Path("./electron") / selected_filename electron_file_path = pathlib.Path("./electron") / selected_filename
if electron_file_path.exists(): if electron_file_path.exists():
pass pass
else: else:
electron_file_path.touch() electron_file_path.touch()
electron_file = pt.AtomicFile(pathlib.Path("./electron") / selected_filename, "electron") electron_file = pt.ElectronUnion(pathlib.Path("./electron") / selected_filename)
# self.notify(f"已选择: {selected_filename}", timeout=2) # self.notify(f"已选择: {selected_filename}", timeout=2)
self.app.push_screen(PreparationScreen(nucleon_file, electron_file)) self.app.push_screen(PreparationScreen(nucleon_file, electron_file))

View File

@@ -3,49 +3,43 @@ import toml
import time import time
import auxiliary as aux import auxiliary as aux
import time
class Electron(): class Electron():
"""电子: 记忆分析元数据及算法""" """电子: 记忆分析元数据及算法"""
algorithm = "SM-2" # 暂时使用 SM-2 算法进行记忆拟合, 考虑 SM-15 替代 algorithm = "SM-2" # 暂时使用 SM-2 算法进行记忆拟合, 考虑 SM-15 替代
"""
content = "" # 内容 def __init__(self, content: str, metadata: dict):
efactor = 2.5 # 易度系数, 越大越简单, 最大为5
real_rept = 0 # (实际)重复次数
rept = 0 # (有效)重复次数
interval = 0 # 最佳间隔
last_date = 0 # 上一次复习的时间戳
next_date = 0 # 将要复习的时间戳
is_activated = 0 # 激活状态
# *NOTE: 此处"时间戳"是以天为单位的整数, 即 UNIX 时间戳除以一天的秒数取整
last_modify = 0 # 最后修改时间戳(此处是UNIX时间戳)
"""
def __init__(self, content: str, data: dict):
self.content = content self.content = content
self.efactor = data.get('efactor', 2.5) self.metadata = metadata
self.real_rept = data.get('real_rept', 0) if metadata == {}:
self.rept = data.get('rept', 0) #print("NULL")
self.interval = data.get('interval', 0) self._default_init()
self.last_date = data.get('last_date', 0)
self.next_date = data.get('next_date', 0) def _default_init(self):
self.is_activated = data.get('is_activated', 0) defaults = {
self.last_modify = time.time() 'efactor': 2.5, # 易度系数, 越大越简单, 最大为5
'real_rept': 0, # (实际)重复次数
'rept': 0, # (有效)重复次数
'interval': 0, # 最佳间隔
'last_date': 0, # 上一次复习的时间戳
'next_date': 0, # 将要复习的时间戳
'is_activated': 0, # 激活状态
# *NOTE: 此处"时间戳"是以天为单位的整数, 即 UNIX 时间戳除以一天的秒数取整
'last_modify': time.time() # 最后修改时间戳(此处是UNIX时间戳)
}
self.metadata = defaults
def activate(self): def activate(self):
self.is_activated = 1 self.metadata['is_activated'] = 1
self.metadata['last_modify'] = time.time()
def modify(self, var: str, value): def modify(self, var: str, value):
setattr(self, var, value) if var in self.metadata:
self.last_modify = time.time() self.metadata[var] = value
self.metadata['last_modify'] = time.time()
def export_data(self): else:
return { print(f"警告: '{var}' 非已知元数据字段")
'efactor': self.efactor,
'real_rept': self.real_rept,
'rept': self.rept,
'interval': self.interval,
'last_date': self.last_date,
'next_date': self.next_date,
'is_activated': self.is_activated
}
def revisor(self, quality: int = 5, is_new_activation: bool = False): def revisor(self, quality: int = 5, is_new_activation: bool = False):
"""SM-2 算法迭代决策机制实现 """SM-2 算法迭代决策机制实现
@@ -58,85 +52,120 @@ class Electron():
if quality == -1: if quality == -1:
return -1 return -1
self.efactor = self.efactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)) self.metadata['efactor'] = self.metadata['efactor'] + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
self.efactor = max(1.3, self.efactor) self.metadata['efactor'] = max(1.3, self.metadata['efactor'])
if quality < 3: if quality < 3:
# 若保留率低于 3重置重复次数 # 若保留率低于 3重置重复次数
self.rept = 0 self.metadata['rept'] = 0
self.interval = 0 # 设为0以便下面重新计算 I(1) self.metadata['interval'] = 0 # 设为0以便下面重新计算 I(1)
else: else:
self.rept += 1 self.metadata['rept'] += 1
self.real_rept += 1
self.metadata['real_rept'] += 1
if is_new_activation: # 初次激活 if is_new_activation: # 初次激活
self.rept = 0 self.metadata['rept'] = 0
self.efactor = 2.5 self.metadata['efactor'] = 2.5
if self.rept == 0: # 刚被重置或初次激活后复习 if self.metadata['rept'] == 0: # 刚被重置或初次激活后复习
self.interval = 1 # I(1) self.metadata['interval'] = 1 # I(1)
elif self.rept == 1: elif self.metadata['rept'] == 1:
self.interval = 6 # I(2) 经验公式 self.metadata['interval'] = 6 # I(2) 经验公式
else: else:
self.interval = round(self.interval * self.efactor) self.metadata['interval'] = round(self.metadata['interval'] * self.metadata['efactor'])
self.last_date = aux.get_daystamp() self.metadata['last_date'] = aux.get_daystamp()
self.next_date = aux.get_daystamp() + self.interval self.metadata['next_date'] = aux.get_daystamp() + self.metadata['interval']
self.metadata['last_modify'] = time.time()
def __str__(self): def __str__(self):
return (f"记忆单元预览 \n" return (f"记忆单元预览 \n"
f"内容: '{self.content}' \n" f"内容: '{self.content}' \n"
f"易度系数: {self.efactor:.2f} \n" f"易度系数: {self.metadata['efactor']:.2f} \n"
f"已经重复的次数: {self.rept} \n" f"已经重复的次数: {self.metadata['rept']} \n"
f"下次间隔: {self.interval}\n" f"下次间隔: {self.metadata['interval']}\n"
f"下次复习日期时间戳: {self.next_date}") f"下次复习日期时间戳: {self.metadata['next_date']}")
def __eq__(self, other): def __eq__(self, other):
if self.content == other.content: if self.content == other.content:
return 1 return True
return 0 return False
def __hash__(self): def __hash__(self):
return hash(self.content) return hash(self.content)
def __getitem__(self, key):
if key == "content":
return self.content
if key in self.metadata:
return self.metadata[key]
else:
raise KeyError(f"Key '{key}' not found in metadata.")
def __setitem__(self, key, value):
if key == "content":
raise AttributeError("content 应为只读")
# 可以在此处添加更复杂的验证逻辑,例如只允许修改预定义的 metadata 键
# 或者根据键进行类型检查等。
self.metadata[key] = value
self.metadata['last_modify'] = time.time()
def __iter__(self):
yield from self.metadata.keys()
def __len__(self):
return len(self.metadata)
@staticmethod @staticmethod
def placeholder(): def placeholder():
return Electron("电子对象样例内容", {}) return Electron("电子对象样例内容", {})
@staticmethod class Nucleon:
def import_from_file(path: pathlib.Path):
name = path.name.replace(path.suffix, "")
with open(path, 'r') as f:
all = toml.load(f)
lst = list()
for i in all.keys():
lst.append(Electron(i, all[i]))
return (name, lst)
@staticmethod
def save_to_file(electron_dictized, path: pathlib.Path):
with open(path, 'w') as f:
toml.dump(electron_dictized, f)
class Nucleon():
"""核子: 材料元数据""" """核子: 材料元数据"""
def __init__(self, content: str, data: dict): def __init__(self, content: str, data: dict):
self.metadata = data self.metadata = data
self.content = content self.content = content
def __getitem__(self, key):
if key == "content":
return self.content
if key in self.metadata:
return self.metadata[key]
else:
raise KeyError(f"Key '{key}' not found in metadata.")
@staticmethod def __iter__(self):
def save_to_file(nucleon_dictized, path: pathlib.Path): yield from self.metadata.keys()
with open(path, 'w') as f:
toml.dump(nucleon_dictized, f) def __len__(self):
return len(self.metadata)
@staticmethod @staticmethod
def placeholder(): def placeholder():
return Nucleon("核子对象样例内容", {}) return Nucleon("核子对象样例内容", {})
class NucleonUnion(): class NucleonUnion():
"取代原有 NucleonFile 类, 以支持复杂逻辑" """
替代原有 NucleonFile 类, 支持复杂逻辑
Attributes:
path (Path): 对应于 NucleonUnion 实例的文件路径。
name (str): 核联对象的显示名称,从文件名中派生。
nucleons (list): 内部核子对象的列表。
nucleons_dict (dict): 内部核子对象的字典,以核子内容作为键。
keydata (dict): 核子对象字典键名的翻译。
testdata (dict): 记忆测试项目的元数据。
Parameters:
path (Path): 包含核子数据的文件路径。
"""
def __init__(self, path): def __init__(self, path):
name = path.name.replace(path.suffix, "") self.path = path
self.name = path.name.replace(path.suffix, "")
with open(path, 'r') as f: with open(path, 'r') as f:
all = toml.load(f) all = toml.load(f)
lst = list() lst = list()
@@ -147,11 +176,41 @@ class NucleonUnion():
self.keydata = all["keydata"] self.keydata = all["keydata"]
self.testdata = all["testdata"] self.testdata = all["testdata"]
self.nucleons = lst self.nucleons = lst
self.nucleons_dict = {elect.content: nucleons for elect in electron_file.datalist} self.nucleons_dict = {i.content: i for i in lst}
def query(content):
def __len__(self):
return len(self.nucleons)
class AtomicFile(): def save(self):
with open(self.path, 'w') as f:
tmp = {i.content: i.metadata for i in self.nucleons}
toml.dump(tmp, f)
class ElectronUnion():
"取代原有 ElectronFile 类, 以支持复杂逻辑"
def __init__(self, path):
self.path = path
self.name = path.name.replace(path.suffix, "")
with open(path, 'r') as f:
all = toml.load(f)
lst = list()
for i in all.keys():
lst.append(Electron(i, all[i]))
self.electrons = lst
self.electrons_dict = {i.content: i.metadata for i in lst}
def sync(self):
"""同步 electrons_dict 中新增对到 electrons 中"""
self.electrons = self.electrons_dict.values()
def save(self):
#print(1)
with open(self.path, 'w') as f:
tmp = {i.content: i.metadata for i in self.electrons}
#print(tmp)
toml.dump(tmp, f)
"""class AtomicFile():
def __init__(self, path, type_="unknown"): def __init__(self, path, type_="unknown"):
self.path = path self.path = path
self.type_ = type_ self.type_ = type_
@@ -176,6 +235,7 @@ class AtomicFile():
def get_len(self): def get_len(self):
return len(self.datalist) return len(self.datalist)
"""
class Atom(): class Atom():
@staticmethod @staticmethod

0
puzzles.py Normal file
View File

View File

@@ -7,7 +7,7 @@ class Parser():
class Reactor(): class Reactor():
"""反应堆对象, 用于全面解析文件, 并处理和分配一次文件记忆流程的资源与策略""" """反应堆对象, 用于全面解析文件, 并处理和分配一次文件记忆流程的资源与策略"""
def __init__(self, nucleon_file: pt.AtomicFile, electron_file: pt.AtomicFile, tasked_num): def __init__(self, nucleon_file: pt.NucleonUnion, electron_file: pt.ElectronUnion, tasked_num):
# 导入原子对象 # 导入原子对象
self.reported = set() self.reported = set()
self.nucleon_file = nucleon_file self.nucleon_file = nucleon_file
@@ -17,7 +17,7 @@ class Reactor():
self.atoms_review = list() self.atoms_review = list()
counter = self.tasked_num counter = self.tasked_num
self.electron_dict = {elect.content: elect for elect in electron_file.datalist} self.electron_dict = electron_file.electrons_dict
def electron_dict_get_fallback(key) -> pt.Electron: def electron_dict_get_fallback(key) -> pt.Electron:
value = self.electron_dict.get(key) value = self.electron_dict.get(key)
@@ -26,20 +26,20 @@ class Reactor():
if value is None: if value is None:
value = pt.Electron(key, {}) # 获取默认值 value = pt.Electron(key, {}) # 获取默认值
self.electron_dict[key] = value # 将默认值存入字典 self.electron_dict[key] = value # 将默认值存入字典
value = self.electron_dict[key] electron_file.sync()
return value # 返回获取的值(可能是默认值) return value # 返回获取的值(可能是默认值)
for nucleon in nucleon_file.datalist: for nucleon in nucleon_file.nucleons:
atom = (electron_dict_get_fallback(nucleon.content), nucleon) atom = (electron_dict_get_fallback(nucleon.content), nucleon)
if atom[0].is_activated == 0: if atom[0]["is_activated"] == 0:
if counter > 0: if counter > 0:
atom[0].is_activated = 1 atom[0]["is_activated"] = 1
self.atoms_new.append(atom) self.atoms_new.append(atom)
counter -= 1 counter -= 1
else: else:
if atom[0].next_date <= aux.get_daystamp(): if atom[0]["next_date"] <= aux.get_daystamp():
atom[0].last_date = aux.get_daystamp() atom[0]["last_date"] = aux.get_daystamp()
self.atoms_review.append(atom) self.atoms_review.append(atom)
# 设置运行时 # 设置运行时
self.index: int self.index: int
@@ -96,10 +96,6 @@ class Reactor():
def save(self): def save(self):
print("Progress saved") print("Progress saved")
# self.nucleon_file.save() # self.nucleon_file.save()
temp = list()
for i in self.electron_dict.keys():
temp.append(self.electron_dict[i])
self.electron_file.datalist = temp
self.electron_file.save() self.electron_file.save()
def report(self, atom, quality): def report(self, atom, quality):