import os from pathlib import Path import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties from io import BytesIO import base64 from datetime import datetime import calendar def load_template(template_name): """ 从模板文件加载模板内容 Args: template_name: 模板文件名(不带路径) Returns: str: 模板内容 """ # 获取模板文件路径 plugin_dir = Path(__file__).parent.parent template_path = plugin_dir / "templates" / template_name # 检查文件是否存在 if not template_path.exists(): raise FileNotFoundError(f"模板文件不存在: {template_path}") # 读取模板内容 with open(template_path, 'r', encoding='utf-8') as f: return f.read() def flatten_field(data_list, field_name): """ 提取并扁平化指定字段的值 从一个字典列表中,收集指定字段的所有值。 如果字段的值是列表,则会被展开(扁平化)加入结果; 如果字段的值是单个元素,则直接加入结果。 Args: data_list (list[dict]): 数据列表,每个元素是字典 field_name (str): 要提取的字段名 Returns: list: 扁平化后的字段值列表 """ extracted_data = [] for item in data_list: if field_name in item and item[field_name]: # 如果是 list,展开 if isinstance(item[field_name], list): extracted_data.extend(item[field_name]) else: extracted_data.append(item[field_name]) return extracted_data def extract_fields(data_list, fields): """ 从字典列表中提取多个字段 Args: data_list (list[dict]): 包含字典的列表 fields (list[str]): 要提取的字段名列表 Returns: list[dict]: 只包含指定字段的新字典列表 """ result = [] try: for item in data_list: extracted = {field: item.get(field) for field in fields} result.append(extracted) except Exception as e: print(f"提取字段时出错: {e}") return [] return result def extract_field(data_list, field_name): """ 从列表中的字典提取指定字段的所有值 Args: data_list (list): 数据列表,每个元素是 dict field_name (str): 要提取的字段名 Returns: list: 提取出来的字段值列表 """ return [item[field_name] for item in data_list if field_name in item] def gold_to_string(gold_amount): """ 将金钱数值转换为字符串表示形式 Args: gold_amount (int): 金钱数值,单位为铜币 Returns: str: 格式化后的金钱字符串,例如 "1金2银3铜" """ if not gold_amount: return "无价格" parts = [] started = False # 标记是否已经遇到第一个非零位 bricks = gold_amount // 100000000 gold = (gold_amount % 100000000) // 10000 silver = (gold_amount % 10000) // 100 copper = gold_amount % 100 for value, unit in [(bricks, "砖"), (gold, "金"), (silver, "银"), (copper, "铜")]: if value != 0: started = True if started: parts.append(f"{value}{unit}") return "".join(parts) def plot_line_chart_base64(data, x_field, y_field, title=None, reverse_x=False): """ 根据列表数据绘制折线图,返回 base64 图片字符串。 :param data: list[dict] 数据列表 :param x_field: str X轴字段 :param y_field: str Y轴字段 :param title: str 图表标题(可选) :param reverse_x: bool 是否反转 X 轴方向(默认 False:从左往右;True:从右往左) :return: str base64 图片字符串,可直接放到 中 """ if not data: raise ValueError("数据列表不能为空") # 字体设置(支持中文) plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 提取 X / Y 数据 x_values = [str(item.get(x_field, "")) for item in data] y_values = [float(item.get(y_field, 0)) for item in data] # 创建画布 plt.figure(figsize=(8, 5)) plt.plot(x_values, y_values, marker='o', color='#4a90e2', linewidth=2) # 反转 X 轴 if reverse_x: plt.gca().invert_xaxis() # 标题与标签 if title is None: title = f"{y_field} 折线图" plt.grid(True, linestyle='--', alpha=0.5) plt.xticks(rotation=30) plt.tight_layout() # 转 Base64 buffer = BytesIO() plt.savefig(buffer, format='png', dpi=150) plt.close() img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8') return f"data:image/png;base64,{img_base64}" def build_calendar_items(start_date_str, data): start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d").date() weekday = start_date.weekday() # 周一0 ~ 周日6 weekday = (weekday + 1) % 7 # 调整:周日=0, 周一=1 ... 周六=6 result = [] row = [] # 第一行补空 for _ in range(weekday): row.append({"date": "", "items": [], "is_today": False}) # 排数据 for i, day in enumerate(data): # 识别今天 is_today = (day["date"] == start_date_str) row.append({ "date": day["day"], "items": [day], "is_today": is_today, }) if len(row) == 7: result.append(row) row = [] # 最后一行剩余补空 if row: while len(row) < 7: row.append({"date": "", "items": [], "is_today": False}) result.append(row) return result