Files
StarAsh042 1042e4d1df feat(core): CSV 读取与参数归一化
CSV 读取要处理编码、列识别与旧版记录解析;参数归一化负责把界面上
可能传来的字符串容错转成 int / float / bool。
2026-10-12 09:12:00 +08:00

90 lines
3.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""旧版 CSV 文件的读取与备份工具。
4.0.0 起运行期数据(已测记录)改由 :mod:`ra_core.db` 管理,
本模块只保留两类仍在使用的功能:
- 读取旧版 CSV 记录,供一次性导入数据库(``read_rows`` / ``parse_tested_row``)
- 导入完成后把旧文件改名留底(``backup_file``)
刻意不再提供 CSV 写入能力:写入只有一个入口(数据库),避免出现两套记录来源。
"""
import csv
import os
from typing import List, Optional, Sequence, Tuple
from .constants import CSV_READ_ENCODING, LEGACY_BACKUP_SUFFIX, LOGGER
from .weights import parse_prompt, parse_weight_list
__all__ = [
"ensure_parent_dir",
"read_rows",
"parse_tested_row",
"backup_file",
]
# ----------------------------------------------------------------------
# 基础 IO
# ----------------------------------------------------------------------
def ensure_parent_dir(path: str) -> None:
"""确保父目录存在(路径没有目录部分时忽略)。"""
directory = os.path.dirname(os.path.abspath(path))
if directory and not os.path.isdir(directory):
os.makedirs(directory, exist_ok=True)
def read_rows(path: str) -> List[List[str]]:
"""读取 CSV,返回非空行列表;文件不存在或读取失败时返回空列表。"""
if not os.path.isfile(path):
return []
try:
with open(path, encoding=CSV_READ_ENCODING, newline="") as handle:
return [row for row in csv.reader(handle) if row]
except (OSError, csv.Error, UnicodeDecodeError) as error:
LOGGER.error("[RollingArtist] CSV 读取失败: %s -> %s", path, error)
return []
def backup_file(path: str, suffix: str = LEGACY_BACKUP_SUFFIX) -> Optional[str]:
"""把文件改名留底(``xxx.csv`` -> ``xxx.csv.bak``),返回新路径。
- 目标名已存在时不覆盖,自动追加序号(``.bak1``、``.bak2`` …)
- 失败只记日志并返回 None,绝不打断生成流程
"""
if not os.path.isfile(path):
return None
candidate = f"{path}{suffix}"
index = 1
while os.path.exists(candidate):
candidate = f"{path}{suffix}{index}"
index += 1
try:
os.replace(path, candidate)
except OSError as error:
LOGGER.warning("[RollingArtist] 旧文件改名失败(已跳过): %s -> %s", path, error)
return None
return candidate
def parse_tested_row(row: Sequence[str]) -> Tuple[List[str], List[float]]:
"""解析旧版已测 CSV 的一行 -> (艺术家列表, 权重列表)。
行格式为 ``[艺术家(| 分隔), 权重(, 分隔), prompt]``;
兼容更早的「仅含提示词」单列格式。
"""
artists: List[str] = []
weights: List[float] = []
if not row:
return artists, weights
if len(row) >= 2:
artists = [name.strip() for name in str(row[0]).split("|") if name.strip()]
weights = parse_weight_list(row[1])
elif str(row[0]).strip():
artists, weights = parse_prompt(row[0])
if artists and len(weights) != len(artists):
weights = [weights[index] if index < len(weights) else 1.0 for index in range(len(artists))]
return artists, weights