fix: add undo disk selection capability #2

Closed
bot wants to merge 26 commits from fio_workflow into main
7 changed files with 134 additions and 55 deletions
Showing only changes of commit 415d8b3601 - Show all commits
+5
View File
@@ -1 +1,6 @@
"""Web backend module for disk-checker API."""
from .web_routes import web_router
from .routes import api_router
__all__ = ["web_router", "api_router"]
+3 -47
View File
@@ -1,56 +1,12 @@
"""API routes for disk-checker web backend."""
import asyncio
import tempfile
from pathlib import Path
from typing import List, Optional, Dict
from fastapi import APIRouter, Request
from fastapi import APIRouter, UploadFile, File, HTTPException, Depends, Request
from fastapi.responses import JSONResponse, FileResponse
import pandas as pd
from disk_checker.core.extractor import load_latency_csv
from disk_checker.core.helpers import percentile_table
from disk_checker.core.tool_scanner import ToolInfo
api_router = APIRouter()
from disk_checker.plotting.plots import generate_plot
from disk_checker.web.models import (
JobStatusResponse,
)
router = APIRouter()
@router.get("/jobs/{job_id}", response_model=JobStatusResponse)
async def get_job_status(job_id: str, request: Request):
"""
Check the status of a benchmark job.
"""
jobs = request.app.state.jobs
if job_id not in jobs:
raise HTTPException(status_code=404, detail="Job not found")
job = jobs[job_id]
task = job["task"]
if task.done():
result = task.result()
return JobStatusResponse(**result)
return JobStatusResponse(job_id=job_id, status="running")
@router.get("/jobs", response_model=List[str])
async def list_jobs(request: Request):
"""
List all benchmark jobs.
"""
return list(request.app.state.jobs.keys())
@router.get("/tools")
@api_router.get("/tools")
async def get_available_tools(request: Request):
"""
Get list of available benchmarking tools.
+28 -6
View File
@@ -2,14 +2,31 @@
import asyncio
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging
from disk_checker.core.orchestrator import Orchestrator
from disk_checker.core.tool_scanner import scan_tools
# Import and include routers
from disk_checker.web.routes import api_router
from disk_checker.web.web_routes import web_router
# Get application root
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
TEMPLATES_DIR = BASE_DIR / "templates"
STATIC_DIR = BASE_DIR / "static"
# Setup templates
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
# Setup logger
logger = logging.getLogger(__name__)
@@ -24,6 +41,10 @@ async def lifespan(app: FastAPI):
app.state.available_tools = await asyncio.to_thread(scan_tools)
logger.info(f"Available tools: {list(app.state.available_tools.keys())}")
# Store templates and static_dir path in app.state
app.state.templates = templates
app.state.static_dir = STATIC_DIR
logger.info("Disk Checker API started")
yield
@@ -37,7 +58,13 @@ app = FastAPI(
version="0.1.0",
lifespan=lifespan,
)
# Setup static files
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# Include routers
app.include_router(api_router, prefix="/api/v1")
app.include_router(web_router)
# Add CORS (permissive by default)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
@@ -46,13 +73,8 @@ app.add_middleware(
allow_headers=["*"],
)
# Simple health endpoint
@app.get("/health")
async def health_check():
"""Health check endpoint."""
return {"status": "ok", "version": "0.1.0"}
# Import and include routers
from disk_checker.web.routes import router as api_router
app.include_router(api_router, prefix="/api/v1")
+19
View File
@@ -0,0 +1,19 @@
from fastapi import APIRouter, Request
from fastapi.templating import Jinja2Templates
from typing import Dict
web_router = APIRouter()
@web_router.get("/")
async def render_home(request: Request):
"""
Render the main page
"""
tools = request.app.state.available_tools
return request.app.state.templates.TemplateResponse(
name="index.html",
request=request,
context={
"tools":tools
}
)
+17
View File
@@ -0,0 +1,17 @@
/* Basic styling for Disk Checker */
.container {
max-width: 1200px;
}
.card {
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.badge {
font-size: 0.9rem;
}
+26
View File
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Disk Checker</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">Disk Checker</a>
</div>
</nav>
<!--Available tools block, tools are loaded automatically-->
<div class="container">
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block content %}
<div class="container mt-4">
<h2>Available Benchmarking Tools</h2>
<!-- Server-side rendered tools -->
{% if tools %}
<div class="row">
{% for tool_name, tool in tools.items() %}
<div class="col-md-4 mb-3">
<div class="card {% if tool.available %}border-success{% else %}border-secondary{% endif %}">
<div class="card-body">
<h5 class="card-title">{{ tool.name }}</h5>
<p class="card-text">
{% if tool.available %}
<span class="badge bg-success">Available</span>
{% if tool.version %}
<small class="text-muted">{{ tool.version }}</small>
{% endif %}
{% else %}
<span class="badge bg-secondary">Not Installed</span>
{% endif %}
</p>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div id="tools-content"></div>
</div>
{% endblock %}