From cea8ef6007b9c9a2b54abe5604c084a5145c7b50 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Fri, 18 Nov 2016 19:23:57 -0800 Subject: [PATCH] else flask template --- Templates/flask/decorators.py | 51 +++++++ Templates/flask/flasksite.py | 120 ++++++++++++++++ Templates/flask/flasksite_launch.py | 29 ++++ Templates/flask/helpers.py | 135 ++++++++++++++++++ .../flask/https/csr, key, crt goes here.txt | 0 Templates/flask/requirements.txt | 2 + Templates/flask/static/common.css | 32 +++++ Templates/flask/static/common.js | 78 ++++++++++ Templates/flask/static/favicon.png | Bin 0 -> 448 bytes Templates/flask/templates/header.html | 5 + Templates/flask/templates/root.html | 29 ++++ 11 files changed, 481 insertions(+) create mode 100644 Templates/flask/decorators.py create mode 100644 Templates/flask/flasksite.py create mode 100644 Templates/flask/flasksite_launch.py create mode 100644 Templates/flask/helpers.py create mode 100644 Templates/flask/https/csr, key, crt goes here.txt create mode 100644 Templates/flask/requirements.txt create mode 100644 Templates/flask/static/common.css create mode 100644 Templates/flask/static/common.js create mode 100644 Templates/flask/static/favicon.png create mode 100644 Templates/flask/templates/header.html create mode 100644 Templates/flask/templates/root.html diff --git a/Templates/flask/decorators.py b/Templates/flask/decorators.py new file mode 100644 index 0000000..6c47418 --- /dev/null +++ b/Templates/flask/decorators.py @@ -0,0 +1,51 @@ +import flask +from flask import request +import functools +import time +import uuid +import warnings + +def _generate_session_token(): + token = str(uuid.uuid4()) + #print('MAKE SESSION', token) + return token + +def give_session_token(function): + @functools.wraps(function) + def wrapped(*args, **kwargs): + # Inject new token so the function doesn't know the difference + token = request.cookies.get('flasksite_session', None) + if not token: + token = _generate_session_token() + request.cookies = dict(request.cookies) + request.cookies['flasksite_session'] = token + + ret = function(*args, **kwargs) + + # Send the token back to the client + if not isinstance(ret, flask.Response): + ret = flask.Response(ret) + ret.set_cookie('flasksite_session', value=token, max_age=60) + + return ret + return wrapped + +def not_implemented(function): + ''' + Decorator to remember what needs doing. + ''' + warnings.warn('%s is not implemented' % function.__name__) + return function + +def time_me(function): + ''' + After the function is run, print the elapsed time. + ''' + @functools.wraps(function) + def timed_function(*args, **kwargs): + start = time.time() + result = function(*args, **kwargs) + end = time.time() + print('%s: %0.8f' % (function.__name__, end-start)) + return result + return timed_function diff --git a/Templates/flask/flasksite.py b/Templates/flask/flasksite.py new file mode 100644 index 0000000..17b13d0 --- /dev/null +++ b/Templates/flask/flasksite.py @@ -0,0 +1,120 @@ +import flask +from flask import request +import json +import mimetypes +import os + +import decorators + + +site = flask.Flask(__name__) +site.config.update( + SEND_FILE_MAX_AGE_DEFAULT=180, + TEMPLATES_AUTO_RELOAD=True, +) +site.jinja_env.add_extension('jinja2.ext.do') +site.debug = True + + +#################################################################################################### +#################################################################################################### +#################################################################################################### +#################################################################################################### + +def make_json_response(j, *args, **kwargs): + dumped = json.dumps(j) + response = flask.Response(dumped, *args, **kwargs) + response.headers['Content-Type'] = 'application/json;charset=utf-8' + return response + +def send_file(filepath): + ''' + Range-enabled file sending. + ''' + try: + file_size = os.path.getsize(filepath) + except FileNotFoundError: + flask.abort(404) + + outgoing_headers = {} + mimetype = mimetypes.guess_type(filepath)[0] + if mimetype is not None: + if 'text/' in mimetype: + mimetype += '; charset=utf-8' + outgoing_headers['Content-Type'] = mimetype + + if 'range' in request.headers: + desired_range = request.headers['range'].lower() + desired_range = desired_range.split('bytes=')[-1] + + int_helper = lambda x: int(x) if x.isdigit() else None + if '-' in desired_range: + (desired_min, desired_max) = desired_range.split('-') + range_min = int_helper(desired_min) + range_max = int_helper(desired_max) + else: + range_min = int_helper(desired_range) + + if range_min is None: + range_min = 0 + if range_max is None: + range_max = file_size + + # because ranges are 0-indexed + range_max = min(range_max, file_size - 1) + range_min = max(range_min, 0) + + range_header = 'bytes {min}-{max}/{outof}'.format( + min=range_min, + max=range_max, + outof=file_size, + ) + outgoing_headers['Content-Range'] = range_header + status = 206 + else: + range_max = file_size - 1 + range_min = 0 + status = 200 + + outgoing_headers['Accept-Ranges'] = 'bytes' + outgoing_headers['Content-Length'] = (range_max - range_min) + 1 + + if request.method == 'HEAD': + outgoing_data = bytes() + else: + outgoing_data = helpers.read_filebytes(filepath, range_min=range_min, range_max=range_max) + + response = flask.Response( + outgoing_data, + status=status, + headers=outgoing_headers, + ) + return response + + +#################################################################################################### +#################################################################################################### +#################################################################################################### +#################################################################################################### + +@site.route('/') +@decorators.give_session_token +def root(): + return flask.render_template('root.html') + +@site.route('/favicon.ico') +@site.route('/favicon.png') +def favicon(): + filename = os.path.join('static', 'favicon.png') + return flask.send_file(filename) + +@site.route('/static/') +def get_static(filename): + filename = filename.replace('\\', os.sep) + filename = filename.replace('/', os.sep) + filename = os.path.join('static', filename) + return flask.send_file(filename) + +if __name__ == '__main__': + #site.run(threaded=True) + pass diff --git a/Templates/flask/flasksite_launch.py b/Templates/flask/flasksite_launch.py new file mode 100644 index 0000000..0566bc6 --- /dev/null +++ b/Templates/flask/flasksite_launch.py @@ -0,0 +1,29 @@ +import gevent.monkey +gevent.monkey.patch_all() + +import flasksite +import gevent.pywsgi +import gevent.wsgi +import sys + +if len(sys.argv) == 2: + port = int(sys.argv[1]) +else: + port = 5000 + +if port == 443: + http = gevent.pywsgi.WSGIServer( + listener=('', port), + application=flasksite.site, + keyfile='https\\flasksite.key', + certfile='https\\flasksite.crt', + ) +else: + http = gevent.pywsgi.WSGIServer( + listener=('', port), + application=flasksite.site, + ) + + +print('Starting server') +http.serve_forever() diff --git a/Templates/flask/helpers.py b/Templates/flask/helpers.py new file mode 100644 index 0000000..dd82b63 --- /dev/null +++ b/Templates/flask/helpers.py @@ -0,0 +1,135 @@ +import math + +FILE_READ_CHUNK = 512 * 1024 + +def chunk_sequence(sequence, chunk_length, allow_incomplete=True): + ''' + Given a sequence, divide it into sequences of length `chunk_length`. + + allow_incomplete: + If True, allow the final chunk to be shorter if the + given sequence is not an exact multiple of `chunk_length`. + If False, the incomplete chunk will be discarded. + ''' + (complete, leftover) = divmod(len(sequence), chunk_length) + if not allow_incomplete: + leftover = 0 + + chunk_count = complete + min(leftover, 1) + + chunks = [] + for x in range(chunk_count): + left = chunk_length * x + right = left + chunk_length + chunks.append(sequence[left:right]) + + return chunks + +def comma_split(s): + ''' + Split the string apart by commas, discarding all extra whitespace and + blank phrases. + ''' + if s is None: + return s + s = s.replace(' ', ',') + s = [x.strip() for x in s.split(',')] + s = [x for x in s if x] + return s + +def edit_params(original, modifications): + ''' + Given a dictionary representing URL parameters, + apply the modifications and return a URL parameter string. + + {'a':1, 'b':2}, {'b':3} => ?a=1&b=3 + ''' + new_params = original.copy() + new_params.update(modifications) + if not new_params: + return '' + new_params = ['%s=%s' % (k, v) for (k, v) in new_params.items() if v] + new_params = '&'.join(new_params) + if new_params: + new_params = '?' + new_params + return new_params + +def fit_into_bounds(image_width, image_height, frame_width, frame_height): + ''' + Given the w+h of the image and the w+h of the frame, + return new w+h that fits the image into the frame + while maintaining the aspect ratio. + ''' + ratio = min(frame_width/image_width, frame_height/image_height) + + new_width = int(image_width * ratio) + new_height = int(image_height * ratio) + + return (new_width, new_height) + +def hms_to_seconds(hms): + ''' + Convert hh:mm:ss string to an integer seconds. + ''' + hms = hms.split(':') + seconds = 0 + if len(hms) == 3: + seconds += int(hms[0])*3600 + hms.pop(0) + if len(hms) == 2: + seconds += int(hms[0])*60 + hms.pop(0) + if len(hms) == 1: + seconds += int(hms[0]) + return seconds + +def is_xor(*args): + ''' + Return True if and only if one arg is truthy. + ''' + return [bool(a) for a in args].count(True) == 1 + +def read_filebytes(filepath, range_min, range_max): + ''' + Yield chunks of bytes from the file between the endpoints. + ''' + range_span = range_max - range_min + + #print('read span', range_min, range_max, range_span) + f = open(filepath, 'rb') + f.seek(range_min) + sent_amount = 0 + with f: + while sent_amount < range_span: + #print(sent_amount) + chunk = f.read(FILE_READ_CHUNK) + if len(chunk) == 0: + break + + yield chunk + sent_amount += len(chunk) + +def seconds_to_hms(seconds): + ''' + Convert integer number of seconds to an hh:mm:ss string. + Only the necessary fields are used. + ''' + seconds = math.ceil(seconds) + (minutes, seconds) = divmod(seconds, 60) + (hours, minutes) = divmod(minutes, 60) + parts = [] + if hours: parts.append(hours) + if minutes: parts.append(minutes) + parts.append(seconds) + hms = ':'.join('%02d' % part for part in parts) + return hms + +def truthystring(s): + if isinstance(s, (bool, int)) or s is None: + return s + s = s.lower() + if s in {'1', 'true', 't', 'yes', 'y', 'on'}: + return True + if s in {'null', 'none'}: + return None + return False diff --git a/Templates/flask/https/csr, key, crt goes here.txt b/Templates/flask/https/csr, key, crt goes here.txt new file mode 100644 index 0000000..e69de29 diff --git a/Templates/flask/requirements.txt b/Templates/flask/requirements.txt new file mode 100644 index 0000000..652376a --- /dev/null +++ b/Templates/flask/requirements.txt @@ -0,0 +1,2 @@ +flask +gevent diff --git a/Templates/flask/static/common.css b/Templates/flask/static/common.css new file mode 100644 index 0000000..9558b01 --- /dev/null +++ b/Templates/flask/static/common.css @@ -0,0 +1,32 @@ +body +{ + display: flex; + flex-direction: column; + background-color:#fff; + margin: 8px; +} +#header +{ + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + margin-bottom: 4px; +} +.header_element +{ + display: flex; + justify-content: center; + flex: 1; + background-color: rgba(0, 0, 0, 0.1); +} +.header_element:hover +{ + background-color: #f00; +} +#content_body +{ + flex: 0 0 auto; + display: flex; + flex-direction: row; +} diff --git a/Templates/flask/static/common.js b/Templates/flask/static/common.js new file mode 100644 index 0000000..99ac5c1 --- /dev/null +++ b/Templates/flask/static/common.js @@ -0,0 +1,78 @@ +function post_example(key, value) +{ + var url = "/postexample"; + data = new FormData(); + data.append(key, value); + return post(url, data, callback); +} + +function post(url, data, callback) +{ + var request = new XMLHttpRequest(); + request.answer = null; + request.onreadystatechange = function() + { + if (request.readyState == 4) + { + var text = request.responseText; + if (callback != null) + { + console.log(text); + callback(JSON.parse(text)); + } + } + }; + var asynchronous = true; + request.open("POST", url, asynchronous); + request.send(data); +} + +function bind_box_to_button(box, button) +{ + box.onkeydown=function() + { + if (event.keyCode == 13) + { + button.click(); + } + }; +} +function entry_with_history_hook(box, button) +{ + //console.log(event.keyCode); + if (box.entry_history === undefined) + {box.entry_history = [];} + if (box.entry_history_pos === undefined) + {box.entry_history_pos = -1;} + if (event.keyCode == 13) + { + /* Enter */ + box.entry_history.push(box.value); + button.click(); + box.value = ""; + } + else if (event.keyCode == 38) + { + + /* Up arrow */ + if (box.entry_history.length == 0) + {return} + if (box.entry_history_pos == -1) + { + box.entry_history_pos = box.entry_history.length - 1; + } + else if (box.entry_history_pos > 0) + { + box.entry_history_pos -= 1; + } + box.value = box.entry_history[box.entry_history_pos]; + } + else if (event.keyCode == 27) + { + box.value = ""; + } + else + { + box.entry_history_pos = -1; + } +} diff --git a/Templates/flask/static/favicon.png b/Templates/flask/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..7140f04a133b3e9573f2542dd199ef84d4135b5f GIT binary patch literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@)?VR+?^QK zos)S9l1z8>b!1@J*w6hZkrl}2EbxddW?X?_wfUrhf|4b!5hcO-X(i=}MX3zs z<>h*rdD+Fui3O>8`9z`|(a%bX}N=_rp` zT?Mz`Vz+t?OQ76Wk0`Ovh@IG0h0*hHr5x>C#-kssQ9#Oxji8_E$jBQ&nnk!4jf?$ zU;SXxj6LtGyGo?8|$^oh63e_8@XG_(6U7EL#(m=xOl z{&#Tf^(S_ZJvja)&xv2WO6%<}(d}*`fzwo5H!|ljJ$~_M{R-2fTU-a{N~^hN^>X>P l#Q12vJhf4{U^7F5xqMQ_@rBPeRRaT*!PC{xWt~$(697o^uG# + Home + +{% endmacro %} \ No newline at end of file diff --git a/Templates/flask/templates/root.html b/Templates/flask/templates/root.html new file mode 100644 index 0000000..5340c3f --- /dev/null +++ b/Templates/flask/templates/root.html @@ -0,0 +1,29 @@ + + + + + + + {% import "header.html" as header %} + Flasksite + + + + + + {{header.make_header()}} +

Welcome to my flask site

+ + + + +