Skip to content

Commit 4bfcd86

Browse files
committed
Release: consolidate remaining changes
1 parent cb16206 commit 4bfcd86

36 files changed

+542
-204
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ python -m pc_client.main
7070

7171
📚 **[Full Documentation](docs/README.md)** - Complete documentation and guides
7272

73+
### Localization & Translation Status
74+
- **English UI coverage**: all `/web/*.html` screens now rely on `web/assets/i18n.js` keys, the Polish strings have English fallbacks, and the chat/chat-pc/control/system scripts no longer declare duplicate helpers.
75+
- **Verification**: run `node scripts/check_i18n.mjs` to ensure every key includes an English value, and `pytest` (inside `.venv`) covers the back-end regressions tied to UI rendering.
76+
- **Notes**: run `?lang=en` against `view`, `control`, `navigation`, `system`, `models`, `project`, `assistant`, `chat`, `chat-pc`, `google_home`, `mode`, and `providers` to keep translations in sync.
77+
7378
### Quick Links
7479

7580
- **[Quick Start Guide](docs/QUICKSTART.md)** - Get started quickly

docs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ Full instructions: [QUICKSTART.md](QUICKSTART.md)
6565
### Management
6666
- **[SERVICE_AND_RESOURCE_MANAGEMENT.md](SERVICE_AND_RESOURCE_MANAGEMENT.md)** - Operations, monitoring, troubleshooting
6767

68+
### Localization & Translation
69+
- **Screens in English**: Every `/web/*.html` view now uses `data-i18n` bindings and the shared dictionary (`web/assets/i18n.js`), so the `/view`, `/control`, `/navigation`, `/system`, `/models`, `/project`, `/assistant`, `/chat`, `/chat-pc`, `/google_home`, `/mode`, and `/providers` pages display correctly when `?lang=en`.
70+
- **Dictionary guard**: `scripts/check_i18n.mjs` parses the JS dictionary at runtime and fails if any key lacks an English entry.
71+
- **Validation**: Activating `.venv` (`source .venv/bin/activate`) and running `pytest` ensures the Playwright UIs and related back-end APIs remain stable after these translation updates.
72+
6873
### API Specifications
6974
- **[api-specs/](api-specs/)** - Detailed REST endpoint specifications
7075
- [api-specs/README.md](api-specs/README.md) - API overview

docs_pl/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ Pełna instrukcja: [SZYBKI_START.md](SZYBKI_START.md)
6565
### Zarządzanie
6666
- **[ZARZADZANIE_USLUGAMI_I_ZASOBAMI.md](ZARZADZANIE_USLUGAMI_I_ZASOBAMI.md)** - Operacje, monitoring, troubleshooting
6767

