def _calc_facts(self, df: pd.DataFrame, hours_target: float, hours_max: float): shifted_dt = df[wc.COL_LOG_DATETIME].shift(1) stop_mask = df[wc.COL_TYPE] == wc.TOKEN_STOP # calculate total working time total_time = (df[stop_mask][wc.COL_LOG_DATETIME] - shifted_dt[stop_mask]).sum() total_time_str = format_timedelta(total_time) # calculate breaks break_duration = self.auto_break.get_duration(total_time) break_duration_str = format_timedelta(break_duration) hours_target_dt = timedelta(hours=hours_target) + break_duration hours_max_dt = timedelta(hours=hours_max) + break_duration # calculate remaining time now = (datetime.now(timezone.utc).astimezone( tz=wc.LOCAL_TIMEZONE).replace(microsecond=0)) eow_dt = now + (hours_target_dt - total_time) eow_str = eow_dt.strftime("%H:%M:%S") remaining_time = max(eow_dt - now, timedelta(minutes=0)) remaining_time_str = format_timedelta(remaining_time) # calculate overtime overtime = max(total_time - hours_target_dt, timedelta(minutes=0)) overtime_str = format_timedelta(overtime) # calculcate percentage values percentage_done = round(total_time.total_seconds() / hours_target_dt.total_seconds() * 100) percentage_remaining = max(0, 100 - percentage_done) percentage_overtime = max( round(overtime.total_seconds() / (hours_max_dt - hours_target_dt).total_seconds() * 100), 0, ) def _short_hours_str(value: str): return value[:len("00:00")] return dict( break_duration=break_duration_str, break_duration_short=_short_hours_str(break_duration_str), eow=eow_str, eow_short=_short_hours_str(eow_str), overtime=overtime_str, overtime_short=_short_hours_str(overtime_str), percentage_done=percentage_done, percentage_overtime=percentage_overtime, percentage_remaining=percentage_remaining, remaining_time=remaining_time_str, remaining_time_short=_short_hours_str(remaining_time_str), total_time=total_time_str, total_time_short=_short_hours_str(total_time_str), )
def test_format_timedelta_valid_days(self): actual = format_timedelta(np.timedelta64(2, "D")) expected = "48:00:00" self.assertEqual(actual, expected) actual = format_timedelta(np.timedelta64(20, "D")) expected = "480:00:00" self.assertEqual(actual, expected)
def task_report(self, task_id): """Generate a report of a given task.""" task_mask = self._log_df[wc.COL_CATEGORY] == wc.TOKEN_TASK task_id_mask = self._log_df[wc.COL_TASK_IDENTIFIER] == task_id mask = task_mask & task_id_mask task_df = self._log_df[mask] if task_df.shape[0] == 0: sys.stderr.write((f"Task ID {task_id} is unknown. " "See 'wl task list' to list all known tasks.\n")) exit(1) intervals = extract_intervals(task_df, logger=logger) intervals_detailed = intervals[["date", "start", "stop", "interval"]].rename( columns={ "date": "Date", "start": "Start", "stop": "Stop", "interval": "Duration", }) print("Log entries:\n") print( intervals_detailed.to_string( index=False, formatters={ "Start": lambda x: x.strftime("%H:%M:%S"), "Stop": lambda x: x.strftime("%H:%M:%S"), "Duration": lambda x: format_timedelta( timedelta(microseconds=int(x) / 1e3)), }, )) print("---") print("Daily aggregated:\n") intervals_daily = intervals.groupby(by="date")[["interval"]].sum() intervals_daily.index.name = "Date" intervals_daily = intervals_daily.rename( columns={"interval": "Duration"}) print(intervals_daily.to_string()) print(f"---\nTotal: {intervals_detailed['Duration'].sum()}")
def test_invalid_value_none(self): expected = "00:00:00" td = None actual = format_timedelta(td) self.assertEqual(expected, actual)
def test_format_timedelta_valid(self): actual = format_timedelta(np.timedelta64(100, "s")) expected = "00:01:40" self.assertEqual(actual, expected)
def test_format_timedelta_nat(self): """It should not break if a numpy not-a-time value is passed.""" actual = format_timedelta(np.timedelta64("nAt")) expected = "00:00:00" self.assertEqual(actual, expected)
def test_default_behaviour(self): td = timedelta(hours=1, minutes=5, seconds=30) expected = "01:05:30" actual = format_timedelta(td) self.assertEqual(expected, actual)