diff --git a/plugins/merriam-webster.py b/plugins/merriam-webster.py index 6596dcb..36e7e81 100644 --- a/plugins/merriam-webster.py +++ b/plugins/merriam-webster.py @@ -1,4 +1,3 @@ -from importlib.abc import InspectLoader from PyQt6.QtGui import QColor, QFont from trycast import trycast import json @@ -13,6 +12,7 @@ from lib.definition import Line, Fragment registration = { 'source': 'mw', 'name': 'Merriam-Webster', + 'language': 'en-us', } API = "https://www.dictionaryapi.com/api/v3/references/collegiate/json/{word}?key={key}" @@ -67,7 +67,7 @@ class Variant(TypedDict): prs: NotRequired[list[Pronunciation]] spl: NotRequired[str] -Inflection =TypedDict('Inflection', { +Inflection = TypedDict('Inflection', { 'if': NotRequired[str], 'ifc': NotRequired[str], 'il': NotRequired[str], @@ -244,29 +244,29 @@ def getFirstSound(definition: Any) -> QUrl: return url return QUrl() -def do_prs(hwi: Any) -> list[Fragment]: +def do_prs(prs: list[Pronunciation] | None) -> list[Fragment]: + assert prs is not None r = Resources() frags: list[Fragment] = [] font = r.labelFont linkColor = r.linkColor subduedColor = r.subduedColor - if 'prs' not in hwi: - return [] - for pr in hwi['prs']: + for pr in prs: if 'pun' in pr: pun = pr['pun'] else: pun = ' ' if 'l' in pr: frags.append( - Fragment(pr['l'] + pun, font, color=subduedColor) + Fragment(pr['l'] + pun, r.italicFont, color=subduedColor) ) frag = Fragment(pr['mw'], font, color=subduedColor) if 'sound' in pr: frag.setAudio(soundUrl(pr['sound'])) frag.setColor(linkColor) frags.append(frag) + frags.append(Fragment(' ', r.phonicFont)) if 'l2' in pr: frags.append( Fragment(pun + pr['l2'], font, color=subduedColor) @@ -283,33 +283,32 @@ def do_vis(vis: list[VerbalIllustration]|None,indent=0) -> list[Line]: lines: list[Line] = [] for vi in vis: line = Line() - frag = Fragment(vi['t'], r.textFont, color=r.baseColor) + frag = Fragment(vi['t'], r.textFont, color=r.subduedColor) if indent > 0: frag.setIndent(indent) line.addFragment(frag) lines.append(line) if 'aq' in vi: lines += do_aq(trycast(AttributionOfQuote, vi['aq'])) - return [] + return lines -def do_uns(uns: list[list[list[Pair]]]|None, indent:int) -> list[Line]: +def do_uns(uns: list[list[list[Pair]]]|None, indent:int) -> tuple[list[Fragment], list[Line]]: assert uns is not None r = Resources() - lines:list[Line] = [] + frags: list[Fragment] = [] + lines: list[Line] = [] for note in uns: for entry in note: for pair in entry: if pair['objType'] == 'text': - frag = Fragment(' \u2014'+pair['obj'], r.textFont, color=r.baseColor) + frag = Fragment('\u2192 '+pair['obj'], r.textFont, color=r.baseColor) frag.setIndent(indent) - line = Line() - line.addFragment(frag) - lines.append(line) + frags.append(frag) elif pair['objType'] == 'vis': lines += do_vis(trycast(list[VerbalIllustration], pair['obj']), indent) elif pair['objType'] == 'ri': raise NotImplementedError("NO ri") - return lines + return (frags, lines) def do_dt(dt: list[list[Pair]]|None, indent: int) -> tuple[list[Fragment], list[Line]]: assert dt is not None @@ -329,9 +328,10 @@ def do_dt(dt: list[list[Pair]]|None, indent: int) -> tuple[list[Fragment], list[ line.addFragment(frag) lines.append(line) elif pair['objType'] == 'vis': - lines += do_vis(trycast(list[VerbalIllustration], pair['obj'])) + lines += do_vis(trycast(list[VerbalIllustration], pair['obj']),indent) elif pair['objType'] == 'uns': - newLines = do_uns(trycast(list[list[list[Pair]]], pair['obj']),indent) + (newFrags,newLines) = do_uns(trycast(list[list[list[Pair]]], pair['obj']),indent) + frags += newFrags lines += newLines else: print(json.dumps(pair, indent=2)) @@ -345,13 +345,13 @@ def do_sense(sense: Sense|None, indent:int=3) -> tuple[list[Fragment], list[Line lines: list[Line] = [] frags: list[Fragment] = [] r = Resources() - dt = sense['dt'] - (newFrags, newLines) = do_dt(trycast(list[list[Pair]], dt),indent) - frags += newFrags - lines += newLines for k,v in sense.items(): - if k == 'dt' or k == 'sn': + if k == 'sn': continue + elif k == 'dt': + (newFrags, newLines) = do_dt(trycast(list[list[Pair]], sense['dt']), indent) + frags += newFrags + lines += newLines elif k == 'sdsense': # XXX - This needs to expand to handle et, ins, lbs, prs, sgram, sls, vrs sdsense = trycast(DividedSense, v) @@ -367,50 +367,47 @@ def do_sense(sense: Sense|None, indent:int=3) -> tuple[list[Fragment], list[Line elif k == 'sls': labels = trycast(list[str], v) assert labels is not None - frag = Fragment(", ".join(labels)+' ', r.textFont, color=r.subduedColor) + frag = Fragment(", ".join(labels)+' ', r.boldFont, color=r.subduedColor) + frag.setIndent(indent) + frag.setBackground(r.subduedBackground) frags.append(frag) else: print(k,v) raise NotImplementedError(f"Unknown or unimplimented element {k}") return (frags, lines) -def do_pseq(outer: int, - inner: int, +def do_pseq(inner: int, + outer: int, pseq: list[Any] ) -> tuple[list[Fragment], list[Line]]: lines: list[Line] = [] frags: list[Fragment] = [] + indent = 3 # XXX - Should this be a parameter passed in? count = 1 r = Resources() - first = True + newLine = False for entry in pseq: for pair in entry: if pair['objType'] == 'bs': sense = pair['obj']['sense'] - (newFrags, newLines) = do_sense(trycast(Sense, sense)) - if first: - frags += newFrags - first = False - else: - line = Line() - line.addFragment(newFrags) - lines.append(line) + (newFrags, newLines) = do_sense(trycast(Sense, sense),indent=indent) + frags += newFrags lines += newLines + newLine = True elif pair['objType'] == 'sense': - if first: - frag = Fragment(f"({count})", r.textFont, color=r.baseColor) - frag.setIndent(3) - frags.append(frag) - (newFrags, newLines) = do_sense(trycast(Sense, pair['obj']), indent=4) - frags += newFrags - first = False - else: + frag = Fragment(f"({count})", r.textFont, color=r.baseColor) + frag.setIndent(indent) + if newLine: line = Line() - frag = Fragment(f"({count})", r.textFont, color=r.baseColor) - frag.setIndent(3) line.addFragment(frag) - (newFrags, newLines) = do_sense(trycast(Sense, pair['obj']), indent=4) + else: + frags.append(frag) + (newFrags, newLines) = do_sense(trycast(Sense, pair['obj']), indent=indent+1) + if newLine: line.addFragment(newFrags) lines.append(line) + else: + frags += newFrags + newLine = True lines += newLines count += 1 else: @@ -426,14 +423,17 @@ def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]: frag.setIndent(1) line.addFragment(frag) for inner, item_i in enumerate(item_o): - frag =Fragment(chr(ord('a')+inner), r.boldFont, color=r.baseColor) - frag.setIndent(2) - line.addFragment(frag) + indent = 2 + if len(item_o) > 1: + frag =Fragment(chr(ord('a')+inner), r.boldFont, color=r.baseColor) + frag.setIndent(2) + line.addFragment(frag) + indent = 3 for pair in item_i: objType = pair['objType'] if objType == 'sense': sense = trycast(Sense, pair['obj']) - (frags, newlines) = do_sense(sense) + (frags, newlines) = do_sense(sense, indent=indent) line.addFragment(frags) lines.append(line) line = Line() @@ -448,7 +448,7 @@ def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]: lines += newlines elif objType == 'bs': sense = pair['obj']['sense'] - (newFrags, newLines) = do_sense(trycast(Sense, sense)) + (newFrags, newLines) = do_sense(trycast(Sense, sense),indent=indent) line.addFragment(newFrags) lines.append(line) line = Line() @@ -457,6 +457,60 @@ def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]: raise NotImplementedError(f"Unknown object[{objType}] for \n{json.dumps(pair['obj'],indent=2)}") return lines +def do_ins(inflections:list[Inflection]|None) -> list[Fragment]: + assert inflections is not None + r = Resources() + frags: list[Fragment] = [] + sep = '' + for inflection in inflections: + if sep == '; ': + frag = Fragment('; ', font=r.boldFont, color=r.baseColor) + frags.append(frag) + elif sep != '': + frag = Fragment(sep, font=r.italicFont, color=r.baseColor) + frags.append(frag) + + if 'ifc' in inflection: + text = inflection['ifc'] + elif 'if' in inflection: + text = inflection['if'] + else: + raise ValueError(f"Missing 'if' or 'ifc' in {inflection}") + + frag = Fragment(text, r.boldFont, color=r.baseColor) + frags.append(frag) + sep = '; ' + if 'il' in inflection: + sep = ' ' + inflection['il'] + ' ' + if 'prs' in inflection: + newFrags = do_prs(trycast(list[Pronunciation], inflection['prs'])) + frags += newFrags + if 'spl' in inflection: + raise NotImplementedError(f"We haven't implimented 'spl' for inflection: {inflection}") + return frags + +def do_ets(ets:list[list[Pair]]|None) -> list[Line]: + assert ets is not None + r = Resources() + lines: list[Line] = [] + for et in ets: + for pair in et: + if pair['objType'] == 'text': + line = Line() + line.addFragment( + Fragment(pair['obj'], r.textFont, color=r.baseColor) + ) + lines.append(line) + elif pair['objType'] == 'et_snote': + line = Line() + line.addFragment( + Fragment('Note: '+pair['obj'], r.textFont, color=r.baseColor) + ) + lines.append(line) + else: + raise NotImplementedError(f"Unknown key {pair['objType']} in et") + return lines + def do_def(entry: DefinitionSection) -> list[Line]: assert entry is not None r = Resources() @@ -505,6 +559,8 @@ def getDef(defines: Any) -> list[Line]: for k in uses.keys(): used[k] = 0 + ets: list[Line] = [] + for count, work in enumerate(workList): testId = work['meta']['id'].lower().split(':')[0] # @@ -528,8 +584,7 @@ def getDef(defines: Any) -> list[Line]: line.addFragment(Fragment(', ' + hw, r.headerFont, color=r.baseColor)) if entries > 1: frag = Fragment(f" {count + 1} of {entries} ", r.textFont, color= r.subduedColor) - # XXX - Use a resource color!!! - frag.setBackground(QColor(Qt.GlobalColor.gray)) + frag.setBackground(r.subduedBackground) line.addFragment(frag) if 'fl' in work: text = work['fl'] @@ -547,9 +602,16 @@ def getDef(defines: Any) -> list[Line]: if hwi['hw'].find('*') >= 0: hw = re.sub(r'\*', '\u00b7', hwi['hw']) line.addFragment(Fragment(hw + ' ', r.textFont, color=r.subduedColor)) - for frag in do_prs(hwi): - line.addFragment(frag) - if len(line.getLine()) > 0: + if 'prs' in hwi: + newFrags = do_prs(trycast(list[Pronunciation], hwi['prs'])) + line.addFragment(newFrags) + lines.append(line) + line = Line() + if 'ins' in work: + inflections = trycast(list[Inflection], work['ins']) + newFrags = do_ins(inflections) + line = Line() + line.addFragment(newFrags) lines.append(line) defines = trycast(list[DefinitionSection], work['def']) assert defines is not None @@ -558,6 +620,25 @@ def getDef(defines: Any) -> list[Line]: lines += do_def(define) except NotImplementedError as e: print(e) + if 'et' in work: + line = Line() + line.addFragment( + Fragment(f"{work['fl']} ({used[work['fl']]})", r.labelFont, color=r.baseColor) + ) + ets.append(line) + ets += do_ets(trycast(list[list[Pair]], work['et'])) + for k in work.keys(): + if k not in [ 'meta', 'hom', 'hwi', 'fl', 'def', 'ins', 'prs', 'et', + 'date', 'shortdef']: + #raise NotImplementedError(f"Unknown key {k} in work") + print(f"Unknown key {k} in work") + if len(ets)>0: + line = Line() + line.addFragment( + Fragment('Etymology', r.labelFont, color=r.baseColor) + ) + lines.append(line) + lines+=ets return lines def parseText(frag: Fragment) -> list[Fragment]: @@ -622,10 +703,11 @@ def parseText(frag: Fragment) -> list[Fragment]: end = text.find("}") token = text[1:end] frag.setText(text[end + 1 :]) - newFrag = Fragment(frag) oldFont = QFont(frag.font()) if token == "bc": - results.append(Fragment(": ", boldFont, color=baseColor)) + newFrag = Fragment(": ", boldFont, color=baseColor) + newFrag.setIndent(frag.indent()) + results.append(newFrag) continue if token in [ "b",