Skip to content

Commit 78640f0

Browse files
committed
close #4, will include modaq methods separately in future commits
1 parent e46ba8a commit 78640f0

File tree

4 files changed

+226
-4
lines changed

4 files changed

+226
-4
lines changed

qbreader/asynchronous.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ async def query(
6767
queryString: Optional[str] = "",
6868
exactPhrase: Optional[bool] = False,
6969
ignoreDiacritics: Optional[bool] = False,
70+
ignoreWordOrder: Optional[bool] = False,
7071
regex: Optional[bool] = False,
7172
randomize: Optional[bool] = False,
7273
setName: Optional[str] = None,
@@ -94,6 +95,8 @@ class type.
9495
Ensure that the query string is an exact phrase.
9596
ignoreDiacritics : bool, default = False
9697
Ignore or transliterate diacritical marks in `queryString`.
98+
ignoreWordOrder : bool, default = False
99+
Treat `queryString` as a set of keywords that can appear in any order.
97100
regex : bool, default = False
98101
Treat `queryString` as a regular expression.
99102
randomize : bool, default = False
@@ -142,8 +145,14 @@ class type.
142145

143146
for name, param in tuple(
144147
zip(
145-
("exactPhrase", "ignoreDiacritics", "regex", "randomize"),
146-
(exactPhrase, ignoreDiacritics, regex, randomize),
148+
(
149+
"exactPhrase",
150+
"ignoreDiacritics",
151+
"ignoreWordOrder",
152+
"regex",
153+
"randomize",
154+
),
155+
(exactPhrase, ignoreDiacritics, ignoreWordOrder, regex, randomize),
147156
)
148157
):
149158
if not isinstance(param, bool):
@@ -175,6 +184,7 @@ class type.
175184
"queryString": queryString,
176185
"exactPhrase": api_utils.normalize_bool(exactPhrase),
177186
"ignoreDiacritics": api_utils.normalize_bool(ignoreDiacritics),
187+
"ignoreWordOrder": api_utils.normalize_bool(ignoreWordOrder),
178188
"regex": api_utils.normalize_bool(regex),
179189
"randomize": api_utils.normalize_bool(randomize),
180190
"setName": setName,
@@ -590,3 +600,63 @@ async def check_answer(
590600
return await AnswerJudgement.check_answer_async(
591601
answerline, givenAnswer, self.session
592602
)
603+
604+
async def tossup_by_id(self: Self, id: str) -> Tossup:
605+
"""Get a tossup by its ID.
606+
607+
Original API doc at https://www.qbreader.org/api-docs/tossup-by-id.
608+
609+
Parameters
610+
----------
611+
id : str
612+
The ID of the tossup to get.
613+
614+
Returns
615+
-------
616+
Tossup
617+
A `Tossup` object.
618+
"""
619+
url = BASE_URL + "/tossup-by-id"
620+
621+
data = {
622+
"id": id,
623+
}
624+
625+
async with self.session.get(url, params=data) as response:
626+
if response.status != 200:
627+
if response.status == 400:
628+
raise ValueError(f"Invalid tossup ID: {id}")
629+
raise Exception(str(response.status) + " bad request")
630+
631+
json = await response.json()
632+
return Tossup.from_json(json["tossup"])
633+
634+
async def bonus_by_id(self: Self, id: str) -> Bonus:
635+
"""Get a bonus by its ID.
636+
637+
Original API doc at https://www.qbreader.org/api-docs/bonus-by-id.
638+
639+
Parameters
640+
----------
641+
id : str
642+
The ID of the bonus to get.
643+
644+
Returns
645+
-------
646+
Bonus
647+
A `Bonus` object.
648+
"""
649+
url = BASE_URL + "/bonus-by-id"
650+
651+
data = {
652+
"id": id,
653+
}
654+
655+
async with self.session.get(url, params=data) as response:
656+
if response.status != 200:
657+
if response.status == 400:
658+
raise ValueError(f"Invalid bonus ID: {id}")
659+
raise Exception(str(response.status) + " bad request")
660+
661+
json = await response.json()
662+
return Bonus.from_json(json["bonus"])

qbreader/synchronous.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def query(
3232
queryString: Optional[str] = "",
3333
exactPhrase: Optional[bool] = False,
3434
ignoreDiacritics: Optional[bool] = False,
35+
ignoreWordOrder: Optional[bool] = False,
3536
regex: Optional[bool] = False,
3637
randomize: Optional[bool] = False,
3738
setName: Optional[str] = None,
@@ -59,6 +60,8 @@ class type.
5960
Ensure that the query string is an exact phrase.
6061
ignoreDiacritics : bool, default = False
6162
Ignore or transliterate diacritical marks in `queryString`.
63+
ignoreWordOrder : bool, default = False
64+
Treat `queryString` as a set of keywords that can appear in any order.
6265
regex : bool, default = False
6366
Treat `queryString` as a regular expression.
6467
randomize : bool, default = False
@@ -107,8 +110,14 @@ class type.
107110

108111
for name, param in tuple(
109112
zip(
110-
("exactPhrase", "ignoreDiacritics", "regex", "randomize"),
111-
(exactPhrase, ignoreDiacritics, regex, randomize),
113+
(
114+
"exactPhrase",
115+
"ignoreDiacritics",
116+
"ignoreWordOrder",
117+
"regex",
118+
"randomize",
119+
),
120+
(exactPhrase, ignoreDiacritics, ignoreWordOrder, regex, randomize),
112121
)
113122
):
114123
if not isinstance(param, bool):
@@ -140,6 +149,7 @@ class type.
140149
"queryString": queryString,
141150
"exactPhrase": api_utils.normalize_bool(exactPhrase),
142151
"ignoreDiacritics": api_utils.normalize_bool(ignoreDiacritics),
152+
"ignoreWordOrder": api_utils.normalize_bool(ignoreWordOrder),
143153
"regex": api_utils.normalize_bool(regex),
144154
"randomize": api_utils.normalize_bool(randomize),
145155
"setName": setName,
@@ -551,3 +561,63 @@ def check_answer(self: Self, answerline: str, givenAnswer: str) -> AnswerJudgeme
551561
A `AnswerJudgement` object containing the response.
552562
"""
553563
return AnswerJudgement.check_answer_sync(answerline, givenAnswer)
564+
565+
def tossup_by_id(self: Self, id: str) -> Tossup:
566+
"""Get a tossup by its ID.
567+
568+
Original API doc at https://www.qbreader.org/api-docs/tossup-by-id.
569+
570+
Parameters
571+
----------
572+
id : str
573+
The ID of the tossup to get.
574+
575+
Returns
576+
-------
577+
Tossup
578+
A `Tossup` object.
579+
"""
580+
url = BASE_URL + "/tossup-by-id"
581+
582+
data = {
583+
"id": id,
584+
}
585+
586+
response: requests.Response = requests.get(url, params=data)
587+
588+
if response.status_code != 200:
589+
if response.status_code == 400:
590+
raise ValueError(f"Invalid tossup ID: {id}")
591+
raise Exception(str(response.status_code) + " bad request")
592+
593+
return Tossup.from_json(response.json()["tossup"])
594+
595+
def bonus_by_id(self: Self, id: str) -> Bonus:
596+
"""Get a bonus by its ID.
597+
598+
Original API doc at https://www.qbreader.org/api-docs/bonus-by-id.
599+
600+
Parameters
601+
----------
602+
id : str
603+
The ID of the bonus to get.
604+
605+
Returns
606+
-------
607+
Bonus
608+
A `Bonus` object.
609+
"""
610+
url = BASE_URL + "/bonus-by-id"
611+
612+
data = {
613+
"id": id,
614+
}
615+
616+
response: requests.Response = requests.get(url, params=data)
617+
618+
if response.status_code != 200:
619+
if response.status_code == 400:
620+
raise ValueError(f"Invalid bonus ID: {id}")
621+
raise Exception(str(response.status_code) + " bad request")
622+
623+
return Bonus.from_json(response.json()["bonus"])

tests/test_async.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,53 @@ async def test_check_answer_bad_response(self, qbr, mock_get):
541541
givenAnswer="Rubik's cubes",
542542
)
543543

544+
@pytest.mark.asyncio
545+
@pytest.mark.parametrize(
546+
"id, expected_answer",
547+
[
548+
("657fd7d7de6df0163bbe3b3d", "Sweden"),
549+
("657fd7d8de6df0163bbe3b43", "jQuery"),
550+
],
551+
)
552+
async def test_tossup_by_id(self, qbr, id: str, expected_answer: str):
553+
tu: qb.Tossup = await qbr.tossup_by_id(id)
554+
judgement: qb.AnswerJudgement = await tu.check_answer_async(
555+
expected_answer, session=qbr.session
556+
)
557+
assert judgement.correct()
558+
559+
@pytest.mark.asyncio
560+
async def test_tossup_by_id_bad_response(self, qbr, mock_get):
561+
await async_assert_exception(qbr.tossup_by_id, ValueError, id="not a valid id")
562+
mock_get(mock_status_code=404)
563+
await async_assert_exception(
564+
qbr.tossup_by_id, Exception, id="657fd7d7de6df0163bbe3b3d"
565+
)
566+
567+
@pytest.mark.asyncio
568+
@pytest.mark.parametrize(
569+
"id, expected_answers",
570+
[
571+
("648938e130bd7ab56b095a42", ["volcano", "Magellan", "terra"]),
572+
("648938e130bd7ab56b095a60", ["pH", "NADPH", "perforin"]),
573+
],
574+
)
575+
async def test_bonus_by_id(self, qbr, id: str, expected_answers: list[str]):
576+
b: qb.Bonus = await qbr.bonus_by_id(id)
577+
for i, answer in enumerate(expected_answers):
578+
judgement: qb.AnswerJudgement = await b.check_answer_async(
579+
i, answer, session=qbr.session
580+
)
581+
assert judgement.correct()
582+
583+
@pytest.mark.asyncio
584+
async def test_bonus_by_id_bad_response(self, qbr, mock_get):
585+
await async_assert_exception(qbr.bonus_by_id, ValueError, id="not a valid id")
586+
mock_get(mock_status_code=404)
587+
await async_assert_exception(
588+
qbr.bonus_by_id, Exception, id="648938e130bd7ab56b095a42"
589+
)
590+
544591
@pytest.mark.asyncio
545592
async def test_close(self, qbr):
546593
await qbr.close()

tests/test_sync.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,38 @@ def test_check_answer_bad_response(self, mock_get):
431431
answerline="Rubik's cubes",
432432
givenAnswer="Rubik's cubes",
433433
)
434+
435+
@pytest.mark.parametrize(
436+
"id, expected_answer",
437+
[
438+
("657fd7d7de6df0163bbe3b3d", "Sweden"),
439+
("657fd7d8de6df0163bbe3b43", "jQuery"),
440+
],
441+
)
442+
def test_tossup_by_id(self, id: str, expected_answer: str):
443+
tu: qb.Tossup = qbr.tossup_by_id(id)
444+
judgement: qb.AnswerJudgement = tu.check_answer_sync(expected_answer)
445+
assert judgement.correct()
446+
447+
def test_tossup_by_id_bad_response(self, mock_get):
448+
assert_exception(qbr.tossup_by_id, ValueError, id="not a valid id")
449+
mock_get(mock_status_code=404)
450+
assert_exception(qbr.tossup_by_id, Exception, id="657fd7d7de6df0163bbe3b3d")
451+
452+
@pytest.mark.parametrize(
453+
"id, expected_answers",
454+
[
455+
("648938e130bd7ab56b095a42", ["volcano", "Magellan", "terra"]),
456+
("648938e130bd7ab56b095a60", ["pH", "NADPH", "perforin"]),
457+
],
458+
)
459+
def test_bonus_by_id(self, id: str, expected_answers: list[str]):
460+
b: qb.Bonus = qbr.bonus_by_id(id)
461+
for i, answer in enumerate(expected_answers):
462+
judgement: qb.AnswerJudgement = b.check_answer_sync(i, answer)
463+
assert judgement.correct()
464+
465+
def test_bonus_by_id_bad_response(self, mock_get):
466+
assert_exception(qbr.bonus_by_id, ValueError, id="not a valid id")
467+
mock_get(mock_status_code=404)
468+
assert_exception(qbr.bonus_by_id, Exception, id="648938e130bd7ab56b095a42")

0 commit comments

Comments
 (0)