diff --git a/doc/example_config.json b/doc/example_config.json index 14775ad..eeea41d 100644 --- a/doc/example_config.json +++ b/doc/example_config.json @@ -1 +1,9 @@ -{"user_input_valid_color": 3, "user_input_invalid_color": 2, "reference_text_color": 8, "stats_template": "\n\nwpm: {stats.wpm}\ntime: {stats.total_seconds}s", "stats_color": 5, "summary_template": "WPM: {stats.wpm}\nCPM: {stats.cpm}\nRAW WPM: {stats.raw_wpm}\nRAW CPM: {stats.raw_cpm}\ntotal seconds: {stats.total_seconds}\ntotal minutes: {stats.total_minutes}\ncorrect words: {stats.correct_words}\ncorrect chars: {stats.correct_chars}\nincorrect words: {stats.incorrect_words}\nincorrect chars: {stats.incorrect_chars}\ntotal words: {stats.total_words}\ntotal chars: {stats.total_chars}\naccuracy: {stats.accuracy}%"} \ No newline at end of file +{ + "user_input_valid_color": 3, + "user_input_invalid_color": 2, + "reference_text_color": 8, + "stats_template": "\n\nwpm: {stats.wpm}\ntime: {stats.total_seconds}s", + "stats_color": 5, + "summary_template": "WPM: {stats.wpm}\nCPM: {stats.cpm}\nRAW WPM: {stats.raw_wpm}\nRAW CPM: {stats.raw_cpm}\ntotal seconds: {stats.total_seconds}\ntotal minutes: {stats.total_minutes}\ncorrect words: {stats.correct_words}\ncorrect chars: {stats.correct_chars}\nincorrect words: {stats.incorrect_words}\nincorrect chars: {stats.incorrect_chars}\ntotal words: {stats.total_words}\ntotal chars: {stats.total_chars}\naccuracy: {stats.accuracy}%", + "summary_datafile": "~/.config/cache/fasttyper/datafile.csv" +} \ No newline at end of file diff --git a/fasttyper/__main__.py b/fasttyper/__main__.py index 2dd3ca9..9f0e233 100644 --- a/fasttyper/__main__.py +++ b/fasttyper/__main__.py @@ -12,32 +12,7 @@ import argparse import json -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("file", metavar="FILE", nargs="?") - parser.add_argument( - "--config", - "-c", - metavar="FILE", - help="configuration file", - default="~/.config/fasttyper/config.json", - ) - args = parser.parse_args() - - if args.file is None: - input_lines = sys.stdin.readlines() - os.dup2(3, 0) - rbuffer = io.StringIO("".join(input_lines)) - else: - with open(args.file) as f: - rbuffer = io.StringIO(f.read()) - - try: - with open(args.config) as f: - configmap = json.load(f) - except FileNotFoundError: - configmap = {} - +def initialize(configmap, rbuffer): config = Config(configmap) reference_buffer = Buffer(rbuffer) @@ -59,7 +34,41 @@ def main(): user_buffer.close() reference_buffer.close() - print(config.get("summary_template").format(stats=application.stats)) + application.summarize() + + +def main(): + is_tty = sys.stdin.isatty() + + parser = argparse.ArgumentParser() + + if is_tty: + parser.add_argument("file", metavar="FILE") + + parser.add_argument( + "--config", + "-c", + metavar="FILE", + help="configuration file", + default="~/.config/fasttyper/config.json", + ) + args = parser.parse_args() + + if is_tty: + with open(os.path.expanduser(args.file)) as f: + rbuffer = io.StringIO(f.read()) + else: + input_lines = sys.stdin.readlines() + os.dup2(3, 0) + rbuffer = io.StringIO("".join(input_lines)) + + try: + with open(os.path.expanduser(args.config)) as f: + configmap = json.load(f) + except FileNotFoundError: + configmap = {} + + initialize(configmap, rbuffer) if __name__ == "__main__": diff --git a/fasttyper/application.py b/fasttyper/application.py index 9c69bc6..b5eba85 100644 --- a/fasttyper/application.py +++ b/fasttyper/application.py @@ -43,6 +43,10 @@ class Application: self._running = False self.stats.signal_stop() + def summarize(self): + self.stats.summarize(self.config.get("summary_template")) + self.stats.export_to_datafile(self.config.get("summary_datafile")) + class StoppingSignal(Exception): pass diff --git a/fasttyper/config.py b/fasttyper/config.py index 89e60a3..9fdbc32 100644 --- a/fasttyper/config.py +++ b/fasttyper/config.py @@ -20,6 +20,7 @@ class Config: "total chars: {stats.total_chars}\n" "accuracy: {stats.accuracy}%" ), + "summary_datafile": "~/.config/cache/fasttyper/datafile.csv", } def __init__(self, configmap): diff --git a/fasttyper/stats.py b/fasttyper/stats.py index 80afe46..8400c25 100644 --- a/fasttyper/stats.py +++ b/fasttyper/stats.py @@ -1,5 +1,8 @@ from .listener import Action from datetime import datetime +import os +import csv +import pathlib class Stats: @@ -72,3 +75,45 @@ class Stats: if self.total_chars: return self.correct_chars / self.total_chars * 100 return 100 + + def summarize(self, template): + print(template.format(stats=self)) + + def produce_record(self): + return { + "start_dtime": self.start_dtime.isoformat(), + "stop_dtime": self.stop_dtime.isoformat(), + "total_seconds": self.total_seconds, + "total_minutes": self.total_minutes, + "total_chars": self.total_chars, + "correct_chars": self.correct_chars, + "incorrect_chars": self.incorrect_chars, + "total_words": self.total_words, + "correct_words": self.correct_words, + "incorrect_words": self.incorrect_words, + "wpm": self.wpm, + "cpm": self.cpm, + "raw_wpm": self.raw_wpm, + "raw_cpm": self.raw_cpm, + "accuracy": self.accuracy, + } + + def export_to_datafile(self, datafile): + if datafile is None: + return + + datafile = os.path.expanduser(datafile) + data_dir, filename = os.path.split(datafile) + + pathlib.Path(data_dir).mkdir(exist_ok=True, parents=True) + exists = os.path.isfile(datafile) + + record = self.produce_record() + + with open(datafile, "w") as f: + writter = csv.DictWriter(f, fieldnames=record.keys()) + + if not exists: + writter.writeheader() + + writter.writerow(record)