diff --git a/.gitignore b/.gitignore index bee63b2..26c67b7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__/ .idea/ cache/ data/repo/cngk +data/repo/eotgk *.egg-info/ build/ dist/ diff --git a/pyproject.toml b/pyproject.toml index 2a76263..e4a829c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "openai==1.0.0", "playsound==1.2.2", "psutil>=7.2.1", + "pygobject>=3.54.5", "tabulate>=0.9.0", "textual==7.0.0", "toml==0.10.2", diff --git a/src/heurams/interface/screens/about.py b/src/heurams/interface/screens/about.py index 782362f..beff979 100644 --- a/src/heurams/interface/screens/about.py +++ b/src/heurams/interface/screens/about.py @@ -41,21 +41,24 @@ class AboutScreen(Screen): 一个基于启发式算法的辅助记忆调度器, 旨在帮助用户更高效地进行记忆工作与学习规划. -以 AGPL-3.0 开放源代码 +以 AGPL-3.0 开放源代码, 这直接意味着任何个体直接基于此代码对外或内部提供的应用和服务, 无论本地或网络, 必须向所有用户公开完整修改后的源代码, 且继续沿用 AGPL-3.0 协议. -您可在项目主页 https://ams.imwangzhiyu.xyz 获取用户指南, 开发文档与软件更新 +您可在项目主页 https://ams.imwangzhiyu.xyz 获取用户指南, 开发文档与软件更新. -如果您觉得这个软件有用, 请给它添加一个星标 :) +如果您觉得这个软件有用, 可以给它添加一个星标 :) -我们的共同目标是为人人带来高品质的辅助记忆 & 学习软件. +> 此软件, 以及它作为一个"程序库"是自由且免费的, 但是开发工作必须投入大量精力 +> 即使您不是软件开发人员, 我们也欢迎您加入 HeurAMS 的队伍! +> 您可以加入各种语言的翻译团队来翻译软件的界面, 您还可以制作图像、主题、音效, 或者改进软件配套的文档…… +> 不管您来自何方, 我们都欢迎您加入社区并做出贡献. +> 我们的共同目标是为人人带来高品质的辅助记忆 & 学习软件. +> 您的慷慨支持, 我们必当涌泉相报. -不管您来自何方, 我们都欢迎您加入社区并做出贡献. +开发人员列表: -开发人员: +- Wang Zhiyu([@pluvium27](https://github.com/pluvium27)): 发起项目与主要维护者 -- Wang Zhiyu([@pluvium27](https://github.com/pluvium27)): 项目作者 - -特别感谢: +特别感谢以下人士, 他们的算法与理论构成了此软件算法的基石: - [Piotr A. Woźniak](https://supermemo.guru/wiki/Piotr_Wozniak): SM-2 算法与 SM-15 算法理论 - [Kazuaki Tanida](https://github.com/slaypni): SM-15 算法的 CoffeeScript 实现 @@ -64,18 +67,14 @@ class AboutScreen(Screen): # 运行环境信息 -Textual 框架版本: {textual_version} - -终端模拟器: {terminal_info} - Python 解释器版本: {python_version} - +Textual 框架版本: {textual_version} +终端模拟器: {terminal_info} 操作系统版本: {os_version} - 存储余量: {disk_usage} - 内存大小: {memory_info} - + +报告问题时, 请复制这些信息到问题描述, 并上传软件日志 `heurams.log` 作为附件, 以协助开发者定位错误 """ yield Markdown(about_text, classes="about-markdown") diff --git a/src/heurams/interface/widgets/recognition.py b/src/heurams/interface/widgets/recognition.py index f56f963..142d959 100644 --- a/src/heurams/interface/widgets/recognition.py +++ b/src/heurams/interface/widgets/recognition.py @@ -95,7 +95,7 @@ class Recognition(BasePuzzleWidget): if isinstance(item, Dict): total = "" for j, k in item.items(): # type: ignore - total += f"> **{j}**: {k} \n" + total += f"> {j}: {k} \n" yield Markdown(total) if isinstance(item, str): yield Markdown(item) diff --git a/src/heurams/kernel/particles/nucleon.py b/src/heurams/kernel/particles/nucleon.py index 9369bba..9015be7 100644 --- a/src/heurams/kernel/particles/nucleon.py +++ b/src/heurams/kernel/particles/nucleon.py @@ -13,13 +13,28 @@ class Nucleon: def __init__(self, ident, payload, common): self.ident = ident - env = { - "payload": payload, - "default": config_var.get()["puzzles"], - "nucleon": (payload | common), - } - self.evalizer = Evalizer(environment=env) - self.data: dict = self.evalizer(deepcopy((payload | common))) # type: ignore + try: + data_safe = deepcopy((payload | common)) + data_puz = deepcopy(data_safe['puzzles']) + data_safe['puzzles'] = {} + env = { + "payload": data_safe, + "default": config_var.get()["puzzles"], + "nucleon": data_safe, + } + self.evalizer = Evalizer(environment=env) + data_safe = self.evalizer(deepcopy(data_safe)) + env = { + "payload": data_safe, + "default": config_var.get()["puzzles"], + "nucleon": data_safe, + } + self.evalizer = Evalizer(environment=env) + data_puz = self.evalizer(deepcopy(data_puz)) + data_safe['puzzles'] = data_puz # type: ignore + self.data: dict = data_safe # type: ignore + except Exception: + self.data = (payload | common) def __getitem__(self, key): if isinstance(key, str): diff --git a/src/heurams/tools/csv2payload.py b/src/heurams/tools/csv2payload.py new file mode 100755 index 0000000..a32cb20 --- /dev/null +++ b/src/heurams/tools/csv2payload.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +""" +将符合条件的CSV转为符合Payload需要的TOML格式 + +使用命令: python3 csv2payload.py <生成TOML路径, 默认为文件名相同, 后缀为toml的TOML文件> + + +转换规则: +1. `ident` 列用作 TOML 的 section 标题(`[ident]`) +2. 若某行的 `ident` 为空,则自动按顺序生成标识符,例如 `idx_1`、`idx_2` 等 +3. 所有其他列(除 `ident` 外)都作为该 section 下的键值对 +4. 所有列都是可选的,但 `ident` 为空时会自动生成 + +示例 CSV: +```csv +ident, content, meaning, ... +"Fox", "Fox", "狐狸(一种动物)", ... +"Dog", "Dog", "狗(一种比猫聪明的动物)", ... +"Cat", "Cat", "猫(一种不如狗聪明的动物)", ... +"Dolphin", "Dolphin", "一种很聪明的海洋哺乳动物", ... +, "Duck", "一种扁嘴水禽" +, "Meow", "猫发出的声音" +"Doge", "Doge", "神烦狗(一张搞笑狗狗表情包的代称)", ... +, "Woof", "狗发出的声音" +``` + +转换后的 TOML: +```toml +[Fox] +content = "Fox" +meaning = "狐狸(一种动物)" + +[Dog] +content = "Dog" +meaning = "狗(一种比猫聪明的动物)" + +[Cat] +content = "Cat" +meaning = "猫(一种不如狗聪明的动物)" + +[Dolphin] +content = "Dolphin" +meaning = "一种很聪明的海洋哺乳动物" + +[idx_1] +content = "Duck" +meaning = "一种扁嘴水禽" + +[idx_2] +content = "Meow" +meaning = "猫发出的声音" + +[Doge] +content = "Doge" +meaning = "神烦狗(一张搞笑狗狗表情包的代称)" + +[idx_3] +content = "Woof" +meaning = "狗发出的声音" +``` + +补充说明: +- 自动生成的标识符使用 `idx_` 前缀加数字序列 +- 生成序列基于原始 CSV 中 `ident` 为空的行出现的顺序 +- 所有值都保留为字符串类型,符合 TOML 字符串格式要求 +- 如果 CSV 包含更多列,它们也会以相同方式转换为键值对 +""" +import csv +import sys +import os +from pathlib import Path + +def csv_to_toml(csv_path, toml_path=None): + """ + 将CSV文件转换为TOML格式 + + Args: + csv_path (str): 输入CSV文件路径 + toml_path (str): 输出TOML文件路径,默认为相同目录下同名文件 + """ + # 检查CSV文件是否存在 + csv_file = Path(csv_path) + if not csv_file.exists(): + print(f"错误: CSV文件不存在 - {csv_path}") + sys.exit(1) + + # 确定输出TOML文件路径 + if toml_path is None: + toml_path = csv_file.with_suffix('.toml') + else: + toml_path = Path(toml_path) + + # 读取CSV文件 + try: + with open(csv_file, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + rows = list(reader) + except Exception as e: + print(f"错误: 无法读取CSV文件 - {e}") + sys.exit(1) + + # 检查CSV文件是否有数据 + if not rows: + print("错误: CSV文件为空或格式不正确") + sys.exit(1) + + # 生成TOML内容 + toml_content = [] + idx_counter = 1 + + for row in rows: + # 处理ident列,为空时生成自动标识符 + ident = row.get('ident', '').strip() + if not ident: + ident = f"idx_{idx_counter}" + idx_counter += 1 + + # 添加section标题 + toml_content.append(f"[{ident}]") + + # 添加所有其他列作为键值对(排除ident列) + for key, value in row.items(): + if key == 'ident': + continue + + # 确保值存在且不为空 + if value is not None and str(value).strip() != '': + # 转义特殊字符并添加引号 + escaped_value = str(value).replace('"', '\\"') + toml_content.append(f'"{key}" = "{escaped_value}"') + + # section之间添加空行 + toml_content.append("") + + # 写入TOML文件 + try: + with open(toml_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(toml_content).strip()) + print(f"成功: 已生成TOML文件 - {toml_path}") + except Exception as e: + print(f"错误: 无法写入TOML文件 - {e}") + sys.exit(1) + +def main(): + """主函数""" + if len(sys.argv) < 2: + print("用法: python3 csv2payload.py []") + print("示例: python3 csv2payload.py input.csv output.toml") + print("示例: python3 csv2payload.py input.csv # 自动生成input.toml") + sys.exit(1) + + csv_path = sys.argv[1] + toml_path = sys.argv[2] if len(sys.argv) > 2 else None + + csv_to_toml(csv_path, toml_path) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/uv.lock b/uv.lock index 864f120..7581f96 100644 --- a/uv.lock +++ b/uv.lock @@ -306,6 +306,7 @@ dependencies = [ { name = "openai" }, { name = "playsound" }, { name = "psutil" }, + { name = "pygobject" }, { name = "tabulate" }, { name = "textual" }, { name = "toml" }, @@ -324,6 +325,7 @@ requires-dist = [ { name = "openai", specifier = "==1.0.0" }, { name = "playsound", specifier = "==1.2.2" }, { name = "psutil", specifier = ">=7.2.1" }, + { name = "pygobject", specifier = ">=3.54.5" }, { name = "tabulate", specifier = ">=0.9.0" }, { name = "textual", specifier = "==7.0.0" }, { name = "toml", specifier = "==0.10.2" }, @@ -724,6 +726,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/73/2ce007f4198c80fcf2cb24c169884f833fe93fbc03d55d302627b094ee91/psutil-7.2.1-cp37-abi3-win_arm64.whl", hash = "sha256:0d67c1822c355aa6f7314d92018fb4268a76668a536f133599b91edd48759442", size = 133836, upload-time = "2025-12-29T08:26:43.086Z" }, ] +[[package]] +name = "pycairo" +version = "1.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/d9/1728840a22a4ef8a8f479b9156aa2943cd98c3907accd3849fb0d5f82bfd/pycairo-1.29.0.tar.gz", hash = "sha256:f3f7fde97325cae80224c09f12564ef58d0d0f655da0e3b040f5807bd5bd3142", size = 665871, upload-time = "2025-11-11T19:13:01.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/28/6363087b9e60af031398a6ee5c248639eefc6cc742884fa2789411b1f73b/pycairo-1.29.0-cp312-cp312-win32.whl", hash = "sha256:91bcd7b5835764c616a615d9948a9afea29237b34d2ed013526807c3d79bb1d0", size = 751486, upload-time = "2025-11-11T19:11:54.451Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d2/d146f1dd4ef81007686ac52231dd8f15ad54cf0aa432adaefc825475f286/pycairo-1.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:3f01c3b5e49ef9411fff6bc7db1e765f542dc1c9cfed4542958a5afa3a8b8e76", size = 845383, upload-time = "2025-11-11T19:12:01.551Z" }, + { url = "https://files.pythonhosted.org/packages/01/16/6e6f33bb79ec4a527c9e633915c16dc55a60be26b31118dbd0d5859e8c51/pycairo-1.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:eafe3d2076f3533535ad4a361fa0754e0ee66b90e548a3a0f558fed00b1248f2", size = 694518, upload-time = "2025-11-11T19:12:06.561Z" }, + { url = "https://files.pythonhosted.org/packages/f0/21/3f477dc318dd4e84a5ae6301e67284199d7e5a2384f3063714041086b65d/pycairo-1.29.0-cp313-cp313-win32.whl", hash = "sha256:3eb382a4141591807073274522f7aecab9e8fa2f14feafd11ac03a13a58141d7", size = 750949, upload-time = "2025-11-11T19:12:12.198Z" }, + { url = "https://files.pythonhosted.org/packages/43/34/7d27a333c558d6ac16dbc12a35061d389735e99e494ee4effa4ec6d99bed/pycairo-1.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:91114e4b3fbf4287c2b0788f83e1f566ce031bda49cf1c3c3c19c3e986e95c38", size = 844149, upload-time = "2025-11-11T19:12:19.171Z" }, + { url = "https://files.pythonhosted.org/packages/15/43/e782131e23df69e5c8e631a016ed84f94bbc4981bf6411079f57af730a23/pycairo-1.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:09b7f69a5ff6881e151354ea092137b97b0b1f0b2ab4eb81c92a02cc4a08e335", size = 693595, upload-time = "2025-11-11T19:12:23.445Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fa/87eaeeb9d53344c769839d7b2854db7ff2cd596211e00dd1b702eeb1838f/pycairo-1.29.0-cp314-cp314-win32.whl", hash = "sha256:69e2a7968a3fbb839736257bae153f547bca787113cc8d21e9e08ca4526e0b6b", size = 767198, upload-time = "2025-11-11T19:12:42.336Z" }, + { url = "https://files.pythonhosted.org/packages/3c/90/3564d0f64d0a00926ab863dc3c4a129b1065133128e96900772e1c4421f8/pycairo-1.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:e91243437a21cc4c67c401eff4433eadc45745275fa3ade1a0d877e50ffb90da", size = 871579, upload-time = "2025-11-11T19:12:48.982Z" }, + { url = "https://files.pythonhosted.org/packages/5e/91/93632b6ba12ad69c61991e3208bde88486fdfc152be8cfdd13444e9bc650/pycairo-1.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:b72200ea0e5f73ae4c788cd2028a750062221385eb0e6d8f1ecc714d0b4fdf82", size = 719537, upload-time = "2025-11-11T19:12:55.016Z" }, + { url = "https://files.pythonhosted.org/packages/93/23/37053c039f8d3b9b5017af9bc64d27b680c48a898d48b72e6d6583cf0155/pycairo-1.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5e45fce6185f553e79e4ef1722b8e98e6cde9900dbc48cb2637a9ccba86f627a", size = 874015, upload-time = "2025-11-11T19:12:28.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/54/123f6239685f5f3f2edc123f1e38d2eefacebee18cf3c532d2f4bd51d0ef/pycairo-1.29.0-cp314-cp314t-win_arm64.whl", hash = "sha256:caba0837a4b40d47c8dfb0f24cccc12c7831e3dd450837f2a356c75f21ce5a15", size = 721404, upload-time = "2025-11-11T19:12:36.919Z" }, +] + [[package]] name = "pydantic" version = "2.12.5" @@ -819,6 +840,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pygobject" +version = "3.54.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycairo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/a5/68f883df1d8442e3b267cb92105a4b2f0de819bd64ac9981c2d680d3f49f/pygobject-3.54.5.tar.gz", hash = "sha256:b6656f6348f5245606cf15ea48c384c7f05156c75ead206c1b246c80a22fb585", size = 1274658, upload-time = "2025-10-18T13:45:03.121Z" } + [[package]] name = "repath" version = "0.9.0"