Skip to content

Commit 96e7f95

Browse files
committed
Fix remaining documentation; enable Pylint
Make Pylint fail if there are any warnings or suggestions.
1 parent 52d2c01 commit 96e7f95

File tree

6 files changed

+115
-19
lines changed

6 files changed

+115
-19
lines changed

.githooks/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ times=()
1515

1616
for i in "${!tools[@]}"; do
1717
SECONDS=0
18+
echo "Checking with ${tools[i]}..."
1819
tool_name="${tools[i]}"
1920
command="${commands[i]}"
2021
if ! eval "$command"; then

emmio/graph.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Visualization utilities."""
2+
13
import random
24
from collections import defaultdict
35
from collections.abc import Callable, Iterator
@@ -23,16 +25,24 @@
2325

2426

2527
class Visualizer:
28+
"""Graph visualizer."""
29+
30+
interactive: bool
31+
"""Whether to show the graph interactively."""
32+
2633
def __init__(self, interactive: bool = True):
2734
self.interactive: bool = interactive
2835

2936
def plot(self):
37+
"""Plot the graph."""
3038
if self.interactive:
3139
plt.show()
3240
else:
3341
plt.savefig("out/graph.svg")
3442

3543
def actions(self, records: list[tuple[LearningRecord, Learning]]):
44+
"""Show the graph of learning actions."""
45+
3646
x: list[datetime] = []
3747
y: list[int] = []
3848
count_learning: int = 0
@@ -52,12 +62,13 @@ def cumulative_actions(
5262
width: float,
5363
by_language: bool = False,
5464
) -> None:
65+
"""Show the graph of cumulative learning actions."""
5566

5667
data: dict[str | int, dict[datetime, int]]
5768
size: int
5869

5970
if by_language:
60-
learnings = {x[1] for x in records}
71+
learnings: set[Learning] = {x[1] for x in records}
6172
data = {
6273
x.learning_language.get_code(): defaultdict(int)
6374
for x in learnings
@@ -158,9 +169,11 @@ def cumulative_actions_moving(
158169
self,
159170
records: list[tuple[LearningRecord, Learning]],
160171
days: int,
161-
):
162-
day_min = day_start(min(x[0].time for x in records))
163-
day_max = day_start(max(x[0].time for x in records))
172+
) -> None:
173+
"""Show the graph of cumulative learning actions with moving average."""
174+
175+
day_min: datetime = day_start(min(x[0].time for x in records))
176+
day_max: datetime = day_start(max(x[0].time for x in records))
164177

165178
data: list[int] = [0] * ((day_max - day_min).days + 1)
166179

@@ -176,7 +189,9 @@ def cumulative_actions_moving(
176189

177190
self.plot()
178191

179-
def knowing(self, learnings: list[Learning]):
192+
def knowing(self, learnings: list[Learning]) -> None:
193+
"""Show the graph of knowing words."""
194+
180195
records: list[tuple[str, LearningRecord]] = []
181196

182197
for learning in learnings:
@@ -324,6 +339,8 @@ def knowing(self, learnings: list[Learning]):
324339
def history(
325340
self, learnings: Iterator[Learning], marker_size: float = 0.5
326341
) -> None:
342+
"""Show the graph of learning history."""
343+
327344
data: dict[datetime, int] = {}
328345
index: int = 0
329346
indices: dict[str, int] = {}
@@ -346,7 +363,9 @@ def history(
346363
)
347364
self.plot()
348365

349-
def next_question_time(self, learnings: Iterator[Learning]):
366+
def next_question_time(self, learnings: Iterator[Learning]) -> None:
367+
"""Show the graph of next question time."""
368+
350369
data: dict[datetime, float] = {}
351370
for learning in learnings:
352371
for word in learning.knowledge:
@@ -365,6 +384,8 @@ def next_question_time(self, learnings: Iterator[Learning]):
365384
self.plot()
366385

367386
def graph_mistakes(self, learnings: Iterator[Learning]) -> None:
387+
"""Show the graph of mistakes."""
388+
368389
for learning in learnings:
369390
records: list[tuple[str, LearningRecord]] = []
370391
for record in learning.process.records:
@@ -406,8 +427,7 @@ def response_time(
406427
steps: int = 5,
407428
max_: int = 60,
408429
) -> None:
409-
"""
410-
Draw user response time histogram.
430+
"""Draw user response time histogram.
411431
412432
:param records: user response records
413433
:param steps: number of histogram steps per second

emmio/learn/teacher.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def __init__(
115115
self.max_for_day: int = self.learning.config.max_for_day
116116

117117
async def get_new_question(self) -> str | None:
118+
"""Get new question to learn."""
118119

119120
for question_id, list_, index in self.question_ids[
120121
self.question_index :
@@ -129,8 +130,13 @@ async def get_new_question(self) -> str | None:
129130
return None
130131

131132
async def check_question_id(self, question_id) -> bool:
132-
# Check whether the learning process already has the word: whether
133-
# it was initially known or it is learning.
133+
"""Check whether the learning process already has the word.
134+
135+
Whether it was initially known or it is learning.
136+
137+
:param question_id: question identifier
138+
:return: whether the word is known
139+
"""
134140
if self.learning.has(question_id):
135141
if self.learning.is_initially_known(question_id):
136142
logging.info("Was initially known")
@@ -154,6 +160,10 @@ async def check_question_id(self, question_id) -> bool:
154160
return await self.check2(question_id)
155161

156162
async def check2(self, question_id: str) -> bool:
163+
"""Check whether the word should be asked.
164+
165+
:param question_id: question identifier
166+
"""
157167
# TODO: rename.
158168
# Check user lexicon. Skip the word if it was mark as known by user
159169
# while checking lexicon. This should be done after checking
@@ -199,6 +209,10 @@ async def check2(self, question_id: str) -> bool:
199209
return True
200210

201211
async def check_common(self, question_id: str) -> bool:
212+
"""Check whether the word is common.
213+
214+
:param question_id: question identifier
215+
"""
202216
# Request word definition in the dictionary.
203217
items: list[DictionaryItem] = (
204218
await self.dictionaries_to_check.get_items(
@@ -217,8 +231,7 @@ async def check_common(self, question_id: str) -> bool:
217231
question_id, self.learning.learning_language, follow_links=False
218232
)
219233
)
220-
221-
not_common = False
234+
not_common: bool = False
222235
for item in items_no_links:
223236
for language in self.learning.base_languages:
224237
if item.has_definitions() and item.is_not_common(language):
@@ -231,6 +244,11 @@ async def check_common(self, question_id: str) -> bool:
231244
return True
232245

233246
async def repeat(self, max_actions: int | None = None) -> bool:
247+
"""Start repeating process.
248+
249+
:param max_actions: maximum number of actions
250+
:return: whether to continue
251+
"""
234252
actions: int = 0
235253
to_continue: bool
236254
session: LearningSession = LearningSession(
@@ -265,6 +283,11 @@ async def repeat(self, max_actions: int | None = None) -> bool:
265283
return to_continue
266284

267285
async def learn_new(self, max_actions: int | None = None) -> bool:
286+
"""Start learning new words.
287+
288+
:param max_actions: maximum number of actions
289+
:return: whether to continue
290+
"""
268291
actions: int = 0
269292
to_continue: bool
270293
session: LearningSession = LearningSession(
@@ -300,6 +323,11 @@ async def learn_new(self, max_actions: int | None = None) -> bool:
300323
async def repeat_and_learn_new(
301324
self, max_actions: int | None = None
302325
) -> bool:
326+
"""Start repeating and learning new words.
327+
328+
:param max_actions: maximum number of actions
329+
:return: whether to continue
330+
"""
303331
actions: int = 0
304332
to_continue: bool
305333
session: LearningSession = LearningSession(
@@ -349,7 +377,11 @@ async def repeat_and_learn_new(
349377

350378
return to_continue
351379

352-
def play(self, word: str):
380+
def play(self, word: str) -> None:
381+
"""Play the audio pronunciation of the word.
382+
383+
:param word: word to play
384+
"""
353385
self.audio.play(word)
354386

355387
def print_sentence(
@@ -404,7 +436,11 @@ def print_sentence(
404436
self.interface.print("\n".join(translations))
405437

406438
async def learn(self, word: str, knowledge: Knowledge | None) -> str:
439+
"""Start learning the word.
407440
441+
:param word: word to learn
442+
:param knowledge: knowledge of the word
443+
"""
408444
ids_to_skip: set[int] = set()
409445
# if word in self.data.exclude_sentences:
410446
# ids_to_skip = set(self.data.exclude_sentences[word])
@@ -598,8 +634,15 @@ async def learn(self, word: str, knowledge: Knowledge | None) -> str:
598634
elif index == len(rated_sentences):
599635
self.interface.print("No more sentences.")
600636

601-
async def process_command(self, command: str, word: str, sentence_id: int):
637+
async def process_command(
638+
self, command: str, word: str, sentence_id: int
639+
) -> None:
640+
"""Process the command.
602641
642+
:param command: user command
643+
:param word: current learning word
644+
:param sentence_id: sentence identifier
645+
"""
603646
if command in ["s", "/skip"]:
604647
self.learning.register(Response.SKIP, sentence_id, word)
605648
print(f'Word "{word}" is no longer in the learning process.')

emmio/learn/visualizer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# type: ignore
2+
"""Learning visualizer."""
23

34
from collections import defaultdict
45
from dataclasses import dataclass
@@ -26,6 +27,13 @@
2627

2728

2829
def get_depth(interval: timedelta) -> int:
30+
"""Get depth of the word learning.
31+
32+
The bigger the interval, the deeper the learning.
33+
34+
:param interval: interval of the learning process
35+
:return: depth of the learning process
36+
"""
2937
if not (seconds := interval.total_seconds()):
3038
return 0
3139

@@ -34,6 +42,8 @@ def get_depth(interval: timedelta) -> int:
3442

3543
@dataclass
3644
class LearningVisualizer:
45+
"""Learning process visualizer."""
46+
3747
# User responses.
3848
records: list[tuple[LearningRecord, Learning]]
3949

emmio/plot.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Plotting utilities."""
2+
13
import logging
24
from dataclasses import dataclass, field
35
from pathlib import Path
@@ -39,6 +41,8 @@ def map_(value, current_min, current_max, target_min, target_max):
3941

