Add flask templates

This commit is contained in:
2023-05-28 00:04:30 +02:00
parent c10bf27a16
commit 219b626061
18 changed files with 554 additions and 19 deletions

View File

@@ -13,21 +13,27 @@ ALL_METRICS = (
class EnvData:
@staticmethod
def load():
args = {}
def __init__(self):
self.args = {}
def load(self):
try:
with open(Files.dot_env) as f:
for line in f.read().splitlines():
if line == "" or line.startswith("#"):
continue
key, value = line.split("=")
args[key] = value
self.args[key] = value
except FileNotFoundError:
print(NO_ENV, file=sys.stderr)
exit(1)
else:
return args
return self.args
def save(self):
with open(Files.dot_env, "w") as f:
for key, value in self.args.items():
f.write(f"{key}={value}\n")
class EnvDefault(argparse.Action):
@@ -35,7 +41,7 @@ class EnvDefault(argparse.Action):
def __init__(
self, envvar, required=True, default=None, mandatory=False, **kwargs
):
self._args = EnvData.load()
self._args = EnvData().load()
self._overrides = {}
if required and not mandatory:
default = self._args[envvar]

View File

@@ -109,7 +109,7 @@ class DatasetsSurcov:
class Datasets:
def __init__(self, dataset_name=None, discretize=None):
env_data = EnvData.load()
env_data = EnvData().load()
# DatasetsSurcov, DatasetsTanveer, DatasetsArff,...
source_name = getattr(
__import__(__name__),

View File

@@ -22,7 +22,7 @@ from .Arguments import EnvData
class Randomized:
@staticmethod
def seeds():
return json.loads(EnvData.load()["seeds"])
return json.loads(EnvData().load()["seeds"])
class BestResults:
@@ -117,7 +117,7 @@ class Experiment:
discretize=None,
folds=5,
):
env_data = EnvData.load()
env_data = EnvData().load()
today = datetime.now()
self.time = today.strftime("%H:%M:%S")
self.date = today.strftime("%Y-%m-%d")

View File

@@ -71,7 +71,6 @@ class Report(BaseReport):
self._load_best_results(
self.data["score_name"], self.data["model"]
)
self._compare_totals = {}
self.header_line("*")
self.header_line(
f" {self.data['model']} ver. {self.data['version']}"

View File

@@ -52,10 +52,11 @@ class BaseReport(abc.ABC):
self.score_name = self.data["score_name"]
self.__load_env_data()
self.__compute_best_results_ever()
self._compare_totals = {}
def __load_env_data(self):
# Set the labels for nodes, leaves, depth
env_data = EnvData.load()
env_data = EnvData().load()
self.nodes_label = env_data["nodes"]
self.leaves_label = env_data["leaves"]
self.depth_label = env_data["depth"]
@@ -149,6 +150,7 @@ class BaseReport(abc.ABC):
class StubReport(BaseReport):
def __init__(self, file_name):
self.compare = False
super().__init__(file_name=file_name, best_file=False)
def print_line(self, line) -> None:
@@ -165,7 +167,7 @@ class StubReport(BaseReport):
class Summary:
def __init__(self, hidden=False, compare=False) -> None:
self.results = Files().get_all_results(hidden=hidden)
self.results = Files.get_all_results(hidden=hidden)
self.data = []
self.data_filtered = []
self.datasets = {}

View File

@@ -620,7 +620,7 @@ class Benchmark:
self.__compute_best_results_ever()
def __compute_best_results_ever(self):
args = EnvData.load()
args = EnvData().load()
key = args["source_data"]
best = BestResultsEver()
_, self.best_score_value = best.get_name_value(key, self._score)

View File

@@ -108,7 +108,8 @@ class Files:
)
return None
def get_all_results(self, hidden) -> list[str]:
@staticmethod
def get_all_results(hidden) -> list[str]:
result_path = os.path.join(
".", Folders.hidden_results if hidden else Folders.results
)
@@ -117,7 +118,7 @@ class Files:
else:
raise ValueError(f"{result_path} does not exist")
result = []
prefix, suffix = self.results_suffixes()
prefix, suffix = Files.results_suffixes()
for result_file in files_list:
if result_file.startswith(prefix) and result_file.endswith(suffix):
result.append(result_file)

85
benchmark/scripts/be_flask.py Executable file
View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python
import os
import json
import webbrowser
from benchmark.Utils import Files, Folders, Symbols
from benchmark.Arguments import Arguments, EnvData
from benchmark.ResultsBase import StubReport
from flask import Flask
from flask import render_template, request, redirect, url_for
# Launch a flask server to serve the results
app = Flask(__name__)
FRAMEWORK = "framework"
FRAMEWORKS = "frameworks"
HIDDEN = "hidden"
def process_data(file_name, data):
report = StubReport(os.path.join(Folders.results, file_name))
new_list = []
for result in data["results"]:
symbol = report._compute_status(result["dataset"], result["score"])
result["symbol"] = symbol if symbol != " " else " "
new_list.append(result)
data["results"] = new_list
# Compute summary with explanation of symbols
summary = {}
for key, value in report._compare_totals.items():
summary[key] = (report._status_meaning(key), value)
return summary
@app.route("/index")
@app.route("/")
def index():
# Get a list of files in a directory
files = Files.get_all_results(hidden=app.config[HIDDEN])
candidate = app.config[FRAMEWORKS].copy()
candidate.remove(app.config[FRAMEWORK])
return render_template(
f"select_{app.config[FRAMEWORK]}.html",
files=files,
framework=candidate[0],
)
@app.route("/show", methods=["post"])
def show():
selected_file = request.form["selected-file"]
with open(os.path.join(Folders.results, selected_file)) as f:
data = json.load(f)
summary = process_data(selected_file, data)
return render_template(
f"report_{app.config[FRAMEWORK]}.html", data=data, summary=summary
)
@app.route("/config/<framework>")
def config(framework):
if not framework in app.config[FRAMEWORKS]:
message = f"framework {framework} not supported"
return render_template("error.html", message=message)
env = EnvData()
env.load()
env.args[FRAMEWORK] = framework
env.save()
app.config[FRAMEWORK] = framework
return redirect(url_for("index"))
def main(args_test=None):
arguments = Arguments(prog="be_flask")
arguments.xset("model", required=False)
arguments.xset("score", required=False).xset("compare").xset("hidden")
arguments.xset("nan")
args = arguments.parse(args_test)
app.config[FRAMEWORK] = EnvData().load()[FRAMEWORK]
app.config[HIDDEN] = args.hidden
app.config[FRAMEWORKS] = ["bootstrap", "bulma"]
webbrowser.open_new("http://127.0.0.1:1234/")
app.run(port=1234)
# Poner checkboxes para seleccionar resultados y poner un botón abajo para hacer un excel con los seleccionados
# Calcular símbolo igual que en list, o bien si ha puesto el parámetro de compare, con best o con zeror

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="alert alert-danger my-5" role="alert">
<h4 class="alert-heading"><button class="btn-close btn-sm" type="button"
onclick="location.href='/index';"></button>Error</h4>
<p>There was an error processing action, {{ message }}. Please try again later.</p>
<hr>
<p class="mb-0">If the problem persists, please contact support.</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,28 @@
{% for item in data.results %}
<tr>
<td>
{{item.dataset}}
</td>
<td class="{{ right }}">
{{'{:,}'.format(item.samples)}}
</td>
<td class="{{ right }}">
{{"%d" % item.features}}
</td>
<td class="{{ right }}">
{{"%d" % item.classes}}
</td>
<td class="{{ right }}">
{{'{:,.2f}'.format(item.nodes)}}
</td>
<td class="{{ right }}">
{{"%.6f±%.4f" % (item.score, item.score_std)}} {{ item.symbol|safe }}
</td>
<td class="{{ right }}">
{{"%.6f±%.4f" % (item.time, item.time_std)}}
</td>
<td class="{{ center }}">
{{item.hyperparameters}}
</td>
</tr>
{% endfor %}

View File

@@ -0,0 +1,30 @@
<table id="file-table" class={{ table_class }}>
<thead>
<tr>
<th>Model</th>
<th>Metric</th>
<th>Platform</th>
<th>Date</th>
<th>Time</th>
<th>Stratified</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in files %}
{% set parts = file.split('_') %}
{% set stratified = parts[6].split('.')[0] %}
<tr>
<td>{{ parts[2] }}</td>
<td>{{ parts[1] }}</td>
<td>{{ parts[3] }}</td>
<td>{{ parts[4] }}</td>
<td>{{ parts[5] }}</td>
<td>{{ 'True' if stratified =='1' else 'False' }}</td>
<td>
{{ button_pre | safe }}{{ file }}{{ button_post | safe }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

View File

@@ -0,0 +1,13 @@
{% for key, value in summary.items() %}
<tr>
<td class="{{ center }}">
{{key}}
</td>
<td >
{{value[0]}}
</td>
<td class={{ right }}>
{{'{:,}'.format(value[1])}}
</td>
</tr>
{% endfor %}

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<title>Report Viewer</title>
<style>
body {
font-family: Courier;
}
</style>
</head>
<body>
{% set center = "text-center" %}
{% set right = "text-end" %}
<div id="app">
<section class="section">
<div class="container">
<div class="p-4 bg-primary text-white">
<button type="button" class="btn-close" aria-label="Close" onclick="location.href='/index';"></button>
<h1>{{ data.title }}</h1>
</div>
<div>
<table class="table table-bordered">
<thead>
<tr class="bg-info text-white">
<th class="text-center">Platform</th>
<th class="text-center">Model</th>
<th class="text-center">Date</th>
<th class="text-center">Time</th>
{% if data.duration > 7200 %}
{% set unit = "h" %}
{% set divider = 3600 %}
{% else %}
{% set unit = "min" %}
{% set divider = 60 %}
{% endif %}
<th class="text-center">Duration ({{ unit }})</th>
<th class="text-center">Stratified</th>
<th class="text-center">Discretized</th>
<th class="text-center"># Folds</th>
</tr>
<tr>
<th class="text-center">{{ data.platform }}</th>
<th class="text-center">{{ data.model }} {{ data.version }}</th>
<th class="text-center">{{ data.date}}</th>
<th class="text-center">{{ data.time}}</th>
<th class="text-center">{{ "%.2f" % (data.duration/divider) }}</th>
<th class="text-center">{{ data.stratified }}</th>
<th class="text-center">{{ data.discretized }}</th>
<th class="text-center">{{ data.folds }}</th>
</tr>
<tr>
<th class="text-center bg-info text-white">Language</th>
<th class="text-center" colspan=3>{{ data.language }} {{ data.language_version }}</th>
<th class="text-center bg-info text-white">Seeds</th>
<th class="text-center" colspan=6>{{ data.seeds }}</th>
</tr>
</thead>
</table>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr class="bg-primary text-white">
<th class="text-center">Dataset</th>
<th class="text-center">Samples</th>
<th class="text-center">Features</th>
<th class="text-center">Classes</th>
<th class="text-center">Nodes</th>
<th class="text-center">{{data.score_name|capitalize}}</th>
<th class="text-center">Time</th>
<th class="text-center">hyperparameters</th>
</tr>
</thead>
<tbody>
{% include "partials/table_report.html" %}
</tbody>
</table>
{% if summary|length > 0 %}
<div class="col-4 col-lg-4">
<table class="table table-bordered">
<thead>
<tr>
<th class="text-center bg-primary text-white">Symbol</th>
<th class="text-center bg-primary text-white">Meaning</th>
<th class="text-center bg-primary text-white">Count</th>
</tr>
</thead>
{% include "partials/table_summary.html" %}
</table>
</div>
{% endif %}
<button type="button" class="btn-close" aria-label="Close" onclick="location.href='/index';"></button>
<h7><b>
Total score: {{ "%.6f" % (data.results | sum(attribute="score")) }}
</b></h7>
</div>
</section>
</div>
</body>
</html>

View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<title>Report Viewer</title>
<style>
body {
font-family: Courier;
}
</style>
</head>
<body>
{% set center = "has-text-centered" %}
{% set right = "has-text-right" %}
<div id="app">
<header>
<div class="container">
<div class="hero is-info is-bold">
<div class="hero-body">
<button class="delete is-large" onclick="location.href='/index';"></button>
<h1 class="is-size-3">{{ data.title }}</h1>
</div>
</div>
</div>
</header>
<section class="section">
<div class="container">
<div>
<table class="table is-fullwidth is-striped is-bordered">
<thead>
<tr class="is-selected">
<th class="has-text-centered">Platform</th>
<th class="has-text-centered">Model</th>
<th class="has-text-centered">Date</th>
<th class="has-text-centered">Time</th>
{% if data.duration > 7200 %}
{% set unit = "h" %}
{% set divider = 3600 %}
{% else %}
{% set unit = "min" %}
{% set divider = 60 %}
{% endif %}
<th class="has-text-centered">Duration ({{ unit }})</th>
<th class="has-text-centered">Stratified</th>
<th class="has-text-centered">Discretized</th>
<th class="has-text-centered"># Folds</th>
</tr>
<tr>
<th class="has-text-centered">{{ data.platform }}</th>
<th class="has-text-centered">{{ data.model }} {{ data.version }}</th>
<th class="has-text-centered">{{ data.date}}</th>
<th class="has-text-centered">{{ data.time}}</th>
<th class="has-text-centered">{{ "%.2f" % (data.duration/divider) }}</th>
<th class="has-text-centered">{{ data.stratified }}</th>
<th class="has-text-centered">{{ data.discretized }}</th>
<th class="has-text-centered">{{ data.folds }}</th>
</tr>
<tr>
<th class="has-text-center is-selected">Language</th>
<th class="has-text-centered" colspan=3>{{ data.language }} {{ data.language_version }}</th>
<th class="has-text-centered is-selected">Seeds</th>
<th class="has-text-centered" colspan=6>{{ data.seeds }}</th>
</tr>
</thead>
</table>
<table class="table is-fullwidth is-striped is-hoverable is-bordered">
<thead>
<tr class="is-selected">
<th class="has-text-centered">Dataset</th>
<th class="has-text-centered">Samples</th>
<th class="has-text-centered">Features</th>
<th class="has-text-centered">Classes</th>
<th class="has-text-centered">Nodes</th>
<th class="has-text-centered">{{data.score_name|capitalize}}</th>
<th class="has-text-centered">Time</th>
<th class="has-text-centered">hyperparameters</th>
</tr>
</thead>
<tbody>
{% include "partials/table_report.html" %}
</tbody>
</table>
{% if summary|length > 0 %}
<div class="col-2 col-lg-2">
<table class="table is-bordered">
<thead>
<tr class="is-selected">
<th class="has-text-centered">Symbol</th>
<th class="has-text-centered">Meaning</th>
<th class="has-text-centered">Count</th>
</tr>
</thead>
{% include "partials/table_summary.html" %}
</table>
</div>
{% endif %}
<h2 class="has-text-white has-background-primary"><b>
<button class="delete" onclick="location.href='/index';"></button>
Total score: {{ "%.6f" % (data.results | sum(attribute="score")) }}
</b></h2>
</div>
</section>
</div>
</body>
</html>

View File

@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Benchmark</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.min.css">
<style>
body {
font-family: Courier;
}
#file-table tbody tr.selected td{
background-color: #0dcaf0;
color:white;
}
</style>
</head>
<body>
<div class="container">
<h1 class="text-center"><b>Benchmark Results</b></h1>
<button class="btn btn-primary btn-sm" onclick="location.href='/config/{{ framework }}';">Use {{ framework
}}</button>
{% set table_class = "table table-striped table-hover" %}
{% set button_pre = '<button class="btn btn-primary btn-sm"
style="--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .5rem; --bs-btn-font-size: .75rem;"
onclick="showFile(\''%}
{% set button_post = '\')">View
</button>' %}
{% include "partials/table_select.html" %}
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js" integrity="sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V"
crossorigin="anonymous"></script>
<script>
$(document).ready(function () {
var table = $('#file-table').DataTable({
"paging": true,
"searching": true,
"ordering": true,
"info": true,
"columnDefs": [{
"targets": 6,
"orderable": false
}]
});
$('#file-table tbody').on('click', 'tr', function () {
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
table.$('tr.selected').removeClass('selected');
$(this).addClass('selected');
}
});
});
function showFile(selectedFile) {
var form = $('<form action="/show" method="post">' +
'<input type="hidden" name="selected-file" value="' + selectedFile + '" />' +
'</form>');
$('body').append(form);
form.submit();
}
</script>
</body>
</html>

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title>Benchmark Results</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.min.css">
<style>
body {
font-family: Courier;
}
.tag {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title is-1 has-text-centered">Benchmark Results</h1>
<button class="button is-primary is-small" onclick="location.href='/config/{{ framework }}';">Use {{ framework
}}</button>
{% set table_class = "table is-striped is-hoverable cell-border" %}
{% set button_pre = '<span class="tag is-link is-normal" type="button" onclick="showFile(\'' %}
{% set button_post = '\')">View</span>' %}
{% include "partials/table_select.html" %}
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function () {
var table = $('#file-table').DataTable({
"paging": true,
"searching": true,
"ordering": true,
"info": true,
"select.items": "row",
"columnDefs": [{
"targets": 6,
"orderable": false
}]
});
$('#file-table tbody').on('click', 'tr', function () {
if ($(this).hasClass('is-selected')) {
$(this).removeClass('is-selected');
} else {
table.$('tr.is-selected').removeClass('is-selected');
$(this).addClass('is-selected');
}
});
});
function showFile(selectedFile) {
var form = $('<form action="/show" method="post">' +
'<input type="hidden" name="selected-file" value="' + selectedFile + '" />' +
'</form>');
$('body').append(form);
form.submit();
}
</script>
</body>
</html>

View File

@@ -118,7 +118,7 @@ class UtilTest(TestBase):
def test_Files_get_results(self):
os.chdir(os.path.dirname(os.path.abspath(__file__)))
self.assertCountEqual(
Files().get_all_results(hidden=False),
Files.get_all_results(hidden=False),
[
"results_accuracy_STree_iMac27_2021-10-27_09:40:40_0.json",
"results_accuracy_STree_iMac27_2021-09-30_11:42:07_0.json",
@@ -130,7 +130,7 @@ class UtilTest(TestBase):
],
)
self.assertCountEqual(
Files().get_all_results(hidden=True),
Files.get_all_results(hidden=True),
[
"results_accuracy_STree_iMac27_2021-11-01_23:55:16_0.json",
"results_accuracy_XGBoost_MacBookpro16_2022-05-04_11:00:35_"
@@ -143,7 +143,7 @@ class UtilTest(TestBase):
# check with results
os.rename(Folders.results, f"{Folders.results}.test")
try:
Files().get_all_results(hidden=False)
Files.get_all_results(hidden=False)
except ValueError:
pass
else:
@@ -153,7 +153,7 @@ class UtilTest(TestBase):
# check with hidden_results
os.rename(Folders.hidden_results, f"{Folders.hidden_results}.test")
try:
Files().get_all_results(hidden=True)
Files.get_all_results(hidden=True)
except ValueError:
pass
else:

View File

@@ -39,6 +39,7 @@ def script_names():
"report",
"summary",
"init_project",
"flask",
]
result = []
for script in scripts: