Skip to content

Commit

Permalink
feat: add extra chips in chips challenges
Browse files Browse the repository at this point in the history
This adds extra chips in chips challenges that will not be used in
the correct solution.
  • Loading branch information
kantord committed Jun 25, 2021
1 parent 5d1fba0 commit 49eda4d
Show file tree
Hide file tree
Showing 59 changed files with 6,006 additions and 1,282 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

import librelingo_json_export.export

__version__ = '0.6.3'
__version__ = '0.7.0'
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from librelingo_utils import get_dumb_opaque_id, audio_id, clean_word
import editdistance

from librelingo_utils import get_dumb_opaque_id, audio_id, clean_word, iterate_phrases
from .dictionary import _define_words_in_sentence


Expand Down Expand Up @@ -56,10 +58,42 @@ def get_options_challenge(phrase, _):
}]


def get_chips(phrase, course):
def get_chips_from_string(phrase):
return list(map(clean_word, phrase.split()))


def get_chips_from_phrase(get_input_texts, phrase, course):
extra_chips = []
solution_chips = get_chips_from_string(get_input_texts(phrase)[0])

for phrase in iterate_phrases(course):
for variant in get_input_texts(phrase):
for chip in get_chips_from_string(variant):
if chip not in solution_chips:
extra_chips.append(chip)

chips_already_added = set()
deduplicated_chips = []

for chip in extra_chips:
if chip.lower() not in chips_already_added:
deduplicated_chips.append(chip)
chips_already_added.add(chip.lower())

extra_chips = sorted(
deduplicated_chips,
key=lambda chip: sum(
editdistance.eval(other_chip, chip) for other_chip in solution_chips
)
)

return solution_chips + extra_chips[0:max(len(solution_chips) - 1, 2)]


def get_solutions_from_phrase(get_input_texts, phrase):
return [get_chips_from_string(x) for x in get_input_texts(phrase)]


def create_chips_challenge_generator(reverse):
def get_input_texts(phrase):
return phrase.in_source_language if reverse else phrase.in_target_language
Expand Down Expand Up @@ -88,8 +122,8 @@ def get_chips_challenge(phrase, course):
"type": "chips",
"translatesToSourceLanguage": reverse,
"phrase": _define_words_in_sentence(course, get_phrase_text(phrase), reverse),
"chips": get_chips(get_input_text(phrase), course),
"solutions": [get_chips(x, course) for x in get_input_texts(phrase)],
"chips": get_chips_from_phrase(get_input_texts, phrase, course),
"solutions": get_solutions_from_phrase(get_input_texts, phrase),
"formattedSolution": get_input_text(phrase),
"id": get_dumb_opaque_id("Chips", phrase, "reverse chips" if reverse else "chips"),
"priority": 2,
Expand Down
48 changes: 44 additions & 4 deletions apps/librelingo_json_export/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion apps/librelingo_json_export/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "librelingo_json_export"
version = "0.6.3"
version = "0.7.0"
description = "Export LibreLingo courses in the JSON format used by the web app"
authors = ["Dániel Kántor <[email protected]>"]
license = "AGPLv3"
Expand All @@ -12,6 +12,7 @@ librelingo-types = "^3.0.0"
librelingo-utils = "^2.3.0"
python-slugify = "^4.0.1"
librelingo-yaml-loader = "^1.0.0"
editdistance = "^0.5.3"

[tool.poetry.dev-dependencies]
pytest = "^6.2.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
'La',
'femme',
'dit',
'bonjour'
'bonjour',
'la',
'salut',
"L'homme"
],
'formattedSolution': 'La femme dit bonjour',
'group': '921dd5d24167',
Expand Down Expand Up @@ -80,7 +83,9 @@
'The',
'woman',
'says',
'hello'
'hello',
'man',
'hi'
],
'formattedSolution': 'The woman says hello',
'group': '921dd5d24167',
Expand Down Expand Up @@ -143,7 +148,9 @@
'chips': [
"L'homme",
'dit',
'bonjour'
'bonjour',
'La',
'femme'
],
'formattedSolution': "L'homme dit bonjour",
'group': '0f389ca8a0c4',
Expand Down Expand Up @@ -189,7 +196,9 @@
'The',
'man',
'says',
'hello'
'hello',
'hi',
'woman'
],
'formattedSolution': 'The man says hello',
'group': '0f389ca8a0c4',
Expand Down
74 changes: 57 additions & 17 deletions apps/librelingo_json_export/tests/test_challenges.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import collections

from unittest.mock import patch
from unittest import TestCase
from librelingo_json_export.challenges import _get_challenges_data
Expand All @@ -8,7 +10,7 @@
from librelingo_json_export.challenge_types import get_listening_challenge
from librelingo_json_export.challenge_types import get_chips_challenge
from librelingo_json_export.challenge_types import get_options_challenge
from librelingo_json_export.challenge_types import get_chips
from librelingo_json_export.challenge_types import get_chips_from_phrase
from librelingo_types import Phrase
from librelingo_types import Settings
from librelingo_fakes import fakes
Expand Down Expand Up @@ -234,14 +236,15 @@ def test_returns_correct_value1(self):
'id': '9f9b09771a07',
'group': '930c4c4e7552',
"priority": 2,
"chips": ["foous", "barus"],
"chips": ["foous", "barus", "lorem", "ipsum"],
"solutions": [["foous", "barus"]],
"formattedSolution": "foous barus",
}

