From ae04ea54267b76b8abafb97e06a439d330beb4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 5 Jun 2023 12:06:56 +0200 Subject: [PATCH] Begin with model --- __pycache__/__init__.cpython-310.pyc | Bin 0 -> 135 bytes __pycache__/app.cpython-310.pyc | Bin 0 -> 1257 bytes __pycache__/config.cpython-310.pyc | Bin 0 -> 833 bytes __pycache__/forms.cpython-310.pyc | Bin 0 -> 733 bytes __pycache__/main.cpython-310.pyc | Bin 0 -> 1686 bytes __pycache__/models.cpython-310.pyc | Bin 0 -> 1487 bytes .gitignore => app/.gitignore | 0 app/__init__.py | 0 app/app.db | Bin 0 -> 28672 bytes app/app.py | 39 +++++ app/config.py | 17 ++ app/forms.py | 27 +++ app/main.py | 58 +++++++ app/models.py | 40 +++++ app/results/main.py | 45 +++++ app/results/results.ant.py | 212 +++++++++++++++++++++++ app/results/templates/_table_select.html | 50 ++++++ app/results/templates/select.html | 9 + app/static/css/main.css | 52 ++++++ app/static/js/excelFiles.js | 29 ++++ app/static/js/select.js | 97 +++++++++++ app/templates/_nav.html | 30 ++++ app/templates/base.html | 32 ++++ app/templates/config.html | 5 + app/templates/index.html | 13 ++ app/templates/login.html | 6 + dbseed.py | 26 +++ pyproject.toml | 16 ++ run.py | 3 + 29 files changed, 806 insertions(+) create mode 100644 __pycache__/__init__.cpython-310.pyc create mode 100644 __pycache__/app.cpython-310.pyc create mode 100644 __pycache__/config.cpython-310.pyc create mode 100644 __pycache__/forms.cpython-310.pyc create mode 100644 __pycache__/main.cpython-310.pyc create mode 100644 __pycache__/models.cpython-310.pyc rename .gitignore => app/.gitignore (100%) create mode 100644 app/__init__.py create mode 100644 app/app.db create mode 100644 app/app.py create mode 100644 app/config.py create mode 100644 app/forms.py create mode 100644 app/main.py create mode 100644 app/models.py create mode 100644 app/results/main.py create mode 100644 app/results/results.ant.py create mode 100644 app/results/templates/_table_select.html create mode 100644 app/results/templates/select.html create mode 100644 app/static/css/main.css create mode 100644 app/static/js/excelFiles.js create mode 100644 app/static/js/select.js create mode 100644 app/templates/_nav.html create mode 100644 app/templates/base.html create mode 100644 app/templates/config.html create mode 100644 app/templates/index.html create mode 100644 app/templates/login.html create mode 100644 dbseed.py create mode 100644 pyproject.toml create mode 100644 run.py diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1420ee122b32d7c0ff957658c4950f939efcbb7 GIT binary patch literal 135 zcmd1j<>g`k0>`B_DIoeWh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o2BtKeRZts93)! zH$Sf=F%Jm!o%2&t^^;Q5auSQP_2c6+^D;}~}&0tGPR+#Pq=^7!%Co*BGLijvFuU*)|sNc z4y){bvncBaMO`eEfpxss)Cc8a!n*LWRi&V53UGG%ul2at9OGIBXIG2h6$js$3kl_c0ch{L4Zmg_FZhV5a zj>$*h2Uy!aCX_q@)`ifIfOCiBA^DHkJ1@Z-_NOpVH;MM&q*%{gjVyTdCYhcUnx`wH zMQhYS`D(IbFuz?QL|s%$h^{|7B@s``x^x1n(#4P*;0_-gn5GiVY^mnXL~zk3xy!v1 zQEj-!)6Mclsb2E_vaFYdQ1uHwC|0f!MqzpRwo!}HI^f4luT*P*F$Obh`XKm~3nhVA zo4K;KF}@;TsBKTn#nZV_g;S!?8aN!l*55@wJJGiPfN^^jL_$-Vu@q!X6FP+Xr2h)p z_-Dw>6*#m%xx^z+O`jw_@>rZD$RPb7CrDo5-)!)=m`f-yJ3Ov{-`Z|ih!6Yf;S??e113LFI{7}YoIJp5%R$ARea@v1EWOUQV@WhP9EM{Sl I(j?5n-{H?7VgLXD literal 0 HcmV?d00001 diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba864a742ea1485655f54231cd4ade71543ba23a GIT binary patch literal 833 zcmYjQO>@&Q5S8SQI8H*-5~l5i3m0-oJ;RY<7)T+{LoUR8o|y9_ixTyCO-`9NVi$3gUU}nO=A*@pFjTuHlOE)O18cAf zO*L$Yp!v6o&>zRhyXqxTRz1?*+#jK?F%VUqDMX_*-%3GkKq(VqMB}SUTw5~tq(ZW|BD04u!i#dGp*(rzyepWA;V;qJ;eP8cw@&fMfx*_D@Yy{!x=$IqeJ zbRXXP7WWrNa*Er&K>#ADB9cL%D1<$MNHG=kY{OLel}QdU3Lek7ivOPrNR;TQOd;!u zoTvncNnLW@ZmPylA;mijrYSY-&`q+NA>f$lO<7l}>{?@`_&3O@ia8b#sKqR_3{);^vT#r3wV#~Tx1qKQ2S1Shup#>SU*;%aZ7i9F5BzCoeE zcq1IQgN5ITd5Cm?-%saWy`3A~HeK1^H-FhGn6F^%pJ~3v3GK<%CzD{x1Kjo)K@did zDF4F(k0MCvaDS0!G8yuU+iq`Z#n?ha))yBSRm zXt?Gda7d1O=ST1xeC5QMTTWED6UZLuR=aK2!&P5><*MKBFnoUe?OOa2GWI7eo<0FC zzQ9lafrDUz7fkVlD=+bspZF?B0u?5qijs(v+$<PGZ$g+NzUuR5$6meZT0be$sdI zpctrOGIaB>*izfcHfNHHNRBpMGM3}p9nreu?jPTA(H5Ow*@ka;vg`J`DC>%z=s)c4 z$?{Hd1OOat2~szqL=wU%Ie^BT~JX;xj7 zRQ1DR(5R6ex)4)t+vNQ+11)Ls9eMe>0)>m<2XYpuEHCf*Y!%INjO*>3B7XXKxH!V^ z?-&PzmDzC2%sXCnoc`gtQN=qBK1$y)7~{_k_s?hKU(Y~QrO8TsXGfKgvr|cFUd-s+ z!^Y7Cd+xt~qLK zp>=rUp==-`r?b5r-HwU4*WTKLZ_Uoy+wkSCh%bD2)kJgc8I-x0_K4PNCb~C1maTVu z-*Lb@&@tDT7*%FUuAaRtf6j_rOdfY86-+KOt>082R*jVdi>yE@sZJ%V_K{HwDJFS2 z(e->TwLYth;_A=_rF?5FTQ$UW#aXpZ@;YPWKoX0W!#~~TH08x@wWvc5_l^Yn4Wr%G8SGIkY7Y1AF^a`HQb|5m7VO!;( zjUCL-<@^F$p~=X`k29dM06c%>_;2y>h~y!0!B1E z!cP;?4cjmz1~=X#1+gFCLzG}>bl*M!@ZZLK+7rzoFUTLEOp(DMPL8J^G`GskGTv;4EUDwO0OVB~+D%qUfMa!iBM#9BP){8^DhjfEsNJZDKq&8A`C!oNUF-}vW zVw%DbZEXmMfOOY^>sAMii|+}-E-wdj{2BVH3;8)mV?DuP!VcmdBHZI0FZK!RIPeJZ Kpu^+Qfd2y_ES;eM literal 0 HcmV?d00001 diff --git a/__pycache__/models.cpython-310.pyc b/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f7471cafb5d46fd4698afb512e2bef492ee7573 GIT binary patch literal 1487 zcmZuxOOGQp5VqZqbdpXsGqbx42qYv{h#W``2nnHmp*bKVHWC`R_|oc$ouqgAk+C~u zl1ZeMve*3&ndAJOJ^IRN{sNb!%FPT3;Ev1XuiB}v9`2&mY7iJ74%gznL&zVv*&QBi zUc%HP5P}Grl8kanQD-U39PU`%N!`rjUgmQ@tMQt}+%(AQyl!gG9BFz^$7mz&#(G;PWM$-?1%dnKoS>STeiT{}6KEbxSA&1qfjl=>~GBjsXBdby(ELC_X`fIofgE-tRP| zW1WX|d1RgM+xOg~>-#c3flcLp%dW}R5%d~zvvse@nhCb`R%}IWX`R8+o0X%lzgvDY zDa)z;zTcnE=e@Cte~-&p^@c^(uhjn4`h|Fw2$`43u*kpE&#u;8UttLpX32PIF~`d# z^C1+~)F<*%Bx9*dbpYnBQ>ES2$x<5sZ_B^p_bXSJAC09urY=4omQ5RdWgT?e*euv| zQDhoX1U)oMQ4gc&Viu>BMPV`&UQ5Fgp?usOqCm1$2|hyYQxu<}_yWa0jI4$wQ24>a zsquV3LV5^KNCOr)ffG8`K7hMe!_P&UoY|!Cj_vSBU!-yMDN-q-=|DO!4_S)E9W zagwVVzI*@~JzvV%xTocCrjl}DTiUjfVd9K?ukZp{2LDIh1W&8%JjYwE@tTCd{~@sN JP@f$;e*p*rU^D;# literal 0 HcmV?d00001 diff --git a/.gitignore b/app/.gitignore similarity index 100% rename from .gitignore rename to app/.gitignore diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/app.db b/app/app.db new file mode 100644 index 0000000000000000000000000000000000000000..1f61ea9cf6dda6f502a3b20c80da2e3d9ce9b7fd GIT binary patch literal 28672 zcmeI)Z*SU090%|-BqX3xZx0aCp5!V;8c17%ZS3H74?+?*qovT&WNrEc9rigm;a{<{ z(CtCR_8RsIroEKCfxd@HRUh=h9!-0+vk8VsOCMH?_B}W_-~D`?KbQx1Sbp}j?ggB> z!*QPlR3o1eNg_Lx5<*5IuZcWQjwr0mH^jTNRKBh_B6r>QS@|!Ly7f1aQ}XY(f6M-w zeJD1NAOHafKmY;|fB*y_009VmGJ!@amH#p)4ZVTGC*Gue?(=b*_nFrVOKW>At9oeB zqekuNkwt5bed`6i5=EO08b;DWv{Ui-3_o!nW1R?hdQFb?b5@nY8DKu^w6nR*N>8hqQ52uYW}|>3L`LyJ~Ci z+iI&|l#5|~H1xPqQA<%RbNb$Z?lzlstJ(;|M$GpwhGVDQWqx-lyb^_X_`vS=+4!uz z`1EkEC#~A!YU`LjvW{uNbE4P>&6ZU=XoPiz<^77ZWj(N3R%6e4M&pKPw@k5}Oy##W zq&IWgxV;_P##<}b0LKAQcqkm#&_dWlar%w4mfzTremaPVJCX7p`# z9?VYdc0Ag)5*L%=XSN>x)UqN=@-IX@ksts82tWV=5P$##AOHafKmY;|_>=;klp@7x z`o80d^#UGvKSc9(Yck?vug?d8PcOWnOM63`^=KT#qICr}aOf_3&3%szsNE9_H=gV9 zu|FCQUvoR~x8nGy(~LMSN&bU~ClUl8009U<00Izz00bZa0SG_<0ykOUi{$vq009U< z00Izz00bZa0SG_<0ykb@x{_EY`K|Toar)$(`$6=w!zZji>hZhwus`aYIj(xo?=n?4 z?ir;pZSLErrSG5A+()kJzj*lgbW^i6*JQe8>!z)%E;A~&QZ~3-b{)GS8V#qjZ5m81 zo7)w$Vmq2!u4vm#<+g1wr=m}9N$cc}o}M0)aep`n*g!lMrJa-T4iwi8=XAV(Qg+mf zGbNbRUbgtASyEh0uPBD0bjn6q(Nu+%w36jTd@d0$g0TUJ^sV{113a9f&c^{009U<00Izz00bZa0SG|gKPiw)Zp~); R%hxs}@=1MZ*1x#Q;2%vQrCb02 literal 0 HcmV?d00001 diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..f5f9307 --- /dev/null +++ b/app/app.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +from flask import Flask +from flask_bootstrap import Bootstrap5 +from flask_login import LoginManager +from .config import Config +from .models import User, db + +from .results.main import results +from .main import main + +bootstrap = Bootstrap5() + +login_manager = LoginManager() + + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + + +def make_shell_context(): + return {"db": db, "User": User} + + +def create_app(): + app = Flask(__name__) + bootstrap.init_app(app) + # app.register_blueprint(results) + app.config.from_object(Config) + db.init_app(app) + login_manager.init_app(app) + login_manager.login_view = "main.login" + app.jinja_env.auto_reload = True + app.register_blueprint(results, url_prefix="/results") + app.register_blueprint(main) + app.shell_context_processor(make_shell_context) + with app.app_context(): + db.create_all() + return app diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..298c10d --- /dev/null +++ b/app/config.py @@ -0,0 +1,17 @@ +import os +from dotenv import load_dotenv + +basedir = os.path.abspath(os.path.dirname(__file__)) +load_dotenv(os.path.join(basedir, ".env")) + + +class Config(object): + FRAMEWORKS = ["bootstrap", "bulma"] + FRAMEWORK = os.environ.get("FRAMEWORK") or FRAMEWORKS[0] + OUTPUT = os.environ.get("OUTPUT") or "local" # local or docker + TEMPLATES_AUTO_RELOAD = True + SECRET_KEY = os.environ.get("SECRET_KEY") or "really-hard-to-guess-key" + SQLALCHEMY_DATABASE_URI = os.environ.get( + "DATABASE_URL" + ) or "sqlite:///" + os.path.join(basedir, "app.db") + SQLALCHEMY_TRACK_MODIFICATIONS = False diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000..8f9729c --- /dev/null +++ b/app/forms.py @@ -0,0 +1,27 @@ +from flask_wtf import FlaskForm +from wtforms import ( + StringField, + PasswordField, + BooleanField, + SubmitField, +) +from wtforms.validators import ( + DataRequired, + Length, +) + + +class LoginForm(FlaskForm): + username = StringField( + "Username", validators=[DataRequired(), Length(1, 20)] + ) + password = PasswordField( + "Password", validators=[DataRequired(), Length(4, 150)] + ) + remember_me = BooleanField("Remember me") + submit = SubmitField() + + +class BenchmarkSelect(FlaskForm): + + submit = SubmitField("Select") diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..82613ab --- /dev/null +++ b/app/main.py @@ -0,0 +1,58 @@ +from flask import ( + Blueprint, + render_template, + url_for, + flash, + redirect, + request, +) +from flask_login import login_user, current_user, logout_user, login_required +from werkzeug.urls import url_parse +from .forms import LoginForm +from .models import User, Benchmark + +main = Blueprint("main", __name__) + + +@main.route("/") +@main.route("/index") +def index(): + if current_user.is_authenticated: + if current_user.admin: + benchmarks = Benchmark.query.all() + else: + benchmarks = [current_user.benchmark] + else: + benchmarks = [] + return render_template("index.html", benchmarks=benchmarks) + + +@main.route("/config") +@login_required +def config(): + return render_template("config.html") + + +@main.route("/login", methods=["GET", "POST"]) +def login(): + if current_user.is_authenticated: + return redirect(url_for("main.index")) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash("Invalid username or password") + return redirect(url_for("main.login")) + login_user(user, remember=form.remember_me.data) + flash("Logged in successfully.") + next_page = request.args.get("next") + if not next_page or url_parse(next_page).netloc != "": + next_page = url_for("main.index") + return redirect(next_page) + return render_template("login.html", title="Sign In", form=form) + + +@main.route("/logout") +def logout(): + logout_user() + return redirect(url_for("main.index")) diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..53a1a68 --- /dev/null +++ b/app/models.py @@ -0,0 +1,40 @@ +from hashlib import md5 +from flask_sqlalchemy import SQLAlchemy +from flask_login import UserMixin +from werkzeug.security import generate_password_hash, check_password_hash + +db = SQLAlchemy() + + +class User(UserMixin, db.Model): + __tablename__ = "user" + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(64), index=True, unique=True) + email = db.Column(db.String(120), index=True, unique=True) + admin = db.Column(db.Boolean, default=False) + password_hash = db.Column(db.String(128)) + benchmark_id = db.Column(db.Integer, db.ForeignKey("benchmark.id")) + benchmark = db.relationship("Benchmark") + + def __repr__(self): + return "".format(self.username, self.email) + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + def avatar(self, size): + digest = md5(self.email.lower().encode("utf-8")).hexdigest() + return "https://www.gravatar.com/avatar/{}?d=identicon&s={}".format( + digest, size + ) + + +class Benchmark(db.Model): + __tablename__ = "benchmark" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(64), index=True, unique=True) + description = db.Column(db.String(120), index=False, unique=False) + folder = db.Column(db.String(128), index=False, unique=True) diff --git a/app/results/main.py b/app/results/main.py new file mode 100644 index 0000000..a801e57 --- /dev/null +++ b/app/results/main.py @@ -0,0 +1,45 @@ +import os +from benchmark.Utils import Files, Folders +from benchmark.ResultsBase import StubReport +from flask import Blueprint, current_app, send_file +from flask import render_template, current_app, request, redirect, url_for +from flask_login import login_required + +# import json +# import shutil +# import xlsxwriter +# from benchmark.ResultsFiles import Excel, ReportDatasets +# from benchmark.Datasets import Datasets + +results = Blueprint("results", __name__, template_folder="templates") + + +@results.route("/select") +@login_required +def select(compare="False"): + # Get a list of files in a directory + files = {} + names = Files.get_all_results(hidden=False) + for name in names: + report = StubReport(os.path.join(Folders.results, name)) + report.report() + files[name] = { + "duration": report.duration, + "score": report.score, + "title": report.title, + } + candidate = current_app.config["FRAMEWORKS"].copy() + candidate.remove(current_app.config["FRAMEWORK"]) + return render_template( + "select.html", + files=files, + candidate=candidate[0], + framework=current_app.config["FRAMEWORK"], + compare=compare.capitalize() == "True", + ) + + +@results.route("/datasets") +@login_required +def datasets(compare="False"): + return render_template("test.html") diff --git a/app/results/results.ant.py b/app/results/results.ant.py new file mode 100644 index 0000000..43a9c56 --- /dev/null +++ b/app/results/results.ant.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# import os +# import json +# import shutil +# import xlsxwriter +# from benchmark.Utils import Files, Folders +# from benchmark.Arguments import EnvData +# from benchmark.ResultsBase import StubReport +# from benchmark.ResultsFiles import Excel, ReportDatasets +# from benchmark.Datasets import Datasets +# from flask import Blueprint, current_app, send_file +# from flask import render_template, request, redirect, url_for +from flask import Blueprint, render_template + + +results = Blueprint("results", __name__, template_folder="results") +# FRAMEWORK = "framework" +# FRAMEWORKS = "frameworks" +# OUTPUT = "output" +# TEST = "test" + + +# class AjaxResponse: +# def __init__(self, success, file_name, code=200): +# self.success = success +# self.file_name = file_name +# self.code = code + +# def to_string(self): +# return ( +# json.dumps( +# { +# "success": self.success, +# "file": self.file_name, +# "output": current_app.config[OUTPUT], +# } +# ), +# self.code, +# {"ContentType": "application/json"}, +# ) + + +# def process_data(file_name, compare, data): +# report = StubReport( +# os.path.join(Folders.results, file_name), compare=compare +# ) +# 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 + + +@results.route("/results/") +def results(compare="False"): + # # Get a list of files in a directory + # files = {} + # names = Files.get_all_results(hidden=False) + # for name in names: + # report = StubReport(os.path.join(Folders.results, name)) + # report.report() + # files[name] = { + # "duration": report.duration, + # "score": report.score, + # "title": report.title, + # } + # candidate = current_app.config[FRAMEWORKS].copy() + # candidate.remove(current_app.config[FRAMEWORK]) + # return render_template( + # "select.html", + # files=files, + # candidate=candidate[0], + # framework=current_app.config[FRAMEWORK], + # compare=compare.capitalize() == "True", + # ) + return render_template("test.html") + + +""" +@results.route("/datasets/") +@results.route("datasets") +def datasets(compare=False): + dt = Datasets() + datos = [] + for dataset in dt: + datos.append(dt.get_attributes(dataset)) + return render_template( + "datasets.html", + datasets=datos, + compare=compare, + framework=current_app.config[FRAMEWORK], + ) + + +@results.route("/showfile//") +def showfile(file_name, compare, back=None): + compare = compare.capitalize() == "True" + back = request.args["url"] if back is None else back + print(f"back [{back}]") + with open(os.path.join(Folders.results, file_name)) as f: + data = json.load(f) + try: + summary = process_data(file_name, compare, data) + except Exception as e: + return render_template("error.html", message=str(e), compare=compare) + return render_template( + "report.html", + data=data, + file=file_name, + summary=summary, + framework=current_app.config[FRAMEWORK], + back=back, + ) + + +@results.route("/show", methods=["post"]) +def show(): + selected_file = request.form["selected-file"] + compare = request.form["compare"] + return showfile( + file_name=selected_file, + compare=compare, + back=url_for( + "main.index", compare=compare, output=current_app.config[OUTPUT] + ), + ) + + +@results.route("/excel", methods=["post"]) +def excel(): + selected_files = request.json["selectedFiles"] + compare = request.json["compare"] + book = None + if selected_files[0] == "datasets": + # Create a list of datasets + report = ReportDatasets(excel=True, output=False) + report.report() + excel_name = os.path.join(Folders.excel, Files.datasets_report_excel) + if current_app.config[OUTPUT] == "local": + Files.open(excel_name, test=current_app.config[TEST]) + return AjaxResponse(True, Files.datasets_report_excel).to_string() + try: + for file_name in selected_files: + file_name_result = os.path.join(Folders.results, file_name) + if book is None: + file_excel = os.path.join(Folders.excel, Files.be_list_excel) + book = xlsxwriter.Workbook( + file_excel, {"nan_inf_to_errors": True} + ) + excel = Excel( + file_name=file_name_result, + book=book, + compare=compare, + ) + excel.report() + except Exception as e: + if book is not None: + book.close() + return AjaxResponse( + False, "Could not create excel file, " + str(e) + ).to_string() + if book is not None: + book.close() + if current_app.config[OUTPUT] == "local": + Files.open(file_excel, test=current_app.config[TEST]) + return AjaxResponse(True, Files.be_list_excel).to_string() + + +@results.route("/download/") +def download(file_name): + src = os.path.join(Folders.current, Folders.excel, file_name) + dest = os.path.join( + Folders.src(), "scripts", "app", "static", "excel", file_name + ) + shutil.copyfile(src, dest) + return send_file(dest, as_attachment=True) + + +@results.route("/config//") +def config(framework, compare): + if framework not in current_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() + current_app.config[FRAMEWORK] = framework + return redirect(url_for("main.index", compare=compare)) + + +@results.route("/best_results//") +def best_results(file, compare): + compare = compare.capitalize() == "True" + try: + with open(os.path.join(Folders.results, file)) as f: + data = json.load(f) + except Exception as e: + return render_template("error.html", message=str(e), compare=compare) + return render_template( + "report_best.html", + data=data, + compare=compare, + framework=current_app.config[FRAMEWORK], + ) + """ diff --git a/app/results/templates/_table_select.html b/app/results/templates/_table_select.html new file mode 100644 index 0000000..26fa05f --- /dev/null +++ b/app/results/templates/_table_select.html @@ -0,0 +1,50 @@ +{%- macro get_button_tag(icon_name, method, visible=True, name="") -%} + +{%- endmacro -%} + + + + + + + + + + + + + + + {% for file, data in files.items() %} + {% set parts = file.split('_') %} + {% set stratified = parts[6].split('.')[0] %} + + + + + + + + + + + + {% endfor %} + +
ModelMetricPlatformDateTimeStratifiedTitleScore + + +
{{ parts[2] }}{{ parts[1] }}{{ parts[3] }}{{ parts[4] }}{{ parts[5] }}{{ 'True' if stratified =='1' else 'False' }}{{ "%s" % data["title"] }}{{ "%.6f" % data["score"] }} + {{ get_button_tag("table-eye", "showFile('" ~ file ~ "') ") | safe }} + {% set file_best = "best_results_" ~ parts[1] ~ "_" ~ parts[2] ~ ".json" %} + {{ get_button_tag("star-circle-outline", "redirectDouble('best_results', '" ~ file_best ~ "') ", visible=False, name="best_buttons") | safe }} + +
diff --git a/app/results/templates/select.html b/app/results/templates/select.html new file mode 100644 index 0000000..14cf448 --- /dev/null +++ b/app/results/templates/select.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% block content %} + {% include "_table_select.html" %} +{% endblock %} +{% block jscript %} + {{ super() }} + + +{% endblock %} diff --git a/app/static/css/main.css b/app/static/css/main.css new file mode 100644 index 0000000..f299fa6 --- /dev/null +++ b/app/static/css/main.css @@ -0,0 +1,52 @@ +.alternate-font { + font-family: Arial; +} + +tbody { + font-family: Courier; +} + +.tag { + cursor: pointer; +} + +.ajaxLoading { + cursor: progress !important; +} + +#file-table tbody tr.selected td { + background-color: #0dcaf0; + color: white; +} + +#report-table tbody tr.selected td { + background-color: #0dcaf0; + color: white; +} + +.btn-small { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; +} + +body { + padding-bottom: 20px; + background-color: #ffb878; +} + +.navbar { + margin-bottom: 20px; +} + +pre { + background: #ddd; + padding: 10px; +} + +h2 { + margin-top: 20px; +} + +footer { + margin: 20px; +} \ No newline at end of file diff --git a/app/static/js/excelFiles.js b/app/static/js/excelFiles.js new file mode 100644 index 0000000..930b44c --- /dev/null +++ b/app/static/js/excelFiles.js @@ -0,0 +1,29 @@ +function excelFiles(selectedFiles, compare) { + var data = { + "selectedFiles": selectedFiles, + "compare": compare + }; + // send data to server with ajax post + $.ajax({ + type:'POST', + url:'/excel', + data: JSON.stringify(data), + contentType: "application/json", + dataType: 'json', + success: function(data){ + if (data.success) { + if (data.output == "local") { + alert("Se ha generado el archivo " + data.file); + } else { + window.open('/download/' + data.file, "_blank"); + } + } else { + alert(data.file); + } + }, + error: function (xhr, ajaxOptions, thrownError) { + var mensaje = JSON.parse(xhr.responseText || '{\"mensaje\": \"Error indeterminado\"}'); + alert(mensaje.mensaje); + } + }); +} \ No newline at end of file diff --git a/app/static/js/select.js b/app/static/js/select.js new file mode 100644 index 0000000..0ab59f4 --- /dev/null +++ b/app/static/js/select.js @@ -0,0 +1,97 @@ +$(document).ready(function () { + var table = $("#file-table").DataTable({ + paging: true, + searching: true, + ordering: true, + info: true, + "select.items": "row", + pageLength: 25, + columnDefs: [ + { + targets: 8, + orderable: false, + }, + ], + //"language": { + // "lengthMenu": "_MENU_" + //} + }); + $('#file-table').on( 'draw.dt', function () { + enable_disable_best_buttons(); + } ); + // Check if row is selected + $("#file-table tbody").on("click", "tr", function () { + if ($(this).hasClass("selected")) { + $(this).removeClass("selected"); + } else { + table + .$("tr.selected") + .removeClass("selected"); + $(this).addClass("selected"); + } + }); + // Show file with doubleclick + $("#file-table tbody").on("dblclick", "tr", function () { + showFile($(this).attr("id")); + }); + $(document).ajaxStart(function () { + $("body").addClass("ajaxLoading"); + }); + $(document).ajaxStop(function () { + $("body").removeClass("ajaxLoading"); + }); + $('#compare').change(function() { + enable_disable_best_buttons(); + }); + enable_disable_best_buttons(); + }); + function enable_disable_best_buttons(){ + if ($('#compare').is(':checked')) { + $("[name='best_buttons']").addClass("tag is-link is-normal"); + $("[name='best_buttons']").removeAttr("hidden"); + } else { + $("[name='best_buttons']").removeClass("tag is-link is-normal"); + $("[name='best_buttons']").attr("hidden", true); + } + } + function showFile(selectedFile) { + var form = $( + '
' + + '' + + ' +
+ + +
+ diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..1f79176 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,32 @@ + + + + {% block head %} + + + {% block styles %} + {{ bootstrap.load_css() }} + + + {% endblock %} + Benchmark + {% endblock %} + + + {% include "_nav.html" %} + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %}{% endfor %} + {% endif %} + {% endwith %} +
+ {% block content %}{% endblock %} +
+ + {% block jscript %} + + {{ bootstrap.load_js() }} + {% endblock %} + diff --git a/app/templates/config.html b/app/templates/config.html new file mode 100644 index 0000000..2be1d76 --- /dev/null +++ b/app/templates/config.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

