Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set kanji meaning to incorrect on vocabulary #63

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions hebikani/hebikani.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ def __init__(self, data):
super().__init__(data)
self._readings = None
self._auxiliary_readings = None
self._auxiliary_meanings = None
self._meanings = None
self._meaning_question = Question(self, QuestionType.MEANING)
self._reading_question = None
Expand Down Expand Up @@ -940,6 +941,20 @@ def meanings(self):
)
return self._meanings

@property
def auxiliary_meanings(self):
# Only works for vocabulary. We want to set kanji answer as inexact
if (
self.object == SubjectObject.VOCABULARY
and len(self.characters) == 1
and self._auxiliary_meanings is None
):
component_subject_ids = self.data["data"]["component_subject_ids"]
kanji = Cache.get_subject(component_subject_ids[0])
if kanji:
self._auxiliary_meanings = kanji.meanings
return self._auxiliary_meanings

@property
def reading_question(self):
"""Get the reading question."""
Expand Down Expand Up @@ -1043,6 +1058,11 @@ def solve(self, inputed_answer: str, hard_mode: bool = False) -> AnswerType:
)
else:
_answer = self.subject.meanings.solve(inputed_answer)
if _answer == AnswerType.INCORRECT and self.subject.auxiliary_meanings:
_answer = self.subject.auxiliary_meanings.solve(inputed_answer)
_answer = (
AnswerType.INEXACT if _answer == AnswerType.CORRECT else _answer
)

if _answer == AnswerType.INCORRECT:
self.wrong_answer_count += 1
Expand Down
1 change: 1 addition & 0 deletions hebikani/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SubjectObject(enumerate):
RADICAL = "radical"
KANJI = "kanji"
VOCABULARY = "vocabulary"
KANA_VOCABULARY = "kana_vocabulary"


class Gender(enumerate):
Expand Down
237 changes: 237 additions & 0 deletions tests/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,243 @@
],
}

