Write backend
This commit is contained in:
commit
f1ee604638
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
__pycache__
|
||||||
|
venv
|
||||||
|
surprise.txt
|
||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM xxx
|
||||||
|
|
||||||
|
# Install flask and elm
|
||||||
|
|
||||||
|
# Copy files
|
||||||
|
|
||||||
|
# Compile elm
|
||||||
|
|
||||||
|
# Run flask
|
||||||
|
|
||||||
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Surprise countdown
|
||||||
|
|
||||||
|
This is a web application for revealing a surprise at a given time.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
113
backend/app.py
Normal file
113
backend/app.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import base64
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
from dateutil import parser as dt_parser
|
||||||
|
from toolz import compose
|
||||||
|
from toolz.curried import map, filter
|
||||||
|
from atomicwrites import atomic_write
|
||||||
|
|
||||||
|
SURPRISE_TITLE = os.environ.get("SURPRISE_TITLE", "Surprise")
|
||||||
|
print("SURPRISE_TITLE =", SURPRISE_TITLE)
|
||||||
|
|
||||||
|
SURPRISE_STORE_FILE = os.environ.get("SURPRISE_STORE_FILE", "surprise.txt")
|
||||||
|
print("SURPRISE_STORE_FILE =", SURPRISE_STORE_FILE)
|
||||||
|
|
||||||
|
SURPRISE_SECRET = os.environ.get("SURPRISE_SECRET")
|
||||||
|
print("SURPRISE_SECRET =", SURPRISE_SECRET)
|
||||||
|
assert bool(SURPRISE_SECRET)
|
||||||
|
|
||||||
|
SURPRISE_ASSET_DIR = os.environ.get("SURPRISE_ASSET_DIR", "../surprise-assets/gender-reveal")
|
||||||
|
print("SURPRISE_ASSET_DIR =", SURPRISE_ASSET_DIR)
|
||||||
|
assert os.path.isdir(SURPRISE_ASSET_DIR)
|
||||||
|
|
||||||
|
SURPRISE_REVEAL_DATETIME = dt_parser.parse(
|
||||||
|
os.environ.get(
|
||||||
|
"SURPRISE_REVEAL_DATETIME",
|
||||||
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).isoformat()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print("SURPRISE_REVEAL_DATETIME =", SURPRISE_REVEAL_DATETIME)
|
||||||
|
|
||||||
|
def _get_surprise_assets():
|
||||||
|
IMG_TYPES = (
|
||||||
|
'.png',
|
||||||
|
'.jpeg',
|
||||||
|
'.jpg',
|
||||||
|
'.svg',
|
||||||
|
'.apng',
|
||||||
|
'.gif',
|
||||||
|
'.webp',
|
||||||
|
'.avif'
|
||||||
|
)
|
||||||
|
_, _, files = next(os.walk(SURPRISE_ASSET_DIR))
|
||||||
|
surprise_assets = compose(
|
||||||
|
dict,
|
||||||
|
map(lambda xy: (xy[0], os.path.join('/assets', ''.join(xy)))),
|
||||||
|
filter(lambda xy: xy[1] in IMG_TYPES),
|
||||||
|
map(lambda s: os.path.splitext(s)),
|
||||||
|
map(lambda s: s.lower().strip())
|
||||||
|
)(files)
|
||||||
|
if '_null' not in surprise_assets:
|
||||||
|
surprise_assets['_null'] = os.path.join('/static', '_null.png')
|
||||||
|
if '_secret' not in surprise_assets:
|
||||||
|
surprise_assets['_secret'] = os.path.join('/static', '_secret.png')
|
||||||
|
return surprise_assets
|
||||||
|
|
||||||
|
SURPRISE_ASSETS = _get_surprise_assets()
|
||||||
|
assert len(SURPRISE_ASSETS) >= 4
|
||||||
|
print("SURPRISE_ASSETS =", SURPRISE_ASSETS)
|
||||||
|
|
||||||
|
|
||||||
|
from flask import (
|
||||||
|
Flask, jsonify, send_from_directory, render_template, request, url_for,
|
||||||
|
redirect, flash
|
||||||
|
)
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_lowercase) for i in range(20))
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return render_template('index.html', title=SURPRISE_TITLE)
|
||||||
|
|
||||||
|
@app.route('/assets/<path:path>')
|
||||||
|
def assets(path):
|
||||||
|
return send_from_directory(SURPRISE_ASSET_DIR, path)
|
||||||
|
|
||||||
|
@app.route('/get-surprise.json')
|
||||||
|
def get_surprise_json():
|
||||||
|
diff = (SURPRISE_REVEAL_DATETIME - datetime.datetime.now()).total_seconds()
|
||||||
|
if os.path.isfile(SURPRISE_STORE_FILE):
|
||||||
|
if diff < 0:
|
||||||
|
with open(SURPRISE_STORE_FILE) as f:
|
||||||
|
surprise = f.read()
|
||||||
|
else:
|
||||||
|
surprise = '_secret'
|
||||||
|
else:
|
||||||
|
surprise = '_null'
|
||||||
|
return jsonify({
|
||||||
|
'diff_seconds': diff,
|
||||||
|
'surprise': surprise,
|
||||||
|
'asset': SURPRISE_ASSETS[surprise]
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.route('/set-surprise')
|
||||||
|
def set_surprise():
|
||||||
|
enum = [v for v in SURPRISE_ASSETS.keys() if v not in ('_null', '_secret')]
|
||||||
|
return render_template('set_surprise.html', title=SURPRISE_TITLE, enum=enum)
|
||||||
|
|
||||||
|
@app.route('/post-surprise', methods=['POST'])
|
||||||
|
def post_surprise():
|
||||||
|
if request.form.get('password') != SURPRISE_SECRET:
|
||||||
|
flash("Password is wrong")
|
||||||
|
return redirect(url_for('set_surprise'))
|
||||||
|
value = request.form.get('surprise')
|
||||||
|
if value not in SURPRISE_ASSETS.keys():
|
||||||
|
flash("Invalid value '%s'" % value)
|
||||||
|
return redirect(url_for('set_surprise'))
|
||||||
|
with atomic_write(SURPRISE_STORE_FILE, overwrite=True) as f:
|
||||||
|
f.write(value)
|
||||||
|
flash("Value was successfully set to '%s'" % value)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
10
backend/requirements.pip
Normal file
10
backend/requirements.pip
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
atomicwrites==1.4.0
|
||||||
|
click==7.1.2
|
||||||
|
Flask==1.1.2
|
||||||
|
itsdangerous==1.1.0
|
||||||
|
Jinja2==2.11.3
|
||||||
|
MarkupSafe==1.1.1
|
||||||
|
python-dateutil==2.8.1
|
||||||
|
six==1.16.0
|
||||||
|
toolz==0.11.1
|
||||||
|
Werkzeug==1.0.1
|
||||||
0
backend/static/_null.png
Normal file
0
backend/static/_null.png
Normal file
0
backend/static/_secret.png
Normal file
0
backend/static/_secret.png
Normal file
23
backend/templates/index.html
Normal file
23
backend/templates/index.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<ul class="messages">
|
||||||
|
{% for message in messages %}
|
||||||
|
<li>{{ message }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
<h1 style="color: blue">Hei</h1>
|
||||||
|
<p>TODO</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
37
backend/templates/set_surprise.html
Normal file
37
backend/templates/set_surprise.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<ul class="messages">
|
||||||
|
{% for message in messages %}
|
||||||
|
<li>{{ message }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
<form class="form-signin" method="POST" action="/post-surprise">
|
||||||
|
<h2 class="form-signin-heading">Set surprise</h2>
|
||||||
|
{% for v in enum %}
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="surprise" id="{{ v }}" value="{{ v }}">
|
||||||
|
<label for="{{ v }}">{{ v }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div>
|
||||||
|
<input class="form-control" type="password" required name="password" placeholder="Password">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Set surprise</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
0
surprise-assets/gender-reveal/_secret.png
Normal file
0
surprise-assets/gender-reveal/_secret.png
Normal file
0
surprise-assets/gender-reveal/boy.png
Normal file
0
surprise-assets/gender-reveal/boy.png
Normal file
0
surprise-assets/gender-reveal/girl.png
Normal file
0
surprise-assets/gender-reveal/girl.png
Normal file
Loading…
x
Reference in New Issue
Block a user