Compare commits
No commits in common. "6c1e0887b6987aab1785f71b4545c8bdb535e1a0" and "9b932291d2f4458688c5fb05fec5c15877ecd108" have entirely different histories.
6c1e0887b6
...
9b932291d2
@ -1,24 +1,16 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import uuid
|
from flask import Flask, redirect, render_template, g, request, session, url_for
|
||||||
from flask import Flask, redirect, render_template, g, request, send_file, session, url_for
|
|
||||||
from pathlib import Path
|
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
|
from .auth import login_required
|
||||||
|
|
||||||
|
|
||||||
DATABASE_NAME = "kanken_online.sqlite"
|
DATABASE_NAME = "kanken_online.sqlite"
|
||||||
JITEN_DB_NAME = "kanken.db"
|
|
||||||
PFP_DIRECTORY_NAME = "pfp"
|
|
||||||
def create_app(test_config=None):
|
def create_app(test_config=None):
|
||||||
app = Flask(__name__, instance_relative_config=True)
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
app.config.from_mapping(
|
app.config.from_mapping(
|
||||||
SECRET_KEY="dev",
|
SECRET_KEY="dev",
|
||||||
DATABASE=str(Path(app.instance_path) / DATABASE_NAME),
|
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:
|
if test_config is None:
|
||||||
@ -41,92 +33,43 @@ def create_app(test_config=None):
|
|||||||
def about_page():
|
def about_page():
|
||||||
return render_template("about.html")
|
return render_template("about.html")
|
||||||
|
|
||||||
def update_settings(form, files):
|
def update_settings(form):
|
||||||
db = get_database()
|
|
||||||
|
|
||||||
if "user_id" in session:
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
# Set values directly in the session
|
|
||||||
session["language"] = form["language"]
|
session["language"] = form["language"]
|
||||||
session["theme"] = form["theme"]
|
session["theme"] = form["theme"]
|
||||||
|
|
||||||
return redirect("/options")
|
return redirect("/options")
|
||||||
|
|
||||||
@app.route("/options", methods=["GET", "POST"])
|
@app.route("/options", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def options():
|
def options():
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render_template("options.html")
|
return render_template("options.html")
|
||||||
else:
|
else:
|
||||||
return update_settings(request.form, request.files)
|
return update_settings(request.form)
|
||||||
|
|
||||||
@app.get("/user/<int:user_id>")
|
|
||||||
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. 選: えら-ぶ --> <b>えら</b>ぶ. For reading strings which don't have any "-" character in them,
|
|
||||||
one is added to the end and the entire reading is emboldened.
|
|
||||||
Example: 簡: かん --> <b>かん</b>
|
|
||||||
"""
|
|
||||||
if "-" not in reading:
|
|
||||||
reading += "-"
|
|
||||||
|
|
||||||
okurigana_position = reading.index("-")
|
|
||||||
emboldened_part = reading[:okurigana_position]
|
|
||||||
return f"<b>{emboldened_part}</b>{reading[okurigana_position+1:]}"
|
|
||||||
|
|
||||||
@app.route("/kanji/<kanji>")
|
@app.route("/kanji/<kanji>")
|
||||||
def kanji_page(kanji: str):
|
def kanji_page(kanji: str):
|
||||||
kanji_obj = get_kanji_by_character(kanji)
|
# TODO use database to get kanji
|
||||||
|
|
||||||
class Kanji():
|
class Kanji():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
out = Kanji()
|
# Highly tentative testing data
|
||||||
out.character = kanji_obj.character
|
kanji = Kanji()
|
||||||
out.is_joyo = "常用" if kanji_obj.level not in ["1", "準1"] else "表外"
|
kanji.character = "麻"
|
||||||
out.level = kanji_obj.level
|
kanji.is_joyo = "joyo kanji"
|
||||||
out.strokes = kanji_obj.stroke_count
|
kanji.level = "pre-2"
|
||||||
out.radical = kanji_obj.radical
|
kanji.strokes = 11
|
||||||
out.added_strokes = kanji_obj.radical_added_stroke_count
|
kanji.radical = "麻"
|
||||||
out.goon = [format_reading(obj.reading) for obj in kanji_obj.goon]
|
kanji.added_strokes = 0
|
||||||
out.kanon = [format_reading(obj.reading) for obj in kanji_obj.kanon]
|
kanji.goon = "マ"
|
||||||
out.toon = [format_reading(obj.reading) for obj in kanji_obj.toon]
|
kanji.kanon = "バ"
|
||||||
out.soon = [format_reading(obj.reading) for obj in kanji_obj.soon]
|
kanji.toon = ""
|
||||||
out.kanyoon = [format_reading(obj.reading) for obj in kanji_obj.kanyoon]
|
kanji.soon = ""
|
||||||
out.kun = [format_reading(obj.reading) for obj in kanji_obj.kun]
|
kanji.kanyoon = ""
|
||||||
out.meanings = kanji_obj.meanings
|
kanji.kun = "あさ, しびれる"
|
||||||
out.glyph_origin = kanji_obj.glyph_origin
|
kanji.meanings = "①あさ。クワ科の一年草。また、あさ類の総称。「亜麻」「乱麻」 ②しびれる。しびれ。「麻酔」「麻痺(マヒ)」類痲(マ)"
|
||||||
|
kanji.glyph_origin = "会意。广(げん)(いえ)と、𣏟(はい)(あさ)とから成り、屋下であさの繊維をはぎとる、ひいて「あさ」の意を表す。"
|
||||||
|
|
||||||
|
return render_template("kanji.html", kanji=kanji)
|
||||||
return render_template("kanji.html", kanji=out)
|
|
||||||
|
|
||||||
@app.route("/kotoba/<kotoba>")
|
@app.route("/kotoba/<kotoba>")
|
||||||
def kotoba_page(kotoba: str):
|
def kotoba_page(kotoba: str):
|
||||||
@ -143,7 +86,6 @@ def create_app(test_config=None):
|
|||||||
app.register_blueprint(indices.blueprint)
|
app.register_blueprint(indices.blueprint)
|
||||||
|
|
||||||
from . import lang
|
from . import lang
|
||||||
lang.add_translation_commands(app)
|
|
||||||
|
|
||||||
@app.route("/translations", methods=["GET", "POST"])
|
@app.route("/translations", methods=["GET", "POST"])
|
||||||
def strings_translation():
|
def strings_translation():
|
||||||
@ -167,14 +109,6 @@ def create_app(test_config=None):
|
|||||||
lang.update_languages()
|
lang.update_languages()
|
||||||
return redirect("/translations")
|
return redirect("/translations")
|
||||||
|
|
||||||
@app.get(app.config["PFP_STORE"] + "/<img>")
|
|
||||||
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):
|
# def use_english(text_id: str):
|
||||||
# return lang.localize(text_id, lang.JAPANESE)
|
# return lang.localize(text_id, lang.JAPANESE)
|
||||||
app.jinja_env.globals.update(localize=lang.localize)
|
app.jinja_env.globals.update(localize=lang.localize)
|
||||||
|
@ -4,7 +4,7 @@ from flask import Blueprint, jsonify
|
|||||||
import jsonpickle
|
import jsonpickle
|
||||||
from sqlalchemy import create_engine, select
|
from sqlalchemy import create_engine, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from .database import get_database, get_jiten_db
|
from .database import get_database
|
||||||
|
|
||||||
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
||||||
|
|
||||||
@ -125,29 +125,24 @@ class Kanji(Base):
|
|||||||
|
|
||||||
@blueprint.route("/id/<int:kanji_id>")
|
@blueprint.route("/id/<int:kanji_id>")
|
||||||
def kanji_by_id(kanji_id: int):
|
def kanji_by_id(kanji_id: int):
|
||||||
session = get_jiten_db()
|
engine = create_engine("sqlite:///kanken_online/kanken.db")
|
||||||
query = select(Kanji).where(Kanji.id == kanji_id)
|
with Session(engine) as session:
|
||||||
item = session.execute(query).first()
|
query = select(Kanji).where(Kanji.id == kanji_id)
|
||||||
if item is None:
|
item = session.execute(query).first()
|
||||||
return "Invalid ID", 404
|
if item is None:
|
||||||
|
return "Invalid ID", 404
|
||||||
|
|
||||||
kanji = item[0]
|
kanji = item[0]
|
||||||
return kanji.to_json()
|
return kanji.to_json()
|
||||||
|
|
||||||
def get_kanji_by_character(kanji: str) -> Kanji:
|
|
||||||
session = get_jiten_db()
|
|
||||||
query = select(Kanji).where(Kanji.character == kanji)
|
|
||||||
item = session.execute(query).first()
|
|
||||||
if item is None:
|
|
||||||
return "Invalid kanji", 404
|
|
||||||
|
|
||||||
kanji_obj: Kanji = item[0]
|
|
||||||
return kanji_obj
|
|
||||||
|
|
||||||
@blueprint.route("/kanji/<kanji>")
|
@blueprint.route("/kanji/<kanji>")
|
||||||
def kanji_by_character(kanji: str):
|
def kanji_by_character(kanji: str):
|
||||||
kanji_obj = get_kanji_by_character(kanji)
|
engine = create_engine("sqlite:///kanken_online/kanken.db")
|
||||||
return kanji_obj.to_json()
|
with Session(engine) as session:
|
||||||
|
query = select(Kanji).where(Kanji.character == kanji)
|
||||||
|
item = session.execute(query).first()
|
||||||
|
if item is None:
|
||||||
|
return "Invalid kanji", 404
|
||||||
|
|
||||||
def get_kanji_generic():
|
kanji_obj = item[0]
|
||||||
pass
|
return kanji_obj.to_json()
|
||||||
|
@ -53,14 +53,6 @@ def login():
|
|||||||
if error is None:
|
if error is None:
|
||||||
session.clear()
|
session.clear()
|
||||||
session["user_id"] = user["id"]
|
session["user_id"] = user["id"]
|
||||||
settings = db.execute("SELECT * FROM user_settings WHERE user_id = ?",
|
|
||||||
(session["user_id"],)
|
|
||||||
).fetchone()
|
|
||||||
if settings:
|
|
||||||
session["language"] = settings["lang"]
|
|
||||||
session["theme"] = settings["theme"]
|
|
||||||
|
|
||||||
|
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
flash(error)
|
flash(error)
|
||||||
|
@ -2,9 +2,6 @@ import sqlite3
|
|||||||
from typing import IO
|
from typing import IO
|
||||||
import click
|
import click
|
||||||
from flask import Flask, current_app, g
|
from flask import Flask, current_app, g
|
||||||
from sqlalchemy import create_engine
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
|
|
||||||
def get_database():
|
def get_database():
|
||||||
if "db" not in g:
|
if "db" not in g:
|
||||||
@ -16,14 +13,6 @@ def get_database():
|
|||||||
|
|
||||||
return g.db
|
return g.db
|
||||||
|
|
||||||
def get_jiten_db():
|
|
||||||
if "jiten_db" not in g:
|
|
||||||
engine = create_engine(f"sqlite:///{current_app.config['JITEN_DB']}")
|
|
||||||
g.jiten_db = Session(engine)
|
|
||||||
print("Session is", g.jiten_db)
|
|
||||||
|
|
||||||
return g.jiten_db
|
|
||||||
|
|
||||||
def initialize_database():
|
def initialize_database():
|
||||||
db = get_database()
|
db = get_database()
|
||||||
|
|
||||||
@ -46,18 +35,6 @@ def init_db_command():
|
|||||||
else:
|
else:
|
||||||
click.echo("Aborted.")
|
click.echo("Aborted.")
|
||||||
|
|
||||||
@click.command("prune-pfps")
|
|
||||||
def prune_pfps_command():
|
|
||||||
"""Removes excess profile pictures that aren't used in the database."""
|
|
||||||
import os
|
|
||||||
db = get_database()
|
|
||||||
used_pfps = {row[0] for row in db.execute("SELECT pfp_filename FROM user_settings").fetchall()}
|
|
||||||
stored_pfps = set(os.listdir(current_app.config["PFP_STORE"]))
|
|
||||||
unused_pfps = stored_pfps - used_pfps
|
|
||||||
for pfp in unused_pfps:
|
|
||||||
os.remove(os.path.join(current_app.config["PFP_STORE"], pfp))
|
|
||||||
|
|
||||||
def initialize_app(app: Flask):
|
def initialize_app(app: Flask):
|
||||||
app.teardown_appcontext(close_database)
|
app.teardown_appcontext(close_database)
|
||||||
app.cli.add_command(init_db_command)
|
app.cli.add_command(init_db_command)
|
||||||
app.cli.add_command(prune_pfps_command)
|
|
@ -1,37 +1,8 @@
|
|||||||
import datetime
|
from flask import Blueprint, render_template
|
||||||
from flask import Blueprint, flash, redirect, render_template, request, session, url_for
|
|
||||||
|
|
||||||
from kanken_online.auth import login_required
|
|
||||||
from kanken_online.database import get_database
|
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint("forum", __name__, url_prefix="/forum")
|
blueprint = Blueprint("forum", __name__, url_prefix="/forum")
|
||||||
|
|
||||||
@blueprint.route("/")
|
@blueprint.route("/")
|
||||||
def index():
|
def index():
|
||||||
# Get forum posts
|
return render_template("forum/index.html")
|
||||||
db = get_database()
|
|
||||||
|
|
||||||
posts = db.execute("SELECT post.*, user.username FROM post, user WHERE user.id = post.author_id ORDER BY post.created DESC LIMIT 50").fetchall()
|
|
||||||
|
|
||||||
# Pass to the template to render
|
|
||||||
return render_template("forum/index.html", posts=posts)
|
|
||||||
|
|
||||||
@blueprint.route("/new", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def new_post():
|
|
||||||
if request.method == "GET":
|
|
||||||
return render_template("forum/new.html")
|
|
||||||
else:
|
|
||||||
title = request.form["title"]
|
|
||||||
body = request.form["body"]
|
|
||||||
|
|
||||||
author_id = session["user_id"]
|
|
||||||
created = datetime.datetime.now()
|
|
||||||
|
|
||||||
db = get_database()
|
|
||||||
db.execute("INSERT INTO post VALUES (NULL, ?, ?, ?, ?)",
|
|
||||||
(author_id, created, title, body))
|
|
||||||
db.commit()
|
|
||||||
flash("success_new_post")
|
|
||||||
return redirect(url_for("forum.index"))
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
schema {
|
|
||||||
kanji: Kanji,
|
|
||||||
kotoba: Kotoba
|
|
||||||
}
|
|
||||||
|
|
||||||
type Kanji {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type Kotoba {
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import click
|
from flask import session
|
||||||
from flask import Flask, session, current_app
|
|
||||||
|
|
||||||
LanguageMapping = dict[str, str]
|
LanguageMapping = dict[str, str]
|
||||||
|
|
||||||
@ -34,46 +32,3 @@ def localize(text_id: str, language: dict[str, str] = None) -> str:
|
|||||||
preference = session.get("language", "ja")
|
preference = session.get("language", "ja")
|
||||||
language = LANGUAGES.get(preference)
|
language = LANGUAGES.get(preference)
|
||||||
return language[text_id]
|
return language[text_id]
|
||||||
|
|
||||||
|
|
||||||
@click.command("add-string")
|
|
||||||
@click.argument("string")
|
|
||||||
def add_string(string):
|
|
||||||
"""Add a new string to the translations."""
|
|
||||||
for path in Path("kanken_online/static/lang").glob("*.json"):
|
|
||||||
with open(path) as f:
|
|
||||||
data = json.load(f)
|
|
||||||
data[string] = ""
|
|
||||||
with open(path, mode="w") as f:
|
|
||||||
json.dump(data, f, ensure_ascii=False, indent=0)
|
|
||||||
|
|
||||||
@click.command("set-string")
|
|
||||||
@click.argument("lang")
|
|
||||||
@click.argument("string")
|
|
||||||
@click.argument("value")
|
|
||||||
def set_string(lang, string, value):
|
|
||||||
"""Set the value of a string for a particular language's translation file."""
|
|
||||||
path = Path("kanken_online/static/lang", f"{lang}.json")
|
|
||||||
with open(path) as f:
|
|
||||||
data = json.load(f)
|
|
||||||
data[string] = value
|
|
||||||
with open(path, mode="w") as f:
|
|
||||||
json.dump(data, f, ensure_ascii=False, indent=0)
|
|
||||||
|
|
||||||
@click.command("rename-string")
|
|
||||||
@click.argument("string")
|
|
||||||
@click.argument("new_name")
|
|
||||||
def rename_string(string, new_name):
|
|
||||||
"""Rename a string in the translations files."""
|
|
||||||
for path in Path("kanken_online/static/lang").glob("*.json"):
|
|
||||||
with open(path) as f:
|
|
||||||
data = json.load(f)
|
|
||||||
data[new_name] = data[string]
|
|
||||||
del data[string]
|
|
||||||
with open(path, mode="w") as f:
|
|
||||||
json.dump(data, f, ensure_ascii=False, indent=0)
|
|
||||||
|
|
||||||
def add_translation_commands(app: Flask):
|
|
||||||
app.cli.add_command(add_string)
|
|
||||||
app.cli.add_command(set_string)
|
|
||||||
app.cli.add_command(rename_string)
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
DROP TABLE IF EXISTS user;
|
DROP TABLE IF EXISTS user;
|
||||||
DROP TABLE IF EXISTS post;
|
-- DROP TABLE IF EXISTS post;
|
||||||
DROP TABLE IF EXISTS user_settings;
|
|
||||||
|
|
||||||
CREATE TABLE user (
|
CREATE TABLE user (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@ -16,10 +15,3 @@ CREATE TABLE post (
|
|||||||
body TEXT NOT NULL,
|
body TEXT NOT NULL,
|
||||||
FOREIGN KEY (author_id) REFERENCES user (id)
|
FOREIGN KEY (author_id) REFERENCES user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE user_settings (
|
|
||||||
user_id INTEGER PRIMARY KEY AUTOINCREMENT REFERENCES user (id),
|
|
||||||
lang VARCHAR(3) NOT NULL, -- e.g. en/jp; may add support for other (possibly 3-long) codes later
|
|
||||||
theme VARCHAR(5) NOT NULL, -- "light" or "dark"
|
|
||||||
pfp_filename CHAR(36) -- 36-char UUID string
|
|
||||||
);
|
|
@ -1,10 +0,0 @@
|
|||||||
const kanjiTSVButton = document.getElementById("kanji-tsv-download-button");
|
|
||||||
const kotobaTSVButton = document.getElementById("kotoba-tsv-download-button");
|
|
||||||
const databaseButton = document.getElementById("database-download-button");
|
|
||||||
|
|
||||||
for (const button of [kanjiTSVButton, kotobaTSVButton, databaseButton]) {
|
|
||||||
button.addEventListener("click", () => {
|
|
||||||
const fileName = button.getAttribute("data-filename");
|
|
||||||
location.assign(`/static/download/${fileName}`);
|
|
||||||
});
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,44 +0,0 @@
|
|||||||
ul#reading-list-list {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#goon::before {
|
|
||||||
content: "呉";
|
|
||||||
}
|
|
||||||
|
|
||||||
#kanon::before {
|
|
||||||
content: "漢";
|
|
||||||
}
|
|
||||||
|
|
||||||
#kanyoon::before {
|
|
||||||
content: "慣";
|
|
||||||
}
|
|
||||||
|
|
||||||
#soon::before {
|
|
||||||
content: "宋";
|
|
||||||
}
|
|
||||||
|
|
||||||
#toon::before {
|
|
||||||
content: "唐";
|
|
||||||
}
|
|
||||||
|
|
||||||
#on::before {
|
|
||||||
content: "音";
|
|
||||||
}
|
|
||||||
|
|
||||||
#kun::before {
|
|
||||||
content: "訓";
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.reading-list {
|
|
||||||
list-style-type: none;
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.reading-list > li::after {
|
|
||||||
content: " ・";
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.reading-list > li:last-child::after {
|
|
||||||
content: none;
|
|
||||||
}
|
|
@ -18,6 +18,7 @@
|
|||||||
"incorrect_username": "Incorrect username.",
|
"incorrect_username": "Incorrect username.",
|
||||||
"incorrect_password": "Incorrect password.",
|
"incorrect_password": "Incorrect password.",
|
||||||
"about": "About KankenOnline",
|
"about": "About KankenOnline",
|
||||||
|
"about-para": "KankenOnline is a website seeking to provide resources to pass the Kanji Kentei level 1 exam. You can search through the approximately 6,300 characters included in Kanken, as well as generate a number of study materials automatically. For example, you can generate a PDF-format exam resembling the Kanken level 1 exam, which has the goal of mirroring the real thing as closely as possible for your preparation. Additionally, a variety of information about kanji is provided, among which phonetic series, rimes/kanji phonology, radicals, inseparable kanji, character origins and etymologies.",
|
||||||
"indices": "Indices",
|
"indices": "Indices",
|
||||||
"radical_index": "Radical index",
|
"radical_index": "Radical index",
|
||||||
"indivisible_index": "Indivisible kanji index",
|
"indivisible_index": "Indivisible kanji index",
|
||||||
@ -30,18 +31,5 @@
|
|||||||
"en": "English",
|
"en": "English",
|
||||||
"ja": "Japanese",
|
"ja": "Japanese",
|
||||||
"add_string": "Add string",
|
"add_string": "Add string",
|
||||||
"submit": "Submit",
|
"submit": "Submit"
|
||||||
"about_para": "KankenOnline is a website seeking to provide resources to pass the Kanji Kentei level 1 exam. You can search through the approximately 6,300 characters included in Kanken, as well as generate a number of study materials automatically. For example, you can generate a PDF-format exam resembling the Kanken level 1 exam, which has the goal of mirroring the real thing as closely as possible for your preparation. Additionally, a variety of information about kanji is provided, among which phonetic series, rimes/kanji phonology, radicals, inseparable kanji, character origins and etymologies.",
|
|
||||||
"data": "Data",
|
|
||||||
"kanji_tsv_download": "Download kanji TSV",
|
|
||||||
"kotoba_tsv_download": "Download kotoba TSV",
|
|
||||||
"ankipkg_download": "Download Anki package",
|
|
||||||
"database_download": "Download database",
|
|
||||||
"new_post": "New post",
|
|
||||||
"submit_post": "Submit post",
|
|
||||||
"title": "Title",
|
|
||||||
"post_body": "Post body",
|
|
||||||
"success_new_post": "Successfully created new post",
|
|
||||||
"user_introduction": "User",
|
|
||||||
"pfp": "Profile picture"
|
|
||||||
}
|
}
|
@ -18,6 +18,7 @@
|
|||||||
"incorrect_username": "ユーザー名が違います",
|
"incorrect_username": "ユーザー名が違います",
|
||||||
"incorrect_password": "パスワードが違います",
|
"incorrect_password": "パスワードが違います",
|
||||||
"about": "漢検オンラインとは",
|
"about": "漢検オンラインとは",
|
||||||
|
"about-para": "漢検オンラインとは、漢検一級合格を目当てにした資料を供用しているサイトです。ここで漢検のやく6300字を検索でき、いろんな勉強材を自動的に作ることができます。たとえば、漢検一級模様の試験PDFを作ることができて、本物の漢検試験に大抵該当することが目的です。さらに、色々漢字についての情報を取り集めております。諧声域(かいせいいき)・音韻学・部首・不可分漢字・字源・語源などがその内です。",
|
||||||
"indices": "索引",
|
"indices": "索引",
|
||||||
"radical_index": "部首索引",
|
"radical_index": "部首索引",
|
||||||
"indivisible_index": "不可分漢字索引",
|
"indivisible_index": "不可分漢字索引",
|
||||||
@ -30,18 +31,5 @@
|
|||||||
"en": "英語",
|
"en": "英語",
|
||||||
"ja": "日本語",
|
"ja": "日本語",
|
||||||
"add_string": "翻訳語を追加",
|
"add_string": "翻訳語を追加",
|
||||||
"submit": "投入",
|
"submit": "投入"
|
||||||
"about_para": "漢検オンラインとは、漢検一級合格を目当てにした資料を供用しているサイトです。ここで漢検のやく6300字を検索でき、いろんな勉強材を自動的に作ることができます。たとえば、漢検一級模様の試験PDFを作ることができて、本物の漢検試験に大抵該当することが目的です。さらに、色々漢字についての情報を取り集めております。諧声域(かいせいいき)・音韻学・部首・不可分漢字・字源・語源などがその内です。",
|
|
||||||
"data": "データ",
|
|
||||||
"kanji_tsv_download": "漢字TSVをダウンロード",
|
|
||||||
"kotoba_tsv_download": "言葉TSVをダウンロード",
|
|
||||||
"ankipkg_download": "APKGをダウンロード",
|
|
||||||
"database_download": "データーベースをダウンロード",
|
|
||||||
"new_post": "新しい投稿",
|
|
||||||
"submit_post": "投稿する",
|
|
||||||
"title": "話題",
|
|
||||||
"post_body": "投稿内容",
|
|
||||||
"success_new_post": "無事に新しい投稿を作りました",
|
|
||||||
"user_introduction": "利用者の",
|
|
||||||
"pfp": "利用者アイコン"
|
|
||||||
}
|
}
|
@ -5,5 +5,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ localize("about_para") }}
|
{{ localize("about-para") }}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,9 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{session['language'] or 'ja'}}">
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<title>{% block title %}{% endblock %} - {{ localize("kanken_online") }}</title>
|
||||||
<title>{% block title %}{% endblock %} ・ {{ localize("kanken_online") }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
{% if session %}
|
{% if session %}
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='dark_theme.css') if session['theme'] == 'dark' else url_for('static', filename='light_theme.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='dark_theme.css') if session['theme'] == 'dark' else url_for('static', filename='light_theme.css')}}">
|
||||||
@ -18,18 +17,16 @@
|
|||||||
<h1><a href="/">{{ localize("kanken_online") }}</a></h1>
|
<h1><a href="/">{{ localize("kanken_online") }}</a></h1>
|
||||||
<ul>
|
<ul>
|
||||||
{% if g.user %}
|
{% if g.user %}
|
||||||
<li><a href="{{ url_for('my_page') }}">{{ g.user['username'] }}</a>
|
<li><span>{{ g.user['username'] }}</span>
|
||||||
|
<li><a href="{{ url_for('options') }}">{{ localize("options") }}</a>
|
||||||
<li><a href="{{ url_for('auth.logout') }}">{{ localize("log_out") }}</a>
|
<li><a href="{{ url_for('auth.logout') }}">{{ localize("log_out") }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('auth.register') }}">{{ localize("register") }}</a>
|
<li><a href="{{ url_for('auth.register') }}">{{ localize("register") }}</a>
|
||||||
<li><a href="{{ url_for('auth.login') }}">{{ localize("log_in") }}</a>
|
<li><a href="{{ url_for('auth.login') }}">{{ localize("log_in") }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="{{ url_for('options') }}">{{ localize("options") }}</a>
|
|
||||||
<li><a href="{{ url_for('about_page') }}">{{ localize("about") }}</a></li>
|
<li><a href="{{ url_for('about_page') }}">{{ localize("about") }}</a></li>
|
||||||
<li><a href="{{ url_for('indices.indices_page') }}">{{ localize("indices") }}</a></li>
|
<li><a href="{{ url_for('indices.indices_page') }}">{{ localize("indices") }}</a></li>
|
||||||
<li><a href="{{ url_for('search.search_page') }}">{{ localize("search") }}</a></li>
|
<li><a href="{{ url_for('search.search_page') }}">{{ localize("search") }}</a></li>
|
||||||
<li><a href="{{ url_for('forum.index') }}">{{ localize("forum") }}</a></li>
|
|
||||||
<li><a href="{{ url_for('data_page') }}">{{ localize("data") }}</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script src="/static/data.js" defer></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
<h1>{% block title %}{{ localize("data") }}{% endblock %}</h1>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<button id="kanji-tsv-download-button" data-filename="kanji.tsv"> {{ localize("kanji_tsv_download") }}</button>
|
|
||||||
<button id="kotoba-tsv-download-button" data-filename="kotoba.tsv"> {{ localize("kotoba_tsv_download") }}</button>
|
|
||||||
<button id="ankipkg-download-button"> {{ localize("ankipkg_download") }}</button>
|
|
||||||
<button id="database-download-button" data-filename="kanken.db"> {{ localize("database_download") }}</button>
|
|
||||||
{% endblock %}
|
|
@ -5,19 +5,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="{{ url_for('forum.new_post') }}">
|
...
|
||||||
<input id="new-post-button" type="submit" value="{{ localize('new_post') }}">
|
|
||||||
</form>
|
|
||||||
<hr>
|
|
||||||
<div id="forum-container">
|
|
||||||
{% for post in posts %}
|
|
||||||
<div class="post">
|
|
||||||
<h2 class="post-title">{{ post.title }}</h2>
|
|
||||||
<span class="post-author">{{ post.username }}</span>
|
|
||||||
<p>{{ post.body }} </p>
|
|
||||||
<br>
|
|
||||||
<span class="post-timestamp">{{ post.created }}</span>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,15 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
<h1>{% block title %}{{ localize("new_post") }}{% endblock %}</h1>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div id="new-post-area">
|
|
||||||
<form id="post" method="post">
|
|
||||||
<input type="text" name="title" id="post-title" placeholder="{{localize('title')}}" required>
|
|
||||||
<textarea id="post-body" name="body" placeholder="{{localize('post_body')}}" required></textarea>
|
|
||||||
<button type="submit"> {{ localize("submit_post") }}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -2,45 +2,41 @@
|
|||||||
|
|
||||||
{% block scripts %}<script src="/static/kanji.js" defer></script>{%endblock %}
|
{% block scripts %}<script src="/static/kanji.js" defer></script>{%endblock %}
|
||||||
|
|
||||||
{% block styles %}
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='kanji.css') }}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<h1>{% block title %}{{localize("kanji")}} - {{ kanji.character }}{% endblock %}</h1>
|
<h1>{% block title %}{{localize("kanji")}} - {{ kanji.character }}{% endblock %}</h1>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="entry" lang="ja">
|
<div id="kanji-and-meaning">
|
||||||
<div id="kanji-and-meaning">
|
<div id="kanji-box">
|
||||||
<div id="kanji-box">
|
<div id="character-box">
|
||||||
<div id="character-box">
|
<span id="kanji">{{ kanji.character }}</span>
|
||||||
<span id="kanji">{{ kanji.character }}</span>
|
|
||||||
</div>
|
|
||||||
<div id="metadata-box">
|
|
||||||
<span id="joyo-class">{{ kanji.is_joyo }}</span>
|
|
||||||
<span id="level">{{ kanji.level }}級</span>
|
|
||||||
<span id="stroke-count">{{ kanji.strokes }}</span> (<span id="radical">{{ kanji.radical }}</span> + <span id="radical-added-strokes">{{ kanji.added_strokes }}</span>)
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="meaning-box">
|
<div id="metadata-box">
|
||||||
<p id="meanings">{{ kanji.meanings }}</p>
|
<span id="joyo-class">{{ kanji.is_joyo }}</span>
|
||||||
|
<span id="level">{{ kanji.level }}</span>
|
||||||
|
<span id="stroke-count">{{ kanji.strokes }}</span>
|
||||||
|
<span id="radical">{{ kanji.radical }}</span>
|
||||||
|
<span id="radical-added-strokes">{{ kanji.added_strokes }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="readings-and-glyph-origin">
|
<div id="meaning-box">
|
||||||
<div id="reading-box">
|
<p id="meanings">{{ kanji.meanings }}</p>
|
||||||
<ul id="reading-list-list">
|
|
||||||
{% if kanji.goon %}<li id="goon"><ul class="reading-list">{% for reading in kanji.goon %}<li class="goon-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
{% if kanji.kanon %}<li id="kanon"><ul class="reading-list">{% for reading in kanji.kanon %}<li class="kanon-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
{% if kanji.kanyoon %}<li id="kanyoon"><ul class="reading-list">{% for reading in kanji.kanyoon %}<li class="kanyoon-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
{% if kanji.toon %}<li id="toon"><ul class="reading-list">{% for reading in kanji.toon %}<li class="toon-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
{% if kanji.soon %}<li id="soon"><ul class="reading-list">{% for reading in kanji.soon %}<li class="soon-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
{% if kanji.kun %}<li id="kun"><ul class="reading-list">{% for reading in kanji.kun %}<li class="kun-reading">{{reading|safe}}</li>{% endfor %}</ul></li>{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="glyph-origin-box">
|
|
||||||
<p id="glyph-origin">{{ kanji.glyph_origin }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="readings-and-glyph-origin">
|
||||||
|
<div id="reading-box">
|
||||||
|
<ul id="reading-list">
|
||||||
|
<li id="goon">{{ kanji.goon }}</li>
|
||||||
|
<li id="kanon">{{ kanji.kanon }}</li>
|
||||||
|
<li id="kanyoon">{{ kanji.kanyoon }}</li>
|
||||||
|
<li id="toon">{{ kanji.toon }}</li>
|
||||||
|
<li id="soon">{{ kanji.soon }}</li>
|
||||||
|
<li id="kun">{{ kanji.kun }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="glyph-origin-box">
|
||||||
|
<p id="glyph-origin">{{ kanji.glyph_origin }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -5,7 +5,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form method="post" enctype=multipart/form-data>
|
<form method="post">
|
||||||
<label for="language-select">{{ localize("language") }}</label>
|
<label for="language-select">{{ localize("language") }}</label>
|
||||||
<select name="language" id="language-select">
|
<select name="language" id="language-select">
|
||||||
<option value="ja">日本語</option>
|
<option value="ja">日本語</option>
|
||||||
@ -17,10 +17,6 @@
|
|||||||
<option value="dark">{{ localize("dark_theme") }}</option>
|
<option value="dark">{{ localize("dark_theme") }}</option>
|
||||||
<option value="light">{{ localize("light_theme") }}</option>
|
<option value="light">{{ localize("light_theme") }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="pfp">{{ localize("pfp") }}</label>
|
|
||||||
<input type="file" id="pfp" name="pfp">
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<button type="submit">Okay</button>
|
<button type="submit">Okay</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -7,40 +7,12 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<form method="get">
|
<form method="get">
|
||||||
<label for="search_bar">{{ localize("search") }}</label>
|
<label for="search_bar">{{ localize("search") }}</label>
|
||||||
<input type="text" id="search_bar" name="keywords" placeholder="{{ localize('search_placeholder') }}" value="{{ value }}" autofocus onfocus="this.select()">
|
<input type="text" id="search_bar" name="keywords" placeholder="{{ localize("search_placeholder") }}" value="{{value}}">
|
||||||
|
<label for="include_kanji">{{ localize("include_kanji") }}</label>
|
||||||
<fieldset>
|
<input type="checkbox" id="include_kanji" checked>
|
||||||
<label for="search_method_includes">Includes</label>
|
<label for="include_kanji">{{ localize("include_kotoba") }}</label>
|
||||||
<input type="radio" id="search_method_includes" name="search_method" value="includes">
|
<input type="checkbox" id="include_kanji" checked>
|
||||||
<label for="search_method_includes">Matches</label>
|
</form>
|
||||||
<input type="radio" id="search_method_matches" name="search_method" value="matches">
|
|
||||||
<label for="search_method_includes">Starts with</label>
|
|
||||||
<input type="radio" id="search_method_starts_with" name="search_method" value="starts_with">
|
|
||||||
<label for="search_method_includes">Ends with</label>
|
|
||||||
<input type="radio" id="search_method_ends_with" name="search_method" value="ends_with">
|
|
||||||
<label for="search_method_regex">Regular expression</label>
|
|
||||||
<input type="radio" id="search_method_regex" name="search_method" value="regex">
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<input type="text" id="onkun" name="onkun" placeholder="midashi onkun">
|
|
||||||
<input type="text" id="kanji_goon" name="goon" placeholder="goon">
|
|
||||||
<input type="text" id="kanji_kanon" name="kanon" placeholder="kanon">
|
|
||||||
<input type="text" id="kanji_soon" name="soon" placeholder="soon">
|
|
||||||
<input type="text" id="kanji_toon" name="toon" placeholder="toon">
|
|
||||||
<input type="text" id="kanji_kanyoon" name="kanyoon" placeholder="kanyoon">
|
|
||||||
<input type="text" id="kanji_kun" name="kun" placeholder="kun">
|
|
||||||
<input type="text" id="radical" name="radical" placeholder="radical">
|
|
||||||
<input type="text" id="stroke_min" name="stroke_min" placeholder="min stroke count">
|
|
||||||
<input type="text" id="stroke_max" name="stroke_max" placeholder="max stroke count">
|
|
||||||
<input type="text" id="level_min" name="level_min" placeholder="min level">
|
|
||||||
<input type="text" id="level_max" name="level_max" placeholder="max level">
|
|
||||||
|
|
||||||
<label for="kokuji">kokuji</label>
|
|
||||||
<select name="kokuji" id="kokuji">
|
|
||||||
<option value="include">include</option>
|
|
||||||
<option value="exclude">exclude</option>
|
|
||||||
</select>
|
|
||||||
</form>
|
|
||||||
{% block results %}
|
{% block results %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,10 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
<h1>{% block title %}{{ localize("user_introduction") }} {{ username }}{% endblock %}</h1>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<img id="pfp" src="{{ pfp_filename }}" width=128 height=128>
|
|
||||||
<span id="username">{{ username }}</span>
|
|
||||||
{% endblock %}
|
|
Loading…
Reference in New Issue
Block a user