mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 07:23:03 +08:00
refactor(config): 引入 ConfigPath 值对象消除 ConfigType/ConfigKey 的消费者 API 冗余
This commit is contained in:
@@ -24,6 +24,7 @@ import managers.config.ConfigManager as ConfigManager
|
|||||||
|
|
||||||
from utils.JSONReader import JSONReader
|
from utils.JSONReader import JSONReader
|
||||||
from utils.JSONWriter import JSONWriter
|
from utils.JSONWriter import JSONWriter
|
||||||
|
from interfaces.ConfigProvider import ConfigProvider, CfgKey
|
||||||
from managers.config.ConfigUtils import ConfigUtils
|
from managers.config.ConfigUtils import ConfigUtils
|
||||||
|
|
||||||
from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget
|
from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget
|
||||||
@@ -43,7 +44,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.__cfg_mgr = ConfigManager.instance()
|
self.__cfg_mgr: ConfigProvider = ConfigManager.instance()
|
||||||
self.__config_paths = ConfigUtils.getAutomationConfigPaths()
|
self.__config_paths = ConfigUtils.getAutomationConfigPaths()
|
||||||
self.__config_data = {"run": {}, "user": {}}
|
self.__config_data = {"run": {}, "user": {}}
|
||||||
|
|
||||||
@@ -985,13 +986,13 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.setRunConfigToWidget(data)
|
self.setRunConfigToWidget(data)
|
||||||
self.__config_paths["run"] = run_config_path
|
self.__config_paths["run"] = run_config_path
|
||||||
self.CurrentRunConfigEdit.setText(run_config_path)
|
self.CurrentRunConfigEdit.setText(run_config_path)
|
||||||
paths = self.__cfg_mgr.get(ConfigManager.ConfigType.GLOBAL, "automation.run_path.paths", [])
|
paths = self.__cfg_mgr.get(CfgKey.GLOBAL.AUTOMATION.RUN_PATH.PATHS, [])
|
||||||
if run_config_path not in paths:
|
if run_config_path not in paths:
|
||||||
paths.append(run_config_path)
|
paths.append(run_config_path)
|
||||||
index = len(paths) - 1
|
index = len(paths) - 1
|
||||||
else:
|
else:
|
||||||
index = paths.index(run_config_path)
|
index = paths.index(run_config_path)
|
||||||
self.__cfg_mgr.set(ConfigManager.ConfigType.GLOBAL, "automation.run_path", {"current": index, "paths": paths})
|
self.__cfg_mgr.set(CfgKey.GLOBAL.AUTOMATION.RUN_PATH.ROOT, {"current": index, "paths": paths})
|
||||||
else:
|
else:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
@@ -1020,13 +1021,13 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.setUsersToTreeWidget(data)
|
self.setUsersToTreeWidget(data)
|
||||||
self.__config_paths["user"] = user_config_path
|
self.__config_paths["user"] = user_config_path
|
||||||
self.CurrentUserConfigEdit.setText(user_config_path)
|
self.CurrentUserConfigEdit.setText(user_config_path)
|
||||||
paths = self.__cfg_mgr.get(ConfigManager.ConfigType.GLOBAL, "automation.user_path.paths", [])
|
paths = self.__cfg_mgr.get(CfgKey.GLOBAL.AUTOMATION.USER_PATH.PATHS, [])
|
||||||
if user_config_path not in paths:
|
if user_config_path not in paths:
|
||||||
paths.append(user_config_path)
|
paths.append(user_config_path)
|
||||||
index = len(paths) - 1
|
index = len(paths) - 1
|
||||||
else:
|
else:
|
||||||
index = paths.index(user_config_path)
|
index = paths.index(user_config_path)
|
||||||
self.__cfg_mgr.set(ConfigManager.ConfigType.GLOBAL, "automation.user_path", {"current": index, "paths": paths})
|
self.__cfg_mgr.set(CfgKey.GLOBAL.AUTOMATION.USER_PATH.ROOT, {"current": index, "paths": paths})
|
||||||
else:
|
else:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ from PySide6.QtGui import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import managers.config.ConfigManager as ConfigManager
|
import managers.config.ConfigManager as ConfigManager
|
||||||
|
|
||||||
from utils.TimerUtils import TimerUtils
|
from utils.TimerUtils import TimerUtils
|
||||||
|
from interfaces.ConfigProvider import ConfigProvider, CfgKey
|
||||||
|
|
||||||
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
|
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
|
||||||
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
|
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
|
||||||
@@ -190,7 +192,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.__cfg_mgr = ConfigManager.instance()
|
self.__cfg_mgr: ConfigProvider = ConfigManager.instance()
|
||||||
self.__timer_tasks = []
|
self.__timer_tasks = []
|
||||||
self.__check_timer = None
|
self.__check_timer = None
|
||||||
self.__sort_policy = self.SortPolicy.BY_EXECUTE_TIME
|
self.__sort_policy = self.SortPolicy.BY_EXECUTE_TIME
|
||||||
@@ -244,7 +246,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
) -> list:
|
) -> list:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
timer_tasks = self.__cfg_mgr.get(ConfigManager.ConfigType.TIMERTASK)
|
timer_tasks = self.__cfg_mgr.get(CfgKey.TIMERTASK.ROOT)
|
||||||
if timer_tasks and "timer_tasks" in timer_tasks:
|
if timer_tasks and "timer_tasks" in timer_tasks:
|
||||||
for task in timer_tasks["timer_tasks"]:
|
for task in timer_tasks["timer_tasks"]:
|
||||||
task["added_time"] = datetime.strptime(task["added_time"], "%Y-%m-%d %H:%M:%S")
|
task["added_time"] = datetime.strptime(task["added_time"], "%Y-%m-%d %H:%M:%S")
|
||||||
@@ -277,7 +279,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
if "repeat_history" in task:
|
if "repeat_history" in task:
|
||||||
for item in task["repeat_history"]:
|
for item in task["repeat_history"]:
|
||||||
item["result"] = item["result"].value
|
item["result"] = item["result"].value
|
||||||
self.__cfg_mgr.set(ConfigManager.ConfigType.TIMERTASK, "", { "timer_tasks": timer_tasks })
|
self.__cfg_mgr.set(CfgKey.TIMERTASK.ROOT, { "timer_tasks": timer_tasks })
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
@@ -437,7 +439,7 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
|||||||
f"下次执行时间:{datetime.strftime(timer_task["execute_time"], "%Y-%m-%d %H:%M:%S")}\n"
|
f"下次执行时间:{datetime.strftime(timer_task["execute_time"], "%Y-%m-%d %H:%M:%S")}\n"
|
||||||
f"已记录次数:{history_count}"
|
f"已记录次数:{history_count}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def deleteTask(
|
def deleteTask(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2026 KenanZhu.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
|
See the LICENSE file for details.
|
||||||
|
"""
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigType(Enum):
|
||||||
|
"""
|
||||||
|
Config type enum. Values represent the default filename.
|
||||||
|
"""
|
||||||
|
GLOBAL = "autolibrary.json"
|
||||||
|
BULLETIN = "bulletin.json"
|
||||||
|
TIMERTASK = "timer_task.json"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ConfigPath:
|
||||||
|
"""
|
||||||
|
A typed configuration path that carries both the config file
|
||||||
|
and the dot-separated key in a single object.
|
||||||
|
|
||||||
|
Consumers pass this directly to ConfigProvider.get/set,
|
||||||
|
eliminating the need to import ConfigType separately.
|
||||||
|
"""
|
||||||
|
config_type: ConfigType
|
||||||
|
key: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class CfgKey:
|
||||||
|
"""
|
||||||
|
Type-safe hierarchical configuration key constants.
|
||||||
|
|
||||||
|
Each leaf is a ConfigPath that can be passed directly to
|
||||||
|
``ConfigProvider.get()`` or ``ConfigProvider.set()``.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
CfgKey.GLOBAL.AUTOMATION.RUN_PATH.PATHS
|
||||||
|
# -> ConfigPath(ConfigType.GLOBAL, "automation.run_path.paths")
|
||||||
|
|
||||||
|
config.get(CfgKey.GLOBAL.AUTOMATION.RUN_PATH.PATHS, [])
|
||||||
|
config.set(CfgKey.GLOBAL.AUTOMATION.RUN_PATH.PATHS, value)
|
||||||
|
"""
|
||||||
|
|
||||||
|
class GLOBAL:
|
||||||
|
class AUTOMATION:
|
||||||
|
ROOT = ConfigPath(ConfigType.GLOBAL, "automation")
|
||||||
|
|
||||||
|
class RUN_PATH:
|
||||||
|
ROOT = ConfigPath(ConfigType.GLOBAL, "automation.run_path")
|
||||||
|
CURRENT = ConfigPath(ConfigType.GLOBAL, "automation.run_path.current")
|
||||||
|
PATHS = ConfigPath(ConfigType.GLOBAL, "automation.run_path.paths")
|
||||||
|
|
||||||
|
class USER_PATH:
|
||||||
|
ROOT = ConfigPath(ConfigType.GLOBAL, "automation.user_path")
|
||||||
|
CURRENT = ConfigPath(ConfigType.GLOBAL, "automation.user_path.current")
|
||||||
|
PATHS = ConfigPath(ConfigType.GLOBAL, "automation.user_path.paths")
|
||||||
|
|
||||||
|
class TIMERTASK:
|
||||||
|
ROOT = ConfigPath(ConfigType.TIMERTASK, "")
|
||||||
|
TIMER_TASKS = ConfigPath(ConfigType.TIMERTASK, "timer_tasks")
|
||||||
|
|
||||||
|
class BULLETIN:
|
||||||
|
ROOT = ConfigPath(ConfigType.BULLETIN, "")
|
||||||
|
BULLETIN = ConfigPath(ConfigType.BULLETIN, "bulletin")
|
||||||
|
LAST_SYNC_TIME = ConfigPath(ConfigType.BULLETIN, "last_sync_time")
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigProvider(Protocol):
|
||||||
|
"""
|
||||||
|
Abstract interface for configuration storage access.
|
||||||
|
|
||||||
|
Concrete implementations (e.g. ConfigManager) conform to
|
||||||
|
this protocol structurally rather than through explicit
|
||||||
|
inheritance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self,
|
||||||
|
key: ConfigPath,
|
||||||
|
default: Optional[Any] = None
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Retrieve a configuration value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: A ConfigPath object specifying which config file
|
||||||
|
and key to read from.
|
||||||
|
default: Fallback value if the key is not found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The configuration value at the given key path.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set(
|
||||||
|
self,
|
||||||
|
key: ConfigPath,
|
||||||
|
value: Any = None
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Set a configuration value and persist to disk.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: A ConfigPath object specifying which config file
|
||||||
|
and key to write to.
|
||||||
|
value: The value to store.
|
||||||
|
"""
|
||||||
|
...
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
Interfaces module for the AutoLibrary project.
|
||||||
|
|
||||||
|
Defines abstract interfaces (Protocols) and shared type definitions
|
||||||
|
used across layers to decouple consumers from concrete implementations.
|
||||||
|
|
||||||
|
Key components:
|
||||||
|
- ConfigProvider: Abstract interface for configuration access.
|
||||||
|
- ConfigType: Enumeration of configuration file types.
|
||||||
|
- ConfigKey: Type-safe hierarchical key constants for config lookups.
|
||||||
|
"""
|
||||||
@@ -10,26 +10,17 @@ See the LICENSE file for details.
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from utils.JSONReader import JSONReader
|
from utils.JSONReader import JSONReader
|
||||||
from utils.JSONWriter import JSONWriter
|
from utils.JSONWriter import JSONWriter
|
||||||
|
from interfaces.ConfigProvider import ConfigType, ConfigPath
|
||||||
|
|
||||||
|
|
||||||
# This config manager class only responsible for global and other
|
# This config manager class only responsible for global and other
|
||||||
# unconfigurable config files.
|
# unconfigurable config files.
|
||||||
|
|
||||||
|
|
||||||
class ConfigType(Enum):
|
|
||||||
"""
|
|
||||||
Config type class. Values represent the default filename.
|
|
||||||
"""
|
|
||||||
GLOBAL = "autolibrary.json" # Global config file.
|
|
||||||
BULLETIN = "bulletin.json" # Bulletin board config file.
|
|
||||||
TIMERTASK = "timer_task.json" # Timer task config file.
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigTemplate:
|
class ConfigTemplate:
|
||||||
"""
|
"""
|
||||||
Config template class.
|
Config template class.
|
||||||
@@ -120,16 +111,15 @@ class ConfigManager:
|
|||||||
|
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
config_type: ConfigType,
|
key: ConfigPath,
|
||||||
key: str = "",
|
|
||||||
default: Optional[Any] = None
|
default: Optional[Any] = None
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
|
||||||
with self.__config_lock:
|
with self.__config_lock:
|
||||||
config_data = self.__config_data[config_type.value]
|
config_data = self.__config_data[key.config_type.value]
|
||||||
if key == "":
|
if key.key == "":
|
||||||
return config_data
|
return config_data
|
||||||
keys = key.split('.')
|
keys = key.key.split('.')
|
||||||
for k in keys[:-1]:
|
for k in keys[:-1]:
|
||||||
config_data = config_data.get(k, None)
|
config_data = config_data.get(k, None)
|
||||||
if config_data is None:
|
if config_data is None:
|
||||||
@@ -139,24 +129,23 @@ class ConfigManager:
|
|||||||
|
|
||||||
def set(
|
def set(
|
||||||
self,
|
self,
|
||||||
config_type: ConfigType,
|
key: ConfigPath,
|
||||||
key: str = "",
|
|
||||||
value: Any = None
|
value: Any = None
|
||||||
):
|
):
|
||||||
|
|
||||||
with self.__config_lock:
|
with self.__config_lock:
|
||||||
root_data = self.__config_data[config_type.value]
|
root_data = self.__config_data[key.config_type.value]
|
||||||
if key == "":
|
if key.key == "":
|
||||||
self.__config_data[config_type.value] = value
|
self.__config_data[key.config_type.value] = value
|
||||||
else:
|
else:
|
||||||
keys = key.split('.')
|
keys = key.key.split('.')
|
||||||
config_data = root_data
|
config_data = root_data
|
||||||
for k in keys[:-1]:
|
for k in keys[:-1]:
|
||||||
if k not in config_data:
|
if k not in config_data:
|
||||||
config_data[k] = {}
|
config_data[k] = {}
|
||||||
config_data = config_data[k]
|
config_data = config_data[k]
|
||||||
config_data[keys[-1]] = value
|
config_data[keys[-1]] = value
|
||||||
self.save(config_type)
|
self.save(key.config_type)
|
||||||
|
|
||||||
|
|
||||||
def save(
|
def save(
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import os
|
|||||||
|
|
||||||
import managers.config.ConfigManager as ConfigManager
|
import managers.config.ConfigManager as ConfigManager
|
||||||
|
|
||||||
|
from interfaces.ConfigProvider import CfgKey
|
||||||
|
|
||||||
class ConfigUtils:
|
class ConfigUtils:
|
||||||
"""
|
"""
|
||||||
Config utilities class.
|
Config utilities class.
|
||||||
@@ -29,7 +31,7 @@ class ConfigUtils:
|
|||||||
cfg_mgr = ConfigManager.instance() # config manager instance
|
cfg_mgr = ConfigManager.instance() # config manager instance
|
||||||
|
|
||||||
config_paths = {"run": "", "user": ""}
|
config_paths = {"run": "", "user": ""}
|
||||||
auto_config = cfg_mgr.get(ConfigManager.ConfigType.GLOBAL, "automation", {})
|
auto_config = cfg_mgr.get(CfgKey.GLOBAL.AUTOMATION.ROOT, {})
|
||||||
for cfg_type in ["run", "user"]:
|
for cfg_type in ["run", "user"]:
|
||||||
paths = auto_config.get(f"{cfg_type}_path", {}).get("paths", [])
|
paths = auto_config.get(f"{cfg_type}_path", {}).get("paths", [])
|
||||||
index = auto_config.get(f"{cfg_type}_path", {}).get("current", 0)
|
index = auto_config.get(f"{cfg_type}_path", {}).get("current", 0)
|
||||||
@@ -42,5 +44,5 @@ class ConfigUtils:
|
|||||||
config_paths[cfg_type] = paths[index]
|
config_paths[cfg_type] = paths[index]
|
||||||
data = {"current": index, "paths": paths}
|
data = {"current": index, "paths": paths}
|
||||||
auto_config[f"{cfg_type}_path"] = data
|
auto_config[f"{cfg_type}_path"] = data
|
||||||
cfg_mgr.set(ConfigManager.ConfigType.GLOBAL, "automation", auto_config)
|
cfg_mgr.set(CfgKey.GLOBAL.AUTOMATION.ROOT, auto_config)
|
||||||
return config_paths
|
return config_paths
|
||||||
|
|||||||
Reference in New Issue
Block a user