diff --git a/fasttyper/application.py b/fasttyper/application.py index b3db2f9..98fade3 100644 --- a/fasttyper/application.py +++ b/fasttyper/application.py @@ -29,6 +29,9 @@ class Application: self.silent_exit = True if self.running(): self.buffer.stats.signal_stop() + except: + if self.running(): + self.buffer.stats.signal_stop() def summarize(self): if self.finished: diff --git a/fasttyper/components.py b/fasttyper/components.py index a478094..5c422cc 100644 --- a/fasttyper/components.py +++ b/fasttyper/components.py @@ -326,22 +326,50 @@ class TextBox(BufferDependentComponent): self.refresh() -class StatsBox(BorderWithImprintedStats): +class StatsBox(BorderedBox): """ Displays stats """ def __init__(self, config): super().__init__(config) - self.wpm_color = config.get("wpm_graph_color") - self.raw_wpm_color = config.get("raw_wpm_graph_color") - self.errors_color = config.get("errors_graph_color") + self.logo = config.get("logo") + self.logo_color = config.get("logo_color") + self.resume_text = config.get("resume_text") + self.resume_text_color = config.get("resume_text_color") + self.template = config.get("end_template") + self.color = config.get("end_color") + + def paint_logo(self): + if len(self.logo) <= self.width: + self.paint_text( + -1, (self.width - len(self.logo)) // 2, self.logo, self.logo_color + ) + + def paint_resume_text(self): + if len(self.resume_text) <= self.width: + self.paint_text( + self.height, + (self.width - len(self.resume_text)) // 2, + self.resume_text, + self.resume_text_color, + ) + + def paint_stats(self, application): + record = application.buffer.stats.produce_record(for_csv=True) + lines = [l.format_map(record) for l in self.template] + max_l = max((len(l) for l in lines)) + + if max_l > self.width: + return + + for i, text in enumerate(lines): + self.paint_text(i, 1, text, self.color) def paint(self, screen, application): self.maxy, self.maxx = screen.getmaxyx() self.init(screen, application) - self.paint_stats() - self.paint_text( - self.height // 2, self.width // 2 - 5, "FASTTYPER", self.errors_color - ) + self.paint_logo() + self.paint_resume_text() + self.paint_stats(application) self.refresh() diff --git a/fasttyper/config.py b/fasttyper/config.py index 4cb445d..7cb4f53 100644 --- a/fasttyper/config.py +++ b/fasttyper/config.py @@ -10,9 +10,16 @@ class Config: "top_margin_percentage": 40, "left_margin_percentage": 35, "lines_on_screen": 3, - "wpm_graph_color": 3, - "raw_wpm_graph_color": 9, - "errors_graph_color": 2, + "logo": "~FastTyper~", + "logo_color": 8, + "resume_text": "press any key to continue, C to exit", + "resume_text_color": 9, + "end_template": ( + "wpm: {wpm:.1f}/{peak_wpm:.1f} raw: {raw_wpm:.1f}/{peak_raw_wpm:.1f}", + "acc: {accuracy:.1f} chars: {correct_chars}/{total_chars} words: {correct_words}/{total_words}", + "time: {total_seconds:.1f}s", + ), + "end_color": 9, } def __init__(self, configmap): diff --git a/fasttyper/stats.py b/fasttyper/stats.py index 81b42a9..9c7aa00 100644 --- a/fasttyper/stats.py +++ b/fasttyper/stats.py @@ -60,7 +60,7 @@ class Stats: def total_seconds(self): stop_dtime = self.stop_dtime or datetime.now() start_dtime = self.start_dtime or datetime.now() - return (stop_dtime - start_dtime).total_seconds() or 1 + return max(((stop_dtime - start_dtime).total_seconds(), 1)) @property def total_minutes(self): @@ -90,16 +90,30 @@ class Stats: def raw_cpm(self): return self.total_chars / self.total_minutes + @property + def peak_raw_cpm(self): + return max([s["raw_cpm"] for s in self.snaps] + [0.0]) + + @property + def peak_cpm(self): + return max([s["raw_cpm"] for s in self.snaps] + [0.0]) + + @property + def peak_raw_wpm(self): + return self.peak_raw_cpm / 5 + + @property + def peak_wpm(self): + return self.peak_cpm / 5 + @property def accuracy(self): if self.total_chars: return self.correct_chars / self.total_chars * 100 return 100 - def produce_record(self): - return { - "start_dtime": self.start_dtime.isoformat() if self.start_dtime else None, - "stop_dtime": self.stop_dtime.isoformat() if self.stop_dtime else None, + def produce_record(self, for_csv=False): + result = { "total_seconds": self.total_seconds, "total_minutes": self.total_minutes, "total_chars": self.total_chars, @@ -113,10 +127,26 @@ class Stats: "raw_wpm": self.raw_wpm, "raw_cpm": self.raw_cpm, "accuracy": self.accuracy, - "mode": self.runtime_config.mode, - "language": self.runtime_config.language, - "words": self.runtime_config.words, } + if for_csv: + result.update( + { + "peak_cpm": self.peak_cpm, + "peak_raw_cpm": self.peak_raw_cpm, + "peak_wpm": self.peak_wpm, + "peak_raw_wpm": self.peak_raw_wpm, + "start_dtime": self.start_dtime.isoformat() + if self.start_dtime + else None, + "stop_dtime": self.stop_dtime.isoformat() + if self.stop_dtime + else None, + "mode": self.runtime_config.mode, + "language": self.runtime_config.language, + "words": self.runtime_config.words, + } + ) + return result def export_to_datafile(self, datafile): if datafile is None: @@ -128,7 +158,7 @@ class Stats: pathlib.Path(data_dir).mkdir(exist_ok=True, parents=True) exists = os.path.isfile(datafile) - record = self.produce_record() + record = self.produce_record(for_csv=True) if not exists: with open(datafile, "w") as f: diff --git a/setup.cfg b/setup.cfg index acc31ee..d72f28e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.6 +current_version = 2.1.0 [wheel] universal = 1 diff --git a/setup.py b/setup.py index 2dab2bb..794c874 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ with open("requirements.txt", "r", encoding="utf-8") as fh: setup( name="fasttyper", - version="2.0.6", + version="2.1.0", author="Piotr Domanski", author_email="pi.domanski@gmail.com", description="Minimalistic typing exercise",