|
1 | 1 | import sys |
| 2 | +import os |
| 3 | +import yaml |
2 | 4 | from modules.item_manager import read_item_data, write_item_data |
3 | 5 | from modules import variables as Variables |
| 6 | +from modules import status_utils |
| 7 | +from modules.scheduler import status_current_path |
4 | 8 |
|
5 | 9 | # --- Global Variables for Scripting --- |
6 | 10 | # This dictionary stores variables set by the 'set var' command. |
7 | 11 | # In a more robust system, this would be managed by the Console or a dedicated scripting engine |
8 | 12 | GLOBAL_VARS = {} |
| 13 | +ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
| 14 | +PROFILE_PATH = os.path.join(ROOT_DIR, "user", "profile", "profile.yml") |
| 15 | +TIMER_SETTINGS_PATH = os.path.join(ROOT_DIR, "user", "settings", "timer_settings.yml") |
| 16 | +TIMER_PROFILES_PATH = os.path.join(ROOT_DIR, "user", "settings", "timer_profiles.yml") |
| 17 | + |
| 18 | +def _sync_status_var_to_yaml(var_name: str, var_value: str): |
| 19 | + """ |
| 20 | + Persist `status_*` variable assignments to current_status.yml and keep |
| 21 | + runtime mirrored vars in sync. |
| 22 | + """ |
| 23 | + raw_name = str(var_name or "").strip() |
| 24 | + if not raw_name.lower().startswith("status_"): |
| 25 | + return None, None |
| 26 | + |
| 27 | + indicator = status_utils.status_slug(raw_name[len("status_"):]) |
| 28 | + if not indicator: |
| 29 | + return "Invalid status variable name.", None |
| 30 | + normalized_value, err = status_utils.canonicalize_status_value(indicator, var_value) |
| 31 | + if err: |
| 32 | + return err, None |
| 33 | + |
| 34 | + path = status_current_path() |
| 35 | + try: |
| 36 | + if os.path.exists(path): |
| 37 | + with open(path, "r", encoding="utf-8") as f: |
| 38 | + current = yaml.safe_load(f) or {} |
| 39 | + else: |
| 40 | + current = {} |
| 41 | + if not isinstance(current, dict): |
| 42 | + current = {} |
| 43 | + except Exception: |
| 44 | + current = {} |
| 45 | + |
| 46 | + current[indicator] = normalized_value |
| 47 | + try: |
| 48 | + with open(path, "w", encoding="utf-8") as f: |
| 49 | + yaml.dump(current, f, default_flow_style=False) |
| 50 | + except Exception as e: |
| 51 | + return f"Failed to write status file: {e}", None |
| 52 | + |
| 53 | + try: |
| 54 | + Variables.sync_status_vars(current) |
| 55 | + except Exception: |
| 56 | + pass |
| 57 | + return None, str(normalized_value) |
| 58 | + |
| 59 | + |
| 60 | +def _sync_nickname_var_to_profile(var_name: str, var_value: str): |
| 61 | + """ |
| 62 | + Persist nickname variable assignment to profile.yml and keep runtime var |
| 63 | + aligned with profile source-of-truth. |
| 64 | + """ |
| 65 | + raw_name = str(var_name or "").strip().lower() |
| 66 | + if raw_name != "nickname": |
| 67 | + return None, None |
| 68 | + |
| 69 | + nickname = str(var_value or "").strip() |
| 70 | + if not nickname: |
| 71 | + return "Nickname cannot be empty.", None |
| 72 | + |
| 73 | + profile = {} |
| 74 | + try: |
| 75 | + if os.path.exists(PROFILE_PATH): |
| 76 | + with open(PROFILE_PATH, "r", encoding="utf-8") as f: |
| 77 | + profile = yaml.safe_load(f) or {} |
| 78 | + if not isinstance(profile, dict): |
| 79 | + profile = {} |
| 80 | + except Exception: |
| 81 | + profile = {} |
| 82 | + |
| 83 | + profile["nickname"] = nickname |
| 84 | + try: |
| 85 | + os.makedirs(os.path.dirname(PROFILE_PATH), exist_ok=True) |
| 86 | + with open(PROFILE_PATH, "w", encoding="utf-8") as f: |
| 87 | + yaml.dump(profile, f, default_flow_style=False, sort_keys=False) |
| 88 | + except Exception as e: |
| 89 | + return f"Failed to write profile nickname: {e}", None |
| 90 | + |
| 91 | + return None, nickname |
| 92 | + |
| 93 | + |
| 94 | +def _sync_timer_profile_var_to_settings(var_name: str, var_value: str): |
| 95 | + """ |
| 96 | + Persist timer_profile variable assignment to timer_settings.yml. |
| 97 | + Validates profile exists in timer_profiles.yml when available. |
| 98 | + """ |
| 99 | + raw_name = str(var_name or "").strip().lower() |
| 100 | + if raw_name != "timer_profile": |
| 101 | + return None, None |
| 102 | + |
| 103 | + profile_name = str(var_value or "").strip() |
| 104 | + if not profile_name: |
| 105 | + return "Timer profile cannot be empty.", None |
| 106 | + |
| 107 | + profiles = {} |
| 108 | + try: |
| 109 | + if os.path.exists(TIMER_PROFILES_PATH): |
| 110 | + with open(TIMER_PROFILES_PATH, "r", encoding="utf-8") as f: |
| 111 | + profiles = yaml.safe_load(f) or {} |
| 112 | + if not isinstance(profiles, dict): |
| 113 | + profiles = {} |
| 114 | + except Exception: |
| 115 | + profiles = {} |
| 116 | + |
| 117 | + if profiles and profile_name not in profiles: |
| 118 | + # Case-insensitive convenience for profile selection. |
| 119 | + lower_map = {str(k).lower(): str(k) for k in profiles.keys()} |
| 120 | + match = lower_map.get(profile_name.lower()) |
| 121 | + if not match: |
| 122 | + return f"Unknown timer profile '{profile_name}'.", None |
| 123 | + profile_name = match |
| 124 | + |
| 125 | + settings = {} |
| 126 | + try: |
| 127 | + if os.path.exists(TIMER_SETTINGS_PATH): |
| 128 | + with open(TIMER_SETTINGS_PATH, "r", encoding="utf-8") as f: |
| 129 | + settings = yaml.safe_load(f) or {} |
| 130 | + if not isinstance(settings, dict): |
| 131 | + settings = {} |
| 132 | + except Exception: |
| 133 | + settings = {} |
| 134 | + |
| 135 | + settings["default_profile"] = profile_name |
| 136 | + try: |
| 137 | + os.makedirs(os.path.dirname(TIMER_SETTINGS_PATH), exist_ok=True) |
| 138 | + with open(TIMER_SETTINGS_PATH, "w", encoding="utf-8") as f: |
| 139 | + yaml.dump(settings, f, default_flow_style=False, sort_keys=False) |
| 140 | + except Exception as e: |
| 141 | + return f"Failed to write timer settings: {e}", None |
| 142 | + |
| 143 | + return None, profile_name |
9 | 144 |
|
10 | 145 |
|
11 | 146 | def run(args, properties): |
@@ -41,9 +176,44 @@ def run(args, properties): |
41 | 176 |
|
42 | 177 | var_assignment = args[1] |
43 | 178 | if ':' in var_assignment: |
44 | | - var_name, var_value = var_assignment.split(':', 1) |
45 | | - Variables.set_var(var_name, var_value) |
46 | | - print(f"✅. Variable '{var_name}' set to '{var_value}'.") |
| 179 | + raw_var_name, var_value = var_assignment.split(':', 1) |
| 180 | + var_name = Variables.canonical_var_name(raw_var_name) |
| 181 | + err, normalized_status_value = _sync_status_var_to_yaml(var_name, var_value) |
| 182 | + if err: |
| 183 | + print(f"❌ {err}") |
| 184 | + return |
| 185 | + normalized_nickname_value = None |
| 186 | + normalized_timer_profile_value = None |
| 187 | + if normalized_status_value is None: |
| 188 | + nick_err, normalized_nickname_value = _sync_nickname_var_to_profile(var_name, var_value) |
| 189 | + if nick_err: |
| 190 | + print(f"❌ {nick_err}") |
| 191 | + return |
| 192 | + if normalized_status_value is None and normalized_nickname_value is None: |
| 193 | + timer_err, normalized_timer_profile_value = _sync_timer_profile_var_to_settings(var_name, var_value) |
| 194 | + if timer_err: |
| 195 | + print(f"❌ {timer_err}") |
| 196 | + return |
| 197 | + final_value = ( |
| 198 | + normalized_status_value |
| 199 | + if normalized_status_value is not None |
| 200 | + else normalized_nickname_value |
| 201 | + if normalized_nickname_value is not None |
| 202 | + else normalized_timer_profile_value |
| 203 | + if normalized_timer_profile_value is not None |
| 204 | + else var_value |
| 205 | + ) |
| 206 | + Variables.set_var(var_name, final_value) |
| 207 | + if str(raw_var_name).strip() != str(var_name).strip(): |
| 208 | + print(f"✅. Variable '{raw_var_name}' (alias of '{var_name}') set to '{final_value}'.") |
| 209 | + else: |
| 210 | + print(f"✅. Variable '{var_name}' set to '{final_value}'.") |
| 211 | + if str(var_name).strip().lower().startswith("status_"): |
| 212 | + print("↳ Synced to current_status.yml") |
| 213 | + elif str(var_name).strip().lower() == "nickname": |
| 214 | + print("↳ Synced to profile.yml") |
| 215 | + elif str(var_name).strip().lower() == "timer_profile": |
| 216 | + print("↳ Synced to timer_settings.yml") |
47 | 217 | else: |
48 | 218 | print(f"❌ Invalid variable assignment: {var_assignment}. Expected format: <variable_name>:<value>") |
49 | 219 | return |
@@ -148,6 +318,10 @@ def get_help_message(): |
148 | 318 | Description: Sets properties of an item or defines a script variable. |
149 | 319 | Example: set note MyMeetingNotes priority:high category:work |
150 | 320 | Example: set var my_variable:some_value |
| 321 | +Example: set var status_energy:high # updates var and current_status.yml |
| 322 | +Example: set var location:home # alias of status_place; updates current_status.yml |
| 323 | +Example: set var nickname:Alice # updates var and user/profile/profile.yml |
| 324 | +Example: set var timer_profile:classic_pomodoro # updates var and user/settings/timer_settings.yml |
151 | 325 |
|
152 | 326 | Special (goals): |
153 | 327 | set goal "<name>" template:true # mark goal as a template |
|
0 commit comments