Begin adding sockets

This commit is contained in:
2023-06-09 22:27:09 +02:00
parent bba590c0a5
commit 9c43e2721a
13 changed files with 269 additions and 53 deletions

View File

@@ -2,11 +2,13 @@
from flask import Flask
from flask_bootstrap import Bootstrap5
from flask_login import LoginManager
from flask_socketio import SocketIO
from .config import Config
from .models import User, db
from .results.main_results import results
from .admin.main_admin import admin
from .main import main
bootstrap = Bootstrap5()
@@ -36,6 +38,12 @@ def create_app():
app.register_blueprint(admin, url_prefix="/admin")
app.register_blueprint(main)
app.shell_context_processor(make_shell_context)
socketio = SocketIO(app)
with app.app_context():
db.create_all()
return app
app.socket = socketio
from .interactive.main_interactive import interactive
app.register_blueprint(interactive, url_prefix="/admin")
return socketio, app

View File

@@ -1 +1 @@
COMPARE=True
SECRET=Really-hard-to-guess-secret.

View File

@@ -1,8 +1,10 @@
from flask_wtf import FlaskForm
from wtforms import SubmitField, SelectField
from wtforms import SubmitField, SelectField, TextAreaField
from benchmark.Arguments import ALL_METRICS
##### NOT USED #####
class RankingForm(FlaskForm):
score = SelectField("Score", choices=ALL_METRICS)
output = TextAreaField("Output")
submit = SubmitField("Generate Ranking")

View File

@@ -0,0 +1,91 @@
import os
import shutil
from pathlib import Path
from flask import Blueprint, render_template, url_for, current_app
from benchmark.ResultsFiles import Benchmark
from flask_login import current_user, login_required
interactive = Blueprint("interactive", __name__, template_folder="templates")
@interactive.route("/ranking")
@login_required
def ranking():
os.chdir(current_user.benchmark.folder)
return render_template("ranking.html")
@current_app.socket.on("client")
def handle_client(message):
current_app.logger.info(message)
if message.get("action") == "ReadyToRock!":
get_benchmark(message.get("score"), excel=message.get("excel", False))
current_app.socket.emit("server", {"message": "Ready!", "percentage": 0})
def send_message(message, percentage, status="Ok", payload={}):
output = {
"message": message,
"percentage": percentage,
"status": status,
"payload": payload,
}
current_app.socket.emit("server", output)
def get_benchmark(score, excel=False):
def move_exreport():
src = os.path.join(
current_user.benchmark.folder, "exreport", "exreport_output"
)
dst = os.path.join(
current_app.static_folder, "exreport", "exreport_output"
)
shutil.copytree(src, dst, dirs_exist_ok=True)
benchmark = Benchmark(score=score, visualize=True)
send_message("Start", 0)
try:
send_message("Generating ranking...", 0)
benchmark.compile_results()
send_message("Saving results...", 20 if excel else 40)
benchmark.save_results()
send_message("Generating report...", 40 if excel else 60)
benchmark.report(tex_output=False)
send_message("Generating exreport...", 60 if excel else 80)
benchmark.exreport()
if excel:
send_message("Generating excel...", 80)
benchmark.excel()
except ValueError as e:
send_message(
"Error: Couldn't generate ranking. " + str(e),
percentage=100,
status="Error",
)
return
except KeyError as e:
send_message(
"Couldn't generate ranking. It seems that there are "
"partial results for some classifierss. "
f"Key not found {str(e)}",
percentage=100,
status="Error",
)
return
# copy the results to the static folder
move_exreport()
excel_payload = (
"" if not excel else str(Path(benchmark.get_excel_file_name()))
)
current_app.logger.info("excel_payload:" + excel_payload)
send_message(
"Done!",
100,
payload={
"excel": excel_payload,
"html": url_for(
"static", filename="exreport/exreport_output/report.html"
),
},
)

View File

