Files
esl-reader/lib/words.py
2024-04-12 11:18:39 -04:00

114 lines
3.4 KiB
Python

import importlib
import pkgutil
import json
from typing import Any, TypedDict, cast
from PyQt6.QtCore import (
Qt,
pyqtSlot,
)
from PyQt6.QtSql import QSqlQuery
from PyQt6.QtWidgets import QScrollArea
from lib.utils import query_error
from lib.sounds import SoundOff
from lib.definition import Definition, Line
import plugins
def find_plugins(ns_pkg):
return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + '.')
discovered_plugins = {
# finder, name, ispkg
importlib.import_module(name).registration['source']: importlib.import_module(name) for _, name, _ in find_plugins(plugins)
}
API = "https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
class WordType(TypedDict):
word: str
source: str
definition: str
class Word:
"""All processing of a dictionary word."""
_words: dict[str, WordType] = {}
def __init__(self, word: str) -> None:
#
# Have we already retrieved this word?
#
try:
self.current = Word._words[word]
return
except KeyError:
pass
query = QSqlQuery()
query.prepare("SELECT * FROM words " "WHERE word = :word")
query.bindValue(":word", word)
if not query.exec():
query_error(query)
if query.next():
Word._words[word] = {
"word": word,
"source": query.value("source"),
"definition": json.loads(query.value("definition")),
}
self.current = Word._words[word]
return
#
# The code should look at our settings to see if we have an API
# key for MW to decide on the source to use.
#
source = "mw"
self._words[word] = discovered_plugins[source].fetch(word)
self.current = Word._words[word]
query.prepare(
"INSERT INTO words "
"(word, source, definition) "
"VALUES (:word, :source, :definition)"
)
query.bindValue(":word", self.current["word"])
query.bindValue(":source", self.current["source"])
query.bindValue(":definition", json.dumps(self.current["definition"]))
if not query.exec():
query_error(query)
return
@pyqtSlot()
def playSound(self) -> None:
url = discovered_plugins[self.current['source']].getFirstSound(self.current['definition'])
if url.isValid():
snd = SoundOff()
snd.playSound(url)
return
def getWord(self) -> str:
return cast(str, self.current["word"])
def get_html(self) -> str | None:
src = self.current['source']
try:
return discovered_plugins[src].getHtml(self.current)
except KeyError:
raise Exception(f"Unknown source: {src}")
def get_def(self) -> list[Line]:
src = self.current['source']
try:
lines = discovered_plugins[src].getDef(self.current["definition"])
return lines
except KeyError:
raise Exception(f"Unknown source: {self.current['source']}")
class DefinitionArea(QScrollArea):
def __init__(self, w: Word, *args: Any, **kwargs: Any) -> None:
super(DefinitionArea, self).__init__(*args, *kwargs)
d = Definition(w)
self.setWidget(d)
self.setWidgetResizable(True)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
return