mirror of
https://github.com/Horizontal42/LaptopTester.git
synced 2026-11-10 14:52:01 +03:00
4.9 KiB
4.9 KiB
LaptopTester Architecture
Files
config.py # Load sheet_config.json, validate, expose AppConfig dataclass
scanner.py # WMI/smartctl hardware detection in QThread; matching logic
sheet_manager.py # Google Sheets auth (service account or OAuth) and row upsert
tester.py # PyQt5 GUI: pages, QLineEdit/QComboBox fields, thread signals
sheet_config.example.json # Template: worksheet, column numbers, hardware lists, brand aliases
requirements.txt # Dependencies: PyQt5, WMI, gspread, google-auth libs
LaptopTester.spec # PyInstaller config (datas, hidden imports)
How hardware scan flows
- User starts app →
TesterApp.__init__checks admin rights, builds UI (3 pages: load, hardware, tests) start_scan()spawnsHardwareScannerQThread withAppConfig- Scanner queries WMI for brand, model, CPU, RAM; smartctl for SSD SMART data
find_best_match(raw_wmi_name, config_list)normalizes names and tokens to match against lists- Exact normalized match wins
- Falls back to longest list entry whose tokens all appear in raw name
- For GPU entries like "NVIDIA MX: MX450", also tries matching only the model part (after
:)
- Scanner emits
log()signal → progress label updates; emitsfinished(dict)→on_scan_finished() on_scan_finished()populates combo boxes: finds matching list entry, callssetCurrentIndex()
How results flow to Google Sheets
- User fills hardware page, checks tests, clicks "ОТПРАВИТЬ"
finish_testing()gathers all form data into dict- If second battery group checked, adds
Bat2_Cyc/Bat2_Cap - Validates serial number (non-empty) — shows warning if missing
- Creates
SheetManager(url, cfg)→ connects to sheet (service account or OAuth) - Calls
send_test_results(data_dict):- Finds row by serial number in
cfg.serial_column - If found: updates; if not found: appends after last filled row
- Iterates config columns: for each key in
cfg.columns, writes corresponding data value
- Finds row by serial number in
- Returns status message, shows in QMessageBox
Adding new hardware checks
- Edit
sheet_config.example.jsonlists.gpus(or other list) - Add brand alias if needed:
brand_aliases: {"Raw Name": "Canonical"} - Restart app — scanner uses new lists
To add a new type of check (e.g., WiFi module):
- Add to
sheet_config.example.jsoncolumns:"WiFi": 51(pick next free column) - Add to
tester.pyinit_tests_ui():add(17, "WiFi", ["+", "-", "Не инфы"], "🌐", None) - Add to
finish_testing()data dict:"WiFi": self.checks["WiFi"].currentText(), - Update example config to include in
columnsso it gets written
Adding new hardware detection
- Add field to
scanner.pyHardwareScanner.run():self.log.emit("New hardware...") data["NewField"] = self.detect_new_field() - Add corresponding column to
sheet_config.example.json - Add combo box or text field to
tester.pyhardware page - Add to data dict in
finish_testing()
Gotchas
- WMI names are messy: "NVIDIA GeForce RTX 3060" vs config "NVIDIA RTX: 3060". Token matching handles this, but config lists must be carefully curated (ditch duplicates like both "GeForce GT 620M" and "NVIDIA GT: GT 620M").
- SSD sizes: Drives report decimal GB (1 TB = 1e12 bytes), but older
int(bytes / 1024**3)gave binary GiB. Now matches by closest size ±7%, so 240 GB at 256e9 bytes finds "240 GB" correctly. - AMD Ryzen format: WMI says "Ryzen 5 3500U", config lists use "R5 3500U". Scanner normalizes "Ryzen X" → "RX" before matching.
- Column order matters:
sheet_config.jsoncolumnsdict values are 1-indexed Google Sheets column numbers. Serial column isserial_column(used for find/upsert logic), separate fromcolumnsmap. - Rescan: Once started,
HardwareScannerthread blocks until done. If user clicks Rescan again before done, old thread keeps running (potential race). Checkscanner.isRunning()before starting new one, or emit a signal to prevent UI clicks. - Password reset:
net accounts /maxpwage:unlimitedchanges system state. Runs silently in background; returncode 0 = success. Needs admin rights or fails silently. - Empty serial number:
sheet_managerchecks for non-empty serial; if empty, returns error string (not written to sheet). Caller (tester.py) should validate before calling.
Config format
sheet_config.json structure:
{
"worksheet": "Laptops",
"serial_column": 21,
"columns": {
"Brand": 19,
"CPU": 22,
...
},
"brand_aliases": {
"Hewlett-Packard": "HP",
...
},
"lists": {
"brands": ["HP", "Lenovo", ...],
"models": [...],
"cpus": [...],
"ram": [...],
"ssd": [...],
"gpus": ["", "NVIDIA RTX: 3060", ...]
}
}
Empty string in GPU list = "no dedicated GPU" (matches Intel/AMD integrated).