114 lines
3.4 KiB
Python
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
|