68+
### Lokalizacja i Tłumaczenia
69+
- **Ekrany po angielsku**: Każdy widok `/web/*.html` korzysta z `data-i18n` oraz `web/assets/i18n.js`, więc `/view`, `/control`, `/navigation`, `/system`, `/models`, `/project`, `/assistant`, `/chat`, `/chat-pc`, `/google_home`, `/mode` i `/providers` działają poprawnie po ustawieniu `?lang=en`.
70+
- **Kontrola słownika**: Skrypt `scripts/check_i18n.mjs` analizuje `web/assets/i18n.js` i raportuje brakujące wpisy `en`.
71+
- **Testy**: Aktywuj środowisko `.venv` (`source .venv/bin/activate`) i uruchom `pytest`, aby zweryfikować, że Playwright i backend znoszą zmiany z tłumaczeniami.
72+
6873
### Specyfikacje API
6974
- **[api-specs/](api-specs/)** - Szczegółowe specyfikacje endpointów REST
7075
- [api-specs/README.md](api-specs/README.md) - Przegląd API
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Zadanie #151: Nowa Funkcja X
2+
3+
**Status:** :hourglass_flowing_sand: W trakcie
4+
**Link:** https://github.com/mpieniak01/rider-pc/issues/151
5+
**Autor:** Nieprzypisane
6+
7+
## Cel
8+
(brak opisu)
9+
10+
## Plan Realizacji
11+
- [ ] Analiza
12+
- [ ] Implementacja
13+
- [ ] Testy
14+
15+
## Audyt środowiska / req
16+
17+
### Stan repozytorium
18+
- `git status -sb``## main...origin/main` z lokalnym diffem ograniczonym do tego req (`D docs_pl/_to_do/151_nowa-funkcja-x.md`, `?? docs_pl/_to_do/000_nowa-funkcja-x.md`).
19+
20+
### Środowisko wykonawcze
21+
- System bazowy: Ubuntu 24.04.3 LTS (`lsb_release -a`), Docker CLI 28.2.2 – dostępne tylko stare `docker-compose 1.29.2`, polecenie `docker compose` nie istnieje.
22+
- Python: `python` nie jest zmapowany; dostępny `python3 3.12.3` oraz `pip 24.0`. Skrypty wywołujące `python` wymagają aliasu lub instalacji `python-is-python3`.
23+
- Node toolchain: `node 18.19.1`, `npm 9.2.0`. Lokalne `node_modules` istnieją, ale brak możliwości weryfikacji aktualności pakietów offline.
24+
- `pip list --outdated` i `npm outdated` kończą się błędami sieciowymi (`EAI_AGAIN`, brak DNS do PyPI/npm) – audyt wersji zależy od odblokowania dostępu do rejestrów.
25+
26+
### Co trzeba zaktualizować / „podbić stos”
27+
- Doinstalować Compose v2 (plugin `docker compose`) lub aliasować polecenia z Makefile/dokumentacji na `docker-compose`, aby pipeline Dockera był zgodny z bieżącą składnią.
28+
- Zapewnić alias `python`/`pip` (np. `sudo apt install python-is-python3`) i odświeżyć wirtualne środowiska `.venv` oraz `node_modules`, bo projekt nie był aktualizowany od miesięcy.
29+
- Po odblokowaniu sieci uruchomić `npm outdated`, `npm audit`, `pip list --outdated`, `pip-audit` i zaktualizować kluczowe zależności (`fastapi`, `uvicorn`, `redis`, `celery`, `torch`, `stylelint`, `ollama`, `prometheus-client`, itp.) pod Python 3.12.
30+
- Zweryfikować `requirements*.txt` i `package-lock.json` vs. realne środowisko; możliwe, że wersje (np. `grpcio`, `protobuf`, modele vision/audio) wymagają rewizji przed wdrożeniem nowej funkcji X.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Zadanie #160: Nowa Funkcja Y
2+
3+
**Status:** :hourglass_flowing_sand: W trakcie
4+
**Link:** https://github.com/mpieniak01/rider-pc/issues/160
5+
**Autor:** Nieprzypisane
6+
7+
## Cel
8+
Opis zadania testowego
9+
10+
## Plan Realizacji
11+
- [ ] Analiza
12+
- [ ] Implementacja
13+
- [ ] Testy

pc_client/adapters/git_adapter.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def invalidate_cache(self) -> None:
9191
self._cache = {}
9292
self._cache_ts = 0
9393

94-
async def _run_command(self, *args: str) -> Tuple[int, str, str]:
94+
async def _run_command(self, *args: str) -> Tuple[Optional[int], str, str]:
9595
"""
9696
Run an async subprocess command.
9797
@@ -116,6 +116,11 @@ async def _run_command(self, *args: str) -> Tuple[int, str, str]:
116116
logger.error("Failed to execute command %s: %s", " ".join(args), e)
117117
return -1, "", str(e)
118118

119+
@staticmethod
120+
def _normalize_returncode(returncode: Optional[int]) -> int:
121+
"""Normalize return codes coming from subprocess (None -> -1)."""
122+
return returncode if returncode is not None else -1
123+
119124
async def _is_in_git_repo(self) -> bool:
120125
"""
121126
Check if current directory is inside a git repository.
@@ -131,7 +136,8 @@ async def _is_in_git_repo(self) -> bool:
131136
cmd = ["git", "-C", self._repo_path, "rev-parse", "--git-dir"]
132137

133138
returncode, _, _ = await self._run_command(*cmd)
134-
return returncode == 0
139+
rc = self._normalize_returncode(returncode)
140+
return rc == 0
135141

