diff --git a/puzzlemarkers.py b/CONTRIBUTING.md similarity index 100% rename from puzzlemarkers.py rename to CONTRIBUTING.md diff --git a/README.md b/README.md index fc431c2..247bc03 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ ![scrshot1](readme_src/image_1.png) ## 技术架构 - +> 有关技术与实现的细节, 请参阅 CONTRIBUTING.md +> 提交拉取请求以参与到此开放源代码项目 ``` mermaid graph TD subgraph 后端 diff --git a/auxiliary.py b/auxiliary.py index b5e394c..2dc73a2 100644 --- a/auxiliary.py +++ b/auxiliary.py @@ -27,7 +27,7 @@ def get_daystamp() -> int: time_override = config.get("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.time() // (24 * 3600)) diff --git a/config.toml b/config.toml index e64f846..6d7a27e 100644 --- a/config.toml +++ b/config.toml @@ -1,7 +1,7 @@ # [调试] 将更改保存到文件 save = 1 # [调试] 覆写时间 -time_override = 11 +time_override = 12 # 对于每个项目的新记忆核子数量 tasked_number = 12 # 竖屏适配 diff --git a/electron/陈情表.toml b/electron/陈情表.toml index 670bb85..e69de29 100644 --- a/electron/陈情表.toml +++ b/electron/陈情表.toml @@ -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 diff --git a/main.py b/main.py index 2c2d25f..8538e7e 100644 --- a/main.py +++ b/main.py @@ -37,8 +37,8 @@ class MemScreen(Screen): btn = dict() def __init__( self, - nucleon_file: pt.AtomicFile, - electron_file: pt.AtomicFile, + nucleon_file: pt.NucleonUnion, + electron_file: pt.ElectronUnion, tasked_num ): super().__init__(name=None, id=None, classes=None) @@ -147,7 +147,7 @@ class PreparationScreen(Screen): ("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) self.nucleon_file = nucleon_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"核子文件对象: ./nucleon/[b]{self.nucleon_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 Static(f"\n全文如下:\n") - yield Static(self.nucleon_file.get_full_content(), classes="full") + yield Static(self._get_full_content(), classes="full") 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): self.app.pop_screen() @@ -216,13 +222,13 @@ class FileSelectorScreen(Screen): return 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 if electron_file_path.exists(): pass else: 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.app.push_screen(PreparationScreen(nucleon_file, electron_file)) diff --git a/particles.py b/particles.py index 24f8f81..0e3ff5e 100644 --- a/particles.py +++ b/particles.py @@ -3,49 +3,43 @@ import toml import time import auxiliary as aux +import time + class Electron(): """电子: 记忆分析元数据及算法""" algorithm = "SM-2" # 暂时使用 SM-2 算法进行记忆拟合, 考虑 SM-15 替代 - """ - content = "" # 内容 - 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): + + def __init__(self, content: str, metadata: dict): self.content = content - self.efactor = data.get('efactor', 2.5) - self.real_rept = data.get('real_rept', 0) - self.rept = data.get('rept', 0) - self.interval = data.get('interval', 0) - self.last_date = data.get('last_date', 0) - self.next_date = data.get('next_date', 0) - self.is_activated = data.get('is_activated', 0) - self.last_modify = time.time() + self.metadata = metadata + if metadata == {}: + #print("NULL") + self._default_init() + + def _default_init(self): + defaults = { + '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): - self.is_activated = 1 + self.metadata['is_activated'] = 1 + self.metadata['last_modify'] = time.time() def modify(self, var: str, value): - setattr(self, var, value) - self.last_modify = time.time() - - def export_data(self): - return { - '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 - } + if var in self.metadata: + self.metadata[var] = value + self.metadata['last_modify'] = time.time() + else: + print(f"警告: '{var}' 非已知元数据字段") def revisor(self, quality: int = 5, is_new_activation: bool = False): """SM-2 算法迭代决策机制实现 @@ -58,85 +52,120 @@ class Electron(): if quality == -1: return -1 - self.efactor = self.efactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)) - self.efactor = max(1.3, self.efactor) + self.metadata['efactor'] = self.metadata['efactor'] + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)) + self.metadata['efactor'] = max(1.3, self.metadata['efactor']) if quality < 3: # 若保留率低于 3,重置重复次数 - self.rept = 0 - self.interval = 0 # 设为0,以便下面重新计算 I(1) + self.metadata['rept'] = 0 + self.metadata['interval'] = 0 # 设为0,以便下面重新计算 I(1) else: - self.rept += 1 - self.real_rept += 1 + self.metadata['rept'] += 1 + + self.metadata['real_rept'] += 1 if is_new_activation: # 初次激活 - self.rept = 0 - self.efactor = 2.5 + self.metadata['rept'] = 0 + self.metadata['efactor'] = 2.5 - if self.rept == 0: # 刚被重置或初次激活后复习 - self.interval = 1 # I(1) - elif self.rept == 1: - self.interval = 6 # I(2) 经验公式 + if self.metadata['rept'] == 0: # 刚被重置或初次激活后复习 + self.metadata['interval'] = 1 # I(1) + elif self.metadata['rept'] == 1: + self.metadata['interval'] = 6 # I(2) 经验公式 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.next_date = aux.get_daystamp() + self.interval + self.metadata['last_date'] = aux.get_daystamp() + self.metadata['next_date'] = aux.get_daystamp() + self.metadata['interval'] + self.metadata['last_modify'] = time.time() def __str__(self): return (f"记忆单元预览 \n" f"内容: '{self.content}' \n" - f"易度系数: {self.efactor:.2f} \n" - f"已经重复的次数: {self.rept} \n" - f"下次间隔: {self.interval} 天 \n" - f"下次复习日期时间戳: {self.next_date}") + f"易度系数: {self.metadata['efactor']:.2f} \n" + f"已经重复的次数: {self.metadata['rept']} \n" + f"下次间隔: {self.metadata['interval']} 天 \n" + f"下次复习日期时间戳: {self.metadata['next_date']}") def __eq__(self, other): if self.content == other.content: - return 1 - return 0 + return True + return False def __hash__(self): 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 def placeholder(): return Electron("电子对象样例内容", {}) - @staticmethod - 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(): +class Nucleon: """核子: 材料元数据""" + def __init__(self, content: str, data: dict): self.metadata = data self.content = content - - @staticmethod - def save_to_file(nucleon_dictized, path: pathlib.Path): - with open(path, 'w') as f: - toml.dump(nucleon_dictized, f) + 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 __iter__(self): + yield from self.metadata.keys() + + def __len__(self): + return len(self.metadata) @staticmethod def placeholder(): return Nucleon("核子对象样例内容", {}) 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): - name = path.name.replace(path.suffix, "") + self.path = path + self.name = path.name.replace(path.suffix, "") with open(path, 'r') as f: all = toml.load(f) lst = list() @@ -147,11 +176,41 @@ class NucleonUnion(): self.keydata = all["keydata"] self.testdata = all["testdata"] self.nucleons = lst - self.nucleons_dict = {elect.content: nucleons for elect in electron_file.datalist} - def query(content): + self.nucleons_dict = {i.content: i for i in lst} + 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"): self.path = path self.type_ = type_ @@ -176,6 +235,7 @@ class AtomicFile(): def get_len(self): return len(self.datalist) +""" class Atom(): @staticmethod diff --git a/puzzles.py b/puzzles.py new file mode 100644 index 0000000..e69de29 diff --git a/reactor.py b/reactor.py index 088def5..20c0b12 100644 --- a/reactor.py +++ b/reactor.py @@ -7,7 +7,7 @@ class Parser(): 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.nucleon_file = nucleon_file @@ -17,7 +17,7 @@ class Reactor(): self.atoms_review = list() 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: value = self.electron_dict.get(key) @@ -26,20 +26,20 @@ class Reactor(): if value is None: value = pt.Electron(key, {}) # 获取默认值 self.electron_dict[key] = value # 将默认值存入字典 - value = self.electron_dict[key] + electron_file.sync() return value # 返回获取的值(可能是默认值) - for nucleon in nucleon_file.datalist: + for nucleon in nucleon_file.nucleons: atom = (electron_dict_get_fallback(nucleon.content), nucleon) - if atom[0].is_activated == 0: + if atom[0]["is_activated"] == 0: if counter > 0: - atom[0].is_activated = 1 + atom[0]["is_activated"] = 1 self.atoms_new.append(atom) counter -= 1 else: - if atom[0].next_date <= aux.get_daystamp(): - atom[0].last_date = aux.get_daystamp() + if atom[0]["next_date"] <= aux.get_daystamp(): + atom[0]["last_date"] = aux.get_daystamp() self.atoms_review.append(atom) # 设置运行时 self.index: int @@ -96,10 +96,6 @@ class Reactor(): def save(self): print("Progress saved") # 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() def report(self, atom, quality):