get_subject_fresh_kanji_vocab = {
"object": "collection",
"url": "https://api.wanikani.com/v2/subjects?ids=850%2C2579",
"pages": {"per_page": 1000, "next_url": None, "previous_url": None},
"total_count": 2,
"data_updated_at": "2023-09-20T03:36:15.305537Z",
"data": [
{
"id": 850,
"object": "kanji",
"url": "https://api.wanikani.com/v2/subjects/850",
"data_updated_at": "2023-09-20T03:36:15.305537Z",
"data": {
"created_at": "2012-06-05T23:58:47.796331Z",
"level": 3,
"slug": "生",
"hidden_at": None,
"document_url": "https://www.wanikani.com/kanji/%E7%94%9F",
"characters": "生",
"meanings": [
{"meaning": "Life", "primary": True, "accepted_answer": True}
],
"auxiliary_meanings": [],
"readings": [
{
"type": "onyomi",
"primary": True,
"reading": "せい",
"accepted_answer": True,
},
{
"type": "onyomi",
"primary": True,
"reading": "しょう",
"accepted_answer": True,
},
{
"type": "kunyomi",
"primary": False,
"reading": "い",
"accepted_answer": False,
},
{
"type": "kunyomi",
"primary": False,
"reading": "なま",
"accepted_answer": False,
},
{
"type": "kunyomi",
"primary": False,
"reading": "う",
"accepted_answer": False,
},
{
"type": "kunyomi",
"primary": False,
"reading": "は",
"accepted_answer": False,
},
{
"type": "kunyomi",
"primary": False,
"reading": "き",
"accepted_answer": False,
},
],
"component_subject_ids": [210],
"amalgamation_subject_ids": [
2576,
2577,
2578,
2579,
2653,
2665,
2788,
2916,
3407,
3436,
3449,
3451,
3473,
3715,
3806,
3827,
4059,
4195,
4498,
4659,
4969,
5154,
5326,
5548,
5571,
6225,
6346,
6432,
6471,
6640,
6903,
6980,
7189,
7429,
7500,
7617,
7671,
7731,
7738,
8196,
8668,
8701,
8745,
9141,
],
"visually_similar_subject_ids": [526, 654, 1887, 546, 503, 511],
"meaning_mnemonic": "The <radical>life</radical> radical and the <kanji>life</kanji> kanji are the same, so hopefully you now just know them both.",
"meaning_hint": "Study your radicals and you'll read good.",
"reading_mnemonic": "You can save <kanji>life</kanji> or take it away with your new <reading>sa</reading>ber (<ja>せい</ja>). This is the saber you got from doing the correct thing (remember?), and now you need to figure out how to use it with this life. So what do you do?",
"reading_hint": "Imagine having someone's life in the palm of your hands. It's up to you whether you protect it with your saber or... well... do the other thing sabers are for.",
"lesson_position": 50,
"spaced_repetition_system_id": 1,
},
},
{
"id": 2579,
"object": "vocabulary",
"url": "https://api.wanikani.com/v2/subjects/2579",
"data_updated_at": "2023-09-18T23:37:01.015481Z",
"data": {
"created_at": "2012-02-29T07:50:29.000000Z",
"level": 3,
"slug": "生",
"hidden_at": None,
"document_url": "https://www.wanikani.com/vocabulary/%E7%94%9F",
"characters": "生",
"meanings": [
{"meaning": "Fresh", "primary": True, "accepted_answer": True},
{"meaning": "Raw", "primary": False, "accepted_answer": True},
{"meaning": "Live", "primary": False, "accepted_answer": True},
],
"auxiliary_meanings": [],
"readings": [
{"primary": True, "reading": "なま", "accepted_answer": True}
],
"parts_of_speech": ["noun", "の adjective"],
"component_subject_ids": [850],
"meaning_mnemonic": "Normally a single kanji alone like this would mean the same thing as its parent kanji, but in the case of <kanji>生</kanji> it's a little different (but still related). When you think of something that has life, it's <vocabulary>fresh</vocabulary> or <vocabulary>raw</vocabulary>. Think sashimi or sushi, for example. You want that to be as close as possible to life as you can, so you eat it when it's fresh and raw. This word can also be used to describe a <vocabulary>live</vocabulary> event, like a concert or sporting event. \r\n\r\nYou'll see this word at the beginning of other words. For example, <ja>生たまご</ja> is \"raw egg.\"",
"reading_mnemonic": "This uses one of the kun'yomi readings. Here's a mnemonic to help you remember it:\r\n\r\nAfter eating <vocabulary>raw</vocabulary> fish, my yoga teacher and I say <reading>nama</reading>ste (<ja>なま</ja>) to the sushi chef, bowing low to show our appreciation.",
"context_sentences": [
{"en": "One draft beer, please.", "ja": "生ビール、一つ下さい。"},
{"en": "Are raw peppers tasty?", "ja": "生のパプリカは、おいしいですか?"},
{
"en": "Grandma has seen The Beatles in person.",
"ja": "おばあちゃんはビートルズを生でみたことがある。",
},
],
"pronunciation_audios": [
{
"url": "https://files.wanikani.com/u93fhhk0hyq8ob2e28fz4e1kwknn",
"metadata": {
"gender": "male",
"source_id": 7144,
"pronunciation": "なま",
"voice_actor_id": 2,
"voice_actor_name": "Kenichi",
"voice_description": "Tokyo accent",
},
"content_type": "audio/webm",
},
{
"url": "https://files.wanikani.com/shgr3klx57ehp6x6nltpwwodgi0l",
"metadata": {
"gender": "female",
"source_id": 24080,
"pronunciation": "なま",
"voice_actor_id": 1,
"voice_actor_name": "Kyoko",
"voice_description": "Tokyo accent",
},
"content_type": "audio/webm",
},
{
"url": "https://files.wanikani.com/cx0jxv2xowp0726ahkfgjgj2vrar",
"metadata": {
"gender": "male",
"source_id": 7144,
"pronunciation": "なま",
"voice_actor_id": 2,
"voice_actor_name": "Kenichi",
"voice_description": "Tokyo accent",
},
"content_type": "audio/ogg",
},
{
"url": "https://files.wanikani.com/cew3bcc59rg5h3px9lnm78nph9nf",
"metadata": {
"gender": "male",
"source_id": 7144,
"pronunciation": "なま",
"voice_actor_id": 2,
"voice_actor_name": "Kenichi",
"voice_description": "Tokyo accent",
},
"content_type": "audio/mpeg",
},
{
"url": "https://files.wanikani.com/ppwiwn9q1xc3c9zozrxkox7o06kh",
"metadata": {
"gender": "female",
"source_id": 24080,
"pronunciation": "なま",
"voice_actor_id": 1,
"voice_actor_name": "Kyoko",
"voice_description": "Tokyo accent",
},
"content_type": "audio/mpeg",
},
{
"url": "https://files.wanikani.com/vdvg8qhcg3kwenjensadk3tg392c",
"metadata": {
"gender": "female",
"source_id": 24080,
"pronunciation": "なま",
"voice_actor_id": 1,
"voice_actor_name": "Kyoko",
"voice_description": "Tokyo accent",
},
"content_type": "audio/ogg",
},
],
"lesson_position": 77,
"spaced_repetition_system_id": 1,
},
},
],
}

