Skip to content

Commit 50f3190

Browse files
committed
优化路径
1 parent 39ce626 commit 50f3190

9 files changed

Lines changed: 136 additions & 62 deletions

File tree

CHANGELOG/v2.2.1/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ v2.0 - Koharu(小鸟游星野) release 4
1616
- 优化 **结果显示**,提升**颜色/图片**性能
1717
- 优化 **数据导出**,统一**通用导出**逻辑
1818
- 优化 **抽奖动画**,过程同步显示**学生**
19+
- 优化 **数据导入**,增加更多导入数据文件夹
20+
- 优化 **路径统一**,统一更多文件的**路径处理**
1921

2022
## 🐛 修复问题
2123

2224
- 修复 **抽取逻辑**,修复**类型/参数**错误
2325
- 修复 **学生导出**,修复**AttributeError**
2426
- 修复 **音频模块**,修复**初始化/异常**问题
2527
- 修复 **配置读取**,修复**空文件解析**错误
28+
- 修复 **数据导入**,避免占用**失败**
2629
- 修复 **抽奖显示**,修复格式映射**KeyError**
2730
- 修复 **通知显示**,修复**父组件/回调**异常
2831
- 修复 **抽奖通知**,修复小组显示并统一**分隔符**

app/Language/modules/basic_settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@
173173
"import_success_content": {
174174
"name": "数据已成功导入\n重启应用程序以使更改生效"
175175
},
176+
"import_success_content_skipped": {
177+
"name": "数据已成功导入(跳过 {count} 个占用文件)\n重启应用程序以使更改生效"
178+
},
176179
"import_success_button": {"name": "我知道了"},
177180
"import_failure_title": {"name": "导入所有数据"},
178181
"import_failure_content": {"name": "导入所有数据失败:\n{error}"},
@@ -378,6 +381,9 @@
378381
"import_success_content": {
379382
"name": "Data imported successfully into\nRestart APP to take effect"
380383
},
384+
"import_success_content_skipped": {
385+
"name": "Data imported successfully (skipped {count} locked files)\nRestart APP to take effect"
386+
},
381387
"import_success_button": {"name": "Got it"},
382388
"import_failure_title": {"name": "Import all data"},
383389
"import_failure_content": {"name": "Failed to import all data: \n{error}"},

app/common/extraction/extract.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import json
2-
import os
3-
from pathlib import Path
42
from typing import Dict, Tuple
53

64
from PySide6.QtCore import QDateTime
@@ -233,19 +231,19 @@ def _get_cses_parser() -> CSESParser | None:
233231
"""
234232
try:
235233
cses_dir = get_data_path("CSES")
236-
if not os.path.exists(cses_dir):
234+
if not cses_dir.exists():
237235
logger.info("CSES目录不存在")
238236
return None
239237

240-
cses_file_path = os.path.join(cses_dir, "cses_schedule.yml")
238+
cses_file_path = cses_dir / "cses_schedule.yml"
241239

242-
if not os.path.exists(cses_file_path):
240+
if not cses_file_path.exists():
243241
logger.info("CSES文件不存在")
244242
return None
245243

246244
parser = CSESParser()
247-
if not parser.load_from_file(cses_file_path):
248-
logger.exception(f"加载CSES文件失败: {cses_file_path}")
245+
if not parser.load_from_file(str(cses_file_path)):
246+
logger.exception(f"加载CSES文件失败: {str(cses_file_path)}")
249247
return None
250248

251249
return parser
@@ -449,12 +447,11 @@ def import_cses_schedule(file_path: str) -> Tuple[bool, str]:
449447
"linkage_settings", "no_valid_time_periods"
450448
)
451449

452-
original_file_name = Path(file_path).name
453450
cses_data_path = get_data_path("CSES", "cses_schedule.yml")
454451
ensure_dir(get_data_path("CSES"))
455452
import shutil
456453

457-
shutil.copy2(file_path, cses_data_path)
454+
shutil.copy2(get_path(file_path), cses_data_path)
458455
logger.info(f"已将CSES文件保存到: {cses_data_path}")
459456

460457
summary = parser.get_summary()

app/tools/config.py

Lines changed: 102 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from datetime import datetime
1010
import platform
1111
import sys
12+
import time
1213
import psutil
1314

1415
from PySide6.QtWidgets import QWidget, QFileDialog
@@ -940,11 +941,14 @@ def _export_diagnostic_files(file_path: str, app_dir: Path) -> int:
940941
导出的文件数量
941942
"""
942943
export_folders = [
943-
Path("config"),
944-
Path("app") / "data" / "list",
945-
Path("app") / "data" / "Language",
946-
Path("app") / "data" / "history",
947-
Path("logs"),
944+
get_path("config"),
945+
get_data_path("list"),
946+
get_data_path("Language"),
947+
get_data_path("history"),
948+
get_data_path("audio"),
949+
get_data_path("CSES"),
950+
get_data_path("images"),
951+
get_path(LOG_DIR),
948952
]
949953

