import os import sys import io import matplotlib from matplotlib import pyplot as plt import re def format_ranges(nums): """格式化区间,支持特殊标记""" if not nums: return "" def parse_value(val): """解析值,返回主数值和次要标记""" if isinstance(val, int): return val, "" match = re.match(r"(\d+)[((](.*?)[))]", str(val)) if match: return int(match.group(1)), match.group(2) return int(val), "" # 排序规则:主数值优先,次要标记其次 sorted_nums = sorted(nums, key=lambda x: parse_value(x)) result = [] start = sorted_nums[0] end = sorted_nums[0] def is_continuous(val1, val2): """判断两个值是否连续""" main1, sub1 = parse_value(val1) main2, sub2 = parse_value(val2) # 主数值连续,且次要标记为空(特殊标记视为不连续) return main2 == main1 + 1 and not sub2 for i in range(1, len(sorted_nums)): if is_continuous(end, sorted_nums[i]): end = sorted_nums[i] else: # 处理一个区间 if parse_value(start)[0] == parse_value(end)[0]: # 单值或特殊标记 result.append(str(start)) elif parse_value(end)[0] - parse_value(start)[0] >= 2: # 连续区间 result.append(f"{parse_value(start)[0]}~{parse_value(end)[0]}") else: # 非连续的单值 result.extend(map(str, [start, end])) start = end = sorted_nums[i] # 添加最后一段区间 if parse_value(start)[0] == parse_value(end)[0]: # 单值或特殊标记 result.append(str(start)) elif parse_value(end)[0] - parse_value(start)[0] >= 2: # 连续区间 result.append(f"{parse_value(start)[0]}~{parse_value(end)[0]}") else: # 非连续的单值 result.extend(map(str, [start, end])) return "、".join(result) def support_version_str(lst: list[str]) -> str: """支持的版本字符串""" if len(lst) == 1: return lst[0] elif len(lst) == 2: return f"{lst[0]}、{lst[1]}" else: return f"{lst[0]}~{lst[-1]}" def get_rank(score: int | float) -> str: """根据分数获取等级""" if score >= 0.9: return "优秀" elif score >= 0.8: return "良好" elif score >= 0.7: return "中等" elif score >= 0.6: return "及格" else: return "不及格" def min_score_people_name(data: list, kpi_number: int, start_index: int, end_index: int) -> list[tuple[str]]: """获取最低分人名""" # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 # 20060510XX 覃XX 40.5 45 49 0.738 16 17 0 0.330 24.5 15 14 0.695 remove_indices = [5, 9, 13, 17, 21] # 更新后的数据 # 0 1 2 3 4 5 6 7 8 9 10 # 20060510XX 覃XX 40.5 45 49 16 17 0 24.5 15 14 index = 2 + kpi_number * 3 updated_data = [[item for j, item in enumerate(row) if j not in remove_indices] for row in data] result = [(), (), ()] min_scores = [min(updated_data[start_index:end_index], key=lambda x: x[index + i] if x[index + i] is not None else 0)[index + i] for i in range(3)] for row in updated_data[start_index:end_index]: for i in range(3): if row[index + i] == min_scores[i]: result[i] += (row[1],) return result def get_class_index_range(lst: list[int], num: int): """获取班级的索引范围""" if num < 0 or num > len(lst): raise ValueError("OutOfRange") # 计算前num-1项和加一 first_value = sum(lst[:num]) # 计算前num项和加一 second_value = sum(lst[:num + 1]) if num < len(lst) else "OutOfRange" return (first_value, second_value) def check_version(version, compatible_versions): # 将版本字符串转换为数字列表以便比较 version_nums = [int(v) for v in version.split('.')] min_version_nums = [int(v) for v in compatible_versions[0].split('.')] max_version_nums = [int(v) for v in compatible_versions[-1].split('.')] # 比较版本 if version_nums < min_version_nums: return False, "不支持低于{}的版本,如需尝试生成请勾选‘关闭兼容性检查’".format(compatible_versions[0]) elif version_nums > max_version_nums: return True, "版本过高可能会导致兼容性问题" else: return True, "" def gen_picture(data: list[list[str]], kpi_number: int, start_index: int, end_index: int): # 设置全局字体 matplotlib.rcParams['font.family'] = ['Times New Roman', 'KaiTi'] matplotlib.rcParams['font.size'] = 10 # 将厘米转换为英寸 width_in_inches = 8 * 0.393701 height_in_inches = 6 * 0.393701 # 创建一个图形和三个子图 fig, axs = plt.subplots(1, kpi_number, figsize=(width_in_inches * kpi_number, height_in_inches)) # 遍历每个指定的索引来生成散点图 for ax, index in zip(axs, range(kpi_number)): # 处理数据 data_indices = [5, 9, 13, 17, 21] update_data = [[float(item) for j, item in enumerate(row) if j in data_indices] for row in data] x = [i for i in range(end_index - start_index)] y = [row[index] for row in update_data[start_index:end_index]] # 绘制散点图 ax.scatter(x, y, label=f'目标{index + 1}达成值', s=10) # 添加期望值直线 ax.axhline(y=0.65, color='r', linestyle='-', label='期望值') # 设置轴标签 ax.set_xlabel('学生学号') ax.set_ylabel('达成值') # 设置y轴范围和刻度 ax.set_ylim(0, 1) ax.set_yticks([i * 0.2 for i in range(6)]) # 设置图例 ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.35), ncol=2, frameon=False) # 设置坐标原点为(0, 0) ax.set_xlim(x.index(x[0]), None) # 调整子图间距 plt.tight_layout() # 创建一个内存中的文件对象 buf = io.BytesIO() # 保存图形 plt.savefig(buf, format='png', dpi=300, bbox_inches='tight') # 将文件对象转换为字节对象 res = buf.getvalue() buf.close() return res if __name__ == '__main__': nums = [1, 2, 3, '4(1)', '4(2)'] formatted_ranges = format_ranges(nums) print(formatted_ranges) def resource_path(relative_path: str) -> str: if hasattr(sys, '_MEIPASS'): base_path = sys._MEIPASS else: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path)