vocabulary_subject = {
"id": 2467,
"object": "vocabulary",
Expand Down
24 changes: 23 additions & 1 deletion tests/test_hebikani.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
get_specific_subjects_next,
get_updated_subjects,
get_subject_without_utf_entry,
get_subject_fresh_kanji_vocab,
get_summary,
post_review,
vocab_katakana_equals_hiragna_subject,
Expand Down Expand Up @@ -345,8 +346,25 @@ def test_question_answer_values():
assert question.answer_values == "what"


def test_use_kanji_reading_on_vocabulary():
def test_use_kanji_meaning_on_vocabulary():
"""When using the meaning of a kanji in the vocabulary it should return inexact."""
kanji, vocabulary = [Subject(s) for s in get_subject_fresh_kanji_vocab["data"]]
Cache.subjects[kanji.id] = kanji
Cache.subjects[vocabulary.id] = vocabulary
question = Question(vocabulary, QuestionType.MEANING)
assert question.solve("Life") == AnswerType.INEXACT
assert question.solve("Fresh") == AnswerType.CORRECT
assert question.solve("Test") == AnswerType.INCORRECT

question = Question(kanji, QuestionType.MEANING)
assert question.solve("Life") == AnswerType.CORRECT
assert question.solve("Fresh") == AnswerType.INCORRECT
assert question.solve("Test") == AnswerType.INCORRECT
Cache.subjects = {}


def test_use_kanji_reading_on_vocabulary():
"""When using the reading of a kanji in the vocabulary it should return inexact."""
kanji = Subject(subject_water_kanji)
Cache.subjects[kanji.id] = kanji
vocabulary = Subject(subject_water_vocabulary)
Expand All @@ -355,6 +373,7 @@ def test_use_kanji_reading_on_vocabulary():
assert question.solve("すい") == AnswerType.INEXACT
assert question.solve("みず") == AnswerType.CORRECT
assert question.solve("み") == AnswerType.INCORRECT
Cache.subjects = {}


def test_question_primary():
Expand All @@ -369,6 +388,8 @@ def test_question_primary():
def test_card_is_solved():
"""Test the card is solved."""
subject = Subject(vocabulary_subject)
# To avoid making a query online to get the kanji auxiliaries
Cache.subjects[440] = subject
assert subject.solved is False

subject.meaning_question.solve("one")
Expand All @@ -384,6 +405,7 @@ def test_card_is_solved():
subject.reading_question.solve("いち")

assert subject.solved is False
Cache.subjects = {}


def test_hard_mode():
Expand Down
Loading