import json import os import uuid from flask import Flask, redirect, render_template, g, request, send_file, session, url_for from pathlib import Path from kanken_online.api import get_kanji_by_character from kanken_online.database import get_database from .auth import login_required DATABASE_NAME = "kanken_online.sqlite" JITEN_DB_NAME = "kanken.db" PFP_DIRECTORY_NAME = "pfp" def create_app(test_config=None): app = Flask(__name__, instance_relative_config=True) app.config.from_mapping( SECRET_KEY="dev", DATABASE=str(Path(app.instance_path) / DATABASE_NAME), JITEN_DB=str(Path(app.instance_path) / JITEN_DB_NAME), PFP_STORE=str(Path(app.instance_path) / PFP_DIRECTORY_NAME) ) if test_config is None: app.config.from_pyfile("config.py", silent=True) else: app.config.from_mapping(test_config) # Ensure instance path exists os.makedirs(app.instance_path, exist_ok=True) @app.route("/hello") def hello(): return "Hello, World!" @app.route("/") def index(): return render_template("index.html") @app.route("/about") def about_page(): return render_template("about.html") def update_settings(form, files): db = get_database() # Set values in the database settings = db.execute("SELECT * FROM user_settings WHERE user_id = ?", (session["user_id"],) ).fetchone() if settings: db.execute("UPDATE user_settings SET lang = ?, theme = ? WHERE user_id = ?", (form["language"], form["theme"], session["user_id"])) else: db.execute("INSERT INTO user_settings (user_id, lang, theme) VALUES (?, ?, ?)", (session["user_id"], form["language"], form["theme"])) if "pfp" in files and files["pfp"].filename != "": stored_filename = str(uuid.uuid4()) pfp = files["pfp"] pfp.save(os.path.join(app.config["PFP_STORE"], stored_filename)) db.execute("UPDATE user_settings SET pfp_filename = ?", (stored_filename,)) db.commit() update_logged_out_settings(form) return redirect("/options") def update_logged_out_settings(form): # Set values directly in the session session["language"] = form["language"] session["theme"] = form["theme"] return redirect("/public_options") @app.route("/options", methods=["GET", "POST"]) def options(): if request.method == "GET": if "user_id" in session: return render_template("options.html") else: return redirect("/public_options") else: return update_settings(request.form, request.files) @app.route("/public_options", methods=["GET", "POST"]) def public_options(): if request.method == "GET": if "user_id" in session: return redirect("/public_options") else: return render_template("logged_out_options.html") else: return update_logged_out_settings(request.form) @app.get("/user/") def user_page(user_id: int): db = get_database() (username,pfp_filename) = db.execute("SELECT username, pfp_filename FROM user, user_settings WHERE user.id = ? AND user_settings.user_id = ?", (user_id,user_id)).fetchone() return render_template("user_page.html", username=username, pfp_filename=os.path.join(app.config["PFP_STORE"], pfp_filename)) @app.get("/me") @login_required def my_page(): return user_page(session["user_id"]) def format_reading(reading: str) -> str: """Apply bold to the part of the reading which the kanji represents; for kun, this can be e.g. 選: えら-ぶ --> えらぶ. For reading strings which don't have any "-" character in them, one is added to the end and the entire reading is emboldened. Example: 簡: かん --> かん """ if "-" not in reading: reading += "-" okurigana_position = reading.index("-") emboldened_part = reading[:okurigana_position] return f"{emboldened_part}{reading[okurigana_position+1:]}" @app.route("/kanji/") def kanji_page(kanji: str): kanji_obj = get_kanji_by_character(kanji) class Kanji(): pass out = Kanji() out.character = kanji_obj.character out.is_joyo = "常用" if kanji_obj.level not in ["1", "準1"] else "表外" out.level = kanji_obj.level out.strokes = kanji_obj.stroke_count out.radical = kanji_obj.radical out.added_strokes = kanji_obj.radical_added_stroke_count out.goon = [format_reading(obj.reading) for obj in kanji_obj.goon] out.kanon = [format_reading(obj.reading) for obj in kanji_obj.kanon] out.toon = [format_reading(obj.reading) for obj in kanji_obj.toon] out.soon = [format_reading(obj.reading) for obj in kanji_obj.soon] out.kanyoon = [format_reading(obj.reading) for obj in kanji_obj.kanyoon] out.kun = [format_reading(obj.reading) for obj in kanji_obj.kun] out.meanings = kanji_obj.meanings out.glyph_origin = kanji_obj.glyph_origin return render_template("kanji.html", kanji=out) @app.route("/kotoba/") def kotoba_page(kotoba: str): return render_template("kotoba.html", kotoba=kotoba) from . import database database.initialize_app(app) from . import auth, api, forum, search, indices app.register_blueprint(auth.blueprint) app.register_blueprint(api.blueprint) app.register_blueprint(forum.blueprint) app.register_blueprint(search.blueprint) app.register_blueprint(indices.blueprint) from . import lang lang.add_translation_commands(app) @app.route("/translations", methods=["GET", "POST"]) def strings_translation(): if request.method == "GET": strings = {} for language, language_data in lang.LANGUAGES.items(): strings[language] = { string: translation for string, translation in language_data.items() } return render_template("translations.html", strings=strings) else: strings = {} for i, language in enumerate(lang.LANGUAGES): strings[language] = { string: request.form.getlist(string)[i] for string in request.form } with open(Path(app.root_path, "static", "lang", f"{language}.json"), mode="w") as f: json.dump(strings[language], f, ensure_ascii=False, indent=0) lang.update_languages() return redirect("/translations") @app.get(app.config["PFP_STORE"] + "/") def get_pfp(img: str): return send_file(os.path.join(app.config["PFP_STORE"], img)) @app.route("/data") def data_page(): return render_template("data.html") # def use_english(text_id: str): # return lang.localize(text_id, lang.JAPANESE) app.jinja_env.globals.update(localize=lang.localize) return app