def test_session_and_task(self, mock_now): mock_now.return_value = datetime(2020, 1, 1, tzinfo=timezone.utc) with tempfile.NamedTemporaryFile() as fh: instance = Log(fh.name) instance.commit(wc.TOKEN_SESSION, wc.TOKEN_START, time="2020-01-01T00:00:00+00:00") instance.commit( wc.TOKEN_TASK, wc.TOKEN_START, time="2020-01-01T00:00:00+00:00", identifier="task1", ) instance.commit( wc.TOKEN_TASK, wc.TOKEN_STOP, time="2020-01-01T01:00:00+00:00", identifier="task1", ) instance.commit( wc.TOKEN_SESSION, wc.TOKEN_STOP, time="2020-01-01T01:00:00+00:00", ) fh.seek(0) content = fh.read().decode() self.assertMatchSnapshot(content)
def run() -> None: """ Main method """ logger = configure_logger() parser = get_arg_parser() cli_args = parser.parse_args() log_level = wc.LOG_LEVELS[min(cli_args.verbosity, len(wc.LOG_LEVELS) - 1)] logger.setLevel(log_level) logger.debug(f"Parsed CLI arguments: {cli_args}") logger.debug(f"Path to config files: {wc.CONFIG_FILES}") if cli_args.subcmd is None: parser.print_help() return cfg = ConfigParser() cfg.read(wc.CONFIG_FILES) with StringIO() as ss: cfg.write(ss) ss.seek(0) logger.debug(f"Config content:\n{ss.read()}\nEOF") worklog_fp = os.path.expanduser(cfg.get("worklog", "path")) log = Log(worklog_fp) limits = json.loads(cfg.get("workday", "auto_break_limit_minutes")) durations = json.loads(cfg.get("workday", "auto_break_duration_minutes")) log.auto_break = AutoBreak(limits, durations) dispatch(log, parser, cli_args, cfg)
def test_ok(self): logger = logging.getLogger("test_logger") with patch.object(logger, "error") as mock_logger: fp = self._get_testdata_fp("doctor_session_ok") instance = Log(fp, logger=logger) instance.doctor() mock_logger.assert_not_called()
def test_report_with_tasks(self): fp = self._get_testdata_fp("report_with_tasks") instance = Log(fp) date_from = datetime(2020, 1, 1, tzinfo=timezone.utc) date_to = datetime(2020, 3, 1, tzinfo=timezone.utc) instance.report(date_from, date_to) out, _ = self._capsys.readouterr() self.assertMatchSnapshot(out)
def test_tracking_off(self): with patch("worklog.constants.LOCAL_TIMEZONE", new=timezone.utc): fp = self._get_testdata_fp("status_tracking_off") instance = Log(fp) query_date = date(2020, 1, 1) instance.status(8, 10, query_date=query_date) out, _ = self._capsys.readouterr() self.assertMatchSnapshot(out)
def test_report_with_tasks_and_autobreak(self): fp = self._get_testdata_fp("report_with_tasks") instance = Log(fp) instance.auto_break = AutoBreak(limits=[0], durations=[60]) date_from = datetime(2020, 1, 1, tzinfo=timezone.utc) date_to = datetime(2020, 3, 1, tzinfo=timezone.utc) instance.report(date_from, date_to) out, _ = self._capsys.readouterr() self.assertMatchSnapshot(out)
def test_wrong_order(self): logger = logging.getLogger("test_logger") with patch.object(logger, "error") as mock_logger: fp = self._get_testdata_fp("doctor_session_wrong_order") instance = Log(fp, logger=logger) instance.doctor() calls = (call( ErrMsg.WRONG_SESSION_ORDER.value.format(date="2020-01-01")), ) mock_logger.assert_has_calls(calls)
def test_list_tasks(self): fp = self._get_testdata_fp("tasks_multiple_nested") instance = Log(fp) instance.list_tasks() out, err = self._capsys.readouterr() expected = """These tasks are listed in the log: task1 (2) task2 (2) task3 (2) """ assert out == expected
def test_stop_entry_missing_single(self): logger = logging.getLogger("test_logger") with patch.object(logger, "error") as mock_logger: fp = self._get_testdata_fp("doctor_session_stop_missing_single") instance = Log(fp, logger=logger) instance.doctor() calls = (call( ErrMsg.MISSING_SESSION_ENTRY.value.format( type=wc.TOKEN_STOP, date="2020-01-01")), ) mock_logger.assert_has_calls(calls)
def test_tracking_off_with_fmt(self): with patch("worklog.constants.LOCAL_TIMEZONE", new=timezone.utc): fp = self._get_testdata_fp("status_tracking_off") instance = Log(fp) query_date = date(2020, 1, 1) instance.status(8, 10, query_date=query_date, fmt="{tracking_status}") out, _ = self._capsys.readouterr() self.assertEqual(out, "off")
def test_task_start_entry_missing(self): logger = logging.getLogger("test_logger") with patch.object(logger, "error") as mock_logger: fp = self._get_testdata_fp("doctor_task_start_missing") instance = Log(fp, logger=logger) instance.doctor() calls = (call( ErrMsg.MISSING_TASK_ENTRY.value.format(type=wc.TOKEN_START, date="2020-01-01", task_id="task1")), ) mock_logger.assert_has_calls(calls)
def test_stop_session_with_running_task(self, mock_now): mock_now.return_value = datetime(2020, 1, 1, tzinfo=timezone.utc) with tempfile.NamedTemporaryFile() as fh: instance = Log(fh.name) instance.commit(wc.TOKEN_SESSION, wc.TOKEN_START, time="2020-01-01T00:00:00+00:00") instance.commit( wc.TOKEN_TASK, wc.TOKEN_START, time="2020-01-01T00:00:00+00:00", identifier="task1", ) with self.assertRaises(SystemExit) as err: instance.commit( wc.TOKEN_SESSION, wc.TOKEN_STOP, time="2020-01-01T01:00:00+00:00", ) _, stderr = self._capsys.readouterr() self.assertEqual( stderr, ErrMsg.STOP_SESSION_TASKS_RUNNING.value.format( active_tasks=["task1"]) + "\n", ) self.assertTrue(err.exception.code, 1)
def test_day_with_no_content_fmt(self): with patch("worklog.constants.LOCAL_TIMEZONE", new=timezone.utc): fp = self._get_testdata_fp("status_tracking_off") instance = Log(fp) query_date = date(2020, 1, 2) with self.assertRaises(SystemExit) as err: instance.status(8, 10, query_date=query_date, fmt="{tracking_status}") stdout, _ = self._capsys.readouterr() self.assertEqual(stdout, ErrMsg.NA.value) self.assertEqual(err.exception.code, 0)
def test_empty(self): fp = self._get_testdata_fp("status_empty") instance = Log(fp) query_date = date(2020, 1, 1) with self.assertRaises(SystemExit) as err: instance.status(8, 10, query_date=query_date) _, stderr = self._capsys.readouterr() self.assertEqual( stderr, ErrMsg.EMPTY_LOG_DATA.value + "\n", ) self.assertEqual(err.exception.code, 1)
def test_empty_with_fmt(self): fp = self._get_testdata_fp("status_empty") instance = Log(fp) query_date = date(2020, 1, 1) with self.assertRaises(SystemExit) as err: instance.status(8, 10, query_date=query_date, fmt="{tracking_status}") out, _ = self._capsys.readouterr() self.assertEqual(out, ErrMsg.NA.value) self.assertEqual(err.exception.code, 0)
def test_file_mode(self): with tempfile.TemporaryDirectory() as tmpdir: fp = Path(tmpdir, "foobar") instance = Log(fp) self.assertTrue(os.access(fp, os.R_OK)) self.assertTrue(os.access(fp, os.W_OK))
def test_day_with_no_content(self): with patch("worklog.constants.LOCAL_TIMEZONE", new=timezone.utc): fp = self._get_testdata_fp("status_tracking_off") instance = Log(fp) query_date = date(2020, 1, 2) with self.assertRaises(SystemExit) as err: instance.status(8, 10, query_date=query_date) _, stderr = self._capsys.readouterr() self.assertEqual( stderr, ErrMsg.EMPTY_LOG_DATA_FOR_DATE.value.format(query_date=query_date) + "\n", ) self.assertEqual(err.exception.code, 1)
def test_file_is_read(self): fp = self._get_testdata_fp("session_simple") instance = Log(fp) self.assertFalse(instance._log_df.empty)
def test_file_created(self): with tempfile.TemporaryDirectory() as tmpdir: fp = Path(tmpdir, "foobar") instance = Log(fp) self.assertTrue(fp.exists())
def test_invalid_category(self): with tempfile.NamedTemporaryFile() as fh: instance = Log(fh.name) with self.assertRaises(ValueError): instance.commit("foobar", wc.TOKEN_START)
def dispatch( log: Log, parser: ArgumentParser, cli_args: Namespace, cfg: ConfigParser ) -> None: """ Dispatch request to Log instance based on CLI arguments and configuration values. """ if cli_args.subcmd == wc.SUBCMD_SESSION: if cli_args.type in [wc.TOKEN_START, wc.TOKEN_STOP]: log.commit( wc.TOKEN_SESSION, cli_args.type, cli_args.offset_minutes, cli_args.time, force=cli_args.force, ) elif cli_args.subcmd == wc.SUBCMD_TASK: if cli_args.type in [wc.TOKEN_START, wc.TOKEN_STOP]: if cli_args.type == wc.TOKEN_START and cli_args.auto_stop: commit_dt = calc_log_time(cli_args.offset_minutes, cli_args.time) log.stop_active_tasks(commit_dt) log.commit( wc.TOKEN_TASK, cli_args.type, cli_args.offset_minutes, cli_args.time, identifier=cli_args.id, ) elif cli_args.type == "list": log.list_tasks() elif cli_args.type == "report": log.task_report(cli_args.id) elif cli_args.subcmd == wc.SUBCMD_STATUS: hours_target = float(cfg.get("workday", "hours_target")) hours_max = float(cfg.get("workday", "hours_max")) fmt = cli_args.fmt query_date = date.today() if cli_args.yesterday: query_date -= timedelta(days=1) elif cli_args.date: query_date = cli_args.date.date() log.status(hours_target, hours_max, query_date=query_date, fmt=fmt) elif cli_args.subcmd == wc.SUBCMD_DOCTOR: log.doctor() elif cli_args.subcmd == wc.SUBCMD_LOG: n = cli_args.number no_pager_max_entries = int(cfg.get("worklog", "no_pager_max_entries")) use_pager = not cli_args.no_pager and (cli_args.all or n > no_pager_max_entries) categories = cli_args.category if not cli_args.all: log.log(cli_args.number, use_pager, categories) else: log.log(-1, use_pager, categories) elif cli_args.subcmd == wc.SUBCMD_REPORT: log.report(cli_args.date_from, cli_args.date_to)
def test_invalid_type(self): with tempfile.NamedTemporaryFile() as fh: instance = Log(fh.name) with self.assertRaises(ValueError): instance.commit(wc.TOKEN_SESSION, "foobar")