import functools import json from flask import Blueprint, jsonify import jsonpickle from sqlalchemy import create_engine, select from sqlalchemy.orm import Session from .database import get_database blueprint = Blueprint("api", __name__, url_prefix="/api") @blueprint.route("/") def logout(): # db = get_database() return { "endpoints": ["id", "kanji", "kotoba (not implemented)"] } import random import sqlalchemy from typing import List, Optional, Iterable from sqlalchemy import URL, ForeignKey, String, Boolean, Text, Integer from sqlalchemy.types import CHAR from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, relationship class Base(DeclarativeBase): pass # class Reading(Base): # __tablename__ = "reading" # id: Mapped[int] = mapped_column(primary_key=True) # reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall # kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) # kanji: Mapped["Kanji"] = relationship(back_populates="readings") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Goon(Base): __tablename__ = "goon" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="goon") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Kanon(Base): __tablename__ = "kanon" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="kanon") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Kanyoon(Base): __tablename__ = "kanyoon" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="kanyoon") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Soon(Base): __tablename__ = "soon" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="soon") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Toon(Base): __tablename__ = "toon" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="toon") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Kun(Base): __tablename__ = "kun" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="kun") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class UnclassifiedOn(Base): __tablename__ = "unclassified_on" id: Mapped[int] = mapped_column(primary_key=True) reading: Mapped[str] = mapped_column(String(length=10)) # Assume no reading can be over 10 characters long, a sound assumption overall kanji_id: Mapped[int] = mapped_column(ForeignKey("kanji.id")) kanji: Mapped["Kanji"] = relationship(back_populates="unclassified_on") # reading_type: Mapped[str] = mapped_column(CHAR(1), primary_key=True) # One of: 音漢呉慣唐宋訓 class Kanji(Base): __tablename__ = "kanji" id: Mapped[int] = mapped_column(primary_key=True) character: Mapped[str] = mapped_column(CHAR(length=1), unique=True) level: Mapped[str] = mapped_column(String(length=2)) # Either 1, 2, etc. or 準2 etc. is_kokuji: Mapped[bool] = mapped_column(Boolean()) meanings: Mapped[str] = mapped_column(Text()) # FIXME: make this a list # readings: Mapped[List[Reading]] = relationship(back_populates="kanji") goon: Mapped[List[Goon]] = relationship(back_populates="kanji") kanon: Mapped[List[Kanon]] = relationship(back_populates="kanji") kanyoon: Mapped[List[Kanyoon]] = relationship(back_populates="kanji") toon: Mapped[List[Toon]] = relationship(back_populates="kanji") soon: Mapped[List[Soon]] = relationship(back_populates="kanji") kun: Mapped[List[Kun]] = relationship(back_populates="kanji") unclassified_on: Mapped[List[UnclassifiedOn]] = relationship(back_populates="kanji") radical: Mapped[str] = mapped_column(CHAR(length=1)) # FIXME: normalize? stroke_count: Mapped[int] = mapped_column(Integer()) radical_added_stroke_count: Mapped[int] = mapped_column(Integer()) # FIXME: normalize? this may theoretically be calculated based on the radical stroke count, but I need to validate that this always works glyph_origin: Mapped[str] = mapped_column(Text()) # FIXME: make this a list of possible explanations, possibly, but unsure # diagram: Mapped[str] = ... could be calculated from the kanji name def to_json(self): out = {} for attr, value in self.__dict__.items(): if not isinstance(value, (bool, int, str)): continue else: out[attr] = value for attr in ("goon", "kanon", "kanyoon", "toon", "soon", "kun", "unclassified_on"): out[attr] = [reading_obj.reading for reading_obj in getattr(self, attr)] return out @blueprint.route("/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() @blueprint.route("/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()