Home

+

Welcome to the home page!

+{% endblock content %} diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..660e123 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% block content %} + {% if benchmarks %} +
Benchmarks
+ + {% endif %} +{% endblock %} diff --git a/app/templates/login.html b/app/templates/login.html new file mode 100644 index 0000000..00b45f3 --- /dev/null +++ b/app/templates/login.html @@ -0,0 +1,6 @@ +{% extends 'base.html' %} +{% from 'bootstrap5/form.html' import render_form %} +{% block content %} +

Login

+ {{ render_form(form) }} +{% endblock content %} diff --git a/dbseed.py b/dbseed.py new file mode 100644 index 0000000..49b4439 --- /dev/null +++ b/dbseed.py @@ -0,0 +1,26 @@ +from app.models import Benchmark, db, User +from app import app + +app = app.create_app() + +with app.app_context(): + db.drop_all() + db.create_all() + b = Benchmark( + name="discretizbench", + folder="proyects/discretizbench", + description="Experiments with local discretization and Bayesian classifiers", + ) + u = User(username="rmontanana", email="rmontanana@gmail.com", admin=True) + u.set_password("patata") + u1 = User( + username="guest", + email="guest@example.com", + admin=False, + benchmark_id=1, + ) + u1.set_password("guest") + db.session.add(b) + db.session.add(u) + db.session.add(u1) + db.session.commit() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9bd6669 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[tool.black] +line-length = 79 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 0000000..20cdaa4 --- /dev/null +++ b/run.py @@ -0,0 +1,3 @@ +from app import app + +app.create_app().run(debug=True)