KankenOnline/kanken_online/api.py
2024-10-21 11:14:25 +01:00

156 lines
7.2 KiB
Python

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, get_jiten_db
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
def render(self):
return self.character
@blueprint.route("/id/<int:kanji_id>")
def kanji_by_id(kanji_id: int):
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):
kanji_obj = get_kanji_by_character(kanji)
return kanji_obj.to_json()
def get_kanji_generic():
pass