diff --git a/add-readings.py b/add-readings.py index 0cf77ad..5f987fa 100644 --- a/add-readings.py +++ b/add-readings.py @@ -1,138 +1,17 @@ -import json -import MeCab -import re - -import requests - -request_url = "http://127.0.0.1:19633" -request_timeout = 10 - -# Generate Hiragana and Katakana characters -hiragana = tuple(chr(i) for i in range(12353, 12436)) # \u3041 - \u3094 -katakana = tuple(chr(i) for i in range(12450, 12532)) # \u30A2 - \u30F4 -punctuation = tuple(chr(i) for i in range(65281, 65382)) - -# Combine both into one tuple -all_kana = hiragana + katakana + punctuation + (' ', 'ー', '・') - -KANJI_RE = re.compile(r"[\u4E00-\u9FFF]") - -def katakana_to_hiragana(text: str) -> str: - return "".join( - chr(ord(ch) - 0x60) if 0x30A1 <= ord(ch) <= 0x30F6 else ch - for ch in text - ) - -# def get_node_reading(node) -> str: -# features = node.feature.split(",") -# print() -# if len(features) >= 8 and features[7] != "*": -# return katakana_to_hiragana(features[7]) -# return node.surface - -def has_kanji(text: str) -> bool: - return bool(KANJI_RE.search(text)) - -def get_node_reading(node) -> str: - features = node.feature.split(",") - ## These depend on what dictionary is being used. - if len(features) > 6 and features[6] != "*": - return katakana_to_hiragana(features[6]) - return node.surface - -def annotate_phrase(text: str) -> str: - tagger = MeCab.Tagger() - node = tagger.parseToNode(text) - vocab = () - parts = [] - # かん - furi = [] - while node: - surface = node.surface - if surface: - vocab += (surface,) - if has_kanji(surface): - reading = get_node_reading(node) - parts.append(f"{surface}({reading})") - furi.append(f"{surface}{reading}") - else: - parts.append(surface) - furi.append(surface) - node = node.next - - return {'reading':("".join(parts)),'vocab':vocab,"furigana":("".join(furi))} - -def elide(text: str) -> str: - elide_max_length = 100 - if len(text) > elide_max_length: - return text[:100] + "..." - return text - -def anki_fields_term(term : str) -> dict: - params = { - "text": term, - "type": "term", - "markers": ["audio", "cloze-body-kana", "conjugation", "expression", "furigana", "furigana-plain", "glossary", "glossary-brief", "glossary-no-dictionary", "glossary-first", "glossary-first-brief", "glossary-first-no-dictionary", "part-of-speech", "phonetic-transcriptions", "pitch-accents", "pitch-accent-graphs", "pitch-accent-graphs-jj", "pitch-accent-positions", "pitch-accent-categories", "reading", "tags", "clipboard-image", "clipboard-text", "cloze-body", "cloze-prefix", "cloze-suffix", "dictionary", "dictionary-alias", "document-title", "frequencies", "frequency-harmonic-rank", "frequency-harmonic-occurrence", "frequency-average-rank", "frequency-average-occurrence", "screenshot", "search-query", "popup-selection-text", "sentence", "sentence-furigana", "sentence-furigana-plain", "url"], - "maxEntries": 1, - "includeMedia": False, - } - response = requests.post(request_url + "/ankiFields", json = params, timeout = request_timeout) - return json.loads(response.text) - -def term_entries(term :str) -> dict: - #print("Requesting termEntries:") - params = { - "term": term, - "maxEntries": 1, - "includeMedia": False, - } - response = requests.post(request_url + "/termEntries", json = params, timeout = request_timeout) - return json.loads(response.text) - -# perhaps set up a filter parameter. -def flatten_string(node): - if isinstance(node,str): - yield node - elif isinstance(node, list): - for item in node: - yield from flatten_string(item) - elif isinstance(node,dict): - if isinstance(node.get("content"), str): - yield node["content"] - else: - for v in node.values(): - yield from flatten_string(v) - -def extract_text_by_type(node, target_type): - if isinstance(node,dict): - data = node.get("data") - if isinstance(data,dict) and data.get("content") == target_type: - yield from flatten_string(node.get("content")) - #yield from ",".join(node.get("content")) - for value in node.values(): - yield from extract_text_by_type(value, target_type) - elif isinstance(node,list): - for item in node: - yield from extract_text_by_type(item,target_type) - -def get_glossary(word :str) -> list: - glossary = list() - dict_items = term_entries(word) - - entry = next(iter(dict_items.get("dictionaryEntries",[])),None) - if entry: - definition = next(iter(entry.get("definitions",[])),None) - if definition: - def_entry = next(iter(definition.get("entries", [])),None) - if def_entry: - glossary.extend( list(extract_text_by_type(def_entry, "glossary")) ) - return glossary[:2] +from parser import * #print(annotate_phrase("私は学校へ行きます")) with open("dump2.json", "r", encoding="utf-16") as f: dump = json.load(f) +# foo = anki_fields_term(surface) +# for entry in foo.get("fields"): +# entry['expression'] +# entry['glossary-first-brief'] + # entry['glossary-plain-no-dictionary'] + # entry['frequency-average-rank'] + vocab_dict = {} ## A micab phrase based word parser for cd in dump: @@ -196,8 +75,8 @@ for cd in dump: vocab_dict = sorted(vocab_dict.items(), key= lambda x: x[1]['count'], reverse=True) -with open("dump-withFuri.json", "w", encoding="utf-16") as outfile: - json.dump(dump, outfile, ensure_ascii=False, indent=2) +#with open("dump-withFuri.json", "w", encoding="utf-16") as outfile: +# json.dump(dump, outfile, ensure_ascii=False, indent=2) #with open("dump-vocab.json", "w", encoding="utf-16") as outfile: # json.dump(vocab_dict, outfile, ensure_ascii=False, indent=2) diff --git a/create-notes.py b/create-notes.py index 255647f..377b446 100644 --- a/create-notes.py +++ b/create-notes.py @@ -1,79 +1,13 @@ -import json -import MeCab -import re - -import requests +from parser import * import os from pathlib import Path import shutil - -request_url = "http://127.0.0.1:19633" -request_timeout = 10 +import hashlib destination_directory_name = '/run/media/deck/YF8SD/Sync/Notebooks/Obsidian/Japanese/Yu-Gi-Oh/Cards/' destination_path = Path(destination_directory_name) -# Generate Hiragana and Katakana characters -hiragana = tuple(chr(i) for i in range(12353, 12436)) # \u3041 - \u3094 -katakana = tuple(chr(i) for i in range(12450, 12532)) # \u30A2 - \u30F4 -punctuation = tuple(chr(i) for i in range(65281, 65382)) - -# Combine both into one tuple -all_kana = hiragana + katakana + punctuation + (' ', 'ー', '・') - -KANJI_RE = re.compile(r"[\u4E00-\u9FFF]") - -def katakana_to_hiragana(text: str) -> str: - return "".join( - chr(ord(ch) - 0x60) if 0x30A1 <= ord(ch) <= 0x30F6 else ch - for ch in text - ) - -# def get_node_reading(node) -> str: -# features = node.feature.split(",") -# print() -# if len(features) >= 8 and features[7] != "*": -# return katakana_to_hiragana(features[7]) -# return node.surface - -def has_kanji(text: str) -> bool: - return bool(KANJI_RE.search(text)) - -def anki_fields_term(term : str) -> dict: - params = { - "text": term, - "type": "term", - "markers": ["audio", "cloze-body-kana", "conjugation", "expression", "furigana", "furigana-plain", "glossary", "glossary-brief", "glossary-no-dictionary", "glossary-first", "glossary-first-brief", "glossary-first-no-dictionary", "part-of-speech", "phonetic-transcriptions", "pitch-accents", "pitch-accent-graphs", "pitch-accent-graphs-jj", "pitch-accent-positions", "pitch-accent-categories", "reading", "tags", "clipboard-image", "clipboard-text", "cloze-body", "cloze-prefix", "cloze-suffix", "dictionary", "dictionary-alias", "document-title", "frequencies", "frequency-harmonic-rank", "frequency-harmonic-occurrence", "frequency-average-rank", "frequency-average-occurrence", "screenshot", "search-query", "popup-selection-text", "sentence", "sentence-furigana", "sentence-furigana-plain", "url"], - "maxEntries": 1, - "includeMedia": False, - } - response = requests.post(request_url + "/ankiFields", json = params, timeout = request_timeout) - return json.loads(response.text) - -def term_entries(term :str) -> dict: - #print("Requesting termEntries:") - params = { - "term": term, - "maxEntries": 1, - "includeMedia": False, - } - response = requests.post(request_url + "/termEntries", json = params, timeout = request_timeout) - return json.loads(response.text) - -def get_glossary(word :str) -> list: - glossary = list() - dict_items = term_entries(word) - - entry = next(iter(dict_items.get("dictionaryEntries",[])),None) - if entry: - definition = next(iter(entry.get("definitions",[])),None) - if definition: - def_entry = next(iter(definition.get("entries", [])),None) - if def_entry: - glossary.extend( list(extract_text_by_type(def_entry, "glossary")) ) - return glossary[:2] - #print(annotate_phrase("私は学校へ行きます")) ## Make sure directories exist @@ -81,6 +15,8 @@ if not destination_path.exists(): destination_path.mkdir(parents=True, exist_ok=True) if not destination_path.joinpath('_attachments').exists(): destination_path.joinpath('_attachments').mkdir(parents=True, exist_ok=True) +if not destination_path.joinpath('vocab').exists(): + destination_path.joinpath('vocab').mkdir(parents=True, exist_ok=True) with open("dump-withFuri.json", "r", encoding="utf-16") as f: dump = json.load(f) @@ -97,33 +33,64 @@ for cd in dump: card_note.write("---\n") ## Name at the top card_note.write(f"![[{cd}.jpg]]") - card_note.write("\n##### Name\n") + card_note.write("\n#### Name\n") card_note.write("
") card_note.write(dump[cd]['name_J']) card_note.write("") card_note.write(dump[cd]['name_E']) card_note.write("
") ## the description - card_note.write("\n##### Details\n") + card_note.write("\n#### Details\n") card_note.write("
") card_note.write(dump[cd]['desc_J']) card_note.write("") card_note.write(dump[cd]['desc_E']) card_note.write("
") # desc_Furi - card_note.write("\n##### Reading\n") + card_note.write("\n#### Reading\n") if(dump[cd].get("furi")): card_note.write(f"{dump[cd]['furi']}\n") else: card_note.write(dump[cd]['name_J']) card_note.write("\n") + card_note.write("##### Glossary\n") + for vocab in dump[cd]["glossary_Name_J"]: + print(f"{vocab} : {dump[cd]["glossary_Name_J"][vocab]}", file=card_note) + card_note.write("\n") if(dump[cd].get("desc_Furi")): card_note.write(f"{dump[cd]['desc_Furi']}\n") else: card_note.write(dump[cd]['desc_J']) card_note.write("\n") - card_note.write("\n##### Glossary\n") + card_note.write("##### Glossary\n") for vocab in dump[cd]["glossary_Desc_J"]: - print(f"{vocab} : {dump[cd]["glossary_Desc_J"][vocab]}", file=card_note) + vocab_hash = hashlib.shake_256((vocab + 'Jitendex.org [2026-04-04]').encode()).hexdigest(4) + ## Only search and create a vocab file if it doesn't exist + if Path(destination_path.joinpath('vocab/' + vocab_hash + '.md')).exists() == False: + definitions = anki_fields_term(vocab) + if definitions.get("fields"): + with open(destination_path.as_posix() + '/vocab/' + vocab_hash + '.md', 'w') as vocab_note: + for entry in definitions.get("fields"): + vocab_note.write("\n") + print(entry['expression'], file=vocab_note) + #print(entry['furigana-plain'], file=vocab_note) + if entry['expression'] != entry['reading']: + print(entry['reading'], file=vocab_note) + print(entry['furigana'], file=vocab_note) + #print(f"Part of Speech: {entry['part-of-speech']}", file=vocab_note) + print(f"Frequency: {entry['frequency-average-rank']}", file=vocab_note) + #print(entry['glossary-plain-no-dictionary'], file=vocab_note) + vocab_note.write(entry['glossary-first']) + print("##### Other Definitions", file=vocab_note) + print(entry['glossary-plain-no-dictionary'], file=vocab_note) + print(f"[[{vocab_hash}|{vocab}]] : {dump[cd]["glossary_Desc_J"][vocab]}", file=card_note) #print('>' + dump[cd]["glossary_Desc_J"][vocab], file=card_note) - + card_note.write("\n") + +# foo = anki_fields_term(surface) +# for entry in foo.get("fields"): +# entry['expression'] +# entry['glossary-first-brief'] + # entry['glossary-plain-no-dictionary'] + # entry['frequency-average-rank'] + diff --git a/parser.py b/parser.py new file mode 100644 index 0000000..faf5e8d --- /dev/null +++ b/parser.py @@ -0,0 +1,130 @@ +import json +import MeCab +import re + +import requests + +request_url = "http://127.0.0.1:19633" +request_timeout = 10 + +# Generate Hiragana and Katakana characters +hiragana = tuple(chr(i) for i in range(12353, 12436)) # \u3041 - \u3094 +katakana = tuple(chr(i) for i in range(12450, 12532)) # \u30A2 - \u30F4 +punctuation = tuple(chr(i) for i in range(65281, 65382)) + +# Combine both into one tuple +all_kana = hiragana + katakana + punctuation + (' ', 'ー', '・') + +KANJI_RE = re.compile(r"[\u4E00-\u9FFF]") + +def katakana_to_hiragana(text: str) -> str: + return "".join( + chr(ord(ch) - 0x60) if 0x30A1 <= ord(ch) <= 0x30F6 else ch + for ch in text + ) + +# def get_node_reading(node) -> str: +# features = node.feature.split(",") +# print() +# if len(features) >= 8 and features[7] != "*": +# return katakana_to_hiragana(features[7]) +# return node.surface + +def has_kanji(text: str) -> bool: + return bool(KANJI_RE.search(text)) + +def get_node_reading(node) -> str: + features = node.feature.split(",") + ## These depend on what dictionary is being used. + if len(features) > 6 and features[6] != "*": + return katakana_to_hiragana(features[6]) + return node.surface + +def annotate_phrase(text: str) -> str: + tagger = MeCab.Tagger() + node = tagger.parseToNode(text) + vocab = () + parts = [] + # かん + furi = [] + while node: + surface = node.surface + if surface: + vocab += (surface,) + if has_kanji(surface): + reading = get_node_reading(node) + parts.append(f"{surface}({reading})") + furi.append(f"{surface}{reading}") + else: + parts.append(surface) + furi.append(surface) + node = node.next + + return {'reading':("".join(parts)),'vocab':vocab,"furigana":("".join(furi))} + +def elide(text: str) -> str: + elide_max_length = 100 + if len(text) > elide_max_length: + return text[:100] + "..." + return text + +def anki_fields_term(term : str) -> dict: + params = { + "text": term, + "type": "term", + #"markers": ["audio", "cloze-body-kana", "conjugation", "expression", "furigana", "furigana-plain", "glossary", "glossary-brief", "glossary-no-dictionary", "glossary-first", "glossary-first-brief", "glossary-first-no-dictionary", "part-of-speech", "phonetic-transcriptions", "pitch-accents", "pitch-accent-graphs", "pitch-accent-graphs-jj", "pitch-accent-positions", "pitch-accent-categories", "reading", "tags", "clipboard-image", "clipboard-text", "cloze-body", "cloze-prefix", "cloze-suffix", "dictionary", "dictionary-alias", "document-title", "frequencies", "frequency-harmonic-rank", "frequency-harmonic-occurrence", "frequency-average-rank", "frequency-average-occurrence", "screenshot", "search-query", "popup-selection-text", "sentence", "sentence-furigana", "sentence-furigana-plain", "url"], + "markers": ["audio", "expression", "cloze-body" , "furigana","furigana-plain", "part-of-speech", "reading", "glossary-first", "glossary-plain-no-dictionary" , "glossary-first-no-dictionary" , "dictionary", "frequencies", "frequency-average-rank", "frequency-average-occurrence", "screenshot", "sentence", "sentence-furigana", "sentence-furigana-plain" ], + "maxEntries": 2, + "includeMedia": False, + } + response = requests.post(request_url + "/ankiFields", json = params, timeout = request_timeout) + return json.loads(response.text) + +def term_entries(term :str) -> dict: + #print("Requesting termEntries:") + params = { + "term": term, + "maxEntries": 1, + "includeMedia": False, + } + response = requests.post(request_url + "/termEntries", json = params, timeout = request_timeout) + return json.loads(response.text) + +# perhaps set up a filter parameter. +def flatten_string(node): + if isinstance(node,str): + yield node + elif isinstance(node, list): + for item in node: + yield from flatten_string(item) + elif isinstance(node,dict): + if isinstance(node.get("content"), str): + yield node["content"] + else: + for v in node.values(): + yield from flatten_string(v) + +def extract_text_by_type(node, target_type): + if isinstance(node,dict): + data = node.get("data") + if isinstance(data,dict) and data.get("content") == target_type: + yield from flatten_string(node.get("content")) + #yield from ",".join(node.get("content")) + for value in node.values(): + yield from extract_text_by_type(value, target_type) + elif isinstance(node,list): + for item in node: + yield from extract_text_by_type(item,target_type) + +def get_glossary(word :str) -> list: + glossary = list() + dict_items = term_entries(word) + + entry = next(iter(dict_items.get("dictionaryEntries",[])),None) + if entry: + definition = next(iter(entry.get("definitions",[])),None) + if definition: + def_entry = next(iter(definition.get("entries", [])),None) + if def_entry: + glossary.extend( list(extract_text_by_type(def_entry, "glossary")) ) + return glossary[:2]