def day(date): start_dt = ui.parse_date(date, context="day") end_dt = start_dt.clone().ceil("day") ranges = database.active_ranges(start_dt, end_dt) edits = database.edits(start_dt) current_range = database.current_range() if not state.is_away() else None # tally minutes minutes = 0 for dtr in ranges: minutes += dtr.minutes for edit in edits: minutes += edit.minutes # header print("%s %s %s" % ( coloured("yellow", ui.format_minutes(minutes)), coloured("yellow", start_dt.format("ddd Do MMM")), ui.format_minutes_relative(cfg.work_week * 60 / 5, minutes), )) # active ranges previous_end_dt = None for dtr in ranges: # ignore zero-minute ranges if not dtr.minutes: continue current = (current_range and dtr.start_dt == current_range.start_dt and dtr.end_dt == current_range.end_dt) gap = None if previous_end_dt: delta = dtr.start_dt - previous_end_dt gap = ui.format_minutes(delta.total_seconds() / 60) previous_end_dt = dtr.end_dt print("%5s %s%s%s %s%s" % ( coloured("dark-grey", gap) if gap else "", dtr.start_dt.format("HH:mm"), coloured("dark-grey", "-"), dtr.end_dt.format("HH:mm"), coloured("green", ui.format_minutes(dtr.minutes)), " %s" % ui.CHAR_CURRENT if current else "", )) # edits for edit in edits: print("%s %s" % (ui.format_minutes_relative(0, edit.minutes), edit.reason))
def month(start, end): start = ( arrow.now(tz=cfg.time_zone).format("YYYY-01-01") if start is None else _as_month(start) ) start_dt = ui.parse_date(start, context="month").floor("month") end = start_dt.format("YYYY-12-31") if end is None else _as_month(end) end_dt = ui.parse_date(end, context="month").ceil("month") today = arrow.now(tz=cfg.time_zone).ceil("day") if end_dt > today: end_dt = today days = {} dt = start_dt while dt < end_dt: days[dt.format("YYYYMMDD")] = dict(dt=dt.clone(), minutes=0) dt = dt.shift(days=1) minutes = 0 for dtr in database.active_ranges(start_dt, end_dt): days[dtr.start_dt.format("YYYYMMDD")]["minutes"] += dtr.minutes minutes += dtr.minutes for ymd in sorted(days.keys()): for edit in database.edits(days[ymd]["dt"]): days[ymd]["minutes"] += edit.minutes minutes += edit.minutes # group into months months = {} total = 0 for ymd in sorted(days.keys()): dt = days[ymd]["dt"] ym = dt.format("YYYYMM") months.setdefault(ym, dict(name=dt.format("YYYY MMM"), minutes=0)) months[ym]["minutes"] += days[ymd]["minutes"] total += days[ymd]["minutes"] hour_width = len(ui.format_minutes(total)) # header print( coloured("yellow", "%{}s TOTAL".format(hour_width) % ui.format_minutes(total)) ) # report for m in sorted(months.keys()): print( "%{}s %s".format(hour_width) % (ui.format_minutes(months[m]["minutes"]), months[m]["name"]) )
def input_ex(prompt, *, required=False, default=None, validator=None, options=None): if validator: assert callable(validator) if options: assert not validator def _check_options(value): value = value.lower() if value not in options: raise ValueError("") return value validator = _check_options if default: prompt = "%s (%s)" % (prompt, default) prompt = "%s: " % prompt try: while True: res = input(coloured("green", prompt)).strip() if res == "": if required: continue if default: res = default if validator: try: res = validator(res) except ValueError as e: if str(e): logger.error(e) continue return res except KeyboardInterrupt: print("") sys.exit(1)
def week(date, status): start_dt = ui.parse_date(date, context="week") # start_dt needs to be a monday if start_dt.weekday() != 0: start_dt = start_dt.shift(days=-7).shift(weekday=0) end_dt = start_dt.shift(days=7).shift(minutes=-1) # count days worked days = {} dt = start_dt.clone() while dt < end_dt: days[dt.format("YYYYMMDD")] = dict(dt=dt.clone(), minutes=0, edited=False) dt = dt.shift(days=1) # tally minutes total_minutes = 0 for dtr in database.active_ranges(start_dt, end_dt): days[dtr.start_dt.format("YYYYMMDD")]["minutes"] += dtr.minutes total_minutes += dtr.minutes # process edits for ymd in sorted(days.keys()): for edit in database.edits(days[ymd]["dt"]): days[ymd]["minutes"] += edit.minutes days[ymd]["edited"] = True total_minutes += edit.minutes # header print( "%s %s %s %s - %s" % ( coloured("yellow", ui.format_minutes(total_minutes)), coloured("yellow", "%-9s" % "WEEK"), ui.format_minutes_relative(cfg.work_week * 60, total_minutes), coloured("yellow", start_dt.format("Do MMM")), coloured("yellow", end_dt.format("Do MMM")), ) ) hours_per_day = (cfg.work_week * 60) / 5 today = arrow.now(tz=cfg.time_zone).ceil("day") today_ymd = today.format("YYYYMMDD") is_away = state.is_away() for ymd in sorted(days.keys()): dt = days[ymd]["dt"] minutes = days[ymd]["minutes"] edited = days[ymd]["edited"] current = not is_away and ymd == today_ymd # don't report on the weekend unless there were hours logged if dt.weekday() > 4 and minutes == 0: continue # only report future dates if there's edits if dt > today and not edited: continue print( "%s %s %s%s%s" % ( ui.format_minutes(minutes), "%-9s" % dt.format("dddd"), ui.format_minutes_relative(hours_per_day, minutes), " %s" % ui.CHAR_EDITED if edited else "", " %s" % ui.CHAR_CURRENT if current else "", ) ) if status: invoke(["status", "--no-daemon"])
def format_minutes_relative(target, minutes): if minutes == target: return coloured("green", "⦗ %s⦘" % format_minutes(0)) if minutes < target: return coloured("red", "⦗-%s⦘" % format_minutes(target - minutes)) return coloured("green", "⦗+%s⦘" % format_minutes(minutes - target))
def format(self, record): result = super().format(record) if record.levelname in self.log_colours: result = coloured(self.log_colours[record.levelname], result) return result