@@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block jscript %}
{{ super() }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.6.2/socket.io.js"
integrity="sha512-jMNwWSmjje4fjYut9MBGKXw5FZA6D67NHAuC9szpjbbjg51KefquNfvn4DalCbGfkcv/jHsHnPo1o47+8u4biA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
{% endblock %}

View File

@@ -0,0 +1,144 @@
{% extends "iobase.html" %}
{% block content %}
<div class="alert alert-{{ alert_type }} col-md-4" role="alert">
<h4 class="alert-heading">{{ title }}</h4>
<button class="btn btn-primary"
onclick="window.location.href='{{ url_for("main.index") }}'">Back</button>
<div class="row">
<div>
<div class="mb-3">
<label class="form-label" for="score">Score</label>
<select class="form-select" id="score" name="score">
<option value="accuracy">accuracy</option>
<option value="f1-macro">f1-macro</option>
<option value="f1-micro">f1-micro</option>
<option value="f1-weighted">f1-weighted</option>
<option value="roc-auc-ovr">roc-auc-ovr</option>
</select>
<input id="excel" type="checkbox" name="excel">
Generate Excel
<input id="html" type="checkbox" name="html">
Generate Html
</div>
<button type="button"
class="btn btn-primary"
onclick="send()"
id="submit"
disabled>Generate</button>
<div class="mb-3">
<div class="alert alert-success" role="alert" id="status-alert" hidden>
<input id="status"
name="status"
type="text"
readonly
size="79"
class="bg-success text-white"
hidden>
</div>
<div class="progress"
role="progressbar"
aria-label="Animated striped example"
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="100"
hidden>
<div class="progress-bar progress-bar-striped progress-bar-animated"
id="bar-value"></div>
</div>
<div class="alert alert-success" role="alert" id="button_container" hidden>
<button class="btn btn-primary btn-small"
id="excel_button"
onclick="{{ call }}"
hidden>
<i class="mdi mdi-file-excel"></i> Benchmark
</button>
<button class="btn btn-primary btn-small"
id="html_button"
onclick="{{ call }}"
hidden>
<i class="mdi mdi-language-html5"></i> Exreport
</button>
<button class="btn btn-primary btn-small"
id="go_back"
onclick="location.href='/index'"
hidden>
<i class="mdi mdi-home-circle"></i> Go Back
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block jscript %}
{{ super() }}
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
$("#submit").click(function () {
$("#submit").attr("disabled", true);
$("#submit").val("Generating...");
$("#score").attr("disabled", true);
$("#excel").attr("disabled", true);
$('.progress').removeAttr("hidden");
});
$("#excel").change(function () {
if (!$("#excel").is(":checked") && !$("#html").is(":checked")) {
$("#submit").attr("disabled", true);
} else {
$("#submit").removeAttr("disabled");
}
});
$("#html").change(function () {
if (!$("#excel").is(":checked") && !$("#html").is(":checked")) {
$("#submit").attr("disabled", true);
} else {
$("#submit").removeAttr("disabled");
}
});
});
var socket = io();
var update_bar = false;
socket.on('connect', function() {
socket.emit('client', {data: 'Connected.'});
});
socket.on('server', function(msg) {
var text = document.getElementById('status');
text.value = msg.message;
if (update_bar) {
$(".progress-bar").attr("style", "width: "+msg.percentage+"%");
$('.progress-bar').attr('aria-valuenow', msg.percentage).css('width', msg.percentage+'%');
$('#bar-value').html(msg.percentage+"%");
}
if (msg.message == "Start") {
update_bar = true;
}
if (msg.message == "Done!") {
if ($("#excel").is(":checked")) {
let action = "window.open('/results/download/" + msg.payload.excel +"', '_blank')";
alert(action);
console.log(action);
$("#excel").attr("onclick",action);
$("#excel_button").removeAttr("hidden");
}
$("#html_button").attr("onclick", "window.open('"+msg.payload.html +"', '_blank')");
$("#button_container").removeAttr("hidden");
$("#html_button").removeAttr("hidden");
update_bar = false;
}
if (msg.status=="Error") {
$('#status-alert').removeClass("alert-success").addClass("alert-danger").removeAttr("hidden");
$('#status').addClass("bg-danger text-white").removeClass("bg-success");
$("#status").text=msg.message;
}
$("#go_back").removeAttr("hidden");
});
function send() {
var data= {action: "ReadyToRock!", score: document.getElementById('score').value, excel: document.getElementById('excel').checked};
socket.emit('client', data);
$("#status").removeAttr("hidden");
$("#status-alert").removeAttr("hidden");
$(".progress").removeAttr("hidden");
}
</script>
{% endblock %}

View File

@@ -1,18 +1,16 @@
import json
import os
import shutil
from pathlib import Path
import xlsxwriter
from benchmark.Datasets import Datasets
from benchmark.ResultsBase import StubReport
from benchmark.ResultsFiles import Benchmark, Excel, ReportDatasets
from benchmark.ResultsFiles import Excel, ReportDatasets
from benchmark.Utils import Files, Folders
from dotenv import dotenv_values
from flask import (
Blueprint,
current_app,
redirect,
render_template,
request,
send_file,
@@ -20,8 +18,6 @@ from flask import (
)
from flask_login import current_user, login_required
from .forms import RankingForm
results = Blueprint("results", __name__, template_folder="templates")
@@ -235,32 +231,3 @@ def dataset_report(dataset):
results=results,
app_config=app_config,
)
@results.route("/ranking", methods=["GET", "POST"])
@login_required
def ranking():
os.chdir(current_user.benchmark.folder)
form = RankingForm()
if form.validate_on_submit():
benchmark = Benchmark(score=form.score.data, visualize=False)
try:
benchmark.compile_results()
benchmark.save_results()
benchmark.report(tex_output=False)
benchmark.exreport()
benchmark.excel()
except ValueError as e:
return render_template(
"error.html", message="Couldn't generate ranking", error=str(e)
)
except KeyError as e:
return render_template(
"error.html",
message="Couldn't generate ranking. It seems that there are "
"partial results for some classifiers",
error=f"Key not found {str(e)}",
)
file_name = Path(benchmark.get_excel_file_name()).name
return redirect(url_for("results.download", file_name=file_name))
return render_template("ranking.html", form=form)

View File

@@ -1,13 +0,0 @@
{% extends "base.html" %}
{% from 'bootstrap5/form.html' import render_form %}
{% block content %}
<div class="alert alert-{{ alert_type }} col-md-4" role="alert">
<h4 class="alert-heading">{{ title }}</h4>
<button class="btn btn-primary"
onclick="window.location.href='{{ url_for("main.index") }}'">Back</button>
<div class="row">
<div>{{ render_form(form) }}</div>
</div>
</div>
{% endblock %}
```

1
app/static/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
exreport/**

View File

@@ -20,7 +20,7 @@
</li>
<li>{{ render_nav_item('results.select', 'Results') }}</li>
<li>{{ render_nav_item('results.datasets', 'Datasets') }}</li>
<li>{{ render_nav_item('results.ranking', 'Ranking') }}</li>
<li>{{ render_nav_item('interactive.ranking', 'Ranking') }}</li>
<li>{{ render_nav_item('main.config', 'Config') }}</li>
{% endif %}
</ul>

View File

@@ -1 +0,0 @@
email-validator

8
requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
flask
flask-login
bootstrap-flask
flask-sqlalchemy
flask-socketio
flask-wtf
simple-websocket
email-validator

3
run.py
View File

@@ -1,3 +1,4 @@
from app import app
app.create_app().run(debug=True)
socketio, app = app.create_app()
socketio.run(app, debug=True)