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