示例#1
0
def check_for_completeness(days, actual_work_days):
    okay = True
    for d in actual_work_days:
        if d >= datetime.datetime.today().date():
            break
        if d not in days:
            log.warn(f"Workday {dt.strftime(d, '%d.%m.%Y')} has no entry")
            okay = False
    return okay
示例#2
0
def check_weekends(days, weekends):
    okay = True
    for d in days:
        at_work_entries = [
            e.name not in ["Vacations", "Sick"] for e in days[d]
        ]
        if d.weekday() + 1 in weekends and any(at_work_entries):
            log.warn(
                f"Seems like {dt.strftime(d, '%d.%m.%Y')} is set as a weekend day. Were you really working then?"
            )
            log.info(f"Entry: {days[d]}")
            okay = False
    return okay
示例#3
0
 def wrapper(*args, **kwargs):
     m = f"Check: {msg}"
     log.info(mk_headline(m, ">", indent=5))
     okay = func(*args, **kwargs)
     if not okay:
         log.warn(
             mk_headline(
                 f"{Colour.RED}{Colour.BOLD}Not OK!{Colour.END}"))
     else:
         log.info(
             mk_headline(f"{Colour.GREEN}{Colour.BOLD}OK!{Colour.END}"))
     log.info(mk_headline(sgn="<"))
     log.info("")
     return okay
示例#4
0
    def calculate_percents(self):
        for ws in self.ws:
            ws_obj = Workspace(ws)

            log.info(mk_headline(f"Times in Workspace {ws}", "*"))
            # seconds = {}
            self.total_time = tdelta(0)

            self.project_seconds = {
                "Vacations": 0.0,
                "Courses": 0.0,
                "Sick": 0.0
            }

            for project in ws_obj.native_projects:
                p_name = project.name
                times = project.time_entries.list()

                for i in times:
                    if not hasattr(i, "stop"):
                        log.warn("Entry %s seems to still be running" %
                                 i.description)
                        continue
                    start = i.start.replace(tzinfo=pytz.timezone("UTC"))
                    end = i.stop.replace(tzinfo=pytz.timezone("UTC"))

                    if start.date() < self.start or end.date() > self.end:
                        continue

                    dur = end - start

                    if dur.total_seconds() / 3600. > 11:
                        log.warn("Warning: the entry seems to be too long:")
                        log.warn(
                            f"{p_name} from {start} to {end}; duration {dur}")

                    if start.date() not in self.days:
                        self.days[start.date()] = StrictList(Entry)

                    if p_name not in self.project_seconds:
                        self.project_seconds[p_name] = 0.0

                    e = None

                    tags = list(set([t.lower() for t in i.tags])) if hasattr(
                        i, "tags") else []

                    e = Entry(p_name, start, end, dur, tags)
                    add_dur = not (p_name == "Holidays" or
                                   (hasattr(i, "tags") and "Pause" in i.tags))
                    if add_dur:
                        self.project_seconds[p_name] += dur.total_seconds()

                    self.days[start.date()].append(e)
示例#5
0
def check_for_gaps_and_overlaps(days):
    okay = True
    for d in days:
        items = sorted(days[d], key=lambda x: x.start)

        for i, first in enumerate(items[:-1]):
            second = items[i + 1]

            if first.start.date() != first.end.date():
                log.warn(f"    [step] {first.name:<10s} overlaps midnight")
            if second.start.date() != second.end.date():
                log.warn(f"    [step] {second.name:<10s} overlaps midnight")

            f_end_str = dt.strftime(
                first.end.astimezone(pytz.timezone("Europe/Berlin")),
                "%H:%M:%S")
            s_start_str = dt.strftime(
                second.start.astimezone(pytz.timezone("Europe/Berlin")),
                "%H:%M:%S")

            diff = second.start - first.end
            if diff.total_seconds() >= gap_threshold_seconds:
                stat = "gap"
                okay = False
            elif diff.total_seconds() <= -gap_threshold_seconds:
                stat = "ovl"
                okay = False
            else:
                stat = None

            if stat:
                log.warn(
                    f"    [{stat}] {abs(diff.total_seconds()):>8.0f}s; {first.name:10s} and {second.name:10s} on {first.start.date()}: {f_end_str} -> {s_start_str}"
                )

    return okay
示例#6
0
def check_for_expected_hours(days, get_working_hours_func):
    days_sorted = sorted(days.keys())
    total_hours = 0.0
    overunder_sum = 0.0
    for d in days_sorted:
        pause_hours = 0.0
        expected_day_hours = get_working_hours_func(d)
        actual_day_hours = 0.0
        special_day_type = None

        for e in days[d]:  # type: Entry
            if "pause" in e.tags:
                pause_hours += e.duration.total_seconds() / 3600.0
                continue
            if "off" in e.tags:
                special_day_type = e.name

            entry_time = e.duration
            entry_hours = entry_time.total_seconds() / 3600.0
            actual_day_hours += entry_hours

        total_hours += actual_day_hours
        overunder = actual_day_hours - expected_day_hours
        if overunder < 0.0:
            p = pause_hours
            overunder += p
            pause_hours -= p

        overunder_sum += overunder

        c = Colour.RED if overunder < 0.0 else Colour.BLUE
        overunder_str = f"{Colour.BOLD}{c}{overunder:>+5.2f}{Colour.END}"

        c = Colour.RED if pause_hours < 1.0 else Colour.BLUE
        pause_str = f"{Colour.BOLD}{c}{pause_hours:>5.2f}{Colour.END}"

        result = [dt.strftime(d, '%d.%m.%Y')]

        if special_day_type is None:
            result.append(f"{actual_day_hours:>5.2f}h")
            result.append(f"breaks: {pause_str}h => +/- {overunder_str}h")
        else:
            if special_day_type == "Vacations" or special_day_type == "Holidays":
                result.append(
                    f"{Colour.GREEN}{Colour.BOLD}is a day off{Colour.END}")
            elif special_day_type == "Sick":
                result.append(
                    f"{Colour.YELLOW}{Colour.BOLD}Hope you got well!{Colour.END}"
                )

        if pause_hours < 1.0 and not special_day_type:
            result.append(
                f"{Colour.BOLD}You need sufficient breaks!{Colour.END}")

        log.info("; ".join(result))

    log.info(mk_headline("Sum of daily hours"))
    log.info(f" Sum: {total_hours:>6.1f}h")
    log.info(f" +/- {overunder_sum:>6.1f}h")

    log.info(mk_headline("So..."))
    if overunder_sum < 0.0:
        log.warn("  You have a negative time record in the given period!")
    elif overunder_sum > 0.0:
        log.info("  You have worked overtime in the given period!")

    return overunder_sum >= 0.0