diff --git a/Frontend/main.py b/Frontend/main.py index 3534a44e9599f2e48ab920272c4d675670b17889..86251271d84bb08089fe3f29ddd071f40e493b3c 100644 --- a/Frontend/main.py +++ b/Frontend/main.py @@ -4,7 +4,7 @@ render_template just renders the html from the "template" folder. redirect_url redirects towards a certain url, but does NOT render a template if the pointed url also does not. """ -from flask import Flask, render_template, url_for, request, redirect, flash +from flask import Flask, render_template, url_for, request, redirect, flash, send_file, send_from_directory from datetime import datetime from werkzeug.utils import secure_filename import os, time, sys @@ -13,12 +13,12 @@ The following are the import for the backend. Just write the name of the script Then you can immediately use the function from the script directly in the app. """ sys.path.append(os.path.join(os.path.dirname(sys.path[0]),'backend')) -import input +import input, eventlog #define the app app = Flask(__name__) -#the super secret key that allows browser based flashes :) +#the super secret key that allows browser based flashes : app.secret_key = '54321' #specifies where the upload directory is and the file type available for uploads @@ -26,75 +26,98 @@ UPLOAD_FOLDER = 'upload' ALLOWED_EXTENSIONS = {'.csv','.xes'} app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER -#home page route +#specifies the static folder used for images and stuff +STATIC_FOLDER = 'static' +app.config['STATIC_FOLDER'] = STATIC_FOLDER + +#specifices where the temporary folder for downloaded file would be: +DOWNLOAD_FOLDER = 'upload' +app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER + +#home page route. The type inputs are just for the list selection. @app.route('/') def index(): - return render_template('index.html') + types = ['csv','xes'] + return render_template('index.html',fileTypes = types) """ The following is the upload functionality. It uploads directly into the "upload" directory. -It redirects towards the case ID input page. +Then it redirects towards the case ID input page. """ @app.route('/', methods=['POST']) def upload_file(): uploaded_file = request.files['file'] if uploaded_file.filename != '': + #file is saved in the 'upload' directory. uploaded_file.save(os.path.join(app.config['UPLOAD_FOLDER'],uploaded_file.filename)) elif uploaded_file.filename == '': #if no file has been selected, flashes a warning flash('No files selected! Please select a file!') return redirect(url_for('index')) - return redirect(url_for('case')) - -#login page route. eventually will be removed -@app.route('/login.html') -def login(): - return render_template('login.html') - -#signup page route. eventually will be removed -@app.route('/signup.html') -def signup(): - return render_template('signup.html') - -#case selection page route -@app.route('/case.html') -def case(): - return render_template('case.html') + return redirect(url_for('case_id')) + +""" +The download usage. Currently just downloads fish.jpg. +os.path.join here is setup to point towards static folder and also point at fish.jpg. +Currently it just sends a preexisting file. Meaning the generated file will have to be saved somewhere to import. +""" +@app.route('/download/', methods=['GET']) +def download(): + filetype = request.args.get("selectType") + if filetype == 'csv': + path = os.path.join(app.config['STATIC_FOLDER'], 'fish.jpg') + #flash the confirmation that file is succesfully downloaded + flash('Generated a event log in csv. Please check your attachments!') + return send_file(path, as_attachment=True) + elif filetype == 'xes': + path = os.path.join(app.config['STATIC_FOLDER'], 'fish.jpg') + #flash the confirmation that file is succesfully downloaded + flash('Generated a event log in xes. Please check your attachments!') + return send_file(path, as_attachment=True) """ -Route for inputting case ID. For now it redirects towards "recommendation" alongside "id". +Route for displaying possible case IDs. It is a dropdown menu that displays the list from the python script in the dropdown menu. +There is a preview functionality, when clicked will show a preview of a generated image. It is linked to the 'preview' button in the case id page. +The generated image currently displays static images and not instantly generated ones from the backend. +Currently it displays moyai.jpg by default and fish.jpg on case 1 and 2. Will change this later. """ -@app.route('/case.html', methods=['POST']) -def get_caseid(): - caseid = request.form['caseId'] - if caseid == '': - #if no caseID has been inputted, flashes a warning. - flash('Please input a case ID!') - return redirect(url_for('case')) - elif not caseid.isdigit(): - #if the input is not an integer, flashes a warning. - flash('Please input an integer!') - return redirect(url_for('case')) +@app.route('/case.html', methods=['GET']) +def case_id(): + #temporary choice of cases until I figure out where the actual function from backend is. + #give the list here in selection using functions from + selection = [1,2,3,4,5] + previewid = request.args.get("selectpreview") + if previewid == '1': + preview = os.path.join(app.config['STATIC_FOLDER'], 'fish.jpg') + elif previewid == '2': + preview = os.path.join(app.config['STATIC_FOLDER'], 'fish.jpg') else: - #else it will redirect to ../recommendation/id - return redirect(url_for('recommendation',id=caseid)) - - -#placeholder result page route. Will eventually be removed and is now defunct. -@app.route('/result.html') -def result(): - return render_template('result.html') + preview = os.path.join(app.config['STATIC_FOLDER'], 'moyai.jpg') + + return render_template('case.html', selection=selection, preview=preview) + +""" +Route for sending the case id towards recommendation. This is linked to the 'Optimize now' button in the case ID page. +I have decided to use a separate dropdown list to avoid cross sending the http requests by accident. +""" +@app.route('/case.html', methods=['POST']) +def send_caseid(): + result = request.form.get("selectresult") + return redirect(url_for('recommendation', result=result)) """ The result page. It processes "id" (for now it uses the "foo" function from input.py) using backend functions and prints out the result immediately in the page. """ -@app.route('/result.html/<id>', methods=['GET']) -def recommendation(id): - res = input.foo(id) +@app.route('/result.html/<result>', methods=['GET']) +def recommendation(result): + res = input.foo(result) return render_template('result.html',res=res) +""" +Loading page. Has yet to be used. +""" #loading page route @app.route('/loading.html') def loading(): @@ -105,6 +128,7 @@ def loading(): def aboutus(): return render_template('aboutus.html') +#the main app if __name__ == "__main__": port = int(os.environ.get('PORT', 5000)) app.run(debug=True, host='0.0.0.0', port=port) diff --git a/Frontend/static/ProcessModel.PNG b/Frontend/static/ProcessModel.PNG new file mode 100644 index 0000000000000000000000000000000000000000..552011134d4e9be93787ef5376a59f331c3c5b1a Binary files /dev/null and b/Frontend/static/ProcessModel.PNG differ diff --git a/Frontend/static/fish.jpg b/Frontend/static/fish.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98ab695e02ae4e1d09495ba2144ec8122cb2ac35 Binary files /dev/null and b/Frontend/static/fish.jpg differ diff --git a/Frontend/static/styles.css b/Frontend/static/styles.css index 7b0e469d967a48b82a0e59ed67f6e55888e91a09..6ce17f3425160bcab9c608ba2fe51f59120e7d8f 100644 --- a/Frontend/static/styles.css +++ b/Frontend/static/styles.css @@ -19,33 +19,6 @@ body { margin:0px; } -/*.container { - margin: 1rem; - text-align: center; -} - -.site-title { - font-size: 2rem; -}* - -/* aleks' navbar -.nav { - background-color: #333; - color: white; - display: flex; - justify-content: space-between; - align-items: stretch; - gap: 2rem; - padding: 0 1rem; -} - -/* aleks' div -.myDiv { - border: 5px outset red; - background-color: lightblue; - text-align: center; -}*/ - /* Add a black background color to the top navigation */ .topnav { background-color:rgb(0,114,188); @@ -92,10 +65,10 @@ body { padding: 15px 32px; text-align: center; text-decoration: none; - display: inline-block; font-size: 16px; margin:25px; border-radius:8px; + display:inline-block; } /* styling for text*/ @@ -115,4 +88,63 @@ label { text-align: right; color: red; font-size: large; +} + +.custom-select { + position: relative; + font-family: Arial; +} + +.custom-select select { + display: inline-block; /*hide original SELECT element: */ +} + +.select-selected { + background-color: DodgerBlue; +} + +/* Style the arrow inside the select element: */ +.select-selected:after { + position: absolute; + content: ""; + top: 14px; + right: 10px; + width: 0; + height: 0; + border: 6px solid transparent; + border-color: #fff transparent transparent transparent; +} + +/* Point the arrow upwards when the select box is open (active): */ +.select-selected.select-arrow-active:after { + border-color: transparent transparent #fff transparent; + top: 7px; +} + +/* style the items (options), including the selected item: */ +.select-items div,.select-selected { + color: #ffffff; + padding: 8px 16px; + border: 1px solid transparent; + border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent; + cursor: pointer; +} + +/* Style items (options): */ +.select-items { + position: absolute; + background-color: DodgerBlue; + top: 100%; + left: 0; + right: 0; + z-index: 99; +} + +/* Hide the items when the select box is closed: */ +.select-hide { + display: none; +} + +.select-items div:hover, .same-as-selected { + background-color: rgba(0, 0, 0, 0.1); } \ No newline at end of file diff --git a/Frontend/templates/case.html b/Frontend/templates/case.html index 0ce4ce0403826f87af3f8f0cc0e381482ef33797..bbc9db678b2943beda8b827a1e30abb658119ed8 100644 --- a/Frontend/templates/case.html +++ b/Frontend/templates/case.html @@ -8,12 +8,41 @@ {%endblock%} {%block body%} -<div style="text-align: center;"> - <h1>Choose the case you want to optimize</h1> - <form method="post" id="caseid" action="{{ url_for('case')}}"> - <label for="caseId"> Please enter the case ID:</label><br> - <input type="text" id="caseId" name="caseId" type="number"><br> - </form> - <button onclick="window.location.href='result.html'" type="submit" class="button" form="caseid" value="casevalue"> Optimize now!</button> +<div style="text-align: center;" class="custom-select"> + <!--This is the form for directly sending case ID to be processed by the agent.--> + <form action="{{ url_for('send_caseid') }}" id="selectcaseid" method="post" name="selectresult"> + <select name="selectresult" id="selectcaseid" method="get" action="{{ url_for('case_id') }} form="selectcaseid" > + {% for id in selection %} + <!--case dropdown menu--> + <option value= "{{id}}"> {{id}} </option> + {% endfor %} + </select> + + <div style = "text-align: center;"> + <!--Submit button--> + <button type="submit" class="button" form="selectcaseid" value="casevalue"> Optimize now!</button> + </div> + </form> + + Preview it before optimizing! + <!--The preview functionality. The dropdown menu is separate to avoid mixing the http requests between both button because I have skill issue and am incapable of coding this cleanly.--> + <form action="{{ url_for('case_id') }}" id="previewCase" method="get" name="previewCase"> + <select name="selectpreview" id="selectcaseid" method="get" action="{{ url_for('case_id') }} form="selectcaseid"> + {% for id in selection %} + <!--Dropdown selection--> + <option value= "{{id}}"> {{id}} </option> + {% endfor %} + </select> + + <div style = "text-align: center;"> + <!--The preview image--> + <img src="{{ preview }}"> + </div> + + <div style = "text-align: center;"> + <!--The preview button--> + <button type="submit" class="button" form="previewCase" value="casevalue"> Preview this case! </button> + </div> + </form> </div> {%endblock%} \ No newline at end of file diff --git a/Frontend/templates/index.html b/Frontend/templates/index.html index 596ba7319e15372a47fe6fa5780b367c14a209b0..e485bc2cbff3feab28b254abedb31bca0149255c 100644 --- a/Frontend/templates/index.html +++ b/Frontend/templates/index.html @@ -1,3 +1,4 @@ +<!--Here is the home page. It contains the file upload and download functionality--> {% extends 'base.html' %} {% block head %} @@ -11,18 +12,36 @@ </header> <div style="text-align: center; padding-top: 50 px;"> - <h1 class="introductions">Introduction</h1> - <h2 class="desc">OPTIS is a standalone Python based web application which aims to minimize time and cost requirements of busines processes. <br> It seeks to achieve this by using an intelligent agent that is trained using reinforcement learning techniques.</h2> - <h3>Here is a short introduction video on our app that explains how the app works!</h3> - <iframe width="560" height="315" src="https://www.youtube.com/watch?v=dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe> + <h1 class="introductions">Introduction</h1> + <h2 class="desc">OPTIS is a standalone Python based web application which aims to minimize time and cost requirements of busines processes. <br> It seeks to achieve this by using an intelligent agent that is trained using reinforcement learning techniques.</h2> + <h3>Here is a short introduction video on our app that explains how the app works!</h3> + <!-- This would be the introduction video. Will most likely be replaced with just the user manual???--> + <iframe width="560" height="315" src="https://www.youtube.com/watch?v=dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe> </div> <div style="text-align: center;"> <h1>Upload your files and get started now!</h1> - <form action="" method="POST" enctype="multipart/form-data"> - <p><input type="file" name="file" accept=".xes,.csv"></p> - <p><input type="submit" value="submit" class="button"></p> - </form> - <h2 style="font-size: 14px">please input only .xes and .csv files. please!</h2> + <!--This is the upload functionality. It automatically already filters for xes and csv file. However, a warning is added nonetheless for user experience.--> + <form action="{{ url_for('upload_file') }}" method="POST" enctype="multipart/form-data" name="upload"> + <p><input type="file" name="file" accept=".xes,.csv"></p> + <p><input type="submit" value="submit" class="button"></p> + </form> + <h2 style="font-size: 14px">please input only .xes and .csv files. please!</h2> + </div> + + <div style="text-align: center;"> + <!--The generate and download functionality. While it is just planned to only be xes and csv files. The type selection is dynamnic and can be changed from main.py--> + <h1> Or generate your own event log to immediately try out the app! </h1> + <form action="{{ url_for('download') }}" method="get" name="downloadType" id="downloadType"> + <select name="selectType" id="fileType" method="get" action="{{ url_for('download') }} form="downloadType"> + {% for type in fileTypes %} + <option value= "{{type}}"> import as .{{type}} </option> + {% endfor %} + </select> + + <div style = "text-align: center;"> + <button type="submit" class="button" form="downloadType" value="exportType"> Generate an event log! </button> + </div> + </form> </div> {% endblock %} \ No newline at end of file diff --git a/Frontend/templates/result.html b/Frontend/templates/result.html index a163a4cdc2901e28ef2040abf50208a68975a2d4..3e0893e7ea1daf68ac5f07d63aadc7a9d91d0a74 100644 --- a/Frontend/templates/result.html +++ b/Frontend/templates/result.html @@ -11,10 +11,12 @@ {%block body%} <div style="text-align: center;"> <h1>Here are the results of the optimization!</h1> - <h2>We recommend you to do the following activity for this specific case:</h2> - <!--res is the result--> - <h3>Placeholder result: {{ res }}</h3> - <img src="{{url_for('static',filename='moyai.jpg')}}" alt="Placeholder"> <br> - <button onclick="window.location.href='/'" class="button"> Optimize your next case NOW!</button> + <h2>We recommend you to do the following activity for this specific case:</h2> + <!--res is the result--> + <h3>Placeholder result: {{ res }}</h3> + <!-- currently a placeholder image to display result--> + <img src="{{url_for('static',filename='moyai.jpg')}}" alt="Placeholder"> <br> + <!--button to go back to home page--> + <button onclick="window.location.href='/'" class="button"> Optimize your next case NOW!</button> </div> {%endblock%} \ No newline at end of file diff --git a/backend/__pycache__/agent.cpython-310.pyc b/backend/__pycache__/agent.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..026c7e1b4b379830250c07e61ed70e5426a13e1f Binary files /dev/null and b/backend/__pycache__/agent.cpython-310.pyc differ diff --git a/backend/__pycache__/environment.cpython-310.pyc b/backend/__pycache__/environment.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..808ab0788c7a9873e8eee1f70d90c5d5f11bb612 Binary files /dev/null and b/backend/__pycache__/environment.cpython-310.pyc differ diff --git a/backend/__pycache__/eventlog.cpython-310.pyc b/backend/__pycache__/eventlog.cpython-310.pyc index 1c6480b898af7da1223352847d66a7a6449eaddf..51f57aad5f095e4f47d52cd813ca58ec585a7793 100644 Binary files a/backend/__pycache__/eventlog.cpython-310.pyc and b/backend/__pycache__/eventlog.cpython-310.pyc differ diff --git a/backend/__pycache__/input.cpython-310.pyc b/backend/__pycache__/input.cpython-310.pyc index 4fecd126abbcf7a739b97c04e7e39b9094d0788b..88754de2e08f8b9402c77739605eb9bc187c77d6 100644 Binary files a/backend/__pycache__/input.cpython-310.pyc and b/backend/__pycache__/input.cpython-310.pyc differ diff --git a/backend/__pycache__/simplesimmodel.cpython-310.pyc b/backend/__pycache__/simplesimmodel.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d41f87b858e589fc25921f14dd6f1bc911da5c8d Binary files /dev/null and b/backend/__pycache__/simplesimmodel.cpython-310.pyc differ