4042

4143
def map_array(value, current_min, current_max, target_min, target_max):
44+
"""Remap array of values from current bounds to target bounds."""
45+
4246
result = [None, None]
4347
for index in 0, 1:
4448
result[index] = map_(
@@ -108,7 +112,9 @@ def map_(self, point):
108112
self.canvas.workspace[1],
109113
)
110114

111-
def draw_background(self, svg) -> None:
115+
def draw_background(self, svg: Drawing) -> None:
116+
"""Draw background as a rectangle."""
117+
112118
svg.add(
113119
svg.rect(
114120
insert=(0, 0),
@@ -119,7 +125,9 @@ def draw_background(self, svg) -> None:
119125
)
120126
)
121127

122-
def plot(self, svg: Drawing, data) -> None:
128+
def plot(self, svg: Drawing, data: list) -> None:
129+
"""Plot data."""
130+
123131
recolor: str | None = None
124132

125133
if isinstance(self.color, list):
@@ -179,6 +187,8 @@ def plot(self, svg: Drawing, data) -> None:
179187
last_text_y = text_y
180188

181189
def to_points(self, xs: list, ys: list) -> list:
190+
"""Convert data to points on the canvas."""
191+
182192
xs_second: list[float] = [(x - self.min_x).total_seconds() for x in xs]
183193
points: list = []
184194

