-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsettings.py
More file actions
145 lines (121 loc) · 4.57 KB
/
settings.py
File metadata and controls
145 lines (121 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import json
import os
import sys
from dataclasses import asdict, dataclass, field
from pathlib import Path
from app_info import APP_NAME
SETTINGS_FILENAME = "settings.json"
STARTUP_SCRIPT_NAME = "autocorrect-launch.cmd"
@dataclass
class AppSettings:
enabled: bool = True
launch_at_startup: bool = False
ignored_words: list[str] = field(default_factory=list)
custom_replacements: dict[str, str] = field(default_factory=dict)
def get_app_dir() -> Path:
base_dir = os.environ.get("LOCALAPPDATA")
if base_dir:
return Path(base_dir) / APP_NAME
return Path.home() / f".{APP_NAME.lower()}"
def get_settings_path() -> Path:
return get_app_dir() / SETTINGS_FILENAME
def get_startup_script_path() -> Path:
appdata = os.environ.get("APPDATA")
if not appdata:
raise RuntimeError("APPDATA is not available on this system.")
return (
Path(appdata)
/ "Microsoft"
/ "Windows"
/ "Start Menu"
/ "Programs"
/ "Startup"
/ STARTUP_SCRIPT_NAME
)
class SettingsManager:
def __init__(self) -> None:
self.path = get_settings_path()
def load(self) -> AppSettings:
if not self.path.exists():
return AppSettings()
try:
data = json.loads(self.path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return AppSettings()
return AppSettings(
enabled=bool(data.get("enabled", True)),
launch_at_startup=bool(data.get("launch_at_startup", False)),
ignored_words=self._normalize_words(data.get("ignored_words", [])),
custom_replacements=self._normalize_replacements(
data.get("custom_replacements", {})
),
)
def save(self, settings: AppSettings) -> None:
self.path.parent.mkdir(parents=True, exist_ok=True)
self.path.write_text(
json.dumps(asdict(settings), indent=2, sort_keys=True),
encoding="utf-8",
)
def export_to(self, target_path: str | Path, settings: AppSettings) -> Path:
export_path = Path(target_path)
export_path.parent.mkdir(parents=True, exist_ok=True)
export_path.write_text(
json.dumps(asdict(settings), indent=2, sort_keys=True),
encoding="utf-8",
)
return export_path
def import_from(self, source_path: str | Path) -> AppSettings:
source = Path(source_path)
data = json.loads(source.read_text(encoding="utf-8"))
return AppSettings(
enabled=bool(data.get("enabled", True)),
launch_at_startup=bool(data.get("launch_at_startup", False)),
ignored_words=self._normalize_words(data.get("ignored_words", [])),
custom_replacements=self._normalize_replacements(
data.get("custom_replacements", {})
),
)
@staticmethod
def _normalize_words(words) -> list[str]:
cleaned: list[str] = []
for word in words:
if not isinstance(word, str):
continue
stripped = word.strip().lower()
if stripped:
cleaned.append(stripped)
return sorted(set(cleaned))
@staticmethod
def _normalize_replacements(replacements) -> dict[str, str]:
if not isinstance(replacements, dict):
return {}
cleaned: dict[str, str] = {}
for wrong, corrected in replacements.items():
if not isinstance(wrong, str) or not isinstance(corrected, str):
continue
wrong_key = wrong.strip().lower()
corrected_value = corrected.strip()
if wrong_key and corrected_value:
cleaned[wrong_key] = corrected_value
return dict(sorted(cleaned.items()))
def ensure_startup_state(self, enabled: bool) -> None:
startup_path = get_startup_script_path()
if enabled:
startup_path.parent.mkdir(parents=True, exist_ok=True)
startup_path.write_text(self._build_startup_script(), encoding="utf-8")
elif startup_path.exists():
startup_path.unlink()
@staticmethod
def _build_startup_script() -> str:
target = Path(sys.executable)
if getattr(sys, "frozen", False):
launch_command = f'start "" "{target}"'
else:
script_path = Path(__file__).resolve().parent / "main.py"
launch_command = f'start "" "{target}" "{script_path}"'
return "\n".join(
[
"@echo off",
launch_command,
]
)