-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathweb.py
167 lines (140 loc) · 6.19 KB
/
web.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/env python3
#
# ERPC HP/EV Transformer Overloading Evaluation Tool Web Application
#
# Useful environment variables:
# - WA_SECRET_KEY
# - WA_OUTPUT_DIR
# - WA_ISU_BRANDING
#
import os, uuid
from eprc.tfoverload_tool import TFOverload_Tool
from flask import Flask, flash, request, render_template, session
from flask_session import Session
from cachelib.file import FileSystemCache
from flask_session_captcha import FlaskSessionCaptcha
from flask_wtf import FlaskForm, CSRFProtect
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
from wtforms import Form, BooleanField, StringField, IntegerField, HiddenField, SubmitField, validators
# Start the web application
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('WA_SECRET_KEY', default='THISISNOTSAFE238493418')
app.config['UPLOAD_FOLDER'] = os.getenv('WA_OUTPUT_DIR', default='output')
# Configure server-side session tracking
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_TYPE'] = 'cachelib'
app.config['SESSION_SERIALIZATION_FORMAT'] = 'json'
app.config['SESSION_FILE_DIR'] = os.path.join(app.config['UPLOAD_FOLDER'], 'sessions')
app.config['SESSION_CACHELIB'] = FileSystemCache(threshold=512, cache_dir=app.config['SESSION_FILE_DIR'])
Session(app)
# Configure the Captcha
app.config['CAPTCHA_ENABLE'] = True
app.config['CAPTCHA_LENGTH'] = 5
app.config['CAPTCHA_WIDTH'] = 200
app.config['CAPTCHA_HEIGHT'] = 160
# app.config['CAPTCHA_LOG'] = False # log information to terminal
app.config['CAPTCHA_INCLUDE_ALPHABET'] = False
app.config['CAPTCHA_INCLUDE_NUMERIC'] = True
captcha = FlaskSessionCaptcha(app)
# Misc settings
csrf = CSRFProtect(app)
ALLOWED_EXTENSIONS = {'xlsx'}
USE_ISU_BRANDING = True if os.getenv('WA_ISU_BRANDING', default='T') == "T" else False
# Define the main document form.
class TFOTForm(FlaskForm):
penetration_ev = IntegerField('Electric Vehicle Penetration Percentage')
penetration_hp = IntegerField('Heat Pump Penetration Percentage')
#file_use_existing = BooleanField("Use existing AMI Data and TFC Info", default=False)
file_amidata = FileField('AMI Data (XLSX)', validators=[FileRequired()])
file_tfcinfo = FileField('TFC Info (XLSX)', validators=[FileRequired()])
submit = SubmitField("Calculate")
# Get session uuid (or create on if not set).
def get_session_uuid():
if not 'user_uuid' in session:
session['user_uuid'] = str(uuid.uuid4())
return session['user_uuid']
# Only require one captcha per session.
def has_existing_captcha():
return False if not 'captcha_done' in session else True
# The landing page should describe the purpose of the
# application and how to generate the input files.
@app.route("/")
def page_landing():
user_session_uuid = get_session_uuid()
return render_template("landing.html", USE_ISU_BRANDING=USE_ISU_BRANDING)
# The form page is the heart of the application. It
# allows the user to upload data which is then either
# processed or rejected (returned to the form)
@app.route("/form", methods = ['GET', 'POST'])
def page_form():
user_session_uuid = get_session_uuid()
form_fallback = False
# Set form defaults.
form = TFOTForm()
userformdata = {
"penetration_hp": 10,
"penetration_ev": 20,
"files_good": False,
"captcha_done": has_existing_captcha()
}
# Validate the captcha, if submitted.
if request.method == 'POST' and not has_existing_captcha():
if captcha.validate():
session['captcha_done'] = True
else:
flash("Captcha input incorrect or expired.")
form_fallback = True
# Handle POST case first. We'll fall back to input form
# if there's something wrong with the user supplied data.
#if not form_fallback and form.validate_on_submit():
if request.method == 'POST' and not form_fallback:
# Save user values to userformdata for later rendering.
userformdata["penetration_ev"] = form.penetration_ev.data
userformdata["penetration_hp"] = form.penetration_hp.data
# Handle files - Where to save this user's data (temporarily)
userfilepath = os.path.join(app.config['UPLOAD_FOLDER'], user_session_uuid)
os.makedirs(userfilepath, exist_ok=True)
# Handle files - Save files
if not form.file_amidata.data or not form.file_tfcinfo.data:
flash("Both AMI-Data.xlsx and TFC-Info.xlsx files are required.")
form_fallback = True
else:
form.file_amidata.data.save(os.path.join(userfilepath, "amidata.xlsx"))
form.file_tfcinfo.data.save(os.path.join(userfilepath, "tfcinfo.xlsx"))
if not form_fallback:
try:
tfot = TFOverload_Tool(
os.path.join(userfilepath, 'amidata.xlsx'),
os.path.join(userfilepath, 'tfcinfo.xlsx'),
userformdata["penetration_ev"],
userformdata["penetration_hp"],
userfilepath
)
tfot.run()
xlsx_output, png_output = tfot.run()
except Exception as e:
flash(f"Calculation Error: {e}")
form_fallback = True
if not form_fallback:
# calc should show results, allow result download, and show a truncated form for
# redoing the calculation with different options.
#return render_template("calc.html", USE_ISU_BRANDING=USE_ISU_BRANDING, userformdata=userformdata)
return "Calc didn't erorr. Just need to work on display page."
# Either show form or fallback to form input because of a problem.
if request.method == 'GET' or form_fallback:
# Set form defaults and update.
form.penetration_ev.default = userformdata["penetration_ev"]
form.penetration_hp.default = userformdata["penetration_hp"]
form.process()
# Return the completed form to the user.
return render_template("form.html", USE_ISU_BRANDING=USE_ISU_BRANDING, userformdata=userformdata, form=form)
else:
return "How did you get here?"
# Deal with robots
@app.route("/robots.txt")
def robots_txt():
return render_template("robots.txt")
#
# EOF