refactor: gom i18n/ và theme/ thành gói, gộp requirements về một file
Thư mục gốc: 22 file .py -> 7
--------------------------------
13 file "thành phần" nằm rải rác ngay ngoài thư mục gốc, mỗi file chỉ có ĐÚNG
MỘT nơi import — chính cái hub của nó:
i18n.py + 10 file i18n_*.py -> i18n/__init__.py + i18n/*.py
theme.py + 3 file theme_*.py -> theme/__init__.py + theme/*.py
Đổi hub thành `__init__.py` nên 78 chỗ `from ..i18n import tr` và 24 chỗ
`from ..theme import current_palette` KHÔNG phải sửa một dòng nào. Git nhận ra
11/15 file là đổi tên thuần, 0 dòng thay đổi; 4 file còn lại chỉ sửa đúng dòng
import và mấy tham chiếu tên file trong docstring.
Đối chiếu với bản trước khi gom, cùng một phép băm:
số khoá i18n 1431 -> 1431 hash STRINGS a06cc34b... (trùng)
QSS dark hash 7bb230a4... (trùng)
QSS light hash 884f73ce... (trùng)
`check_loc.py` phải khai thêm "i18n", "theme" vào DEFAULT_TARGET_DIRS: chúng
từng được quét theo diện "module nằm ở thư mục gốc", gom vào gói rồi thì không
khai là lặng lẽ tuột khỏi tầm quét.
Bánh cóc `ui/widgets.py` siết 505 -> 466 sau khi tách SegmentedControl — nợ cũ
co lại thì con số phải co theo, không thì bánh cóc đứng yên mãi ở mức cũ.
Một file requirements
---------------------
Xoá `requirements-test.txt`. Nó chỉ có `pytest` + `pydantic`, nhưng 64/108 file
test dựng widget thật và 20 file trong đó import PySide6 thẳng ở đầu file không
có bảo vệ — nên CI cài mỗi file kia thì pytest chết ngay lúc thu thập test chứ
không phải "vài test bị bỏ qua". Hai file cho một danh sách gần trùng nhau chỉ
tạo thêm một chỗ để lệch phiên bản, và `pydantic` đã bị chép ở cả hai.
CI đổi sang cài `requirements.txt`. Người dùng cuối cài thừa pytest vài MB.
Kèm theo: `install.bat` bỏ cờ `--dev` (không còn gì để cài thêm). Khối `if`
rỗng còn sót lại làm cmd.exe báo "( was unexpected at this time" và script chết
ngay sau bước cài thư viện — đã gỡ hẳn.
859 test xanh · 4/4 cổng CASAN · check_design_parity 32/32 ·
check_layout_geometry trùng từng byte với bản trước refactor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,14 @@ jobs:
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
cache-dependency-path: cowork_local/requirements-test.txt
|
||||
cache-dependency-path: cowork_local/requirements.txt
|
||||
|
||||
- name: Install test dependencies
|
||||
run: python -m pip install --disable-pip-version-check -r requirements-test.txt
|
||||
# Mot file duy nhat: requirements-test.txt cu chi co pytest, nhung
|
||||
# 64/108 file test dung widget that (20 file import PySide6 thang o dau
|
||||
# file, khong co bao ve) nen no van phai keo ve gan nhu ca danh sach
|
||||
# runtime. Cai rieng file kia thi pytest chet ngay luc thu thap test.
|
||||
- name: Install dependencies
|
||||
run: python -m pip install --disable-pip-version-check -r requirements.txt
|
||||
|
||||
- name: Check Python syntax
|
||||
run: |
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ Prefer the existing lightweight Conventional Commit prefixes: `feat:`, `fix:`, `
|
||||
Run the application from the parent directory with `python -m cowork_local`. The current reliable test command is:
|
||||
|
||||
```bash
|
||||
python -m pip install -r requirements-test.txt
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pytest tests -q
|
||||
```
|
||||
|
||||
|
||||
@@ -59,10 +59,16 @@ python -m cowork_local
|
||||
|
||||
### 3. Run Automated Tests
|
||||
```bash
|
||||
python -m pip install -r requirements-test.txt
|
||||
python -m pip install -r requirements.txt
|
||||
pytest -q
|
||||
```
|
||||
|
||||
There is one requirements file, not a runtime/test pair. A separate test file
|
||||
would hold only `pytest`: 64 of the 108 test modules build real widgets, and 20
|
||||
of them import PySide6 unguarded at module scope, so it would have to pull in
|
||||
almost the whole runtime list anyway — two files for one near-identical list is
|
||||
just a second place for the pins to drift.
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ CASAN Quality Gate & Verification
|
||||
|
||||
+20
-20
@@ -27,30 +27,30 @@ _current = DEFAULT_LANGUAGE
|
||||
_listeners: List[Callable[[], None]] = []
|
||||
|
||||
# key -> {"en": ..., "ja": ..., "vi": ...}
|
||||
from . import i18n_login_dialog as _i18n_login_dialog
|
||||
from . import i18n_sidebar as _i18n_sidebar
|
||||
from . import i18n_composer as _i18n_composer
|
||||
from . import i18n_hint as _i18n_hint
|
||||
from . import i18n_cowork_tab as _i18n_cowork_tab
|
||||
from . import i18n_settings_dialog as _i18n_settings_dialog
|
||||
from . import i18n_skills_dialog as _i18n_skills_dialog
|
||||
from . import i18n_libreoffice_view as _i18n_libreoffice_view
|
||||
from . import i18n_agents_admin_tab as _i18n_agents_admin_tab
|
||||
from . import i18n_monitoring_overview as _i18n_monitoring_overview
|
||||
from . import login_dialog as _login_dialog
|
||||
from . import sidebar as _sidebar
|
||||
from . import composer as _composer
|
||||
from . import hint as _hint
|
||||
from . import cowork_tab as _cowork_tab
|
||||
from . import settings_dialog as _settings_dialog
|
||||
from . import skills_dialog as _skills_dialog
|
||||
from . import libreoffice_view as _libreoffice_view
|
||||
from . import agents_admin_tab as _agents_admin_tab
|
||||
from . import monitoring_overview as _monitoring_overview
|
||||
|
||||
# Gộp theo đúng thứ tự cũ: khoá trùng thì cụm sau thắng, y như khi tất cả
|
||||
# còn nằm chung một dict literal.
|
||||
STRINGS: Dict[str, Dict[str, str]] = {
|
||||
**_i18n_login_dialog.STRINGS,
|
||||
**_i18n_sidebar.STRINGS,
|
||||
**_i18n_composer.STRINGS,
|
||||
**_i18n_hint.STRINGS,
|
||||
**_i18n_cowork_tab.STRINGS,
|
||||
**_i18n_settings_dialog.STRINGS,
|
||||
**_i18n_skills_dialog.STRINGS,
|
||||
**_i18n_libreoffice_view.STRINGS,
|
||||
**_i18n_agents_admin_tab.STRINGS,
|
||||
**_i18n_monitoring_overview.STRINGS,
|
||||
**_login_dialog.STRINGS,
|
||||
**_sidebar.STRINGS,
|
||||
**_composer.STRINGS,
|
||||
**_hint.STRINGS,
|
||||
**_cowork_tab.STRINGS,
|
||||
**_settings_dialog.STRINGS,
|
||||
**_skills_dialog.STRINGS,
|
||||
**_libreoffice_view.STRINGS,
|
||||
**_agents_admin_tab.STRINGS,
|
||||
**_monitoring_overview.STRINGS,
|
||||
}
|
||||
|
||||
|
||||
+1
-14
@@ -4,7 +4,6 @@ rem Cowork-Local BamBOO - cai dat thu vien Python (chay MOT lan)
|
||||
rem
|
||||
rem Cach dung:
|
||||
rem install.bat cai vao moi truong ao rieng (khuyen dung)
|
||||
rem install.bat --dev cai them thu vien de chay test
|
||||
rem install.bat --system cai thang vao Python dang co, khong dung venv
|
||||
rem install.bat --force dung lai moi truong ao tu dau
|
||||
rem
|
||||
@@ -26,13 +25,11 @@ set "APPHOME=%LOCALAPPDATA%\CoworkLocal"
|
||||
set "VENV=%APPHOME%\venv"
|
||||
set "LAUNCHER=%APPHOME%\launcher"
|
||||
|
||||
set "DEV=0"
|
||||
set "USE_SYSTEM=0"
|
||||
set "FORCE=0"
|
||||
|
||||
:parse_args
|
||||
if "%~1"=="" goto args_done
|
||||
if /I "%~1"=="--dev" set "DEV=1" & shift & goto parse_args
|
||||
if /I "%~1"=="--system" set "USE_SYSTEM=1" & shift & goto parse_args
|
||||
if /I "%~1"=="--force" set "FORCE=1" & shift & goto parse_args
|
||||
if /I "%~1"=="-h" goto usage
|
||||
@@ -120,15 +117,6 @@ if errorlevel 1 (
|
||||
goto fail
|
||||
)
|
||||
|
||||
if "%DEV%"=="1" (
|
||||
echo [3/5] Cài thêm thư viện chạy test ^(--dev^)...
|
||||
%PIP% install --disable-pip-version-check -r "%REPO%\requirements-test.txt"
|
||||
if errorlevel 1 (
|
||||
echo [LỖI] Cài thư viện test thất bại.
|
||||
goto fail
|
||||
)
|
||||
)
|
||||
|
||||
rem --------------------------------------------------------------------------
|
||||
rem 4. Lien ket de goi import duoc dung ten
|
||||
rem
|
||||
@@ -188,9 +176,8 @@ exit /b 0
|
||||
|
||||
:usage
|
||||
echo.
|
||||
echo install.bat [--dev] [--system] [--force]
|
||||
echo install.bat [--system] [--force]
|
||||
echo.
|
||||
echo --dev cài thêm thư viện để chạy test ^(pytest, pydantic^)
|
||||
echo --system cài thẳng vào Python đang có, không tạo môi trường ảo
|
||||
echo --force xoá môi trường ảo cũ rồi tạo lại từ đầu
|
||||
echo.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
pydantic>=2,<3
|
||||
pytest>=8,<10
|
||||
@@ -39,3 +39,12 @@ pywin32>=306; sys_platform == "win32" # Office -> PDF, thông báo Outlook
|
||||
# opendataloader-pdf # bộ đọc PDF thay thế — KHÔNG cài sẵn có chủ ý:
|
||||
# # application/workspaces/graph_index_service.py tự cài
|
||||
# # khi cần, qua core/deps.py::ensure_module.
|
||||
|
||||
# --- Chạy test ---
|
||||
# Gộp vào đây thay vì để riêng requirements-test.txt: file kia chỉ có đúng
|
||||
# `pytest`, mà 64/108 file test dựng widget thật nên nó vẫn phải kéo về gần
|
||||
# như toàn bộ danh sách trên. Hai file cho một danh sách gần trùng nhau chỉ
|
||||
# tạo thêm một chỗ để lệch phiên bản.
|
||||
#
|
||||
# Người dùng cuối cài thừa pytest vài MB — đổi lại chỉ còn MỘT file phải nhớ.
|
||||
pytest>=8,<10
|
||||
|
||||
@@ -40,6 +40,10 @@ if hasattr(sys.stdout, "reconfigure"):
|
||||
DEFAULT_TARGET_DIRS = [
|
||||
"domain", "application", "infrastructure", "presentation",
|
||||
"ui", "core", "providers", "security", "mcp_servers",
|
||||
# ``i18n/`` và ``theme/`` từng là 13 file rời nằm thẳng ở thư mục gốc nên
|
||||
# được quét theo diện "module gốc"; gom vào gói rồi thì phải khai ở đây,
|
||||
# không thì chúng lặng lẽ tuột khỏi tầm quét.
|
||||
"i18n", "theme",
|
||||
]
|
||||
DEFAULT_MAX_LINES = 400
|
||||
|
||||
@@ -60,7 +64,7 @@ SCAN_ROOT_MODULES = True
|
||||
#: đúng là tách file.
|
||||
LEGACY_ALLOWANCE = {
|
||||
"ui/workspace_tab.py": 566,
|
||||
"ui/widgets.py": 505,
|
||||
"ui/widgets.py": 466,
|
||||
"ui/task_editor_dialog.py": 627,
|
||||
"ui/accounts_tab.py": 559,
|
||||
"core/skills.py": 405,
|
||||
|
||||
@@ -87,12 +87,14 @@ def source() -> dict[str, str]:
|
||||
|
||||
|
||||
def test_template_exposes_exactly_three_provider_neutral_tools() -> None:
|
||||
# The MCP SDK is a RUNTIME dependency (requirements.txt) and is deliberately
|
||||
# absent from requirements-test.txt, which is all CI installs. Importing it at
|
||||
# module scope aborted collection for the ENTIRE suite, so the guard lives here,
|
||||
# inside the only test that touches the SDK. Guarding per-test rather than
|
||||
# per-module keeps the other cases -- pure-Python contract checks that need no
|
||||
# SDK -- running on CI instead of silently skipping with it.
|
||||
# Importing the MCP SDK at module scope aborted collection for the ENTIRE
|
||||
# suite whenever the SDK was missing, so the guard lives here, inside the only
|
||||
# test that touches it. Guarding per-test rather than per-module keeps the
|
||||
# other cases -- pure-Python contract checks that need no SDK -- running
|
||||
# instead of silently skipping with it.
|
||||
#
|
||||
# ``mcp`` is in requirements.txt, so a correctly installed checkout runs this
|
||||
# test for real; the guard only covers an environment installed by hand.
|
||||
types = pytest.importorskip("mcp.types")
|
||||
|
||||
assert set(TOOL_NAMES) == EXPECTED_TOOLS
|
||||
|
||||
@@ -34,10 +34,10 @@ original value, so the deviation is auditable rather than silent.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .theme_palettes import ( # noqa: F401 — giữ đường vào cũ
|
||||
from .palettes import ( # noqa: F401 — giữ đường vào cũ
|
||||
DARK, LIGHT, Palette, _chevron_asset, _FONT, _MONO, _PALETTES,
|
||||
)
|
||||
from .theme_qss import _TEMPLATE
|
||||
from .qss import _TEMPLATE
|
||||
|
||||
from dataclasses import dataclass, asdict
|
||||
from string import Template
|
||||
@@ -4,7 +4,7 @@ Tách khỏi ``theme.py`` vì nó là **dữ liệu**, không phải logic: mộ
|
||||
``string.Template`` mà ``stylesheet()`` thay biến vào. Để chung thì mỗi lần
|
||||
muốn sửa một hàm nhỏ trong theme.py lại phải cuộn qua 470 dòng CSS.
|
||||
|
||||
Sửa màu thì sang ``theme_palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
|
||||
Sửa màu thì sang ``theme/palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -12,7 +12,7 @@ from dataclasses import dataclass, asdict
|
||||
from string import Template
|
||||
|
||||
|
||||
from .theme_qss_controls import QSS_CONTROLS
|
||||
from .qss_controls import QSS_CONTROLS
|
||||
|
||||
_QSS_SHELL = """
|
||||
/* ---- reset ------------------------------------------------------------ */
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Nửa sau của khuôn QSS: bề mặt, tab, ô nhập, nút, badge, log.
|
||||
|
||||
Cắt đôi khuôn QSS đúng mạch của chính nó: ``theme_qss.py`` giữ phần vỏ
|
||||
Cắt đôi khuôn QSS đúng mạch của chính nó: ``theme/qss.py`` giữ phần vỏ
|
||||
(reset + shell: thanh rail, khung chính), file này giữ phần điều khiển.
|
||||
Hai nửa được nối lại trong ``theme_qss.py``.
|
||||
Hai nửa được nối lại trong ``theme/qss.py``.
|
||||
|
||||
Sửa màu thì sang ``theme_palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
|
||||
Sửa màu thì sang ``theme/palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -26,7 +26,7 @@ class SegmentedControl(QWidget):
|
||||
|
||||
currentIndexChanged = Signal(int)
|
||||
|
||||
#: Độ đậm mà ``theme_qss.py`` áp cho nút đang chọn
|
||||
#: Độ đậm mà ``theme/qss.py`` áp cho nút đang chọn
|
||||
#: (``QPushButton#segItem:checked { font-weight: 600 }``). Đổi ở QSS thì
|
||||
#: phải đổi cả ở đây, nếu không chữ lại bị cắt.
|
||||
_CHECKED_WEIGHT = QFont.DemiBold
|
||||
|
||||
Reference in New Issue
Block a user