├── app ├── __init__.py ├── models │ ├── __init__.py │ ├── order.py │ └── session.py ├── routes │ ├── __init__.py │ ├── utils.py │ ├── stats.py │ ├── basic.py │ └── session.py ├── static │ ├── style.css │ ├── utils.css │ ├── template.css │ ├── ai.js │ └── core.css ├── template │ ├── index.html │ ├── login.html │ ├── stats.html │ ├── base.html │ ├── sessions.html │ └── session.html ├── config.py ├── server.py └── database.py ├── database └── .gitkeep ├── .vscode └── launch.json ├── requirements.txt ├── .gitignore ├── Dockerfile ├── docker-compose.yml ├── README.md ├── LICENSE └── main.py /app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-session 3 | waitress 4 | requests -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | **/__pycache__ 3 | **/*.db 4 | **/flask_session 5 | 6 | .env 7 | -------------------------------------------------------------------------------- /app/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .session import Session 2 | from .order import Order, OrderTotal 3 | -------------------------------------------------------------------------------- /app/routes/__init__.py: -------------------------------------------------------------------------------- 1 | from .basic import basic_blueprint 2 | from .session import session_blueprint 3 | from .stats import stats_blueprint -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim-bullseye 2 | COPY . /app 3 | WORKDIR /app 4 | RUN pip install -r requirements.txt 5 | CMD [ "python", "main.py" ] 6 | -------------------------------------------------------------------------------- /app/static/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme CSS 3 | by beeb89gang 4 | */ 5 | 6 | /* ========================================== */ 7 | 8 | @import url(core.css); 9 | @import url(template.css); 10 | @import url(utils.css); -------------------------------------------------------------------------------- /app/routes/utils.py: -------------------------------------------------------------------------------- 1 | from flask import session 2 | 3 | def is_logged(): 4 | return "user" in session.keys() 5 | 6 | def is_man(gender): 7 | if(gender != 'man'): 8 | return False 9 | else: 10 | return True 11 | 12 | def is_nice(number): 13 | return number == 69 14 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | services: 3 | sushai_app: 4 | container_name: "sushai_app" 5 | build: . 6 | restart: unless-stopped 7 | ports: 8 | - "8069:8080" 9 | volumes: 10 | - sessions:/app/flask_session:rw 11 | - database:/app/database:rw 12 | environment: 13 | - SUSHAI_MAX_USER_ORDERS=24 14 | 15 | volumes: 16 | sessions: 17 | database: 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SUShAI App 🐟 2 | 3 | A super sus AI app to order Sushi, full of AI, Blockchain and Cryptography. 4 | 5 | ![spaghettisushiapp](https://github.com/beeb89gang/SUShAI/assets/34033090/f6ac3118-f9a8-4d9a-81a6-b6f6dd1ab2b7) 6 | 7 | ## Installation 8 | 9 | brah, just type `docker-compose up -d --build` 10 | 11 | ## Credits 12 | 13 | - beeb89 gang 14 | - fontawesome 15 | - honk & comic neue (google fonts) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2024 Beeb89gang 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from app.server import app 2 | from app.config import log, DEBUG 3 | from app.database import Database 4 | from waitress import serve 5 | 6 | def main(): 7 | log.info("Initialising Database...") 8 | db = Database() 9 | db.initialize() 10 | db.close() 11 | log.info("Starting server...") 12 | if DEBUG: 13 | app.run( 14 | debug=True, 15 | host="0.0.0.0", 16 | port="8070", 17 | load_dotenv=False, 18 | ) 19 | else: 20 | serve(app, host="0.0.0.0", port="8080") 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /app/template/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |

{{ title }}

6 |
7 |

Welcome to SUShAI app!

8 | 9 |
10 | 11 |

With this app you chan choose the fish to put in your mouth (I hope it will be quite big).

12 | Me 13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /app/template/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |

{{ title }}

6 |

Insert a random username to proceed. The session will last forever.

7 | {% if output %} 8 |

{{ output[1] }}

9 | {% endif %} 10 |
11 |
12 |
13 | 14 | Your username 15 |
16 | 17 |
18 |
19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /app/template/stats.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |

{{ title }}

6 |

These are global stats collected from old Sushi sessions.

7 |

General

8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Total Sessions
Total Orders
Average orders per session
23 |
24 |
25 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /app/models/order.py: -------------------------------------------------------------------------------- 1 | 2 | class Order: 3 | user_id: str 4 | session_id: str 5 | order_number: str 6 | order_name: str 7 | order_received: bool 8 | user_id: str 9 | user_name: str 10 | 11 | def __init__(self, db_row:str=None) -> None: 12 | if db_row is not None: 13 | if len(db_row) != 7: 14 | raise ValueError("Unable to unserialize db row in a Order object") 15 | else: 16 | self.user_id = db_row[0] 17 | self.session_id = db_row[1] 18 | self.order_number = db_row[2] 19 | self.order_name = db_row[3] 20 | self.order_received = db_row[4] == 1 21 | self.user_id = db_row[5] 22 | self.user_name = db_row[6] 23 | 24 | class OrderTotal: 25 | order_number: str 26 | order_total: str 27 | 28 | def __init__(self, db_row:str=None) -> None: 29 | if db_row is not None: 30 | if len(db_row) != 2: 31 | raise ValueError("Unable to unserialize db row in a OrderTotal object") 32 | else: 33 | self.order_number = db_row[0] 34 | self.order_total = db_row[1] 35 | -------------------------------------------------------------------------------- /app/models/session.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | 3 | class Session: 4 | id: str 5 | date: str 6 | title: str 7 | status: bool 8 | creator_id:str 9 | creator_name:str 10 | 11 | def __init__(self, db_row:str=None) -> None: 12 | if db_row is not None: 13 | if len(db_row) != 5: 14 | raise ValueError("Unable to unserialize db row in a Session object") 15 | else: 16 | self.id = db_row[0] 17 | self.date = db_row[1] 18 | self.title = db_row[2] 19 | self.status = False if db_row[3] == None else db_row[3] == 1 20 | self.creator_id = db_row[4] 21 | self.creator_name = None 22 | 23 | @property 24 | def pretty_date(self) -> str: 25 | return datetime.fromisoformat(self.date).strftime('%Y-%m-%d, %H:%M') 26 | 27 | @property 28 | def open(self) -> bool: 29 | return (datetime.fromisoformat(self.date) + timedelta(hours=12)) > datetime.now() and not self.status 30 | 31 | @property 32 | def is_recent(self) -> bool: 33 | return (datetime.fromisoformat(self.date) + timedelta(hours=24*7)) > datetime.now() 34 | -------------------------------------------------------------------------------- /app/routes/stats.py: -------------------------------------------------------------------------------- 1 | import statistics 2 | from flask import render_template, request, session, redirect, Blueprint 3 | from .utils import is_logged 4 | from ..config import VERSION, MAX_ORDER_PER_USER 5 | from ..database import Database 6 | from ..models import Session, Order, OrderTotal 7 | from datetime import datetime 8 | 9 | stats_blueprint = Blueprint('stats', __name__) 10 | 11 | @stats_blueprint.route('/stats') 12 | def stats_index(): 13 | """ Stats page """ 14 | stats:dict = {} 15 | return render_template( 16 | "stats.html", 17 | title="Stats", 18 | version=VERSION, 19 | logged=is_logged(), 20 | user=session["user"] if is_logged() else {}, 21 | stats=stats, 22 | ) 23 | 24 | @stats_blueprint.route('/stats/') 25 | def stats_id(stats_id:str): 26 | """ Stats API """ 27 | db = Database() 28 | if stats_id == "ALL_SESSIONS": 29 | res = db.c.execute('SELECT COUNT(*) FROM session').fetchone() 30 | return {"total":res[0]}, 200 31 | elif stats_id == "ALL_ORDERS": 32 | res = db.c.execute('SELECT COUNT(*) FROM user_session_order').fetchone() 33 | return {"total":res[0]}, 200 34 | elif stats_id == "ORDERS_DISTRIBUTION": 35 | res = db.c.execute('SELECT order_number, COUNT(*) FROM user_session_order GROUP BY order_number').fetchall() 36 | return {"total":len(res), "values":[{x[0]:x[1]} for x in res]}, 200 37 | elif stats_id == "ORDERS_SESSION_AVERAGE": 38 | res = db.c.execute('SELECT COUNT(*) FROM user_session_order GROUP BY session_id').fetchall() 39 | return {"total":statistics.mean([x[0] for x in res])}, 200 40 | elif stats_id == "ORDERS_USER_AVERAGE": 41 | res = db.c.execute('SELECT COUNT(*) FROM user_session_order GROUP BY user_id,session_id').fetchall() 42 | return {"total":statistics.mean([x[0] for x in res])}, 200 43 | elif stats_id == "SESSIONS_DISTRIBUTION": 44 | res = db.c.execute('SELECT date FROM session').fetchall() 45 | return {"total":len(res), "values":[datetime.fromisoformat(x[0]).timestamp() for x in res]}, 200 46 | else: 47 | return {}, 404 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/static/utils.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme CSS - Utils classes 3 | by beeb89gang 4 | */ 5 | 6 | /* ========================================== */ 7 | 8 | 9 | .muted, .gray { 10 | color: #7c7b7b; 11 | } 12 | 13 | .box { 14 | padding: 10px; 15 | border-radius: 8px; 16 | border: 2px solid #CCC; 17 | } 18 | 19 | .bg-muted { 20 | background-color: #747474; 21 | color: white; 22 | font-weight: bold; 23 | } 24 | 25 | .bg-error { 26 | background-color: rgb(181, 57, 57); 27 | color: white; 28 | font-weight: bold; 29 | } 30 | 31 | .bg-success { 32 | background-color: rgb(121, 185, 1);; 33 | color: white; 34 | font-weight: bold; 35 | } 36 | 37 | .bg-warning { 38 | background-color: rgb(185, 132, 1); 39 | color: white; 40 | font-weight: bold; 41 | } 42 | 43 | .error, .red{ 44 | color: rgb(168, 7, 7); 45 | } 46 | 47 | .success, .green { 48 | color: #339b30; 49 | } 50 | 51 | .warning, .orange { 52 | color: #d1a037; 53 | } 54 | 55 | .gray { 56 | color: gray !important; 57 | } 58 | 59 | .black { 60 | color: black !important; 61 | } 62 | 63 | .my-1 { 64 | margin: 10px 0; 65 | } 66 | 67 | .my-2 { 68 | margin: 20px 0; 69 | } 70 | 71 | .strong { 72 | font-weight: bold; 73 | } 74 | 75 | .small { 76 | font-size: small; 77 | } 78 | 79 | .break { 80 | word-wrap: break-word; 81 | } 82 | 83 | .tleft { 84 | text-align: left !important; 85 | } 86 | 87 | .tcenter { 88 | text-align: center !important; 89 | } 90 | 91 | .tright { 92 | text-align: right !important; 93 | } 94 | 95 | .scroll { 96 | text-align: center; 97 | margin: 0 auto; 98 | overflow-x: auto; 99 | } 100 | 101 | .scroll table { 102 | white-space: nowrap; 103 | width: 100%; 104 | } 105 | 106 | .diblock { 107 | display: inline-block !important; 108 | } 109 | 110 | .dblock { 111 | display: block !important; 112 | } 113 | 114 | .my-1-auto { 115 | margin: 10px auto; 116 | } 117 | 118 | .my-2-auto { 119 | margin: 20px auto; 120 | } 121 | 122 | .m-auto { 123 | margin: 0 auto !important; 124 | } -------------------------------------------------------------------------------- /app/routes/basic.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from flask import render_template, request, session, redirect, Blueprint 3 | from .utils import is_logged 4 | from ..config import VERSION, MAX_ORDER_PER_USER 5 | from ..database import Database 6 | from ..models import Session, Order, OrderTotal 7 | from datetime import datetime 8 | 9 | basic_blueprint = Blueprint('basic', __name__) 10 | 11 | @basic_blueprint.route('/') 12 | def index(): 13 | """ Index page """ 14 | if is_logged(): 15 | return redirect(f"/sessions") 16 | return render_template( 17 | "index.html", 18 | title="Welcome", 19 | version=VERSION, 20 | logged=is_logged(), 21 | user=session["user"] if is_logged() else {}, 22 | ) 23 | 24 | @basic_blueprint.route("/login", methods=["POST", "GET"]) 25 | def login(): 26 | """ Login page """ 27 | session_id = request.args.to_dict().get("session_id") 28 | output = "" 29 | if is_logged(): 30 | return redirect("/") 31 | if request.method == "POST": 32 | db = Database() 33 | user_id = str(uuid.uuid4()) 34 | res = db.c.execute( 35 | 'INSERT INTO user (id, name) VALUES (?, ?)', 36 | ( 37 | user_id, 38 | str(request.form["name"]) 39 | ) 40 | ) 41 | db.commit() 42 | if res: 43 | session["user"] = { 44 | "id": user_id, 45 | "name": request.form["name"] 46 | } 47 | session.permanent = True 48 | if session_id is not None: 49 | return redirect(f"/sessions/{session_id}") 50 | else: 51 | return redirect("/sessions") 52 | else: 53 | output = ("error", "You bitch dont you try") 54 | db.close() 55 | return render_template( 56 | "login.html", 57 | output=output, 58 | version=VERSION, 59 | logged=False, 60 | session_id=session_id, 61 | title="Choose a Username", 62 | ) 63 | 64 | @basic_blueprint.route("/logout") 65 | def logout(): 66 | """ Logout API """ 67 | session.pop("user", None) 68 | return redirect("/") -------------------------------------------------------------------------------- /app/static/template.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme CSS - Template styles 3 | by beeb89gang 4 | */ 5 | 6 | /* ========================================== */ 7 | 8 | 9 | header{ 10 | display: block; 11 | margin: -8px -8px 0px; 12 | padding: 0; 13 | text-align: left; 14 | border-bottom: 3px solid #e0e0e0; 15 | color: #FFF; 16 | background: radial-gradient(ellipse at center, #ff5db1 0%,#ef017c 100%); 17 | } 18 | 19 | header img{ 20 | margin-top: 16px; 21 | } 22 | 23 | header a, header a:visited{ 24 | color: #FAFAFA; 25 | } 26 | 27 | header a:hover { 28 | color: #333; 29 | } 30 | 31 | header h1{ 32 | display: inline-block; 33 | margin: 14px 12px; 34 | vertical-align: top; 35 | font-family: 'Honk', sans-serif; 36 | } 37 | 38 | header .container { 39 | max-width: 900px; 40 | } 41 | 42 | footer img{ 43 | display: block; 44 | margin: 20px auto; 45 | max-width: 180px !important; 46 | } 47 | 48 | section{ 49 | margin: 0 0 20px; 50 | background-color: #F0F0F0; 51 | padding: 12px 14px 32px; 52 | max-width: 1000px; 53 | width: 90%; 54 | text-align: left; 55 | border-bottom-left-radius: 8px; 56 | border-bottom-right-radius: 8px; 57 | } 58 | 59 | section.content{ 60 | min-height: calc(100vh - 300px); 61 | margin: 0 auto; 62 | } 63 | 64 | section .section-logo{ 65 | position: absolute; 66 | margin-top: 0; 67 | margin-right: 0; 68 | } 69 | 70 | footer { 71 | color: rgb(243 28 139); 72 | font-size: 90%; 73 | margin: 20px auto; 74 | text-align: center; 75 | } 76 | 77 | .userbar { 78 | background-color: #d3d3d3; 79 | color: black; 80 | text-align: right; 81 | padding: 5px 14px; 82 | font-size: small; 83 | border-bottom-right-radius: 8px; 84 | width: 90%; 85 | max-width: 1000px; 86 | margin: -10px auto; 87 | border-bottom-left-radius: 8px; 88 | } 89 | 90 | .container { 91 | max-width: 950px; 92 | margin: 0 auto; 93 | } 94 | 95 | .nav { 96 | display: inline-block; 97 | margin: 15px 20px; 98 | } 99 | 100 | .nav a{ 101 | display: inline-block; 102 | padding: 10px; 103 | text-decoration: none; 104 | } 105 | -------------------------------------------------------------------------------- /app/static/ai.js: -------------------------------------------------------------------------------- 1 | 2 | function aiUpdatePlateQuantityMessage(quantity) { 3 | let box = document.querySelector("#plate_quantity_message"); 4 | let buttons = document.querySelector("#order_buttons"); 5 | console.log("Starting AI...") 6 | switch (quantity) { 7 | case "1": 8 | box.innerHTML = ""; 9 | break; 10 | case "2": 11 | box.innerHTML = "Yeah, two plates, why not, that's fine."; 12 | break; 13 | case "3": 14 | box.innerHTML = "Ok, that's above the average."; 15 | break; 16 | case "4": 17 | box.innerHTML = "That's enough."; 18 | break; 19 | case "5": 20 | box.innerHTML = "Stop it, you won't be able to finish this."; 21 | break; 22 | case "6": 23 | box.innerHTML = "You really like it, huh? Have fun paying it if you can't finish."; 24 | break; 25 | case "7": 26 | box.innerHTML = "Stop it!"; 27 | break; 28 | case "8": 29 | box.innerHTML = "STOP!! How the f*** can eat the same plate 8 times?"; 30 | break; 31 | case "9": 32 | box.innerHTML = "DUDE! What the actual f***, is this sashimi or some shit?"; 33 | break; 34 | case "10": 35 | box.innerHTML = "AAHAHAH NO MORE! Try to add the order now!"; 36 | buttons.innerHTML = "" + buttons.innerHTML + ""; 37 | break; 38 | default: 39 | box.innerHTML = ""; 40 | break; 41 | } 42 | return; 43 | } 44 | 45 | function getTotalStats(statsId, elemId) { 46 | var xhttp = new XMLHttpRequest(); 47 | xhttp.onreadystatechange = function() { 48 | if (this.readyState == 4 && this.status == 200) { 49 | document.querySelector(elemId).innerHTML = JSON.parse(xhttp.responseText).total; 50 | } 51 | else if (this.status != 0) { 52 | document.querySelector(elemId).innerHTML = "ERR " + this.status; 53 | } 54 | }; 55 | xhttp.open("GET", "/stats/"+statsId, true); 56 | xhttp.send(); 57 | } -------------------------------------------------------------------------------- /app/template/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | SUShAI App - Order Sushi in a more AI way 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

SUShAI

16 | 24 | 25 |
26 |
27 |
28 | {% block content %}{% endblock %} 29 | {% if logged %} 30 |
Sushed as {{ user["name"] }}  
31 | {% endif %} 32 |
33 |
34 | SUShAI Web App ({{ version }}) Spaghetted by beeb89gang 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀ 3 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀ 4 | # ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀ 5 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠛⠿⠿⣿⣿⣷⣄⠀⠀⠀ 6 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀config ⠀⠈⠻⣿⣷⠀⠀ 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 | import os 53 | import logging 54 | DEBUG = os.environ.get('SUSHAI_DEBUG') or False 55 | VERSION = "v420.420.0" 56 | MAX_ORDER_PER_USER = int(os.environ.get('SUSHAI_MAX_USER_ORDERS') or 20) 57 | WEBSITE_URL = os.environ.get('SUSHAI_WEBSITE_URL') or "localhost" 58 | 59 | logging.basicConfig(format='%(asctime)s | %(levelname)s | %(message)s', level=logging.DEBUG if DEBUG else logging.INFO, datefmt="%Y-%m-%d %H:%M:%S") 60 | log = logging.getLogger("sushai-logs") 61 | -------------------------------------------------------------------------------- /app/server.py: -------------------------------------------------------------------------------- 1 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀ 3 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀ 4 | # ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀ 5 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠛⠿⠿⣿⣿⣷⣄⠀⠀⠀ 6 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀sushidb⠀⠈⠻⣿⣷⠀⠀ 7 | # ⠀⠀⢀⣠⣤⣴⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀server⠀⠀⠀⢸⣿⣇⠀ 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 | from datetime import timedelta 53 | from flask import Flask 54 | from flask_session import Session 55 | from .routes import basic_blueprint, session_blueprint, stats_blueprint 56 | 57 | app = Flask(__name__, template_folder='template') 58 | app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=690) 59 | app.config["SESSION_PERMANENT"] = True 60 | app.config["SESSION_TYPE"] = "filesystem" 61 | app.register_blueprint(basic_blueprint) 62 | app.register_blueprint(session_blueprint) 63 | app.register_blueprint(stats_blueprint) 64 | Session(app) 65 | -------------------------------------------------------------------------------- /app/template/sessions.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |

{{ title }}

6 |

Create a new sushi session to start taking orders! If you have multiple tables, create separate sessions.

7 |

New session

8 | {% if output %} 9 |

{{ output[1] }}

10 | {% endif %} 11 |
12 |
13 |
14 |
15 | 16 | Sushi session name 17 |
18 |
19 |

After creation, sushi session expires in 12 hours.

20 | 21 |
22 |
23 |

Recent sessions (last week)

24 | {% if recent_sessions|length == 0 %} 25 |

No recent session yet, be the first!

26 | {% else %} 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | {% for session in recent_sessions %} 35 | 36 | 37 | 38 | 39 | 40 | {% endfor %} 41 |
TitleStatusCreation date
{{ session.title|truncate(40) }}{% if session.open %} Open{% else %} Closed{% endif %}{{ session.pretty_date }}
42 |
43 | {% endif %} 44 | 45 |

All sessions

46 | {% if not show_all %} 47 |

48 | {% else %} 49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | {% for session in sessions %} 57 | 58 | 59 | 60 | 61 | 62 | {% endfor %} 63 |
TitleStatusCreation date
{{ session.title|truncate(40) }}{% if session.open %} Open{% else %} Closed{% endif %}{{ session.pretty_date }}
64 |
65 | {% endif %} 66 |
67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /app/static/core.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme CSS - Core styles 3 | by beeb89gang 4 | */ 5 | 6 | /* ========================================== */ 7 | 8 | 9 | 10 | body { 11 | font-family: Arial, Helvetica, sans-serif; 12 | color: #333; 13 | text-align: center; 14 | background-color: #333; 15 | font-size: 110%; 16 | background: linear-gradient(to right, #fceabb 0%,#fccd4d 25%,#8895e8 100%); 17 | } 18 | 19 | a { 20 | color:rgb(116, 86, 5) 21 | } 22 | 23 | a:hover { 24 | color:rgb(161, 107, 7) 25 | } 26 | 27 | a:visited { 28 | color:rgb(17, 78, 158) 29 | } 30 | 31 | .raw { 32 | font-family: 'Courier New', Courier, monospace; 33 | font-size: 9pt; 34 | } 35 | 36 | img { 37 | max-width: 200px; 38 | } 39 | 40 | h2 { 41 | margin: 0.5rem 0; 42 | word-wrap: break-word; 43 | } 44 | 45 | h2 small { 46 | border: 2px solid #acacac; 47 | border-radius: 8px; 48 | padding: 3px 6px; 49 | font-size: small; 50 | } 51 | 52 | input[type=text], 53 | input[type=number], 54 | table, 55 | textarea{ 56 | width: 95%; 57 | padding: 8px; 58 | } 59 | 60 | textarea { 61 | max-width: 95% !important; 62 | resize: vertical; 63 | border: 0; 64 | } 65 | 66 | input[type=submit] { 67 | font-weight: bold; 68 | } 69 | 70 | input[type=submit], button { 71 | font-family: 'Comic Neue', sans-serif; 72 | } 73 | 74 | input[type=text], 75 | input[type=name], 76 | input[type=number], 77 | .custom-input, 78 | .amount-input { 79 | border-radius: 0px; 80 | border: 0; 81 | font-size: medium; 82 | } 83 | 84 | input::-webkit-outer-spin-button, 85 | input::-webkit-inner-spin-button { 86 | -webkit-appearance: none; 87 | margin: 0; 88 | } 89 | 90 | .input-top-text { 91 | position: relative; 92 | width: 95%; 93 | margin-top: 8px; 94 | } 95 | .input-top-text .inputText{ 96 | width: 100%; 97 | outline: none; 98 | border:none; 99 | border-bottom: 1px solid #777; 100 | box-shadow: none !important; 101 | } 102 | .input-top-text .inputText:focus{ 103 | border-color: #ef017c; 104 | border-width: medium medium 2px; 105 | } 106 | .input-top-text .floating-label { 107 | position: absolute; 108 | color: #666; 109 | font-size: 90%; 110 | pointer-events: none; 111 | top: 18px; 112 | left: 10px; 113 | transition: 0.2s ease all; 114 | } 115 | .input-top-text input:focus ~ .floating-label, 116 | .input-top-text input:not(:placeholder-shown) ~ .floating-label{ 117 | top: -4px; 118 | left: 4px; 119 | font-size: 13px; 120 | opacity: 1; 121 | } 122 | 123 | .input-top-text ::placeholder { 124 | color: #FFF; 125 | opacity: 0; 126 | } 127 | 128 | .input-top-text ::-ms-input-placeholder { 129 | color: red; 130 | } 131 | 132 | .custom-input { 133 | width: 55% !important; 134 | padding: 8px; 135 | } 136 | 137 | .custom-select { 138 | width: auto !important; 139 | min-width: 50px; 140 | padding: 8px; 141 | border: 0; 142 | } 143 | 144 | .amount-input { 145 | min-width: 100px; 146 | padding: 6px 4px; 147 | font-size: 18px; 148 | text-align: right; 149 | } 150 | 151 | code { 152 | color: rebeccapurple; 153 | } 154 | 155 | table { 156 | border-collapse: collapse; 157 | } 158 | 159 | th { 160 | background-color: #333; 161 | color: #e0e0e0; 162 | } 163 | 164 | td,th { 165 | padding: 6px 14px; 166 | border: 1px solid #333; 167 | text-align: left !important; 168 | } 169 | 170 | legend { 171 | background-color: #f0f0f0; 172 | border-radius: 3px; 173 | padding: 6px; 174 | border: 2px solid #cc9607; 175 | font-size: 90%; 176 | } 177 | 178 | fieldset { 179 | border: 2px solid #d3d3d3; 180 | background-color: #FFF; 181 | border-radius: 8px; 182 | } 183 | 184 | fieldset textarea{ 185 | width: 95% !important; 186 | } 187 | 188 | .chart { 189 | min-height: 750px; 190 | height: auto; 191 | border: 2px solid #CCC; 192 | } -------------------------------------------------------------------------------- /app/database.py: -------------------------------------------------------------------------------- 1 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀ 3 | # ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀ 4 | # ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀ 5 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠛⠿⠿⣿⣿⣷⣄⠀⠀⠀ 6 | # ⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀sushidb⠀⠈⠻⣿⣷⠀⠀ 7 | # ⠀⠀⢀⣠⣤⣴⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀build⠀⠀⠀⠀⢸⣿⣇⠀ 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 | from sqlite3 import connect, Cursor, Error 53 | from .config import ( 54 | log 55 | ) 56 | 57 | class Database: 58 | 59 | def __init__(self, file_path:str="database/sushai.db") -> None: 60 | self._file_path = file_path 61 | try: 62 | self._client = connect(self._file_path) 63 | except Error as error: 64 | log.fatal(f"Unable to connect to DB: {error}") 65 | exit(1) 66 | self._cursor = self._client.cursor() 67 | 68 | def initialize(self) -> None: 69 | if self.__create_tables(): 70 | log.warn("Database initialized with tables") 71 | if self.__update_tables(): 72 | log.warn("Updates applied to database") 73 | else: 74 | log.fatal("Unable to update tables in database") 75 | exit(1) 76 | else: 77 | log.fatal("Unable to create tables in database") 78 | exit(1) 79 | 80 | def close(self) -> None: 81 | self._client.close() 82 | 83 | @property 84 | def c(self) -> Cursor: 85 | return self._cursor 86 | 87 | def commit(self) -> None: 88 | return self._client.commit() 89 | 90 | def __create_tables(self) -> bool: 91 | self.c.execute(''' 92 | CREATE TABLE IF NOT EXISTS "session" ( 93 | "id" TEXT NOT NULL, 94 | "date" TEXT NOT NULL, 95 | "title" TEXT NOT NULL, 96 | "status" INTEGER, 97 | "creator_id" TEXT, 98 | PRIMARY KEY("id"), 99 | FOREIGN KEY("creator_id") REFERENCES "user"("id") 100 | ); 101 | ''') 102 | self.commit() 103 | self.c.execute(''' 104 | CREATE TABLE IF NOT EXISTS "user" ( 105 | "id" TEXT NOT NULL, 106 | "name" TEXT NOT NULL, 107 | PRIMARY KEY("id") 108 | ); 109 | ''') 110 | self.commit() 111 | self.c.execute(''' 112 | CREATE TABLE IF NOT EXISTS "user_session_order" ( 113 | "user_id" TEXT NOT NULL, 114 | "session_id" TEXT NOT NULL, 115 | "order_number" TEXT NOT NULL, 116 | "order_name" TEXT, 117 | "order_received" INTEGER, 118 | FOREIGN KEY("user_id") REFERENCES "user"("id"), 119 | FOREIGN KEY("session_id") REFERENCES "session"("id") 120 | ); 121 | ''') 122 | self.commit() 123 | raw_tables = self.c.execute("SELECT name FROM sqlite_master").fetchall() 124 | if raw_tables is None: 125 | return False 126 | tables = [table[0] for table in raw_tables] 127 | return "session" in tables and "user" in tables and "user_session_order" in tables 128 | 129 | def __update_tables(self) -> bool: 130 | """ Add columns where needed """ 131 | 132 | def add_column_to_table(table:str, column:str, datatype:str) -> None: 133 | res_uso = self.c.execute(f'PRAGMA table_info({table});').fetchall() 134 | res_col = [col[1] for col in res_uso if col[1] == column] 135 | if not res_col: 136 | self.c.execute(f'ALTER TABLE "{table}" ADD COLUMN "{column}" {datatype};') 137 | self.commit() 138 | log.warn(f"Adding column {column} to table {table}") 139 | 140 | add_column_to_table("user_session_order", "order_received", 'INTEGER') 141 | add_column_to_table("session", "status", 'INTEGER') 142 | add_column_to_table("session", "creator_id", 'INTEGER REFERENCES "user"("id")') 143 | return True -------------------------------------------------------------------------------- /app/template/session.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |

{{ title|truncate(40) }}

6 |

7 | Created at {{ session.pretty_date }} 8 | {% if session.creator_name %} 9 | by {{ session.creator_name }} 10 | {% endif %} 11 |

12 | {% if session.creator_id == user["id"] and session.open %} 13 | 14 | {% endif %} 15 |

Share this session by copying the link in your browser bar!

16 |

New Order

17 | {% if output %} 18 |

{{ output[1] }}

19 | {% endif %} 20 | {% if session.open %} 21 |
22 |
23 |
24 |
25 | 26 | 🍣 Plate number (e.g. 123, 401A) 27 |
28 |
29 | 30 | 🍽️ Plate name (optional) 31 |
32 |
33 | 34 | ⚖️ Quantity 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 |
44 | {% else %} 45 |

The session is closed!

46 | {% endif %} 47 |

Orders

48 | {% if orders|length == 0 %} 49 |

No orders yet

50 | {% else %} 51 |

My orders

52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | {% for order in my_orders %} 60 | 61 | 65 | 66 | 72 | {% endif %} 73 | 74 | {% endfor %} 75 |
NumberNameActions
62 | {% if order.order_received %}{% endif %} 63 | {{ order.order_number }} 64 | {{ order.order_name }} 67 | {% if session.open %} 68 | {% if not order.order_received %} 69 | 70 | {% endif %} 71 |
76 |
77 |

All orders

78 |
79 | 80 | 81 | 82 | 83 | 84 | {% for order in orders_total %} 85 | 86 | 87 | 88 | 89 | {% endfor %} 90 |
Order numberTotal
{{ order.order_number }}{{ order.order_total }}
91 |
92 |

Orders by user

93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | {% for order in orders %} 101 | 102 | 104 | 105 | 106 | 107 | {% endfor %} 108 |
NumberNameUser
{% if order.order_received %}{% endif %} 103 | {{ order.order_number }}{{ order.order_name }}{{ order.user_name }}
109 |
110 |

Orders stats

111 | 112 | 135 |
136 | {% endif %} 137 |
138 | {% endblock %} 139 | -------------------------------------------------------------------------------- /app/routes/session.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from flask import render_template, request, session, redirect, Blueprint 3 | from .utils import is_logged 4 | from ..config import VERSION, MAX_ORDER_PER_USER 5 | from ..database import Database 6 | from ..models import Session, Order, OrderTotal 7 | from datetime import datetime 8 | 9 | session_blueprint = Blueprint('session', __name__) 10 | 11 | @session_blueprint.route('/sessions', methods=["POST", "GET"]) 12 | def sessions(): 13 | """ Sessions page """ 14 | show_all = request.args.to_dict().get("show_all") 15 | if not is_logged(): 16 | return redirect("/login") 17 | db = Database() 18 | if request.method == "POST": 19 | new_session = str(uuid.uuid4()) 20 | res = db.c.execute( 21 | 'INSERT INTO session (id, date, title, status, creator_id) VALUES (?, ?, ?, 0, ?)', 22 | ( 23 | new_session, 24 | datetime.now().isoformat(), 25 | request.form["title"], 26 | session["user"]["id"] 27 | ) 28 | ) 29 | db.commit() 30 | if res: 31 | return redirect(f"/sessions/{new_session}") 32 | res = db.c.execute( 33 | 'SELECT * FROM session ORDER BY date DESC' 34 | ).fetchall() 35 | sessions = [Session(row) for row in res] 36 | recent_sessions = [x for x in sessions if x.is_recent] 37 | db.close() 38 | return render_template( 39 | "sessions.html", 40 | title="Sushi Sessions", 41 | version=VERSION, 42 | logged=is_logged(), 43 | user=session["user"], 44 | sessions=sessions, 45 | recent_sessions=recent_sessions, 46 | show_all=True if show_all != None else False, 47 | ) 48 | 49 | @session_blueprint.route('/sessions/', methods=["POST", "GET"]) 50 | def sessions_id(session_id:str): 51 | """ Single Session page (GET) and add order (POST) """ 52 | output = "" 53 | if not is_logged(): 54 | return redirect(f"/login?session_id={session_id}") 55 | db = Database() 56 | if request.method == "POST": 57 | quantity = int(request.form["quantity"]) 58 | final_res = [] 59 | for x in range(1, quantity+1): 60 | res_check = db.c.execute( 61 | 'SELECT COUNT(*) FROM user_session_order WHERE user_id = ? AND session_id = ?', 62 | ( 63 | session["user"]["id"], 64 | session_id, 65 | ) 66 | ).fetchone() 67 | if res_check and res_check[0] >= MAX_ORDER_PER_USER: 68 | output = ("error", f"HAI GIA' ORDINATO {MAX_ORDER_PER_USER} PEZZI, MERDACCIA!") 69 | final_res.append(False) 70 | break 71 | else: 72 | res = db.c.execute( 73 | 'INSERT INTO user_session_order (user_id, session_id, order_number, order_name) VALUES (?, ?, ?, ?)', 74 | ( 75 | session["user"]["id"], 76 | session_id, 77 | str(request.form["number"]), 78 | request.form["name"] if quantity == 1 else request.form["name"] + f" ({x}/{quantity})" 79 | ) 80 | ) 81 | db.commit() 82 | final_res.append(res == True) 83 | if all(final_res): 84 | return redirect(f"/sessions/{session_id}") 85 | res_session = db.c.execute( 86 | 'SELECT * FROM session WHERE id = ? LIMIT 1', (session_id,) 87 | ).fetchone() 88 | res_orders = db.c.execute( 89 | 'SELECT * FROM user_session_order INNER JOIN user ON user.id = user_session_order.user_id WHERE session_id = ? ORDER BY name ASC', (session_id,) 90 | ).fetchall() 91 | res_orders_total = db.c.execute( 92 | 'SELECT order_number, COUNT(user_id) FROM user_session_order WHERE session_id = ? GROUP BY order_number', (session_id,) 93 | ).fetchall() 94 | current_session = Session(res_session) 95 | if current_session.creator_id is not None: 96 | res_creator = db.c.execute( 97 | 'SELECT name FROM user WHERE id = ? LIMIT 1', (session["user"]["id"],) 98 | ).fetchone() 99 | if res_creator: 100 | current_session.creator_name = res_creator[0] 101 | orders = [Order(row) for row in res_orders] 102 | orders_total = [OrderTotal(row) for row in res_orders_total] 103 | orders_total.sort(key=lambda x: int(x.order_number) if x.order_number.isnumeric() else 99999, reverse=False) 104 | my_orders = [order for order in orders if order.user_id == session["user"]["id"]] 105 | db.close() 106 | return render_template( 107 | "session.html", 108 | title=f"{current_session.title}", 109 | version=VERSION, 110 | logged=is_logged(), 111 | orders=orders, 112 | orders_total=orders_total, 113 | my_orders=my_orders, 114 | session=current_session, 115 | output=output, 116 | user=session["user"], 117 | ) 118 | 119 | @session_blueprint.route('/sessions//close') 120 | def close_session(session_id:str): 121 | """ Force close session API """ 122 | if not is_logged(): 123 | return redirect("/login") 124 | db = Database() 125 | res = db.c.execute( 126 | 'UPDATE session SET status=1 WHERE id = ? AND creator_id = ?', 127 | ( 128 | session_id, 129 | session["user"]["id"], 130 | ) 131 | ) 132 | db.commit() 133 | if res: 134 | return redirect(f"/sessions/{session_id}") 135 | db.close() 136 | 137 | @session_blueprint.route('/sessions//remove-order/') 138 | def remove_order(session_id:str, order_number:str): 139 | """ Remove order API """ 140 | if not is_logged(): 141 | return redirect("/login") 142 | db = Database() 143 | res = db.c.execute( 144 | 'DELETE FROM user_session_order WHERE session_id = ? AND user_id = ? AND order_number = ?', 145 | ( 146 | session_id, 147 | session["user"]["id"], 148 | order_number, 149 | ) 150 | ) 151 | db.commit() 152 | if res: 153 | return redirect(f"/sessions/{session_id}#my_orders") 154 | db.close() 155 | 156 | @session_blueprint.route('/sessions//receive-order/') 157 | def receive_order(session_id:str, order_number:str): 158 | """ Receive order API """ 159 | if not is_logged(): 160 | return redirect("/login") 161 | db = Database() 162 | res = db.c.execute( 163 | 'UPDATE user_session_order SET order_received = 1 WHERE session_id = ? AND user_id = ? AND order_number = ?', 164 | ( 165 | session_id, 166 | session["user"]["id"], 167 | order_number, 168 | ) 169 | ) 170 | db.commit() 171 | if res: 172 | return redirect(f"/sessions/{session_id}#my_orders") 173 | db.close() --------------------------------------------------------------------------------