187 lines
6.5 KiB
Python
187 lines
6.5 KiB
Python
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
|
||
|
||
|
||
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)
|
||
|
||
|
||
DEVELOPMENT_ENV = getattr(sys, 'frozen', False) |