Skip to content

Commit 9f7e145

Browse files
authored
Improve thread (#84)
* Refactor thread code into separated file * Update version * Clean up readme
1 parent ee6bf65 commit 9f7e145

File tree

10 files changed

+157
-184
lines changed

10 files changed

+157
-184
lines changed

README.rst

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -52,39 +52,4 @@ Check the `CONTRIBUING.rst <https://github.com/grupy-sanca/dojo-toolkit/blob/mai
5252
Dependencies
5353
------------
5454
- Python 3
55-
- `Libnotify <https://developer.gnome.org/libnotify>`_
56-
57-
58-
Roadmap
59-
-------
60-
61-
**0.5**
62-
63-
TDB
64-
65-
66-
Changelog
67-
---------
68-
69-
**0.4**
70-
71-
- Tests + CI
72-
- Sound notifications
73-
- Minor bug fixes
74-
- Minor improvements
75-
76-
**0.3**
77-
78-
- Major refactor
79-
- Uses bumpversion
80-
81-
**0.2**
82-
83-
- Project improvements
84-
- Release to PyPI
85-
86-
**0.1**
87-
88-
- Timer for managing rounds
89-
- Watch code folder and run doctests on changes
90-
- Show notifications using Libnotify
55+
- (Optional) `Libnotify <https://developer.gnome.org/libnotify>`_

dojo_toolkit/code_handler.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
from watchdog.events import PatternMatchingEventHandler
44

5-
from dojo_toolkit.notifier import notifier
6-
75

86
class DojoCodeHandler(PatternMatchingEventHandler):
97
"""Handles the python file changes"""
@@ -23,10 +21,6 @@ def __init__(self, *args, **kwargs):
2321
def get_last_test_run_interval(self):
2422
return time.time() - self.last_test_run_time
2523

26-
def handle_stopped_round(self):
27-
notifier.notify("Round has not been started")
28-
print("Press <Enter> to start the round")
29-
3024
def on_modified(self, event):
3125
"""Called when a file in the dojo directory is modified
3226
runs the doctest and display a notification
@@ -35,8 +29,7 @@ def on_modified(self, event):
3529
"""
3630

3731
if not self.dojo.round_started:
38-
return self.handle_stopped_round()
39-
32+
return
4033
if self.get_last_test_run_interval() < self.min_test_time_interval:
4134
return
4235
self.last_test_run_time = time.time()

dojo_toolkit/dojo.py

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,43 @@
11
import sys
2-
import time
32
from threading import Thread
43
from unittest import mock
54

6-
from clint.textui import colored
75
from watchdog.observers import Observer
86

7+
from dojo_toolkit import dojo_thread
98
from dojo_toolkit.code_handler import DojoCodeHandler
10-
from dojo_toolkit.notifier import notifier
119
from dojo_toolkit.sound_handler import SoundHandler
1210
from dojo_toolkit.test_runner import get_test_runner
1311
from dojo_toolkit.timer import Timer
1412

1513

1614
class Dojo:
1715
ROUND_TIME = 5
18-
round_started = False
1916

2017
def __init__(self, code_path, round_time=None, mute=False, test_runner=None, runner="doctest"):
2118
self.code_path = code_path
2219
self.round_time = round_time or self.ROUND_TIME
2320
self.sound_player = mock.Mock() if mute or sys.platform != "linux" else SoundHandler()
2421
self.info_notified = False
22+
self.timer = Timer(self.round_time)
2523

2624
test_runner = get_test_runner(test_runner, runner, self.code_path, self.sound_player)
25+
self.controller = dojo_thread.DojoController(self.timer, self.sound_player)
2726

28-
event_handler = DojoCodeHandler(dojo=self, test_runner=test_runner)
27+
event_handler = DojoCodeHandler(dojo=self.controller, test_runner=test_runner)
2928

3029
self.observer = Observer()
3130
self.observer.schedule(event_handler, self.code_path, recursive=False)
3231

33-
self.timer = Timer(self.round_time)
34-
3532
def start(self):
3633
self.observer.start()
3734
print("\nWatching: {} folder".format(self.code_path))
3835

3936
self.is_running = True
4037
print("Dojo toolkit started!")
41-
self.thread = Thread(target=self.dojo)
38+
self.thread = Thread(target=dojo_thread.main, args=(self.controller,))
4239
self.thread.daemon = True
4340
self.thread.start()
4441

4542
self.thread.join()
4643
self.observer.join()
47-
48-
def stop(self):
49-
self.is_running = False
50-
51-
def await_pilot_exchange(self):
52-
print("Awaiting the pilot and co-pilot to enter their positions.")
53-
print("Press <Enter> when they are ready")
54-
input()
55-
56-
def round_start(self):
57-
self.timer.start()
58-
self.sound_player.play_start()
59-
self.round_started = True
60-
self.info_notified = False
61-
62-
print("Round started! {} minutes left...".format(self.round_time))
63-
64-
def round_info(self):
65-
if self.timer.ellapsed_time == self.timer.duration - 60 and not self.info_notified:
66-
notifier.notify("60 seconds to round finish...")
67-
print((getattr(colored, "yellow"))("Round is going to finish in 60 seconds"))
68-
self.info_notified = True
69-
70-
def round_finished(self):
71-
notifier.notify("Time Up", timeout=15 * 1000)
72-
self.sound_player.play_timeup()
73-
self.round_started = False
74-
print("Round finished!\n")
75-
76-
def dojo(self):
77-
while self.is_running:
78-
self.await_pilot_exchange()
79-
self.round_start()
80-
while self.timer.is_running:
81-
self.round_info()
82-
time.sleep(0.8)
83-
self.round_finished()

dojo_toolkit/dojo_thread.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import time
2+
from dataclasses import dataclass
3+
4+
from clint.textui import colored
5+
6+
from dojo_toolkit.notifier import notifier
7+
from dojo_toolkit.sound_handler import SoundHandler
8+
from dojo_toolkit.timer import Timer
9+
10+
11+
@dataclass
12+
class DojoController:
13+
timer: Timer
14+
sound_player: SoundHandler
15+
is_running: bool = True
16+
round_started: bool = False
17+
info_notified: bool = False
18+
19+
def await_pilot_exchange(self):
20+
print("Awaiting the pilot and co-pilot to enter their positions.")
21+
print("Press <Enter> when they are ready")
22+
input()
23+
print("Starting round...")
24+
25+
def round_start(self):
26+
self.timer.start()
27+
self.sound_player.play_start()
28+
self.round_started = True
29+
self.info_notified = False
30+
31+
def round_info(self):
32+
if self.timer.elapsed_time == self.timer.duration - 60 and not self.info_notified:
33+
notifier.notify("60 seconds to round finish...")
34+
print((getattr(colored, "yellow"))("Round is going to finish in 60 seconds"))
35+
self.info_notified = True
36+
37+
def round_finished(self):
38+
notifier.notify("Time Up", timeout=15 * 1000)
39+
self.sound_player.play_timeup()
40+
self.round_started = False
41+
print("Round finished!\n")
42+
43+
44+
def main(controller: DojoController):
45+
while controller.is_running:
46+
controller.await_pilot_exchange()
47+
controller.round_start()
48+
while controller.timer.is_running:
49+
controller.round_info()
50+
time.sleep(0.8)
51+
controller.round_finished()

dojo_toolkit/timer.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
class Timer:
77
"""
88
A timer that runs on a separated thread and "ticks" every second. It
9-
keeps track of the ellapsed time.
9+
keeps track of the elapsed time.
1010
"""
1111

1212
def __init__(self, duration_in_minutes):
1313
self.duration = duration_in_minutes * 60
1414

1515
def start(self):
16-
self.ellapsed_time = 0
16+
self.elapsed_time = 0
1717

1818
self.is_running = True
1919
self.start_time = time.time()
@@ -22,7 +22,7 @@ def start(self):
2222
self.thread.start()
2323

2424
def timer(self):
25-
while self.ellapsed_time <= self.duration:
26-
self.ellapsed_time = floor(time.time() - self.start_time)
25+
while self.elapsed_time <= self.duration:
26+
self.elapsed_time = floor(time.time() - self.start_time)
2727
time.sleep(1)
2828
self.is_running = False

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "dojo-toolkit"
3-
version = "0.7.0a0"
3+
version = "0.7.0"
44
description = "Toolkit for Python Coding Dojos."
55
authors = ["grupy-sanca"]
66
packages = [{ include = "dojo_toolkit" }]

tests/test_dojo.py

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -22,79 +22,3 @@ def test_dojo_start(input, thread, mocked_dojo):
2222
assert mocked_dojo.thread.start.called
2323
assert mocked_dojo.thread.join.called
2424
assert mocked_dojo.observer.join.called
25-
26-
27-
@mock.patch("dojo_toolkit.dojo.input")
28-
def test_dojo_dojo(input, mocked_dojo):
29-
mocked_dojo.is_running = True
30-
mocked_dojo.timer.is_running = False
31-
mocked_dojo.round_finished = mock.Mock(side_effect=KeyboardInterrupt)
32-
with pytest.raises(KeyboardInterrupt):
33-
mocked_dojo.dojo()
34-
35-
36-
@mock.patch("dojo_toolkit.dojo.input")
37-
def test_dojo_dojo_timer_running(input, mocked_dojo):
38-
mocked_dojo.is_running = True
39-
mocked_dojo.timer.is_running = True
40-
41-
def stop_dojo():
42-
mocked_dojo.is_running = False
43-
44-
def stop_timer():
45-
mocked_dojo.timer.is_running = False
46-
47-
mocked_dojo.round_info = mock.Mock(side_effect=stop_timer)
48-
mocked_dojo.round_finished = mock.Mock(side_effect=stop_dojo)
49-
mocked_dojo.dojo()
50-
51-
52-
def test_dojo_dojo_stopped(mocked_dojo):
53-
mocked_dojo.is_running = False
54-
mocked_dojo.dojo()
55-
56-
57-
@mock.patch("dojo_toolkit.dojo.input")
58-
def test_dojo_await_pilot_exchange(six_input, mocked_dojo):
59-
mocked_dojo.await_pilot_exchange()
60-
assert six_input.called
61-
62-
63-
def test_dojo_round_start(mocked_dojo):
64-
assert mocked_dojo.round_started is False
65-
66-
mocked_dojo.round_start()
67-
68-
assert mocked_dojo.timer.start.called
69-
assert mocked_dojo.sound_player.play_start.called
70-
assert mocked_dojo.round_started is True
71-
72-
73-
@mock.patch("dojo_toolkit.dojo.notifier")
74-
def test_dojo_round_info_without_notification(notifier, mocked_dojo):
75-
mocked_dojo.round_info()
76-
assert not notifier.notify.called
77-
78-
79-
@mock.patch("dojo_toolkit.dojo.notifier")
80-
def test_dojo_round_info_with_notification(notifier, mocked_dojo):
81-
mocked_dojo.timer.duration = 120
82-
mocked_dojo.timer.ellapsed_time = 60
83-
mocked_dojo.round_info()
84-
assert notifier.notify.called
85-
86-
87-
@mock.patch("dojo_toolkit.dojo.notifier")
88-
def test_dojo_round_finished(notifier, mocked_dojo):
89-
mocked_dojo.round_started = True
90-
91-
mocked_dojo.round_finished()
92-
93-
assert notifier.notify.called
94-
assert mocked_dojo.sound_player.play_timeup.called
95-
assert mocked_dojo.round_started is False
96-
97-
98-
def test_dojo_stop(mocked_dojo):
99-
mocked_dojo.stop()
100-
assert mocked_dojo.is_running is False

0 commit comments

Comments
 (0)