136142
async def get_current_branch(self) -> str:
137143
"""
@@ -148,8 +154,9 @@ async def get_current_branch(self) -> str:
148154
cmd = ["git", "-C", self._repo_path, "rev-parse", "--abbrev-ref", "HEAD"]
149155

150156
returncode, stdout, stderr = await self._run_command(*cmd)
157+
rc = self._normalize_returncode(returncode)
151158

152-
if returncode != 0:
159+
if rc != 0:
153160
logger.warning("Failed to get current branch: %s", stderr or stdout)
154161
return "unknown"
155162

@@ -170,8 +177,9 @@ async def get_current_commit(self) -> str:
170177
cmd = ["git", "-C", self._repo_path, "rev-parse", "--short", "HEAD"]
171178

172179
returncode, stdout, stderr = await self._run_command(*cmd)
180+
rc = self._normalize_returncode(returncode)
173181

174-
if returncode != 0:
182+
if rc != 0:
175183
logger.warning("Failed to get current commit: %s", stderr or stdout)
176184
return "unknown"
177185

@@ -192,8 +200,9 @@ async def is_dirty(self) -> bool:
192200
cmd = ["git", "-C", self._repo_path, "status", "--porcelain"]
193201

194202
returncode, stdout, _ = await self._run_command(*cmd)
203+
rc = self._normalize_returncode(returncode)
195204

196-
if returncode != 0:
205+
if rc != 0:
197206
return False
198207

199208
# If there's any output, the repo is dirty
@@ -214,8 +223,9 @@ async def get_last_commit_message(self) -> str:
214223
cmd = ["git", "-C", self._repo_path, "log", "-1", "--pretty=%B"]
215224

216225
returncode, stdout, stderr = await self._run_command(*cmd)
226+
rc = self._normalize_returncode(returncode)
217227

218-
if returncode != 0:
228+
if rc != 0:
219229
logger.warning("Failed to get last commit message: %s", stderr or stdout)
220230
return ""
221231

@@ -281,8 +291,9 @@ async def get_local_branches(self) -> List[str]:
281291
cmd = ["git", "-C", self._repo_path, "branch", "--format=%(refname:short)"]
282292

283293
returncode, stdout, stderr = await self._run_command(*cmd)
294+
rc = self._normalize_returncode(returncode)
284295

285-
if returncode != 0:
296+
if rc != 0:
286297
logger.warning("Failed to get local branches: %s", stderr or stdout)
287298
return []
288299

@@ -313,8 +324,9 @@ async def checkout_branch(self, name: str) -> Tuple[bool, str]:
313324
cmd = ["git", "-C", self._repo_path, "checkout", name.strip()]
314325

315326
returncode, stdout, stderr = await self._run_command(*cmd)
327+
rc = self._normalize_returncode(returncode)
316328

317-
if returncode != 0:
329+
if rc != 0:
318330
error = stderr or stdout or "Nieznany błąd"
319331
logger.warning("Failed to checkout branch %s: %s", name, error)
320332
return False, error
@@ -350,8 +362,9 @@ async def create_branch(self, name: str, base: str = "main") -> Tuple[bool, str]
350362
cmd = ["git", "-C", self._repo_path, "checkout", "-b", name.strip(), base.strip()]
351363

352364
returncode, stdout, stderr = await self._run_command(*cmd)
365+
rc = self._normalize_returncode(returncode)
353366

354-
if returncode != 0:
367+
if rc != 0:
355368
error = stderr or stdout or "Nieznany błąd"
356369
logger.warning("Failed to create branch %s: %s", name, error)
357370
return False, error
@@ -396,8 +409,9 @@ async def add_file(self, path: str) -> Tuple[bool, str]:
396409
cmd = ["git", "-C", self._repo_path, "add", path.strip()]
397410

398411
returncode, stdout, stderr = await self._run_command(*cmd)
412+
rc = self._normalize_returncode(returncode)
399413

400-
if returncode != 0:
414+
if rc != 0:
401415
error = stderr or stdout or "Nieznany błąd"
402416
logger.warning("Failed to add file %s: %s", path, error)
403417
return False, error
@@ -425,8 +439,9 @@ async def commit(self, message: str) -> Tuple[bool, str]:
425439
cmd = ["git", "-C", self._repo_path, "commit", "-m", message.strip()]
426440

427441
returncode, stdout, stderr = await self._run_command(*cmd)
442+
rc = self._normalize_returncode(returncode)
428443

429-
if returncode != 0:
444+
if rc != 0:
430445
error = stderr or stdout or "Nieznany błąd"
431446
logger.warning("Failed to commit: %s", error)
432447
return False, error

pc_client/adapters/github_adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ async def get_open_issues(
171171
try:
172172
client = await self._get_client()
173173
url = f"{self.BASE_URL}/repos/{self._owner}/{self._repo}/issues"
174-
params = {
174+
params: Dict[str, str | int] = {
175175
"state": "open",
176176
"per_page": min(max(1, limit), 100),
177177
"sort": "updated",

pc_client/adapters/mock_rest_adapter.py

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import copy
88
import logging
99
import time
10-
from typing import Any, Dict, Optional, Tuple
10+
from typing import Any, Dict, Optional, Tuple, cast
1111

1212
logger = logging.getLogger(__name__)
1313

@@ -33,23 +33,27 @@ def __init__(
3333
self.timeout = timeout
3434
logger.info("MockRestAdapter initialized (TEST MODE)")
3535
now = time.time()
36-
self._control_state = {
36+
self._control_state: Dict[str, Any] = {
3737
"present": True,
3838
"mode": "auto",
3939
"tracking": {"mode": "none", "enabled": False, "target": None},
4040
"navigator": {"active": False, "strategy": "standard", "state": "idle"},
4141
"camera": {"vision_enabled": True, "on": True, "res": [1280, 720]},
4242
}
43-
self._ai_mode = {"mode": "pc_offload", "available_modes": ["pc_offload", "local"], "changed_ts": now - 120}
44-
self._provider_state = {
43+
self._ai_mode: Dict[str, Any] = {
44+
"mode": "pc_offload",
45+
"available_modes": ["pc_offload", "local"],
46+
"changed_ts": now - 120,
47+
}
48+
self._provider_state: Dict[str, Any] = {
4549
"domains": {
4650
"vision": {"mode": "pc", "status": "ready", "changed_ts": now - 300, "reason": "mock"},
4751
"voice": {"mode": "local", "status": "ready", "changed_ts": now - 600, "reason": "mock"},
4852
"text": {"mode": "pc", "status": "ready", "changed_ts": now - 30, "reason": "sync"},
4953
},
5054
"pc_health": {"reachable": True, "status": "ok", "latency_ms": 12.4},
5155
}
52-
self._provider_health = {
56+
self._provider_health: Dict[str, Dict[str, Any]] = {
5357
"vision": {"status": "ok", "latency_ms": 11.2, "success_rate": 0.99, "last_check": now - 5},
5458
"voice": {"status": "warn", "latency_ms": 32.5, "success_rate": 0.93, "last_check": now - 15},
5559
"text": {"status": "ok", "latency_ms": 8.7, "success_rate": 0.98, "last_check": now - 9},
@@ -72,13 +76,13 @@ def __init__(
7276
"yaw": 0.2,
7377
},
7478
]
75-
self._remote_models = [
79+
self._remote_models: list[Dict[str, Any]] = [
7680
{"name": "pi-vision-prod", "category": "vision", "type": "onnx", "format": "onnx", "size_mb": 32.1},
7781
{"name": "pi-asr-lo", "category": "voice_asr", "type": "whisper", "format": "en", "size_mb": 42.4},
7882
{"name": "pi-tts-hi", "category": "voice_tts", "type": "piper", "format": "onnx", "size_mb": 48.9},
7983
{"name": "pi-utils", "category": "unknown", "type": "custom", "format": "bin", "size_mb": 12.3},
8084
]
81-
self._services = [
85+
self._services: list[Dict[str, Any]] = [
8286
{
8387
"unit": "rider-cam-preview.service",
8488
"desc": "Camera preview pipeline",
@@ -115,7 +119,7 @@ def __init__(
115119
"enabled": "enabled",
116120
},
117121
]
118-
self._logic_blueprints = [
122+
self._logic_blueprints: list[Dict[str, Any]] = [
119123
{
120124
"name": "s0_manual",
121125
"scenario": "S0",
@@ -141,13 +145,15 @@ def __init__(
141145
"default_active": False,
142146
},
143147
]
144-
self._feature_state = {bp["name"]: bool(bp.get("default_active")) for bp in self._logic_blueprints}
145-
self._home_state = {
148+
self._feature_state: Dict[str, bool] = {
149+
bp["name"]: bool(bp.get("default_active")) for bp in self._logic_blueprints
150+
}
151+
self._home_state: Dict[str, Any] = {
146152
"authenticated": True,
147153
"profile": {"email": "mock-user@rider.ai", "name": "Mock User"},
148154
"scopes": ["homegraph", "cloud-control"],
149155
}
150-
self._home_devices = [
156+
self._home_devices: list[Dict[str, Any]] = [
151157
{
152158
"name": "devices/light/living-room",
153159
"type": "action.devices.types.LIGHT",
@@ -360,9 +366,8 @@ async def get_providers_state(self) -> Dict[str, Any]:
360366
async def patch_provider(self, domain: str, payload: Dict[str, Any]) -> Dict[str, Any]:
361367
"""Return success for provider update."""
362368
target = str(payload.get("target", "local")).lower()
363-
state = self._provider_state.setdefault("domains", {}).setdefault(
364-
domain, {"mode": "local", "status": "local_only", "changed_ts": None}
365-
)
369+
domains = cast(Dict[str, Dict[str, Any]], self._provider_state.setdefault("domains", {}))
370+
state = domains.setdefault(domain, {"mode": "local", "status": "local_only", "changed_ts": None})
366371
state["mode"] = target
367372
state["status"] = "pc_active" if target == "pc" else "local_only"
368373
state["changed_ts"] = time.time()
@@ -556,27 +561,32 @@ async def post_home_command(self, payload: Dict[str, Any]) -> Dict[str, Any]:
556561
device = next((d for d in self._home_devices if d.get("name") == device_id), None)
557562
if not device:
558563
return {"ok": False, "error": "device_not_found"}
559-
traits = device.setdefault("traits", {})
564+
traits = cast(Dict[str, Dict[str, Any]], device.setdefault("traits", {}))
565+
566+
def _trait_bucket(name: str) -> Dict[str, Any]:
567+
return cast(Dict[str, Any], traits.setdefault(name, {}))
568+
560569
if command == "action.devices.commands.OnOff":
561-
traits.setdefault("sdm.devices.traits.OnOff", {})["on"] = bool(params.get("on"))
570+
_trait_bucket("sdm.devices.traits.OnOff")["on"] = bool(params.get("on"))
562571
elif command == "action.devices.commands.BrightnessAbsolute":
563-
traits.setdefault("sdm.devices.traits.Brightness", {})["brightness"] = int(params.get("brightness", 0))
572+
_trait_bucket("sdm.devices.traits.Brightness")["brightness"] = int(params.get("brightness", 0))
564573
elif command == "action.devices.commands.ColorAbsolute":
565-
traits.setdefault("sdm.devices.traits.ColorSetting", {})["color"] = params.get("color", {})
574+
_trait_bucket("sdm.devices.traits.ColorSetting")["color"] = params.get("color", {})
566575
elif command == "action.devices.commands.ThermostatTemperatureSetpoint":
567-
traits.setdefault("sdm.devices.traits.ThermostatTemperatureSetpoint", {}).update(params)
576+
_trait_bucket("sdm.devices.traits.ThermostatTemperatureSetpoint").update(params)
568577
elif command == "action.devices.commands.StartStop":
569-
traits.setdefault("sdm.devices.traits.StartStop", {}).update(
578+
_trait_bucket("sdm.devices.traits.StartStop").update(
570579
{"isRunning": bool(params.get("start")), "isPaused": False}
571580
)
572581
elif command == "action.devices.commands.PauseUnpause":
573-
traits.setdefault("sdm.devices.traits.StartStop", {}).update({"isPaused": bool(params.get("pause"))})
582+
_trait_bucket("sdm.devices.traits.StartStop").update({"isPaused": bool(params.get("pause"))})
574583
elif command == "action.devices.commands.Dock":
575-
traits.setdefault("sdm.devices.traits.Dock", {})["lastDockTs"] = time.time()
584+
_trait_bucket("sdm.devices.traits.Dock")["lastDockTs"] = time.time()
576585
return {"ok": True, "device": device_id, "command": command}
577586

578587
async def post_home_auth(self) -> Dict[str, Any]:
579588
"""Simulate successful Google auth."""
580589
self._home_state["authenticated"] = True
581-
self._home_state.setdefault("profile", {})["updated_at"] = time.time()
590+
profile = cast(Dict[str, Any], self._home_state.setdefault("profile", {}))
591+
profile["updated_at"] = time.time()
582592
return {"ok": True, "profile": self._home_state.get("profile")}

pc_client/adapters/rest_adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(
3838
self.timeout = timeout
3939

4040
# Initialize httpx client based on secure mode
41-
client_kwargs = {"timeout": timeout}
41+
client_kwargs: Dict[str, Any] = {"timeout": timeout}
4242

4343
if secure_mode:
4444
if mtls_cert_path and mtls_key_path and mtls_ca_path:

0 commit comments

Comments
 (0)