checkpoint

This commit is contained in:
Christopher T. Johnson
2024-04-09 11:45:56 -04:00
parent 46580b75ea
commit ad5904f3ae
3 changed files with 312 additions and 182 deletions

View File

@@ -1,12 +1,12 @@
from PyQt6.QtGui import QColor
from trycast import trycast
import json
import re
from typing import Any, Literal, NamedTuple, NotRequired, TypedDict, cast
from typing import Any, NamedTuple, NotRequired, TypedDict
from PyQt6.QtCore import QEventLoop, QUrl, Qt
from PyQt6.QtGui import QColor, QFont
from PyQt6.QtNetwork import QNetworkRequest
from lib.words import Word
from lib.utils import Resources
from lib.definition import Line, Fragment
registration = {
@@ -27,10 +27,6 @@ class VerbalIllustration(TypedDict):
t: str
aq: str
class VerbalIllustrationTuple(NamedTuple):
type_: str # 'vis'
data: list[VerbalIllustration]
class Sound(TypedDict):
audio: str
ref: str
@@ -38,12 +34,10 @@ class Sound(TypedDict):
class Pronunciation(TypedDict):
mw: str
l: str
l2: str
pun: str
sound: Sound
l: NotRequired[str]
l2: NotRequired[str]
pun: NotRequired[str]
sound: NotRequired[Sound]
class Meta(TypedDict):
id: str
@@ -56,12 +50,12 @@ class Meta(TypedDict):
class HeadWordInfo(TypedDict):
hw: str
prs: list[Pronunciation]
prs: NotRequired[list[Pronunciation]]
class HeadWord(TypedDict):
hw: str
prs: list[Pronunciation]
psl: str
prs: NotRequired[list[Pronunciation]]
psl: NotRequired[str]
class Variant(TypedDict):
va: str
@@ -109,34 +103,26 @@ class RunInWrap(TypedDict):
text: str
vrs: list[Variant]
class Sense:
dt: list[str] # not full
et: list[str] # not full
ins: list[Inflection]
lbs: list[str]
prs: list[Pronunciation]
sdsense: DividedSense
sgram: str
sls: list[str]
sn: str
vrs: list[Variant]
class SenseSequence(TypedDict):
sense: Sense
sen: Sense
class Sense(TypedDict):
dt: list[list] # not full
et: NotRequired[list[str]]
ins: NotRequired[list[Inflection]]
lbs: NotRequired[list[str]]
prs: NotRequired[list[Pronunciation]]
sdsense: NotRequired[DividedSense]
sgram: NotRequired[str]
sls: NotRequired[list[str]]
sn: NotRequired[str]
vrs: NotRequired[list[Variant]]
class Definition(TypedDict):
sseq: list[SenseSequence]
vd: str
sseq: list[list[list[Any]]]
vd: NotRequired[str]
class Pair(TypedDict):
objType: str
obj: list[Sense]|Sense|str|list[VerbalIllustration]|list[Any]
class EntryX(TypedDict):
meta: Meta
hom: NotRequired[str]
hwi: HeadWordInfo
ahws: NotRequired[list[HeadWord]]
vrs: NotRequired[list[Variant]]
fl: str
def_: list[Definition]
Entry = TypedDict(
'Entry',
{
@@ -149,13 +135,29 @@ Entry = TypedDict(
'def': list[Definition],
}
)
class WordType(TypedDict):
word: str
source: str
definition: dict[str, Any]
def fetch(word:str) -> dict[str, Any]:
def make_pairs(src: list[Any]) -> list[Pair]:
result:list[Pair] = []
iters = [iter(src)]*2
for entry in zip(*iters):
pair = { 'objType': entry[0],
'obj': entry[1],
}
pair = trycast(Pair, pair)
assert pair is not None
result.append(pair)
return result
def fetch(word:str) -> WordType:
request = QNetworkRequest()
url = QUrl(API.format(word=word, key=key))
request.setUrl(url)
request.setTransferTimeout(3000)
reply = Word._nam.get(request)
reply = Resources.nam.get(request)
assert reply is not None
loop = QEventLoop()
reply.finished.connect(loop.quit)
@@ -195,16 +197,16 @@ def getFirstSound(definition: list[Entry]) -> QUrl:
return url
return QUrl()
def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
def do_prs(hwi: HeadWordInfo) -> list[Fragment]:
r = Resources()
frags: list[Fragment] = []
font = trycast(QFont, Word._resources['fonts']['label'])
assert font is not None
linkColor = trycast(QColor, Word._resources['colors']['link'])
assert linkColor is not None
subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
assert subduedColor is not None
font = r.labelFont
linkColor = r.linkColor
subduedColor = r.subduedColor
for pr in prs:
if 'prs' not in hwi:
return []
for pr in hwi['prs']:
if 'pun' in pr:
pun = pr['pun']
else:
@@ -216,6 +218,7 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
frag = Fragment(pr['mw'], font, color=subduedColor)
if 'sound' in pr:
frag.setAudio(soundUrl(pr['sound']))
frag.setColor(linkColor)
frags.append(frag)
if 'l2' in pr:
frags.append(
@@ -223,38 +226,141 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
)
return frags
def do_sense(sense: Sense|None) -> tuple[list[Fragment], list[Line]]:
if sense is None:
return ([],[])
lines: list[Line] = []
frags: list[Fragment] = []
r = Resources()
if 'sn' in sense:
sn = sense['sn']
else:
sn = ''
print(f'{sn}\n\n',json.dumps(sense['dt'], indent=2))
iters = [iter(sense['dt'])]*2
for pair in zip(*iters):
pair = trycast(tuple[str, Any], pair)
assert pair is not None
print(pair[0])
if pair[0] == 'text':
line = Line()
line.addFragment(
Fragment(pair[1], r.textFont, color=r.baseColor)
)
lines.append(line)
return (frags, lines)
def do_pseq(outer: int,
inner: int,
pseq: list[list[Pair]]| None ) -> tuple[list[Fragment], list[Line]]:
assert pseq is not None
lines: list[Line] = []
frags: list[Fragment] = []
for entry in pseq:
pairs = make_pairs(entry)
for pair in pairs:
if pair['objType'] == 'bs':
(newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
frags += newFrags
lines += newLines
elif pair['objType'] == 'sense':
(newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
frags += newFrags
lines += newLines
else:
raise Exception(f"Unknown object type {pair['objType']}")
return (frags, lines)
def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]:
lines: list[Line] = []
r = Resources()
for outer, item_o in enumerate(sseq):
line = Line()
line.addFragment(
Fragment(str(outer+1), r.boldFont, color=r.baseColor)
)
for inner, item_i in enumerate(item_o):
line.addFragment(
Fragment(chr(ord('a')+inner), r.boldFont, color=r.baseColor)
)
pairs = make_pairs(item_i)
for pair in pairs:
objType = pair['objType']
if objType == 'sense':
sense = trycast(Sense, pair['obj'])
(frags, newlines) = do_sense(sense)
for frag in frags:
line.addFragment(frag)
lines.append(line)
lines += newlines
elif objType == 'sen':
raise Exception(f"sen unimplimented")
elif objType == 'pseq':
pseq = trycast(list[list[Pair]], pair['obj'])
(frags, newlines) = do_pseq(inner, outer, trycast(list[list[Pair]], pair['obj']))
for frag in frags:
line.addFragment(frag)
lines.append(line)
lines += newlines
elif objType == 'bs':
raise Exception(f"bs unimplimented")
else:
raise Exception(f"Unknown object[{objType}] for \n{json.dumps(pair['obj'],indent=2)}")
return lines
def do_def(entry: Definition) -> list[Line]:
r = Resources()
lines: list[Line] = []
assert trycast(Definition, entry) is not None
if 'vd' in entry:
line = Line()
line.addFragment(
Fragment(entry['vd'], r.italicFont, color = r.linkColor)
)
lines.append(line)
#
# sseg is required
#
sseq = entry['sseq']
lines += do_sseq(sseq)
return lines
def getDef(definition: list[Entry]) -> list[Line]:
lines = []
r = Resources()
lines:list[Line] = []
#
# Pull the fonts for ease of use
#
headerFont = trycast(QFont, Word._resources['fonts']['header'])
assert headerFont is not None
textFont = trycast(QFont, Word._resources['fonts']['text'])
assert textFont is not None
labelFont = trycast(QFont, Word._resources['fonts']['label'])
assert labelFont is not None
headerFont = r.headerFont
textFont = r.textFont
labelFont = r.labelFont
#
# Pull the colors for ease of use
#
baseColor = trycast(QColor, Word._resources['colors']['base'])
assert baseColor is not None
linkColor = trycast(QColor, Word._resources['colors']['link'])
assert linkColor is not None
subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
assert subduedColor is not None
baseColor = r.baseColor
linkColor = r.linkColor
subduedColor = r.subduedColor
#
# No need to figure it out each time it is used
#
entries = 0
id = definition[0]['meta']['id']
id = ':'.split(id)[0].lower()
id = definition[0]['meta']['id'].lower().split(':')[0]
uses: dict[str,int] = {}
for entry in definition:
if entry['meta']['id'].lower() == id:
testId = entry['meta']['id'].lower().split(':')[0]
if testId == id:
entries += 1
try:
uses[entry['fl']] = uses.get(entry['fl'], 0) + 1
except KeyError:
pass
used: dict[str, int] = {}
for k in uses.keys():
used[k] = 0
for count, entry in enumerate(definition):
if entry['meta']['id'].lower() != id:
testId = entry['meta']['id'].lower().split(':')[0]
if testId != id:
continue
#
# Create the First line from the hwi, [ahws] and fl
@@ -270,13 +376,16 @@ def getDef(definition: list[Entry]) -> list[Line]:
for ahw in ahws:
hw = re.sub(r'\*', '', ahw['hw'])
line.addFragment(Fragment(', ' + hw, headerFont, color=baseColor))
if 'hom' in entry:
if entries > 1:
frag = Fragment(f" {count + 1} of {entries} ", textFont, color= subduedColor)
frag.setBackground(QColor(Qt.GlobalColor.gray))
line.addFragment(frag)
if 'fl' in entry:
frag = Fragment(f"{count} of {entries} ", textFont, color=
frag.setBackground(QColor(Qt.GlobalColor.gray))
line.addFragment(frag)
line.addFragment(Fragment(entry['fl'], labelFont, color=baseColor))
text = entry['fl']
used[text] += 1
if uses[text] > 1:
text += f' ({used[text]})'
line.addFragment(Fragment(text, labelFont, color=baseColor))
lines.append(line)
#
@@ -284,11 +393,15 @@ def getDef(definition: list[Entry]) -> list[Line]:
# While 'prs' is optional, the headword is not. This gets us what we want.
#
line = Line()
hw = re.sub(r'\*', '\u00b7', hwi['hw'])
line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
for frag in do_prs(hwi['prs']):
if hwi['hw'].find('*') >= 0:
hw = re.sub(r'\*', '\u00b7', hwi['hw'])
line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
for frag in do_prs(hwi):
line.addFragment(frag)
#
# Try for
return [Line()]
if len(line.getLine()) > 0:
lines.append(line)
defines = trycast(list[Definition], entry['def'])
assert defines is not None
for define in defines:
lines += do_def(define)
return lines