-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Web UI - stage1, readonly part #606
Merged
Merged
Changes from 16 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
040d446
added fastapi as web ui backend
VukW 97e6bd7
Added cube + benchmark basic listing
VukW 0382684
Adds navigation
VukW 55fe60e
Aded mlcube detailed page
VukW fb1bca3
Improved mlcubes detailed layout
VukW 64cf53e
Improved mlcube layout
VukW 36611e1
yaml displaying
VukW 56fa5c4
yaml: spinner
VukW 8563887
yaml panel improvement
VukW 07ce4ab
yaml panel layout improvement
VukW b260401
layout fixes
VukW b7980a8
Added benchmark detailed page
VukW ca356cc
added links to mlcube
VukW 6efd724
benchmark page: added owner
VukW 319b1bf
Colors refactoring
VukW 58008f3
Dataset detailed page
VukW 375d89e
Forgot to add js file
VukW c6d8a56
Unified data format for all data fields automatically
VukW 74f7743
(mlcube-detailed) Display image tarball and additional files always
VukW b312882
Fixed scrolling and reinvented basic page layout
VukW 0e282cb
Fix navbar is hiding
VukW 6b28ebb
Make templates & static files independent of user's workdir
VukW 881b281
Added error handling
VukW e28107b
Display invalid entities correctly
VukW 5b718eb
Added invalid entities highlighting + badges
VukW 0f95027
Added benchmark associations
VukW 444786e
Improved association panel style
VukW e273577
Added association card
VukW eea1e77
Sorted associations by status / timestamp
VukW 7b68911
Sorted mlcubes and datasets: mine first
VukW 8251c42
Added associations to dataset page
VukW b669358
Added associations to mlcube page
VukW 039f496
Refactored details page - extracted common styles to the base template
VukW c225a5e
Refactored association sorting to common util
VukW ad0451f
Display my benchmarks first
VukW 12ffef2
Hid empty links
VukW cedad96
Mlcube-as-a-link unified view
VukW 3ac8a74
resources.path cannot return a dir with subdirs for py3.9
VukW 6170b53
Fixed resources path for templates also
VukW 53b557b
linter fix
VukW 2b73c4f
static local resources instead of remote ones
VukW 75d6776
layout fix: align mlcubes vertically
VukW c47a751
bugfix: add some dependencies for isolated run
VukW d837837
Merge branch 'main' into web-ui
VukW c58efd8
Fixes after merging main
VukW 8e73e54
MedperfSchema requires a name field
VukW a78ef8d
Linter fix
VukW 1384b21
Pass mlcube params instead of url
aristizabal95 64d8b3c
Pass mlcube parameters to fetch-yaml
aristizabal95 7d6f01a
Merge pull request #9 from aristizabal95/web-ui-fetch-yaml
VukW File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import typer | ||
from fastapi import FastAPI | ||
from fastapi.responses import RedirectResponse | ||
from fastapi.staticfiles import StaticFiles | ||
|
||
from medperf import config | ||
from medperf.decorators import clean_except | ||
from medperf.web_ui.datasets.routes import router as datasets_router | ||
from medperf.web_ui.benchmarks.routes import router as benchmarks_router | ||
from medperf.web_ui.mlcubes.routes import router as mlcubes_router | ||
from medperf.web_ui.yaml_fetch.routes import router as yaml_fetch_router | ||
|
||
web_app = FastAPI() | ||
|
||
web_app.include_router(datasets_router, prefix="/datasets") | ||
web_app.include_router(benchmarks_router, prefix="/benchmarks") | ||
web_app.include_router(mlcubes_router, prefix="/mlcubes") | ||
web_app.include_router(yaml_fetch_router) | ||
|
||
web_app.mount("/static", StaticFiles(directory="medperf/web_ui/static"), name="static") | ||
|
||
|
||
@web_app.get("/", include_in_schema=False) | ||
def read_root(): | ||
return RedirectResponse(url="/benchmarks/ui") | ||
|
||
|
||
app = typer.Typer() | ||
|
||
|
||
@app.command("run") | ||
@clean_except | ||
def run( | ||
port: int = typer.Option(8100, "--port", help="port to use"), | ||
): | ||
"""Runs a local web UI""" | ||
import uvicorn | ||
uvicorn.run(web_app, host="127.0.0.1", port=port, log_level=config.loglevel) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# medperf/web-ui/benchmarks/routes.py | ||
import logging | ||
|
||
from fastapi import APIRouter, HTTPException | ||
from fastapi.responses import HTMLResponse | ||
from fastapi import Request | ||
from fastapi.templating import Jinja2Templates | ||
|
||
from medperf.entities.benchmark import Benchmark | ||
from medperf.entities.cube import Cube | ||
from medperf.account_management import get_medperf_user_data | ||
|
||
router = APIRouter() | ||
templates = Jinja2Templates(directory="medperf/web_ui/templates") | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@router.get("/ui", response_class=HTMLResponse) | ||
def benchmarks_ui(request: Request, local_only: bool = False, mine_only: bool = False): | ||
filters = {} | ||
if mine_only: | ||
filters["owner"] = get_medperf_user_data()["id"] | ||
|
||
benchmarks = Benchmark.all( | ||
local_only=local_only, | ||
filters=filters, | ||
) | ||
return templates.TemplateResponse("benchmarks.html", {"request": request, "benchmarks": benchmarks}) | ||
|
||
|
||
@router.get("/ui/{benchmark_id}", response_class=HTMLResponse) | ||
def benchmark_detail_ui(request: Request, benchmark_id: int): | ||
benchmark = Benchmark.get(benchmark_id) | ||
data_preparation_mlcube = Cube.get(cube_uid=benchmark.data_preparation_mlcube) | ||
reference_model_mlcube = Cube.get(cube_uid=benchmark.reference_model_mlcube) | ||
metrics_mlcube = Cube.get(cube_uid=benchmark.data_evaluator_mlcube) | ||
|
||
return templates.TemplateResponse( | ||
"benchmark_detail.html", | ||
{ | ||
"request": request, | ||
"benchmark": benchmark, | ||
"data_preparation_mlcube": data_preparation_mlcube, | ||
"reference_model_mlcube": reference_model_mlcube, | ||
"metrics_mlcube": metrics_mlcube | ||
} | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import logging | ||
|
||
from fastapi import APIRouter, HTTPException | ||
from fastapi.responses import HTMLResponse | ||
from typing import List | ||
from fastapi import Request | ||
from fastapi.templating import Jinja2Templates | ||
|
||
from medperf.account_management import get_medperf_user_data | ||
from medperf.entities.cube import Cube | ||
from medperf.entities.dataset import Dataset | ||
|
||
router = APIRouter() | ||
|
||
templates = Jinja2Templates(directory="medperf/web_ui/templates") | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@router.get("/", response_model=List[Dataset]) | ||
def get_datasets(local_only: bool = False, mine_only: bool = False): | ||
filters = {} | ||
if mine_only: | ||
filters["owner"] = get_medperf_user_data()["id"] | ||
|
||
return Dataset.all( | ||
local_only=local_only, | ||
filters=filters, | ||
) | ||
|
||
|
||
@router.get("/ui", response_class=HTMLResponse) | ||
def datasets_ui(request: Request, local_only: bool = False, mine_only: bool = False): | ||
datasets = get_datasets(local_only, mine_only) | ||
return templates.TemplateResponse("datasets.html", {"request": request, "datasets": datasets}) | ||
|
||
|
||
@router.get("/ui/{dataset_id}", response_class=HTMLResponse) | ||
def dataset_detail_ui(request: Request, dataset_id: int): | ||
dataset = Dataset.get(dataset_id) | ||
|
||
prep_cube = Cube.get(cube_uid=dataset.data_preparation_mlcube) | ||
prep_cube_name = prep_cube.name if prep_cube else "Unknown" | ||
return templates.TemplateResponse("dataset_detail.html", {"request": request, "dataset": dataset, "prep_cube_name": prep_cube_name}) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import logging | ||
|
||
from fastapi import APIRouter, HTTPException | ||
from fastapi.responses import HTMLResponse | ||
from fastapi import Request | ||
from fastapi.templating import Jinja2Templates | ||
|
||
from medperf.entities.cube import Cube | ||
from medperf.account_management import get_medperf_user_data | ||
|
||
router = APIRouter() | ||
templates = Jinja2Templates(directory="medperf/web_ui/templates") | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@router.get("/ui", response_class=HTMLResponse) | ||
def mlcubes_ui(request: Request, local_only: bool = False, mine_only: bool = False): | ||
filters = {} | ||
if mine_only: | ||
filters["owner"] = get_medperf_user_data()["id"] | ||
|
||
mlcubes = Cube.all( | ||
local_only=local_only, | ||
filters=filters, | ||
) | ||
return templates.TemplateResponse("mlcubes.html", {"request": request, "mlcubes": mlcubes}) | ||
|
||
|
||
@router.get("/ui/{mlcube_id}", response_class=HTMLResponse) | ||
def mlcube_detail_ui(request: Request, mlcube_id: int): | ||
mlcube = Cube.get(cube_uid=mlcube_id) | ||
return templates.TemplateResponse("mlcube_detail.html", {"request": request, "mlcube": mlcube}) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
<!-- ./cli/medperf/web-ui/templates/base.html --> | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>{% block title %}MedPerf{% endblock %}</title> | ||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> | ||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> | ||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"> | ||
<style> | ||
.badge-state-operational { | ||
background-color: lightgreen; | ||
color: black; | ||
} | ||
.badge-state-development { | ||
background-color: lightcoral; | ||
color: black; | ||
} | ||
.badge-approval-pending { | ||
background-color: coral; | ||
color: black; | ||
} | ||
.badge-approval-approved { | ||
background-color: lightgreen; | ||
color: black; | ||
} | ||
.badge-approval-rejected { | ||
background-color: red; | ||
color: black; | ||
} | ||
.spinner { | ||
display: inline-block; | ||
width: 80px; | ||
height: 80px; | ||
border: 3px solid rgba(0, 0, 0, 0.1); | ||
border-radius: 50%; | ||
border-top-color: #007bff; | ||
animation: spin 1s ease-in-out infinite; | ||
margin: 0 auto; | ||
} | ||
.page-container { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: flex-start; | ||
height: 100vh; /* Ensure the parent container takes full viewport height */ | ||
overflow: hidden; /* Prevent horizontal scrolling on the whole page */ | ||
} | ||
|
||
.main-content-wrapper { | ||
flex: 1; | ||
display: flex; | ||
justify-content: center; /* Center the main content horizontally */ | ||
align-items: flex-start; /* Align items to the top */ | ||
width: 100%; /* Take all the available width */ | ||
} | ||
|
||
.main-content { | ||
/*max-width: 800px;*/ | ||
min-width: 0; /* Allow the main content to shrink below its content size */ | ||
transition: margin-right 0.3s; | ||
} | ||
|
||
#yaml-content { | ||
flex: 0 0 40%; | ||
max-width: 40%; | ||
min-width: 300px; /* Set a reasonable minimum width */ | ||
height: 100%; /* Take full height of the parent container */ | ||
overflow-y: auto; /* Enable vertical scrolling within this container */ | ||
overflow-x: auto; /* Enable horizontal scrolling within this container if needed */ | ||
} | ||
|
||
@keyframes spin { | ||
to { | ||
transform: rotate(360deg); | ||
} | ||
} | ||
pre { | ||
white-space: pre-wrap; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<nav class="navbar navbar-expand-lg navbar-light bg-light"> | ||
<a class="navbar-brand" href="#">MedPerf</a> | ||
<div class="collapse navbar-collapse" id="navbarNav"> | ||
<ul class="navbar-nav"> | ||
<li class="nav-item"> | ||
<a class="nav-link" href="/benchmarks/ui">Benchmarks</a> | ||
</li> | ||
<li class="nav-item"> | ||
<a class="nav-link" href="/mlcubes/ui">MLCubes</a> | ||
</li> | ||
<li class="nav-item"> | ||
<a class="nav-link" href="/datasets/ui">Datasets</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</nav> | ||
<div class="page-container d-flex mt-4"> | ||
<div class="main-content-wrapper"> | ||
<div class="container main-content"> | ||
{% block content %}{% endblock %} | ||
</div> | ||
<div class="container mt-4" id="yaml-content" style="display: none;"> | ||
<h5>YAML Content</h5> | ||
<div id="loading-indicator" class="spinner" style="display: none;"></div> | ||
<pre><code id="yaml-code" class="language-yaml"></code></pre> | ||
</div> | ||
</div> | ||
</div> | ||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/components/prism-yaml.min.js"></script> | ||
<script src="/static/js/common.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<!-- ./cli/medperf/web-ui/templates/benchmark_detail.html --> | ||
{% extends "base.html" %} | ||
|
||
{% block title %}Benchmark Details{% endblock %} | ||
|
||
{% block content %} | ||
<style> | ||
.card-body.d-flex { | ||
flex-wrap: wrap; | ||
} | ||
|
||
.card-text { | ||
word-wrap: break-word; | ||
white-space: normal; | ||
} | ||
</style> | ||
<h1 class="my-4">{{ benchmark.name }}</h1> | ||
<div class="card"> | ||
<div class="card-body"> | ||
<div class="d-flex justify-content-between align-items-center mb-3"> | ||
<h5 class="card-title mb-0">Details</h5> | ||
<span class="badge {% if benchmark.state == 'OPERATION' %}badge-state-operational{% else %}badge-state-development{% endif %}"> | ||
{{ benchmark.state }} | ||
</span> | ||
<span class="badge {% if benchmark.approval_status == 'APPROVED' %}badge-approval-approved{% elif benchmark.approval_status == 'PENDING' %}badge-approval-pending{% else %}badge-approval-rejected{% endif %}"> | ||
{{ benchmark.approval_status }} | ||
</span> | ||
</div> | ||
<div class="card mb-3"> | ||
<div class="card-body"> | ||
<p class="card-text"><strong>Description:</strong> {{ benchmark.description }}</p> | ||
<p class="card-text"><strong>Documentation:</strong> <a href="{{ benchmark.docs_url }}" class="text-primary" target="_blank">{{ benchmark.docs_url }}</a></p> | ||
<p class="card-text"><strong>Demo Dataset Tarball:</strong> <a href="{{ benchmark.demo_dataset_tarball_url }}" class="text-primary" target="_blank">{{ benchmark.demo_dataset_tarball_url }}</a></p> | ||
<p class="card-text text-muted small">{{ benchmark.demo_dataset_tarball_hash }}</p> | ||
</div> | ||
</div> | ||
<div class="card mb-3"> | ||
<div class="card-body d-flex justify-content-between flex-wrap"> | ||
<div class="w-50"> | ||
<p class="card-text"><strong>Data Preparation MLCube:</strong> <a href="/mlcubes/ui/{{ data_preparation_mlcube.id }}" class="text-primary">{{ data_preparation_mlcube.name }}</a></p> | ||
<p class="card-text"><strong>Reference Model MLCube:</strong> <a href="/mlcubes/ui/{{ reference_model_mlcube.id }}" class="text-primary">{{ reference_model_mlcube.name }}</a></p> | ||
<p class="card-text"><strong>Metrics MLCube:</strong> <a href="/mlcubes/ui/{{ metrics_mlcube.id }}" class="text-primary">{{ metrics_mlcube.name }}</a></p> | ||
<p class="card-text"><strong>Owner:</strong> <i class="fas fa-user"></i> <span class="text-muted small">{{ benchmark.owner }}</span></p> | ||
</div> | ||
<div class="text-right w-50"> | ||
<p class="card-text"><strong>Created:</strong> <span class="text-muted small" id="created-at">{{ benchmark.created_at }}</span></p> | ||
<p class="card-text"><strong>Modified:</strong> <span class="text-muted small" id="modified-at">{{ benchmark.modified_at }}</span></p> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<script> | ||
document.addEventListener('DOMContentLoaded', function() { | ||
formatDates(); | ||
}); | ||
</script> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{% extends "base.html" %} | ||
|
||
{% block title %}Benchmarks{% endblock %} | ||
|
||
{% block content %} | ||
<h1 class="my-4">Benchmarks</h1> | ||
<div class="row"> | ||
{% for benchmark in benchmarks %} | ||
<div class="col-md-4 mb-4"> | ||
<div class="card"> | ||
<div class="card-body"> | ||
<h5 class="card-title"><a href="/benchmarks/ui/{{ benchmark.id }}" class="text-primary">{{ benchmark.name }}</a></h5> | ||
<h6 class="card-subtitle mb-2 text-muted">{{ benchmark.state }}</h6> | ||
<p class="card-text">{{ benchmark.description }}</p> | ||
<p class="card-text"><a href="{{ benchmark.docs_url }}" class="card-link">Documentation</a></p> | ||
<p class="card-text"><small class="text-muted">Created At: {{ benchmark.created_at }}</small></p> | ||
<p class="card-text"><small class="text-muted">Data Preparation MLCube: {{ benchmark.data_preparation_mlcube }}</small></p> | ||
<p class="card-text"><small class="text-muted">Reference Model MLCube: {{ benchmark.reference_model_mlcube }}</small></p> | ||
<p class="card-text"><small class="text-muted">Data Evaluator MLCube: {{ benchmark.data_evaluator_mlcube }}</small></p> | ||
<p class="card-text"><small class="text-muted">Approval Status: {{ benchmark.approval_status }}</small></p> | ||
<p class="card-text"><small class="text-muted">Registered: {{ benchmark.is_registered }}</small></p> | ||
</div> | ||
</div> | ||
</div> | ||
{% endfor %} | ||
</div> | ||
{% endblock %} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to put this in a separate css file? It feels like this has too much content to be inserted as an inline style instruction