@patch('librelingo_json_export.challenge_types.get_chips')
def test_returns_correct_value2(self, get_chips):
get_chips.return_value = fakes.fake_value()
@patch('librelingo_json_export.challenge_types.get_solutions_from_phrase')
@patch('librelingo_json_export.challenge_types.get_chips_from_phrase')
def test_returns_correct_value2(self, get_chips_from_phrase, get_solutions_from_phrase):
get_chips_from_phrase.return_value = fakes.fake_value()
challenge = get_chips_challenge(
fakes.phrase_with_alternatives, fakes.course1)[0]
assert challenge == {
Expand All @@ -256,36 +259,73 @@ def test_returns_correct_value2(self, get_chips):
'id': '4b0e9208ce1b',
'group': '66a39e74a2c8',
"priority": 2,
"solutions": [get_chips.return_value, get_chips.return_value],
"chips": get_chips.return_value,
"solutions": get_solutions_from_phrase.return_value,
"chips": get_chips_from_phrase.return_value,
"formattedSolution": "foous barus foous barus ",
}

@patch('librelingo_json_export.challenge_types.get_chips')
def test_calls_get_chips_with_correct_value(self, get_chips):
get_chips_challenge(fakes.phrase1, fakes.course1)
get_chips.assert_called_with(fakes.phrase1.in_target_language[0], fakes.course1)


class GetChipsTest(TestCase):
def test_empty_string(self):
assert get_chips('', fakes.course1) == []
assert get_chips_from_phrase(lambda _: [''], None, fakes.course1) == []

@patch('librelingo_json_export.challenge_types.clean_word')
def test_empty_string_doesnt_call_clean_word(self, clean_word):
get_chips('', fakes.course1)
get_chips_from_phrase(lambda _: [''], None, fakes.course1)
assert not clean_word.called

@patch('librelingo_json_export.challenge_types.clean_word')
def test_calls_clean_word_with_correct_argument(self, clean_word):
get_chips('foo', fakes.course2)
get_chips_from_phrase(lambda _: ['foo'], None, fakes.course2)
clean_word.assert_called_with('foo')

@patch('librelingo_json_export.challenge_types.clean_word')
def test_returns_correct_value(self, clean_word):
clean_word.return_value = fakes.fake_value()
assert get_chips('foo', fakes.course1) == [clean_word.return_value]
assert get_chips_from_phrase(lambda _: 'foo', None, fakes.course1) == [clean_word.return_value]

@patch('librelingo_json_export.challenge_types.clean_word')
def test_returns_correct_number_of_words(self, clean_word):
assert len(get_chips('foo bar', fakes.course1)) == 2
# Includes extra words
clean_word.side_effect = lambda x: x
assert len(get_chips_from_phrase(lambda x: ['Sleep well!'] if x is None else x[0], None, fakes.course1)) == 4

def test_includes_words_from_other_phrases_sorted_by_edit_distance(self):
extra_chips = list(get_chips_from_phrase(lambda x: ['something lipsum'] if x is None else x[0], None, fakes.course1))
while "something" in extra_chips:
extra_chips.remove("something")
while "lipsum" in extra_chips:
extra_chips.remove("lipsum")
assert extra_chips == ['ipsum', 'lorem']

def test_doesnt_include_duplicates_from_another_phrase(self):
my_fake_module = fakes.customize(
fakes.course1.modules[0],
skills=[
fakes.customize(
fakes.course1.modules[0].skills[0],
phrases=[
fakes.customize(
fakes.course1.modules[0].skills[0].phrases[0],
in_target_language=["Ipsum"]
)
]
)
]
)
fake_course_with_duplicated_chip = fakes.customize(
fakes.course1,
modules=[fakes.course1.modules[0], fakes.course1.modules[0], my_fake_module]
)
chips_count = collections.Counter(get_chips_from_phrase(lambda x: ['something lipsum'] if x is None else x[0], None, fake_course_with_duplicated_chip))

assert chips_count["ipsum"] + chips_count["Ipsum"] == 1

def test_returns_correct_number_of_words_2(self):
fake_course_with_duplicated_chip = fakes.customize(
fakes.course1,
modules=[fakes.course1.modules[0], fakes.course1.modules[0]]
)
chips = list(get_chips_from_phrase(lambda x: ['something lipsum dolor'] if x is None else x[0], None, fake_course_with_duplicated_chip))

assert len(chips) == 5
1 change: 1 addition & 0 deletions apps/web/cypress/integration/chips.feature
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Feature: Chips challenge
And I click "Como"
And I click "estás"
And I click "hoy"
Then I have unused chips
Given I click "Submit"
Then I see the challenge panel with no skip button
And I read "Correct solution"
Expand Down
4 changes: 4 additions & 0 deletions apps/web/cypress/integration/chips/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ Then("I see the correct chips", () => {
cy.get(".chip").contains("estás").should("be.visible")
cy.get(".chip").contains("hoy").should("be.visible")
})

Then("I have unused chips", () => {
cy.get("#chips .chip").should("be.visible")
})
Loading

1 comment on commit 49eda4d

@vercel
Copy link

@vercel vercel bot commented on 49eda4d Jun 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.