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