Compare commits
29 Commits
9b932291d2
...
6c1e0887b6
Author | SHA1 | Date | |
---|---|---|---|
|
6c1e0887b6 | ||
|
477c61c4b6 | ||
|
197a5ab242 | ||
|
5d2cf0b280 | ||
|
6a6a210d90 | ||
|
003e1ae635 | ||
|
8148976913 | ||
|
9eeb8bd873 | ||
|
826d240489 | ||
|
242fcfb823 | ||
|
3430bd2392 | ||
|
c66c097101 | ||
|
49bc51d1e0 | ||
|
ae7501bb2e | ||
|
e6be4107f5 | ||
|
215e2618f7 | ||
|
1c777aa253 | ||
|
ddf1d1933c | ||
|
5e8d64544b | ||
|
5e4f73d057 | ||
|
2f8ab9ac96 | ||
|
1ffbc1b6f0 | ||
|
ed1f183a9c | ||
|
9d3ce85cb4 | ||
|
2ac903553c | ||
|
11689fb627 | ||
|
2348d7c425 | ||
|
49d592bfca | ||
|
cbb334c7ea |
@ -1,16 +1,24 @@
|
||||
import json
|
||||
import os
|
||||
from flask import Flask, redirect, render_template, g, request, session, url_for
|
||||
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)
|
||||
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:
|
||||
@ -33,43 +41,92 @@ def create_app(test_config=None):
|
||||
def about_page():
|
||||
return render_template("about.html")
|
||||
|
||||
def update_settings(form):
|
||||
def update_settings(form, files):
|
||||
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["theme"] = form["theme"]
|
||||
|
||||
return redirect("/options")
|
||||
|
||||
@app.route("/options", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def options():
|
||||
if request.method == "GET":
|
||||
return render_template("options.html")
|
||||
else:
|
||||
return update_settings(request.form)
|
||||
return update_settings(request.form, request.files)
|
||||
|
||||
@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>")
|
||||
def kanji_page(kanji: str):
|
||||
# TODO use database to get kanji
|
||||
kanji_obj = get_kanji_by_character(kanji)
|
||||
|
||||
class Kanji():
|
||||
pass
|
||||
|
||||
# Highly tentative testing data
|
||||
kanji = Kanji()
|
||||
kanji.character = "麻"
|
||||
kanji.is_joyo = "joyo kanji"
|
||||
kanji.level = "pre-2"
|
||||
kanji.strokes = 11
|
||||
kanji.radical = "麻"
|
||||
kanji.added_strokes = 0
|
||||
kanji.goon = "マ"
|
||||
kanji.kanon = "バ"
|
||||
kanji.toon = ""
|
||||
kanji.soon = ""
|
||||
kanji.kanyoon = ""
|
||||
kanji.kun = "あさ, しびれる"
|
||||
kanji.meanings = "①あさ。クワ科の一年草。また、あさ類の総称。「亜麻」「乱麻」 ②しびれる。しびれ。「麻酔」「麻痺(マヒ)」類痲(マ)"
|
||||
kanji.glyph_origin = "会意。广(げん)(いえ)と、𣏟(はい)(あさ)とから成り、屋下であさの繊維をはぎとる、ひいて「あさ」の意を表す。"
|
||||
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=kanji)
|
||||
|
||||
return render_template("kanji.html", kanji=out)
|
||||
|
||||
@app.route("/kotoba/<kotoba>")
|
||||
def kotoba_page(kotoba: str):
|
||||
@ -86,6 +143,7 @@ def create_app(test_config=None):
|
||||
app.register_blueprint(indices.blueprint)
|
||||
|
||||
from . import lang
|
||||
lang.add_translation_commands(app)
|
||||
|
||||
@app.route("/translations", methods=["GET", "POST"])
|
||||
def strings_translation():
|
||||
@ -109,6 +167,14 @@ def create_app(test_config=None):
|
||||
lang.update_languages()
|
||||
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):
|
||||
# return lang.localize(text_id, lang.JAPANESE)
|
||||
app.jinja_env.globals.update(localize=lang.localize)
|
||||
|
@ -4,7 +4,7 @@ from flask import Blueprint, jsonify
|
||||
import jsonpickle
|
||||
from sqlalchemy import create_engine, select
|
||||
from sqlalchemy.orm import Session
|
||||
from .database import get_database
|
||||
from .database import get_database, get_jiten_db
|
||||
|
||||
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
||||
|
||||
@ -125,24 +125,29 @@ class Kanji(Base):
|
||||
|
||||
@blueprint.route("/id/<int:kanji_id>")
|
||||
def kanji_by_id(kanji_id: int):
|
||||
engine = create_engine("sqlite:///kanken_online/kanken.db")
|
||||
with Session(engine) as session:
|
||||
query = select(Kanji).where(Kanji.id == kanji_id)
|
||||
item = session.execute(query).first()
|
||||
if item is None:
|
||||
return "Invalid ID", 404
|
||||
|
||||
kanji = item[0]
|
||||
return kanji.to_json()
|
||||
session = get_jiten_db()
|
||||
query = select(Kanji).where(Kanji.id == kanji_id)
|
||||
item = session.execute(query).first()
|
||||
if item is None:
|
||||
return "Invalid ID", 404
|
||||
|
||||
kanji = item[0]
|
||||
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>")
|
||||
def kanji_by_character(kanji: str):
|
||||
engine = create_engine("sqlite:///kanken_online/kanken.db")
|
||||
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
|
||||
|
||||
kanji_obj = item[0]
|
||||
return kanji_obj.to_json()
|
||||
kanji_obj = get_kanji_by_character(kanji)
|
||||
return kanji_obj.to_json()
|
||||
|
||||
def get_kanji_generic():
|
||||
pass
|
@ -53,6 +53,14 @@ def login():
|
||||
if error is None:
|
||||
session.clear()
|
||||
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"))
|
||||
|
||||
flash(error)
|
||||
|
@ -2,6 +2,9 @@ import sqlite3
|
||||
from typing import IO
|
||||
import click
|
||||
from flask import Flask, current_app, g
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
def get_database():
|
||||
if "db" not in g:
|
||||
@ -13,6 +16,14 @@ def get_database():
|
||||
|
||||
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():
|
||||
db = get_database()
|
||||
|
||||
@ -35,6 +46,18 @@ def init_db_command():
|
||||
else:
|
||||
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):
|
||||
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,8 +1,37 @@
|
||||
from flask import Blueprint, render_template
|
||||
import datetime
|
||||
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.route("/")
|
||||
def index():
|
||||
return render_template("forum/index.html")
|
||||
# Get forum posts
|
||||
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"))
|
12
kanken_online/graphql/schema.graphql
Normal file
12
kanken_online/graphql/schema.graphql
Normal file
@ -0,0 +1,12 @@
|
||||
schema {
|
||||
kanji: Kanji,
|
||||
kotoba: Kotoba
|
||||
}
|
||||
|
||||
type Kanji {
|
||||
|
||||
}
|
||||
|
||||
type Kotoba {
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from flask import session
|
||||
import click
|
||||
from flask import Flask, session, current_app
|
||||
|
||||
LanguageMapping = dict[str, str]
|
||||
|
||||
@ -32,3 +34,46 @@ def localize(text_id: str, language: dict[str, str] = None) -> str:
|
||||
preference = session.get("language", "ja")
|
||||
language = LANGUAGES.get(preference)
|
||||
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,5 +1,6 @@
|
||||
DROP TABLE IF EXISTS user;
|
||||
-- DROP TABLE IF EXISTS post;
|
||||
DROP TABLE IF EXISTS post;
|
||||
DROP TABLE IF EXISTS user_settings;
|
||||
|
||||
CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@ -15,3 +16,10 @@ CREATE TABLE post (
|
||||
body TEXT NOT NULL,
|
||||
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
|
||||
);
|
10
kanken_online/static/data.js
Normal file
10
kanken_online/static/data.js
Normal file
@ -0,0 +1,10 @@
|
||||
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}`);
|
||||
});
|
||||
}
|
6232
kanken_online/static/download/kanji.tsv
Normal file
6232
kanken_online/static/download/kanji.tsv
Normal file
File diff suppressed because it is too large
Load Diff
41330
kanken_online/static/download/kotoba.tsv
Normal file
41330
kanken_online/static/download/kotoba.tsv
Normal file
File diff suppressed because it is too large
Load Diff
44
kanken_online/static/kanji.css
Normal file
44
kanken_online/static/kanji.css
Normal file
@ -0,0 +1,44 @@
|
||||
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,7 +18,6 @@
|
||||
"incorrect_username": "Incorrect username.",
|
||||
"incorrect_password": "Incorrect password.",
|
||||
"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",
|
||||
"radical_index": "Radical index",
|
||||
"indivisible_index": "Indivisible kanji index",
|
||||
@ -31,5 +30,18 @@
|
||||
"en": "English",
|
||||
"ja": "Japanese",
|
||||
"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,7 +18,6 @@
|
||||
"incorrect_username": "ユーザー名が違います",
|
||||
"incorrect_password": "パスワードが違います",
|
||||
"about": "漢検オンラインとは",
|
||||
"about-para": "漢検オンラインとは、漢検一級合格を目当てにした資料を供用しているサイトです。ここで漢検のやく6300字を検索でき、いろんな勉強材を自動的に作ることができます。たとえば、漢検一級模様の試験PDFを作ることができて、本物の漢検試験に大抵該当することが目的です。さらに、色々漢字についての情報を取り集めております。諧声域(かいせいいき)・音韻学・部首・不可分漢字・字源・語源などがその内です。",
|
||||
"indices": "索引",
|
||||
"radical_index": "部首索引",
|
||||
"indivisible_index": "不可分漢字索引",
|
||||
@ -31,5 +30,18 @@
|
||||
"en": "英語",
|
||||
"ja": "日本語",
|
||||
"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 %}
|
||||
|
||||
{% block content %}
|
||||
{{ localize("about-para") }}
|
||||
{{ localize("about_para") }}
|
||||
{% endblock %}
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="{{session['language'] or 'ja'}}">
|
||||
|
||||
<head>
|
||||
<title>{% block title %}{% endblock %} - {{ localize("kanken_online") }}</title>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}{% endblock %} ・ {{ localize("kanken_online") }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
{% 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')}}">
|
||||
@ -17,16 +18,18 @@
|
||||
<h1><a href="/">{{ localize("kanken_online") }}</a></h1>
|
||||
<ul>
|
||||
{% if g.user %}
|
||||
<li><span>{{ g.user['username'] }}</span>
|
||||
<li><a href="{{ url_for('options') }}">{{ localize("options") }}</a>
|
||||
<li><a href="{{ url_for('my_page') }}">{{ g.user['username'] }}</a>
|
||||
<li><a href="{{ url_for('auth.logout') }}">{{ localize("log_out") }}</a>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('auth.register') }}">{{ localize("register") }}</a>
|
||||
<li><a href="{{ url_for('auth.login') }}">{{ localize("log_in") }}</a>
|
||||
{% 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('indices.indices_page') }}">{{ localize("indices") }}</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>
|
||||
</nav>
|
||||
<section class="content">
|
||||
|
16
kanken_online/templates/data.html
Normal file
16
kanken_online/templates/data.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% 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,5 +5,19 @@
|
||||
{% endblock %}
|
||||
|
||||
{% 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 %}
|
15
kanken_online/templates/forum/new.html
Normal file
15
kanken_online/templates/forum/new.html
Normal file
@ -0,0 +1,15 @@
|
||||
{% 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,41 +2,45 @@
|
||||
|
||||
{% 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 %}
|
||||
<h1>{% block title %}{{localize("kanji")}} - {{ kanji.character }}{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="kanji-and-meaning">
|
||||
<div id="kanji-box">
|
||||
<div id="character-box">
|
||||
<span id="kanji">{{ kanji.character }}</span>
|
||||
<div id="entry" lang="ja">
|
||||
<div id="kanji-and-meaning">
|
||||
<div id="kanji-box">
|
||||
<div id="character-box">
|
||||
<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 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 id="meaning-box">
|
||||
<p id="meanings">{{ kanji.meanings }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="meaning-box">
|
||||
<p id="meanings">{{ kanji.meanings }}</p>
|
||||
<div id="readings-and-glyph-origin">
|
||||
<div id="reading-box">
|
||||
<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>
|
||||
{% endblock %}
|
||||
</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 %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
<form method="post" enctype=multipart/form-data>
|
||||
<label for="language-select">{{ localize("language") }}</label>
|
||||
<select name="language" id="language-select">
|
||||
<option value="ja">日本語</option>
|
||||
@ -17,6 +17,10 @@
|
||||
<option value="dark">{{ localize("dark_theme") }}</option>
|
||||
<option value="light">{{ localize("light_theme") }}</option>
|
||||
</select>
|
||||
|
||||
<label for="pfp">{{ localize("pfp") }}</label>
|
||||
<input type="file" id="pfp" name="pfp">
|
||||
|
||||
<hr>
|
||||
<button type="submit">Okay</button>
|
||||
</form>
|
||||
|
@ -7,12 +7,40 @@
|
||||
{% block content %}
|
||||
<form method="get">
|
||||
<label for="search_bar">{{ localize("search") }}</label>
|
||||
<input type="text" id="search_bar" name="keywords" placeholder="{{ localize("search_placeholder") }}" value="{{value}}">
|
||||
<label for="include_kanji">{{ localize("include_kanji") }}</label>
|
||||
<input type="checkbox" id="include_kanji" checked>
|
||||
<label for="include_kanji">{{ localize("include_kotoba") }}</label>
|
||||
<input type="checkbox" id="include_kanji" checked>
|
||||
</form>
|
||||
<input type="text" id="search_bar" name="keywords" placeholder="{{ localize('search_placeholder') }}" value="{{ value }}" autofocus onfocus="this.select()">
|
||||
|
||||
<fieldset>
|
||||
<label for="search_method_includes">Includes</label>
|
||||
<input type="radio" id="search_method_includes" name="search_method" value="includes">
|
||||
<label for="search_method_includes">Matches</label>
|
||||
<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 %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
10
kanken_online/templates/user_page.html
Normal file
10
kanken_online/templates/user_page.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% 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