From 23872593347e1489dd220469da545845f19fe4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E5=AF=92?= <2596194220@qq.com> Date: Wed, 10 Dec 2025 22:49:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.env.toml | 97 +++++++++++++++++++++++++++++++++++++++++++ data/names.env.xlsx | Bin 0 -> 6474 bytes main.py | 27 ++++++++---- utils/image_utils.py | 28 +++++++++++++ 4 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 config.env.toml create mode 100644 data/names.env.xlsx create mode 100644 utils/image_utils.py diff --git a/config.env.toml b/config.env.toml new file mode 100644 index 0000000..1e755d7 --- /dev/null +++ b/config.env.toml @@ -0,0 +1,97 @@ +[paths] +# PPT模版路径 +source_file = "templates/大班幼儿学期发展报告.pptx" +# 输出文件夹 +output_folder = "output" +# Excel数据文件路径 +excel_file = "data/names.xlsx" +# 图片资源文件夹 +image_folder = "data/images" +# 字体文件夹 +fonts_dir = "fonts" + +[class_info] +# 班级名称 +class_name = "K4D" +# TODO 老师名单 (数组格式) +teachers = [""] + +[defaults] +# 当Excel中没有评语时的默认内容 +default_comment = "暂无评语" +age_group = "大班上学期" + + +# ======================== +# Excel 配置 (Data) +# ======================== +[excel] +sheet_name = "Sheet1" +# 对应Excel表头名称,顺序必须与代码中的解包顺序一致! +# 顺序:姓名, 英文名, 性别, 生日, 属相, 好朋友, 爱好, 游戏, 食物, 评语 +columns = [ + "姓名", + "英文名", + "性别", + "生日", + "属相", + "我的好朋友", + "我的爱好", + "喜欢的游戏", + "喜欢吃的食物", + "评价" +] + +# TODO API 配置 +[ai] +api_key = "" +api_url = "" +model = "" +prompt = """ +# Role +你是一位拥有20年经验的资深幼儿园主班老师。你的文笔温暖、细腻、充满爱意,擅长发现每个孩子身上独特的闪光点。你的评语风格是“治愈系”的,能让家长读完后感到欣慰并对未来充满希望。 + +# Goal +请根据用户提供的【幼儿姓名】、【年龄段/班级】以及【日常表现关键词/评分数据】,撰写一份高质量的学期末成长评语。 + +# Constraints & Rules +1. **称呼处理**: + - 自动识别用户输入的姓名。 + - **必须去掉姓氏**,只使用名。 + - 统一格式为:“[名]宝贝,你好!”或“[名]宝贝:”。 + - 例如:“王小明” -> “小明宝贝”;“李在这个” -> “在这个宝贝”。 + +2. **分龄侧重 (根据 Age_Group 调整侧重点)**: + - **小班 (3-4岁)**:侧重于适应集体生活、情绪稳定性、基本生活自理能力(吃饭、午睡、如厕)、愿意与老师互动。 + - **中班 (4-5岁)**:侧重于社交互动、分享与合作、动手能力、好奇心、规则意识的建立、自信心的增强。 + - **大班 (5-6岁)**:侧重于学习习惯、逻辑思维、领导力/榜样作用、任务意识、为幼小衔接做的准备、抗挫折能力。 + +3. **写作结构 (三段式)**: + - **开头**:亲切的问候 + 总体印象(用美好的形容词,如文静、活泼、机灵等)。 + - **正文**:结合提供的【表现关键词】,具体描述孩子的进步和优点(必须具体,拒绝空洞)。 + - **结尾**:委婉地提出一点小小的期望(用“如果你能...老师会更为你骄傲”的句式),并送上新学期的祝福。 + +4. **语气风格**: + - 积极正面,多用肯定句。 + - 避免生硬的批评,将缺点转化为“待提升的潜力”或“期望”。 + - 字数控制在 150-250 字之间(适合PPT展示)。 + +# Workflow +1. 分析用户输入的年龄段,确定评价基调。 +2. 处理姓名,提取昵称。 +3. 将输入的关键词串联成通顺、优美的句子。 +4. 按照三段式结构输出最终评语。 + +# Input Format +用户将提供 JSON 格式或特定格式的数据,包含: +- Name {{name}} +- Age_Group {{class_name}} +- Traits (表现关键词/特征,如:吃饭香、爱画画、有些胆小、数学好) + +# Output Example +(假设输入:Name=张图图, Age_Group=小班, Traits=适应能力强, 爱笑, 挑食) +图图宝贝,你好! +你是一个爱笑的小天使,每天早上都能看到你甜甜的笑脸,老师的心都要被你融化了。这个学期你进步真大呀,从一开始的哭鼻子到现在能开心地参与游戏,你的适应能力让老师感到惊喜。在集体活动中,你总是那么投入。 +不过,老师发现你在吃饭时偶尔会把不喜欢的青菜挑出来哦。如果你能和青菜宝宝做好朋友,把身体练得棒棒的,那就更完美啦! +祝可爱的图图宝贝新年快乐,健康成长! +""" \ No newline at end of file diff --git a/data/names.env.xlsx b/data/names.env.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1f139e735c58143c9abd8db21c4fdb5c96bfdf51 GIT binary patch literal 6474 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 T(SH8|3vs~kII@3;Q}2HZ>3 literal 0 HcmV?d00001 diff --git a/main.py b/main.py index f60672b..2369856 100644 --- a/main.py +++ b/main.py @@ -16,6 +16,7 @@ from rich.table import Table from config.config import load_config from utils.agent_utils import generate_comment from utils.font_utils import install_fonts_from_directory +from utils.image_utils import find_image_path from utils.pef_utils import batch_convert_folder from utils.pptx_utils import replace_text_in_slide, replace_picture @@ -112,31 +113,39 @@ def generate_report(): # --- 页面 3 --- student_image_folder = os.path.join(config["image_folder"], name) if os.path.exists(student_image_folder): - me_image = os.path.join(student_image_folder, "me_image.jpg") + me_image_path = find_image_path(student_image_folder, "me_image") # 构造信息字典供 helper 使用 info_dict = { "name": name, "english_name": english_name, "sex": sex, "birthday": birthday, "zodiac": zodiac, "friend": friend, "hobby": hobby, "game": game, "food": food } - replace_three_page(prs, info_dict, me_image) + replace_three_page(prs, info_dict, me_image_path) else: logger.warning(f"错误: 学生图片文件夹不存在 {student_image_folder}") # --- 页面 4 --- - class_image_path = os.path.join(config["image_folder"], config["class_name"] + ".jpg") + class_image_path = find_image_path(config["image_folder"], config["class_name"]) if os.path.exists(class_image_path): replace_four_page(prs, class_image_path) else: logger.warning(f"错误: 班级图片文件不存在 {class_image_path}") - # 原逻辑中如果班级图片不存在会 continue 跳过保存,这里保持一致 - continue - # --- 页面 5 --- + # --- 页面 5 --- if os.path.exists(student_image_folder): - image1 = os.path.join(student_image_folder, "1.jpg") - image2 = os.path.join(student_image_folder, "2.jpg") # 注意:原代码逻辑也是两张一样的图 - replace_five_page(prs, image1, image2) + img1_path = find_image_path(student_image_folder, "1") + img2_path = find_image_path(student_image_folder, "2") + # 逻辑优化: + # 情况A: 两张都找到了 -> 正常插入 + if img1_path and img2_path: + replace_five_page(prs, img1_path, img2_path) + + # 情况B: 只找到了 1 -> 两张图都用 1 (避免报错) + elif img1_path and not img2_path: + replace_five_page(prs, img1_path, img1_path) + # 情况C: 一张都没找到 + else: + logger.warning(f"⚠️ 警告: {name} 缺少生活照片 (1.jpg/png 或 2.jpg/png)[/]") else: logger.warning(f"错误: 学生图片文件夹不存在 {student_image_folder}") diff --git a/utils/image_utils.py b/utils/image_utils.py new file mode 100644 index 0000000..67dff08 --- /dev/null +++ b/utils/image_utils.py @@ -0,0 +1,28 @@ +import os + + +def find_image_path(folder, base_filename): + """ + 在指定文件夹中查找图片,支持 jpg/jpeg/png 等多种后缀。 + :param folder: 文件夹路径 + :param base_filename: 基础文件名(不带后缀,例如 "1" 或 "me_image") + :return: 找到的完整文件路径,如果没找到则返回 None + """ + if not os.path.exists(folder): + return None + + # 支持的后缀列表 (包含大小写) + extensions = [ + ".jpg", ".JPG", + ".jpeg", ".JPEG", + ".png", ".PNG", + ".bmp", ".BMP" + ] + + for ext in extensions: + # 拼凑完整路径,例如 data/images/张三/1.png + full_path = os.path.join(folder, base_filename + ext) + if os.path.exists(full_path): + return full_path + + return None