@@ -195,8 +205,17 @@ def to_points(self, xs: list, ys: list) -> list:
195205
return points
196206

197207
def fill_between(
198-
self, svg: Drawing, xs, ys_1, ys_2, color=None, label=None, opacity=None
208+
self,
209+
svg: Drawing,
210+
xs: list,
211+
ys_1: list,
212+
ys_2: list,
213+
color: Color | str | None = None,
214+
label: str | None = None,
215+
opacity: float | None = None,
199216
) -> None:
217+
"""Fill between two lines."""
218+
200219
recolor: str | None = None
201220

202221
if isinstance(self.color, list):
@@ -240,13 +259,17 @@ def fill_between(
240259
self.text(svg, (points[-1][0] + 15, text_y), label, color)
241260
self.last_text_y = text_y
242261

243-
def write(self, svg):
262+
def write(self, svg: Drawing) -> None:
263+
"""Write the graph to a file."""
264+
244265
with Path(svg.filename).open("w+", encoding="utf-8") as output_file:
245266
svg.write(output_file)
246267

247268
logging.info("Graph was saved to `%s`.", Path(svg.filename).absolute())
248269

249270
def draw_grid(self, svg: Drawing) -> None:
271+
"""Draw grid."""
272+
250273
group: Group = Group(opacity=0.25)
251274
for index in range(self.min_y, self.max_y + 1):
252275
mapped_1: np.ndarray = self.map_((0, index))

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ disable_error_code = ["import-untyped"]
6161

6262
[tool.pylint]
6363
py-version = "3.12"
64-
fail-under = 9.0
6564
disable = [
6665
"E0401", # Import error (Pylint usually fails to track imports correctly).
6766
"W0511", # `TODO`/`FIXME` comments warning.

0 commit comments

Comments
 (0)