mirror of
https://github.com/Doctorado-ML/beflask.git
synced 2025-08-18 16:45:52 +00:00
Begin adding sockets
This commit is contained in:
10
app/app.py
10
app/app.py
@@ -2,11 +2,13 @@
|
|||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bootstrap import Bootstrap5
|
from flask_bootstrap import Bootstrap5
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
|
from flask_socketio import SocketIO
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .models import User, db
|
from .models import User, db
|
||||||
|
|
||||||
from .results.main_results import results
|
from .results.main_results import results
|
||||||
from .admin.main_admin import admin
|
from .admin.main_admin import admin
|
||||||
|
|
||||||
from .main import main
|
from .main import main
|
||||||
|
|
||||||
bootstrap = Bootstrap5()
|
bootstrap = Bootstrap5()
|
||||||
@@ -36,6 +38,12 @@ def create_app():
|
|||||||
app.register_blueprint(admin, url_prefix="/admin")
|
app.register_blueprint(admin, url_prefix="/admin")
|
||||||
app.register_blueprint(main)
|
app.register_blueprint(main)
|
||||||
app.shell_context_processor(make_shell_context)
|
app.shell_context_processor(make_shell_context)
|
||||||
|
socketio = SocketIO(app)
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
return app
|
app.socket = socketio
|
||||||
|
from .interactive.main_interactive import interactive
|
||||||
|
|
||||||
|
app.register_blueprint(interactive, url_prefix="/admin")
|
||||||
|
|
||||||
|
return socketio, app
|
||||||
|
@@ -1 +1 @@
|
|||||||
COMPARE=True
|
SECRET=Really-hard-to-guess-secret.
|
@@ -1,8 +1,10 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import SubmitField, SelectField
|
from wtforms import SubmitField, SelectField, TextAreaField
|
||||||
from benchmark.Arguments import ALL_METRICS
|
from benchmark.Arguments import ALL_METRICS
|
||||||
|
|
||||||
|
|
||||||
|
##### NOT USED #####
|
||||||
class RankingForm(FlaskForm):
|
class RankingForm(FlaskForm):
|
||||||
score = SelectField("Score", choices=ALL_METRICS)
|
score = SelectField("Score", choices=ALL_METRICS)
|
||||||
|
output = TextAreaField("Output")
|
||||||
submit = SubmitField("Generate Ranking")
|
submit = SubmitField("Generate Ranking")
|
91
app/interactive/main_interactive.py
Normal file
91
app/interactive/main_interactive.py
Normal 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"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
8
app/interactive/templates/iobase.html
Normal file
8
app/interactive/templates/iobase.html
Normal 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 %}
|
144
app/interactive/templates/ranking.html
Normal file
144
app/interactive/templates/ranking.html
Normal 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 %}
|
@@ -1,18 +1,16 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import xlsxwriter
|
import xlsxwriter
|
||||||
from benchmark.Datasets import Datasets
|
from benchmark.Datasets import Datasets
|
||||||
from benchmark.ResultsBase import StubReport
|
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 benchmark.Utils import Files, Folders
|
||||||
from dotenv import dotenv_values
|
from dotenv import dotenv_values
|
||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
Blueprint,
|
||||||
current_app,
|
current_app,
|
||||||
redirect,
|
|
||||||
render_template,
|
render_template,
|
||||||
request,
|
request,
|
||||||
send_file,
|
send_file,
|
||||||
@@ -20,8 +18,6 @@ from flask import (
|
|||||||
)
|
)
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
from .forms import RankingForm
|
|
||||||
|
|
||||||
results = Blueprint("results", __name__, template_folder="templates")
|
results = Blueprint("results", __name__, template_folder="templates")
|
||||||
|
|
||||||
|
|
||||||
@@ -235,32 +231,3 @@ def dataset_report(dataset):
|
|||||||
results=results,
|
results=results,
|
||||||
app_config=app_config,
|
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)
|
|
||||||
|
@@ -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
1
app/static/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
exreport/**
|
@@ -20,7 +20,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>{{ render_nav_item('results.select', 'Results') }}</li>
|
<li>{{ render_nav_item('results.select', 'Results') }}</li>
|
||||||
<li>{{ render_nav_item('results.datasets', 'Datasets') }}</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>
|
<li>{{ render_nav_item('main.config', 'Config') }}</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
email-validator
|
|
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
flask
|
||||||
|
flask-login
|
||||||
|
bootstrap-flask
|
||||||
|
flask-sqlalchemy
|
||||||
|
flask-socketio
|
||||||
|
flask-wtf
|
||||||
|
simple-websocket
|
||||||
|
email-validator
|
Reference in New Issue
Block a user