"Script to generate the general views of Haldis" import os from datetime import datetime, timedelta import yaml from typing import Optional from flask import Flask, render_template, make_response from flask import request, jsonify from flask import Blueprint, abort from flask import current_app as app from flask import send_from_directory, url_for from flask_login import login_required from utils import first from hlds.definitions import location_definitions from hlds.models import Location from models import Order # import views from views.order import get_orders import json from flask import jsonify general_bp = Blueprint("general_bp", __name__) with open(os.path.join(os.path.dirname(__file__), "themes.yml"), "r") as _stream: _theme_data = yaml.safe_load(_stream) THEME_OPTIONS = _theme_data["options"] THEMES = _theme_data["themes"] @general_bp.route("/") def home() -> str: "Generate the home view" prev_day = datetime.now() - timedelta(days=1) recently_closed = get_orders( ((Order.stoptime > prev_day) & (Order.stoptime < datetime.now())) ) return render_template( "home.html", orders=get_orders(), recently_closed=recently_closed ) def is_theme_active(theme, now): theme_type = theme["type"] if theme_type == "static": return True if theme_type == "seasonal": start_day, start_month = map(int, theme["start"].split("/")) start_datetime = datetime(year=now.year, day=start_day, month=start_month) end_day, end_month = map(int, theme["end"].split("/")) end_year = now.year + (1 if start_month > end_month else 0) end_datetime = datetime(year=end_year, day=end_day, month=end_month) return start_datetime <= now <= end_datetime raise Exception("Unknown theme type {}".format(theme_type)) def get_theme_css(theme, options): # Build filename # Each option's chosen value is appended, to get something like mytheme_darkmode_heavy.css filename = theme["file"] for option in theme.get("options", []): theme_name = theme["name"] assert option in THEME_OPTIONS, f"Theme `{theme_name}` uses undefined option `{option}`" chosen_value = options[option] possible_values = list(THEME_OPTIONS[option].keys()) value = chosen_value if chosen_value in possible_values \ else THEME_OPTIONS[option]["_default"] filename += "_" + value filename += ".css" theme_css_dir = "static/css/themes/" return os.path.join(app.root_path, theme_css_dir, filename) def get_active_themes(): now = datetime.now() return [theme for theme in THEMES if is_theme_active(theme, now)] @general_bp.route("/theme.css") def theme_css(): "Send appropriate CSS for current theme" themes = get_active_themes() theme_name = request.cookies.get("theme", None) theme = first((t for t in themes if t["file"] == theme_name), default=themes[-1]) options = { name: request.cookies.get("theme_" + name, None) for name in ["atmosphere", "performance"] } path = get_theme_css(theme, options) with open(path) as f: response = make_response(f.read()) response.headers["Content-Type"] = "text/css" return response @general_bp.route("/current_theme.js") def current_theme_js(): themes = get_active_themes() selected_theme_name = request.cookies.get("theme", None) matching_theme = first((t for t in themes if t["file"] == selected_theme_name)) cur_theme = matching_theme or themes[-1] response = make_response(rf''' var currentTheme = {json.dumps(cur_theme['file'])}; var currentThemeOptions = {json.dumps(cur_theme.get('options', []))}; ''') response.headers["Content-Type"] = "text/javascript" # Theme name that is not valid at this moment: delete cookie if matching_theme is None: response.delete_cookie("theme", path="/") return response @general_bp.route("/map") def map_view() -> str: "Generate the map view" return render_template("maps.html", locations=location_definitions) @general_bp.route("/location") def locations() -> str: "Generate the location view" return render_template("locations.html", locations=location_definitions) @general_bp.route("/location/") def location(location_id) -> str: "Generate the location view given an id" loc = first(filter(lambda l: l.id == location_id, location_definitions)) if loc is None: abort(404) return render_template("location.html", location=loc, title=loc.name) @general_bp.route("/location//") def location_dish(location_id, dish_id) -> str: loc: Optional[Location] = first( filter(lambda l: l.id == location_id, location_definitions) ) if loc is None: abort(404) dish = loc.dish_by_id(dish_id) if dish is None: abort(404) return jsonify([ { "type": c[0], "id": c[1].id, "name": c[1].name, "description": c[1].description, "options": [ { "id": o.id, "name": o.name, "description": o.description, "price": o.price, "tags": o.tags, } for o in c[1].options ], } for c in dish.choices ]) @general_bp.route("/about/") def about() -> str: "Generate the about view" return render_template("about.html") @general_bp.route("/profile/") @login_required def profile() -> str: "Generate the profile view" return render_template("profile.html", themes_list=get_active_themes()) @general_bp.route("/favicon.ico") def favicon() -> str: "Generate the favicon" # pylint: disable=R1705 if not get_orders((Order.stoptime > datetime.now())): return send_from_directory( os.path.join(app.root_path, "static"), "favicon.ico", mimetype="image/x-icon", ) else: return send_from_directory( os.path.join(app.root_path, "static"), "favicon_orange.ico", mimetype="image/x-icon", )