950954
exported_count = 0
@@ -1061,11 +1065,14 @@ def _collect_system_info(
10611065
"export_folders": [
10621066
str(folder)
10631067
for folder in [
1064-
Path("config"),
1065-
Path("app") / "data" / "list",
1066-
Path("app") / "data" / "Language",
1067-
Path("app") / "data" / "history",
1068-
Path("logs"),
1068+
get_path("config"),
1069+
get_data_path("list"),
1070+
get_data_path("Language"),
1071+
get_data_path("history"),
1072+
get_data_path("audio"),
1073+
get_data_path("CSES"),
1074+
get_data_path("images"),
1075+
get_path(LOG_DIR),
10691076
]
10701077
],
10711078
"export_location": str(file_path),
@@ -1272,11 +1279,14 @@ def export_all_data(parent: Optional[QWidget] = None) -> None:
12721279
file_path += ".zip"
12731280

12741281
dirs_to_backup = [
1275-
("config", Path("config")),
1276-
("list", Path("app") / "data" / "list"),
1277-
("Language", Path("app") / "data" / "Language"),
1278-
("history", Path("app") / "data" / "history"),
1279-
("logs", Path("logs")),
1282+
("config", get_path("config")),
1283+
("list", get_data_path("list")),
1284+
("Language", get_data_path("Language")),
1285+
("history", get_data_path("history")),
1286+
("audio", get_data_path("audio")),
1287+
("CSES", get_data_path("CSES")),
1288+
("images", get_data_path("images")),
1289+
("logs", get_path(LOG_DIR)),
12801290
]
12811291

12821292
with zipfile.ZipFile(file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
@@ -1360,7 +1370,7 @@ def import_all_data(parent: Optional[QWidget] = None) -> None:
13601370
if not _confirm_import(parent):
13611371
return
13621372

1363-
_extract_data_files(file_path)
1373+
skipped_files = _extract_data_files(file_path)
13641374

13651375
success_dialog = MessageBox(
13661376
get_any_position_value_async(
@@ -1369,11 +1379,20 @@ def import_all_data(parent: Optional[QWidget] = None) -> None:
13691379
"import_success_title",
13701380
"name",
13711381
),
1372-
get_any_position_value_async(
1373-
"basic_settings",
1374-
"data_import_export",
1375-
"import_success_content",
1376-
"name",
1382+
(
1383+
get_any_position_value_async(
1384+
"basic_settings",
1385+
"data_import_export",
1386+
"import_success_content_skipped",
1387+
"name",
1388+
).format(count=len(skipped_files))
1389+
if skipped_files
1390+
else get_any_position_value_async(
1391+
"basic_settings",
1392+
"data_import_export",
1393+
"import_success_content",
1394+
"name",
1395+
)
13771396
),
13781397
parent,
13791398
)
@@ -1480,11 +1499,14 @@ def _check_existing_files(file_path: str) -> list:
14801499
"""检查已存在的文件"""
14811500
existing_files = []
14821501
target_dirs = {
1483-
"config": Path("config"),
1484-
"list": Path("data/list"),
1485-
"Language": Path("data/Language"),
1486-
"history": Path("data/history"),
1487-
"logs": Path("logs"),
1502+
"config": get_path("config"),
1503+
"list": get_data_path("list"),
1504+
"Language": get_data_path("Language"),
1505+
"history": get_data_path("history"),
1506+
"audio": get_data_path("audio"),
1507+
"CSES": get_data_path("CSES"),
1508+
"images": get_data_path("images"),
1509+
"logs": get_path(LOG_DIR),
14881510
}
14891511

14901512
with zipfile.ZipFile(file_path, "r") as zipf:
@@ -1562,16 +1584,22 @@ def _confirm_import(parent: Optional[QWidget]) -> bool:
15621584
return dialog.exec()
15631585

15641586

1565-
def _extract_data_files(file_path: str) -> None:
1587+
def _extract_data_files(file_path: str) -> list:
15661588
"""提取数据文件"""
15671589
target_dirs = {
1568-
"config": Path("config"),
1569-
"list": Path("data/list"),
1570-
"Language": Path("data/Language"),
1571-
"history": Path("data/history"),
1572-
"logs": Path("logs"),
1590+
"config": get_path("config"),
1591+
"list": get_data_path("list"),
1592+
"Language": get_data_path("Language"),
1593+
"history": get_data_path("history"),
1594+
"CSES": get_data_path("CSES"),
1595+
"images": get_data_path("images"),
1596+
"audio": get_data_path("audio"),
1597+
"logs": get_path(LOG_DIR),
15731598
}
15741599

1600+
skipped_files = []
1601+
logs_root = get_path(LOG_DIR).resolve()
1602+
15751603
with zipfile.ZipFile(file_path, "r") as zipf:
15761604
for member in zipf.namelist():
15771605
if member == "version.json":
@@ -1586,9 +1614,50 @@ def _extract_data_files(file_path: str) -> None:
15861614
target_path = target_dirs[dir_name] / relative_path
15871615
target_path.parent.mkdir(parents=True, exist_ok=True)
15881616

1589-
with zipf.open(member) as source, open(target_path, "wb") as target:
1617+
tmp_path = target_path.with_name(f"{target_path.name}.import_tmp")
1618+
if tmp_path.exists():
1619+
try:
1620+
tmp_path.unlink()
1621+
except Exception:
1622+
pass
1623+
1624+
with zipf.open(member) as source, open(tmp_path, "wb") as target:
15901625
shutil.copyfileobj(source, target)
15911626

1627+
for attempt in range(3):
1628+
try:
1629+
os.replace(tmp_path, target_path)
1630+
break
1631+
except PermissionError as e:
1632+
if attempt < 2:
1633+
time.sleep(0.2)
1634+
continue
1635+
1636+
is_logs_file = False
1637+
try:
1638+
is_logs_file = target_path.resolve().is_relative_to(
1639+
logs_root
1640+
)
1641+
except Exception:
1642+
is_logs_file = (
1643+
str(target_path)
1644+
.replace("\\", "/")
1645+
.startswith(str(logs_root).replace("\\", "/") + "/")
1646+
)
1647+
1648+
if is_logs_file:
1649+
skipped_files.append(str(target_path))
1650+
try:
1651+
tmp_path.unlink()
1652+
except Exception:
1653+
pass
1654+
logger.warning(f"导入文件被占用,已跳过: {target_path}")
1655+
break
1656+
1657+
raise e
1658+
1659+
return skipped_files
1660+
15921661

15931662
# ==================== 记录管理模块 ====================
15941663

app/tools/update_utils.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ def check_exe_integrity(exe_path: str) -> bool:
6262
logger.debug(f"检查EXE安装程序完整性: {exe_path}")
6363

6464
# 检查文件是否存在
65-
if not Path(exe_path).exists():
65+
exe_file = get_path(exe_path)
66+
if not exe_file.exists():
6667
logger.exception(f"EXE文件不存在: {exe_path}")
6768
return False
6869

6970
# 检查文件大小
70-
file_size = Path(exe_path).stat().st_size
71+
file_size = exe_file.stat().st_size
7172
if file_size == 0:
7273
logger.exception(f"EXE文件大小为0: {exe_path}")
7374
return False
@@ -100,7 +101,7 @@ def check_update_file_integrity(file_path: str, file_type: str = None) -> bool:
100101
try:
101102
# 自动检测文件类型
102103
if file_type is None:
103-
file_ext = Path(file_path).suffix.lower()
104+
file_ext = get_path(file_path).suffix.lower()
104105
if file_ext == ".exe":
105106
file_type = "exe"
106107
else:
@@ -134,7 +135,7 @@ async def run_installer_and_exit(exe_path: str) -> bool:
134135
logger.info(f"准备运行安装程序: {exe_path}")
135136

136137
# 验证安装程序存在
137-
if not Path(exe_path).exists():
138+
if not get_path(exe_path).exists():
138139
logger.exception(f"安装程序不存在: {exe_path}")
139140
return False
140141

@@ -911,12 +912,12 @@ async def install_update_async(file_path: str) -> bool:
911912
logger.debug(f"开始安装更新文件: {file_path}")
912913

913914
# 验证更新文件存在
914-
if not Path(file_path).exists():
915+
if not get_path(file_path).exists():
915916
logger.exception(f"更新文件不存在: {file_path}")
916917
return False
917918

918919
# 检查文件类型
919-
file_ext = Path(file_path).suffix.lower()
920+
file_ext = get_path(file_path).suffix.lower()
920921

921922
# 只支持 exe 安装程序
922923
if file_ext != ".exe":
@@ -928,7 +929,7 @@ async def install_update_async(file_path: str) -> bool:
928929
logger.exception(f"安装程序不完整或已损坏: {file_path}")
929930
# 删除损坏的文件
930931
try:
931-
Path(file_path).unlink()
932+
get_path(file_path).unlink(missing_ok=True)
932933
logger.info(f"已删除损坏的安装程序: {file_path}")
933934
except Exception as e:
934935
logger.exception(f"删除损坏的安装程序失败: {e}")
@@ -1406,9 +1407,7 @@ def progress_callback(downloaded: int, total: int):
14061407
logger.debug(f"自动下载更新成功: {file_path}")
14071408

14081409
# 获取文件大小
1409-
from pathlib import Path
1410-
1411-
file_size = Path(file_path).stat().st_size
1410+
file_size = get_path(file_path).stat().st_size
14121411

14131412
def format_size(size_bytes):
14141413
"""格式化文件大小"""

app/view/another_window/remaining_list.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
get_content_name_async,
3030
)
3131
from app.tools.config import read_drawn_record, read_drawn_record_simple
32-
from app.tools.path_utils import get_data_path
32+
from app.tools.path_utils import get_data_path, get_path
3333
from app.tools.personalised import load_custom_font
3434
from app.tools.variable import (
3535
APP_INIT_DELAY,
@@ -58,7 +58,7 @@ def __init__(
5858
info_template: Optional[str],
5959
) -> None:
6060
super().__init__()
61-
self._students_file = Path(students_file)
61+
self._students_file = get_path(students_file)
6262
self._class_name = class_name
6363
self._group_index = int(group_index or 0)
6464
self._gender_index = int(gender_index or 0)

0 commit comments

Comments
 (0)