""" Linux BenchTools - Benchmark API """ import json from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from datetime import datetime from app.db.session import get_db from app.core.security import verify_token from app.schemas.benchmark import BenchmarkPayload, BenchmarkResponse, BenchmarkDetail, BenchmarkSummary from app.models.device import Device from app.models.hardware_snapshot import HardwareSnapshot from app.models.benchmark import Benchmark from app.utils.scoring import calculate_global_score router = APIRouter() @router.post("/benchmark", response_model=BenchmarkResponse, status_code=status.HTTP_200_OK) async def submit_benchmark( payload: BenchmarkPayload, db: Session = Depends(get_db), _: bool = Depends(verify_token) ): """ Submit a benchmark result from a client machine. This endpoint: 1. Resolves or creates the device 2. Creates a hardware snapshot 3. Creates a benchmark record 4. Returns device_id and benchmark_id """ # 1. Resolve or create device device = db.query(Device).filter(Device.hostname == payload.device_identifier).first() if not device: device = Device( hostname=payload.device_identifier, created_at=datetime.utcnow(), updated_at=datetime.utcnow() ) db.add(device) db.flush() # Get device.id # Update device timestamp device.updated_at = datetime.utcnow() # 2. Create hardware snapshot hw = payload.hardware snapshot = HardwareSnapshot( device_id=device.id, captured_at=datetime.utcnow(), # CPU cpu_vendor=hw.cpu.vendor if hw.cpu else None, cpu_model=hw.cpu.model if hw.cpu else None, cpu_microarchitecture=hw.cpu.microarchitecture if hw.cpu else None, cpu_cores=hw.cpu.cores if hw.cpu else None, cpu_threads=hw.cpu.threads if hw.cpu else None, cpu_base_freq_ghz=hw.cpu.base_freq_ghz if hw.cpu else None, cpu_max_freq_ghz=hw.cpu.max_freq_ghz if hw.cpu else None, cpu_cache_l1_kb=hw.cpu.cache_l1_kb if hw.cpu else None, cpu_cache_l2_kb=hw.cpu.cache_l2_kb if hw.cpu else None, cpu_cache_l3_kb=hw.cpu.cache_l3_kb if hw.cpu else None, cpu_flags=json.dumps(hw.cpu.flags) if hw.cpu and hw.cpu.flags else None, cpu_tdp_w=hw.cpu.tdp_w if hw.cpu else None, # RAM ram_total_mb=hw.ram.total_mb if hw.ram else None, ram_slots_total=hw.ram.slots_total if hw.ram else None, ram_slots_used=hw.ram.slots_used if hw.ram else None, ram_ecc=hw.ram.ecc if hw.ram else None, ram_layout_json=json.dumps([slot.dict() for slot in hw.ram.layout]) if hw.ram and hw.ram.layout else None, # GPU gpu_summary=f"{hw.gpu.vendor} {hw.gpu.model}" if hw.gpu and hw.gpu.model else None, gpu_vendor=hw.gpu.vendor if hw.gpu else None, gpu_model=hw.gpu.model if hw.gpu else None, gpu_driver_version=hw.gpu.driver_version if hw.gpu else None, gpu_memory_dedicated_mb=hw.gpu.memory_dedicated_mb if hw.gpu else None, gpu_memory_shared_mb=hw.gpu.memory_shared_mb if hw.gpu else None, gpu_api_support=json.dumps(hw.gpu.api_support) if hw.gpu and hw.gpu.api_support else None, # Storage storage_summary=f"{len(hw.storage.devices)} device(s)" if hw.storage and hw.storage.devices else None, storage_devices_json=json.dumps([d.dict() for d in hw.storage.devices]) if hw.storage and hw.storage.devices else None, partitions_json=json.dumps([p.dict() for p in hw.storage.partitions]) if hw.storage and hw.storage.partitions else None, # Network network_interfaces_json=json.dumps([i.dict() for i in hw.network.interfaces]) if hw.network and hw.network.interfaces else None, # OS / Motherboard os_name=hw.os.name if hw.os else None, os_version=hw.os.version if hw.os else None, kernel_version=hw.os.kernel_version if hw.os else None, architecture=hw.os.architecture if hw.os else None, virtualization_type=hw.os.virtualization_type if hw.os else None, motherboard_vendor=hw.motherboard.vendor if hw.motherboard else None, motherboard_model=hw.motherboard.model if hw.motherboard else None, bios_version=hw.motherboard.bios_version if hw.motherboard else None, bios_date=hw.motherboard.bios_date if hw.motherboard else None, # Misc sensors_json=json.dumps(hw.sensors.dict()) if hw.sensors else None, raw_info_json=json.dumps(hw.raw_info.dict()) if hw.raw_info else None ) db.add(snapshot) db.flush() # Get snapshot.id # 3. Create benchmark results = payload.results # Calculate global score if not provided or recalculate global_score = calculate_global_score( cpu_score=results.cpu.score if results.cpu else None, memory_score=results.memory.score if results.memory else None, disk_score=results.disk.score if results.disk else None, network_score=results.network.score if results.network else None, gpu_score=results.gpu.score if results.gpu else None ) # Use provided global_score if available and valid if results.global_score is not None: global_score = results.global_score benchmark = Benchmark( device_id=device.id, hardware_snapshot_id=snapshot.id, run_at=datetime.utcnow(), bench_script_version=payload.bench_script_version, global_score=global_score, cpu_score=results.cpu.score if results.cpu else None, memory_score=results.memory.score if results.memory else None, disk_score=results.disk.score if results.disk else None, network_score=results.network.score if results.network else None, gpu_score=results.gpu.score if results.gpu else None, details_json=json.dumps(results.dict()) ) db.add(benchmark) db.commit() return BenchmarkResponse( status="ok", device_id=device.id, benchmark_id=benchmark.id, message=f"Benchmark successfully recorded for device '{device.hostname}'" ) @router.get("/benchmarks/{benchmark_id}", response_model=BenchmarkDetail) async def get_benchmark( benchmark_id: int, db: Session = Depends(get_db) ): """ Get detailed benchmark information """ benchmark = db.query(Benchmark).filter(Benchmark.id == benchmark_id).first() if not benchmark: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Benchmark {benchmark_id} not found" ) return BenchmarkDetail( id=benchmark.id, device_id=benchmark.device_id, hardware_snapshot_id=benchmark.hardware_snapshot_id, run_at=benchmark.run_at.isoformat(), bench_script_version=benchmark.bench_script_version, global_score=benchmark.global_score, cpu_score=benchmark.cpu_score, memory_score=benchmark.memory_score, disk_score=benchmark.disk_score, network_score=benchmark.network_score, gpu_score=benchmark.gpu_score, details=json.loads(benchmark.details_json) )