From 017bc775091ea609821bf38aa4bd6b86d239304a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 12:47:59 +0000 Subject: [PATCH 001/109] #40 TTCN, added. --- src/nwtimetracking.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 624f550..b8276a1 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -11,10 +11,9 @@ import re import openpyxl from dataclasses import dataclass -from datetime import datetime -from datetime import timedelta -from pandas import DataFrame -from pandas import Series +from datetime import datetime, timedelta +from enum import StrEnum +from pandas import DataFrame, Series from typing import Any, Callable, Optional, cast # LOCAL MODULES @@ -54,6 +53,20 @@ def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_ti @staticmethod def effort_status_not_among_expected_time_values(time : str) -> str: return f"The provided time ('{time}') is not among the expected time values." +class TTCN(StrEnum): + + '''Collects all the column names used by ...''' + + DATE = "Date" + STARTTIME = "StartTime" + ENDTIME = "EndTime" + EFFORT = "Effort" + HASHTAG = "Hashtag" + DESCRIPTOR = "Descriptor" + ISSOFTWAREPROJECT = "IsSoftwareProject" + ISRELEASEDAY = "IsReleaseDay" + YEAR = "Year" + MONTH = "Month" # DTOs @dataclass(frozen=True) @@ -332,16 +345,16 @@ def __enforce_dataframe_definition_for_sessions_df(self, sessions_df : DataFrame '''Enforces definition for the provided dataframe.''' column_names : list[str] = [] - column_names.append("Date") # [0], date - column_names.append("StartTime") # [1], str - column_names.append("EndTime") # [2], str - column_names.append("Effort") # [3], str - column_names.append("Hashtag") # [4], str - column_names.append("Descriptor") # [5], str - column_names.append("IsSoftwareProject") # [6], bool - column_names.append("IsReleaseDay") # [7], bool - column_names.append("Year") # [8], int - column_names.append("Month") # [9], int + column_names.append(TTCN.DATE) # [0], date + column_names.append(TTCN.STARTTIME) # [1], str + column_names.append(TTCN.ENDTIME) # [2], str + column_names.append(TTCN.EFFORT) # [3], str + column_names.append(TTCN.HASHTAG) # [4], str + column_names.append(TTCN.DESCRIPTOR) # [5], str + column_names.append(TTCN.ISSOFTWAREPROJECT) # [6], bool + column_names.append(TTCN.ISRELEASEDAY) # [7], bool + column_names.append(TTCN.YEAR) # [8], int + column_names.append(TTCN.MONTH) # [9], int sessions_df = sessions_df[column_names] From 14145e8a306da74faeb6cd6f2c851937a52d4667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 12:55:19 +0000 Subject: [PATCH 002/109] #40 __get_raw_tt_by_year_month_spnv(): * => TTCN. --- src/nwtimetracking.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index b8276a1..1e50616 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -67,6 +67,8 @@ class TTCN(StrEnum): ISRELEASEDAY = "IsReleaseDay" YEAR = "Year" MONTH = "Month" + PROJECTNAME = "ProjectName" + PROJECTVERSION = "ProjectVersion" # DTOs @dataclass(frozen=True) @@ -380,9 +382,7 @@ def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataF '''Ensures that the columns of the provided dataframe have the expected data types.''' - cn_month : str = "Month" - - df = df.astype({cn_month: int}) + df = df.astype({TTCN.MONTH: int}) # can't enforce the year column as "timedelta" return df @@ -486,25 +486,18 @@ def __get_raw_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[ tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - cn_project_version : str = "ProjectVersion" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[cn_project_version] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - cn_month : str = "Month" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_month, cn_project_name, cn_project_version])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year, cn_month, cn_project_name, cn_project_version]).reset_index(drop = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[cn_project_name].isin(values = software_project_names)) + condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) tt_df = tt_df.loc[condition_three] return tt_df From 4a70692f6e7b8d668329f92d3535e63faf4c781f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:32:30 +0000 Subject: [PATCH 003/109] #40 __get_raw_dme(): * => TTCN. --- src/nwtimetracking.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 1e50616..10ff943 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -69,6 +69,7 @@ class TTCN(StrEnum): MONTH = "Month" PROJECTNAME = "ProjectName" PROJECTVERSION = "ProjectVersion" + DME = "DME" # DTOs @dataclass(frozen=True) @@ -514,26 +515,17 @@ def __get_raw_dme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - cn_project_version : str = "ProjectVersion" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[cn_project_version] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - cn_month : str = "Month" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_month])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year, cn_month]).reset_index(drop = True) - - cn_dme : str = "DME" - tt_df.rename(columns = {cn_effort : cn_dme}, inplace = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) + tt_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) return tt_df def __get_raw_tme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: From 096a5445be40ff3377c0ff5096315c04950ebbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:34:08 +0000 Subject: [PATCH 004/109] #40 __get_raw_tme(): * => TTCN. --- src/nwtimetracking.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 10ff943..7dc85f1 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -70,6 +70,7 @@ class TTCN(StrEnum): PROJECTNAME = "ProjectName" PROJECTVERSION = "ProjectVersion" DME = "DME" + TME = "TME" # DTOs @dataclass(frozen=True) @@ -541,18 +542,13 @@ def __get_raw_tme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition : Series = (sessions_df[cn_year].isin(values = years)) + condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition] - cn_month : str = "Month" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_month])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year, cn_month]).reset_index(drop = True) - - cn_tme : str = "TME" - tt_df.rename(columns = {cn_effort : cn_tme}, inplace = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) + tt_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) return tt_df def __get_raw_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From 868d5cdc12a091701c52872bda4f6edd50e189c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:44:03 +0000 Subject: [PATCH 005/109] #40 __get_raw_tt_by_year_spnv(): * => TTCN. --- src/nwtimetracking.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 7dc85f1..5ea3282 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -562,26 +562,20 @@ def __get_raw_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - cn_project_version : str = "ProjectVersion" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[cn_project_version] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_project_name, cn_project_version])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year, cn_project_name, cn_project_version]).reset_index(drop = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[cn_project_name].isin(values = software_project_names)) + condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [cn_year, cn_project_name, cn_project_version]).reset_index(drop = True) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) return tt_df def __get_raw_dye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: From d1f9cf4a9ccd7834f6d2cef2e407c65ce53d7eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:46:12 +0000 Subject: [PATCH 006/109] #40 __get_raw_dye(): * => TTCN. --- src/nwtimetracking.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 5ea3282..767beef 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -71,6 +71,7 @@ class TTCN(StrEnum): PROJECTVERSION = "ProjectVersion" DME = "DME" TME = "TME" + DYE = "DYE" # DTOs @dataclass(frozen=True) @@ -591,25 +592,17 @@ def __get_raw_dye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - cn_project_version : str = "ProjectVersion" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[cn_project_version] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year]).reset_index(drop = True) - - cn_dye : str = "DYE" - tt_df.rename(columns = {cn_effort : cn_dye}, inplace = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) + tt_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) return tt_df def __get_raw_tye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: From 1f94aa79270697be340883b20d1a4f62360971e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:47:31 +0000 Subject: [PATCH 007/109] #40 __get_raw_tye(): * => TTCN. --- src/nwtimetracking.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 767beef..325d3df 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -72,6 +72,7 @@ class TTCN(StrEnum): DME = "DME" TME = "TME" DYE = "DYE" + TYE = "TYE" # DTOs @dataclass(frozen=True) @@ -618,17 +619,13 @@ def __get_raw_tye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition : Series = (sessions_df[cn_year].isin(values = years)) + condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition] - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year]).reset_index(drop = True) - - cn_tye : str = "TYE" - tt_df.rename(columns = {cn_effort : cn_tye}, inplace = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) + tt_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) return tt_df def __get_raw_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From 0bf56648900d10355de291f5f1b696843d00876c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:49:58 +0000 Subject: [PATCH 008/109] #40 __get_raw_tt_by_spn(): * => TTCN. --- src/nwtimetracking.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 325d3df..26a4e0b 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -641,27 +641,20 @@ def __get_raw_tt_by_spn(self, sessions_df : DataFrame, years : list[int], softwa tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - - cn_effort : str = "Effort" - cn_hashtag : str = "Hashtag" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_project_name, cn_hashtag])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_project_name]).reset_index(drop = True) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.PROJECTNAME, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME]).reset_index(drop = True) - condition_three : Series = (tt_df[cn_project_name].isin(values = software_project_names)) + condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [cn_hashtag, cn_effort], ascending = [False, False]).reset_index(drop = True) + tt_df = tt_df.sort_values(by = [TTCN.HASHTAG, TTCN.EFFORT], ascending = [False, False]).reset_index(drop = True) - tt_df = tt_df[[cn_hashtag, cn_project_name, cn_effort]] + tt_df = tt_df[[TTCN.HASHTAG, TTCN.PROJECTNAME, TTCN.EFFORT]] return tt_df def __get_raw_de(self, sessions_df : DataFrame, years : list[int]) -> timedelta: From 8cc03576b8db4e86a55fb8da9261edadf312c6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:51:56 +0000 Subject: [PATCH 009/109] #40__get_raw_de(): * => TTCN. --- src/nwtimetracking.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 26a4e0b..3928a46 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -663,15 +663,12 @@ def __get_raw_de(self, sessions_df : DataFrame, years : list[int]) -> timedelta: tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - summarized : timedelta = tt_df[cn_effort].sum() + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + summarized : timedelta = tt_df[TTCN.EFFORT].sum() return summarized def __get_raw_te(self, sessions_df : DataFrame, years : list[int], remove_untagged : bool) -> timedelta: From b86b91a7c7b63f027c009dc52cddf5c1e17ee43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:52:58 +0000 Subject: [PATCH 010/109] #40 __get_raw_te(): * => TTCN. --- src/nwtimetracking.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 3928a46..5be91e5 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -677,18 +677,15 @@ def __get_raw_te(self, sessions_df : DataFrame, years : list[int], remove_untagg tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition_one] if remove_untagged: - cn_hashtag : str = "Hashtag" - condition_two : Series = (sessions_df[cn_hashtag] != "#untagged") + condition_two : Series = (sessions_df[TTCN.HASHTAG] != "#untagged") tt_df = tt_df.loc[condition_two] - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - summarized : timedelta = tt_df[cn_effort].sum() + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + summarized : timedelta = tt_df[TTCN.EFFORT].sum() return summarized def __get_raw_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From 02b6288f5ecbf045158f22e667576f3a5cdd8d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 13:56:16 +0000 Subject: [PATCH 011/109] #40 __get_raw_tt_by_spn_spv(): * => TTCN. --- src/nwtimetracking.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 5be91e5..8eb6fed 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -700,26 +700,20 @@ def __get_raw_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], so tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - cn_is_software_project : str = "IsSoftwareProject" - condition_one : Series = (sessions_df[cn_year].isin(values = years)) - condition_two : Series = (sessions_df[cn_is_software_project] == True) + condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) tt_df = tt_df.loc[condition_one & condition_two] - cn_descriptor : str = "Descriptor" - cn_project_name : str = "ProjectName" - cn_project_version : str = "ProjectVersion" - tt_df[cn_project_name] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[cn_project_version] = tt_df[cn_descriptor].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_project_name, cn_project_version])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_project_name, cn_project_version]).reset_index(drop = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[cn_project_name].isin(values = software_project_names)) + condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [cn_project_name, cn_project_version]).reset_index(drop = True) + tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) return tt_df def __get_default_raw_ttm(self, year : int) -> DataFrame: From bde311bba356ecbe3ed144c7c4c855611ffa507c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:01:32 +0000 Subject: [PATCH 012/109] #40 __get_default_raw_ttm(): * => TTCN. --- src/nwtimetracking.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 8eb6fed..8e9941e 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -726,12 +726,11 @@ def __get_default_raw_ttm(self, year : int) -> DataFrame: ... ''' - cn_month : str = "Month" td : timedelta = self.__convert_string_to_timedelta(td_str = "0h 00m") default_df : DataFrame = pd.DataFrame( { - f"{cn_month}": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + f"{TTCN.MONTH}": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], f"{str(year)}": [td, td, td, td, td, td, td, td, td, td, td, td] }, index=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], From 0d9f20f8a8a244afec80b8a9613dc095f5378802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:02:12 +0000 Subject: [PATCH 013/109] #40 __try_complete_raw_ttm(): * => TTCN. --- src/nwtimetracking.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 8e9941e..702365f 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -781,15 +781,13 @@ def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: 11 12 0h 00m ''' - cn_month : str = "Month" - - if ttm_df[cn_month].count() != 12: + if ttm_df[TTCN.MONTH].count() != 12: default_df : DataFrame = self.__get_default_raw_ttm(year = year) - missing_df : DataFrame = default_df.loc[~default_df[cn_month].astype(str).isin(ttm_df[cn_month].astype(str))] + missing_df : DataFrame = default_df.loc[~default_df[TTCN.MONTH].astype(str).isin(ttm_df[TTCN.MONTH].astype(str))] completed_df : DataFrame = pd.concat([ttm_df, missing_df], ignore_index = True) - completed_df = completed_df.sort_values(by = cn_month, ascending = [True]) + completed_df = completed_df.sort_values(by = TTCN.MONTH, ascending = [True]) completed_df = completed_df.reset_index(drop = True) return completed_df From 85f65ab49220a26b44255e1d934ae5dc286562bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:03:31 +0000 Subject: [PATCH 014/109] #40 __get_raw_ttm(): * => TTCN. --- src/nwtimetracking.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 702365f..ee14567 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -820,22 +820,18 @@ def __get_raw_ttm(self, sessions_df : DataFrame, year : int) -> DataFrame: 11 12 0 days 00:00:00 ''' - cn_year : str = "Year" - cn_month : str = "Month" - cn_effort : str = "Effort" - ttm_df : DataFrame = sessions_df.copy(deep=True) - ttm_df = ttm_df[[cn_year, cn_month, cn_effort]] + ttm_df = ttm_df[[TTCN.YEAR, TTCN.MONTH, TTCN.EFFORT]] - condition : Series = (sessions_df[cn_year] == year) + condition : Series = (sessions_df[TTCN.YEAR] == year) ttm_df = ttm_df.loc[condition] - ttm_df[cn_effort] = ttm_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - ttm_df[str(year)] = ttm_df[cn_effort] + ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + ttm_df[str(year)] = ttm_df[TTCN.EFFORT] cn_effort = str(year) - ttm_df = ttm_df.groupby([cn_month])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - ttm_df = ttm_df.sort_values(by = cn_month).reset_index(drop = True) + ttm_df = ttm_df.groupby([TTCN.MONTH])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) + ttm_df = ttm_df.sort_values(by = TTCN.MONTH).reset_index(drop = True) ttm_df = self.__try_complete_raw_ttm(ttm_df = ttm_df, year = year) ttm_df = self.__enforce_dataframe_definition_for_raw_ttm_df(df = ttm_df) From e754ad6255fd20b723e84aa068f2121ace6d2c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:04:27 +0000 Subject: [PATCH 015/109] #40 __expand_raw_ttm_by_year(): * => TTCN. --- src/nwtimetracking.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index ee14567..a04a03d 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -905,13 +905,12 @@ def __expand_raw_ttm_by_year(self, sessions_df : DataFrame, years : list, tts_by actual_df : DataFrame = tts_by_month_df.copy(deep = True) ttm_df : DataFrame = self.__get_raw_ttm(sessions_df = sessions_df, year = years[i]) - cn_month : str = "Month" expansion_df = pd.merge( left = actual_df, right = ttm_df, how = "inner", - left_on = cn_month, - right_on = cn_month) + left_on = TTCN.MONTH, + right_on = TTCN.MONTH) if add_trend == True: @@ -921,10 +920,10 @@ def __expand_raw_ttm_by_year(self, sessions_df : DataFrame, years : list, tts_by expansion_df[cn_trend] = expansion_df.apply(lambda x : self.__get_trend_by_timedelta(td_1 = x[cn_trend_1], td_2 = x[cn_trend_2]), axis = 1) - new_column_names : list = [cn_month, cn_trend_1, cn_trend, cn_trend_2] # for ex. ["Month", "2016", "↕", "2017"] + new_column_names : list = [TTCN.MONTH, cn_trend_1, cn_trend, cn_trend_2] # for ex. ["Month", "2016", "↕", "2017"] expansion_df = expansion_df.reindex(columns = new_column_names) - shared_columns : list = [cn_month, str(years[i-1])] # ["Month", "2016"] + shared_columns : list = [TTCN.MONTH, str(years[i-1])] # ["Month", "2016"] actual_df = pd.merge( left = actual_df, right = expansion_df, From 6bd4427f360d986c6fad27ec75ce39100afa40c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:05:28 +0000 Subject: [PATCH 016/109] #40 __try_consolidate_trend_column_name(): * => TTCN. --- src/nwtimetracking.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index a04a03d..83ab984 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -73,6 +73,7 @@ class TTCN(StrEnum): TME = "TME" DYE = "DYE" TYE = "TYE" + TREND = "↕" # DTOs @dataclass(frozen=True) @@ -942,10 +943,8 @@ def __try_consolidate_trend_column_name(self, column_name : str) -> str: "↕1" => "↕" ''' - cn_trend : str = "↕" - - if column_name.startswith(cn_trend): - return cn_trend + if column_name.startswith(TTCN.TREND): + return TTCN.TREND return column_name def __create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: From c0996b75f82abea524c083ce4efa14ba3766e90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:09:02 +0000 Subject: [PATCH 017/109] #40 __create_effort_status_for_none_values: message moved to _MessageCollection(). --- src/nwtimetracking.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 83ab984..5d90675 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -53,6 +53,10 @@ def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_ti @staticmethod def effort_status_not_among_expected_time_values(time : str) -> str: return f"The provided time ('{time}') is not among the expected time values." + + @staticmethod + def starttime_endtime_are_empty() -> str: + return "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." class TTCN(StrEnum): '''Collects all the column names used by ...''' @@ -949,10 +953,11 @@ def __try_consolidate_trend_column_name(self, column_name : str) -> str: return column_name def __create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: + '''Creates effort status for None values.''' + actual_str : str = effort_str actual_td : timedelta = self.__convert_string_to_timedelta(td_str = effort_str) is_correct : bool = True - message : str = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." effort_status : EffortStatus = EffortStatus( idx = idx, @@ -965,7 +970,7 @@ def __create_effort_status_for_none_values(self, idx : int, effort_str : str) -> expected_td = None, expected_str = None, is_correct = is_correct, - message = message + message = _MessageCollection.starttime_endtime_are_empty() ) return effort_status From a9dbd187cbef23834139abd3aeffc102079b513f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:11:21 +0000 Subject: [PATCH 018/109] #40 __create_effort_status(): message moved to _MessageCollection(). --- src/nwtimetracking.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 5d90675..f1d8a2c 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -57,6 +57,9 @@ def effort_status_not_among_expected_time_values(time : str) -> str: @staticmethod def starttime_endtime_are_empty() -> str: return "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + @staticmethod + def effort_is_correct() -> str: + return "The effort is correct." class TTCN(StrEnum): '''Collects all the column names used by ...''' @@ -1049,7 +1052,8 @@ def __create_effort_status(self, idx : int, start_time_str : str, end_time_str : if actual_td != expected_td: is_correct = False - message : str = "The effort is correct." + message : str = _MessageCollection.effort_is_correct() + if actual_td != expected_td: message = _MessageCollection.effort_status_mismatching_effort( idx = idx, From aa2ffdd5a9abce9b60eaca45927331c4a748d0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:12:32 +0000 Subject: [PATCH 019/109] #40 __get_raw_tt_by_year_hashtag(): * => TTCN. --- src/nwtimetracking.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index f1d8a2c..926e9fc 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1053,7 +1053,7 @@ def __create_effort_status(self, idx : int, start_time_str : str, end_time_str : is_correct = False message : str = _MessageCollection.effort_is_correct() - + if actual_td != expected_td: message = _MessageCollection.effort_status_mismatching_effort( idx = idx, @@ -1122,15 +1122,12 @@ def __get_raw_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition : Series = (sessions_df[cn_year].isin(values = years)) + condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition] - cn_hashtag: str = "Hashtag" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_hashtag])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_hashtag, cn_year]).reset_index(drop = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) return tt_df def __get_raw_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: From 886a20165f43b2409f3ea56e4c36da74a0e60983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:19:18 +0000 Subject: [PATCH 020/109] #40 __get_raw_tt_by_hashtag(): * => TTCN. --- src/nwtimetracking.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 926e9fc..53e84b5 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -81,6 +81,7 @@ class TTCN(StrEnum): DYE = "DYE" TYE = "TYE" TREND = "↕" + EFFORTPRC = "Effort%" # DTOs @dataclass(frozen=True) @@ -1142,14 +1143,11 @@ def __get_raw_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: tt_df : DataFrame = sessions_df.copy(deep = True) - cn_hashtag: str = "Hashtag" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_hashtag])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - cn_effort_prc : str = "Effort%" - summarized : float = tt_df[cn_effort].sum() - tt_df[cn_effort_prc] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = summarized), axis = 1) + summarized : float = tt_df[TTCN.EFFORT].sum() + tt_df[TTCN.EFFORTPRC] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) return tt_df From f458c3a94dfb27e70297cfd3a1d9fd73a91c61bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:23:15 +0000 Subject: [PATCH 021/109] #40 get_tt_by_year(): * => TTCN. --- src/nwtimetracking.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 53e84b5..3b1dd2e 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -82,6 +82,9 @@ class TTCN(StrEnum): TYE = "TYE" TREND = "↕" EFFORTPRC = "Effort%" + YEARLYTARGET = "YearlyTarget" + TARGETDIFF = "TargetDiff" + ISTARGETMET = "IsTargetMet" # DTOs @dataclass(frozen=True) @@ -1195,28 +1198,22 @@ def get_tt_by_year(self, sessions_df : DataFrame, years : list[int], yearly_targ tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition : Series = (sessions_df[cn_year].isin(values = years)) + condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition] - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby([cn_year])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = cn_year).reset_index(drop = True) - - cn_yearly_target : str = "YearlyTarget" - cn_target_diff : str = "TargetDiff" - cn_is_target_met : str = "IsTargetMet" + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby([TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = TTCN.YEAR).reset_index(drop = True) - tt_df[cn_yearly_target] = tt_df[cn_year].apply( + tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEAR].apply( lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) - tt_df[cn_target_diff] = tt_df[cn_effort] - tt_df[cn_yearly_target] - tt_df[cn_is_target_met] = tt_df.apply( - lambda x : self.__is_yearly_target_met(effort = x[cn_effort], yearly_target = x[cn_yearly_target]), axis = 1) + tt_df[TTCN.TARGETDIFF] = tt_df[TTCN.EFFORT] - tt_df[TTCN.YEARLYTARGET] + tt_df[TTCN.ISTARGETMET] = tt_df.apply( + lambda x : self.__is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_yearly_target] = tt_df[cn_yearly_target].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_target_diff] = tt_df[cn_target_diff].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEARLYTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.TARGETDIFF] = tt_df[TTCN.TARGETDIFF].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) return tt_df def get_tt_by_year_month(self, sessions_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: From a59132c96c0c8abb08cfd4a7ac51a97ff8fd1df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:27:33 +0000 Subject: [PATCH 022/109] #40 get_tt_by_year_month(): * => TTCN. --- src/nwtimetracking.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 3b1dd2e..45c1bc2 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -85,6 +85,8 @@ class TTCN(StrEnum): YEARLYTARGET = "YearlyTarget" TARGETDIFF = "TargetDiff" ISTARGETMET = "IsTargetMet" + YEARLYTOTAL = "YearlyTotal" + TOTARGET = "ToTarget" # DTOs @dataclass(frozen=True) @@ -1258,31 +1260,24 @@ def get_tt_by_year_month(self, sessions_df : DataFrame, years : list[int], yearl tt_df : DataFrame = sessions_df.copy(deep = True) - cn_year : str = "Year" - condition : Series = (sessions_df[cn_year].isin(values = years)) + condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) tt_df = tt_df.loc[condition] - cn_month : str = "Month" - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [cn_year, cn_month])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - tt_df = tt_df.sort_values(by = [cn_year, cn_month]).reset_index(drop = True) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) - cn_yearly_total : str = "YearlyTotal" - tt_df[cn_yearly_total] = tt_df[cn_effort].groupby(by = tt_df[cn_year]).cumsum() + tt_df[TTCN.YEARLYTOTAL] = tt_df[TTCN.EFFORT].groupby(by = tt_df[TTCN.YEAR]).cumsum() - cn_yearly_target : str = "YearlyTarget" - tt_df[cn_yearly_target] = tt_df[cn_year].apply( + tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEAR].apply( lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) - cn_to_target : str = "ToTarget" - tt_df[cn_to_target] = tt_df[cn_yearly_total] - tt_df[cn_yearly_target] - - tt_df.drop(columns = [cn_yearly_target], axis = 1, inplace = True) + tt_df[TTCN.TOTARGET] = tt_df[TTCN.YEARLYTOTAL] - tt_df[TTCN.YEARLYTARGET] + tt_df.drop(columns = [TTCN.YEARLYTARGET], axis = 1, inplace = True) - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_yearly_total] = tt_df[cn_yearly_total].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_to_target] = tt_df[cn_to_target].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.YEARLYTOTAL] = tt_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.TOTARGET] = tt_df[TTCN.TOTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) return tt_df def get_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From e39915c079b554e41abd439ba7029b430d38fe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:31:20 +0000 Subject: [PATCH 023/109] #40 get_tt_by_year_month_spnv(): * => TTCN. --- src/nwtimetracking.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 45c1bc2..11d7f0f 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -87,6 +87,8 @@ class TTCN(StrEnum): ISTARGETMET = "IsTargetMet" YEARLYTOTAL = "YearlyTotal" TOTARGET = "ToTarget" + PERCDME = "%_DME" + PERCTME = "%_TME" # DTOs @dataclass(frozen=True) @@ -1296,37 +1298,28 @@ def get_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], dme_df : DataFrame = self.__get_raw_dme(sessions_df = sessions_df, years = years) tme_df : DataFrame = self.__get_raw_tme(sessions_df = sessions_df, years = years) - cn_year : str = "Year" - cn_month : str = "Month" - tt_df : DataFrame = pd.merge( left = spnv_df, right = dme_df, how = "inner", - left_on = [cn_year, cn_month], - right_on = [cn_year, cn_month] + left_on = [TTCN.YEAR, TTCN.MONTH], + right_on = [TTCN.YEAR, TTCN.MONTH] ) - cn_effort : str = "Effort" - cn_dme : str = "DME" - cn_percentage_dme : str = "%_DME" - tt_df[cn_percentage_dme] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_dme]), axis = 1) + tt_df[TTCN.PERCDME] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) tt_df = pd.merge( left = tt_df, right = tme_df, how = "inner", - left_on = [cn_year, cn_month], - right_on = [cn_year, cn_month] + left_on = [TTCN.YEAR, TTCN.MONTH], + right_on = [TTCN.YEAR, TTCN.MONTH] ) - cn_tme : str = "TME" - cn_percentage_tme : str = "%_TME" - tt_df[cn_percentage_tme] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_tme]), axis = 1) - - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_dme] = tt_df[cn_dme].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_tme] = tt_df[cn_tme].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.PERCTME] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.DME] = tt_df[TTCN.DME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.TME] = tt_df[TTCN.TME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df def get_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From 06143c88ddfcb9a373cc13b058290b937131ea8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:39:20 +0000 Subject: [PATCH 024/109] #40 get_tt_by_year_spnv(): * => TTCN. --- src/nwtimetracking.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 11d7f0f..57e4c63 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -89,6 +89,8 @@ class TTCN(StrEnum): TOTARGET = "ToTarget" PERCDME = "%_DME" PERCTME = "%_TME" + PERCDYE = "%_DYE" + PERCTYE = "%_TYE" # DTOs @dataclass(frozen=True) @@ -1338,36 +1340,28 @@ def get_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], softwa dye_df : DataFrame = self.__get_raw_dye(sessions_df = sessions_df, years = years) tye_df : DataFrame = self.__get_raw_tye(sessions_df = sessions_df, years = years) - cn_year : str = "Year" - tt_df : DataFrame = pd.merge( left = spnv_df, right = dye_df, how = "inner", - left_on = [cn_year], - right_on = [cn_year] + left_on = [TTCN.YEAR], + right_on = [TTCN.YEAR] ) - cn_effort : str = "Effort" - cn_dye : str = "DYE" - cn_percentage_dye : str = "%_DYE" - tt_df[cn_percentage_dye] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_dye]), axis = 1) + tt_df[TTCN.PERCDYE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) tt_df = pd.merge( left = tt_df, right = tye_df, how = "inner", - left_on = [cn_year], - right_on = [cn_year] + left_on = [TTCN.YEAR], + right_on = [TTCN.YEAR] ) - cn_tye : str = "TYE" - cn_percentage_tye : str = "%_TYE" - tt_df[cn_percentage_tye] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_tye]), axis = 1) - - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_dye] = tt_df[cn_dye].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_tye] = tt_df[cn_tye].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.PERCTYE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.DYE] = tt_df[TTCN.DYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.TYE] = tt_df[TTCN.TYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df def get_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: From 763d15c826a3eef30a9b2b997905e166bebb0f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:42:58 +0000 Subject: [PATCH 025/109] #40 get_tt_by_spn(): * => TTCN. --- src/nwtimetracking.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 57e4c63..2a0c94a 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -91,6 +91,10 @@ class TTCN(StrEnum): PERCTME = "%_TME" PERCDYE = "%_DYE" PERCTYE = "%_TYE" + DE = "DE" + TE = "TE" + PERCDE = "%_DE" + PERCTE = "%_TE" # DTOs @dataclass(frozen=True) @@ -1384,22 +1388,15 @@ def get_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_pro de : timedelta = self.__get_raw_de(sessions_df = sessions_df, years = years) te : timedelta = self.__get_raw_te(sessions_df = sessions_df, years = years, remove_untagged = remove_untagged) - cn_de : str = "DE" - tt_df[cn_de] = de + tt_df[TTCN.DE] = de + tt_df[TTCN.PERCDE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) - cn_effort : str = "Effort" - cn_percentage_de : str = "%_DE" - tt_df[cn_percentage_de] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_de]), axis = 1) - - cn_te : str = "TE" - tt_df[cn_te] = te - - cn_percentage_te : str = "%_TE" - tt_df[cn_percentage_te] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[cn_effort], whole = x[cn_te]), axis = 1) + tt_df[TTCN.TE] = te + tt_df[TTCN.PERCTE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_de] = tt_df[cn_de].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[cn_te] = tt_df[cn_te].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.DE] = tt_df[TTCN.DE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.TE] = tt_df[TTCN.TE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df def get_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: From dc6d9f5103ff9e37c1d9b9faa7ac4e1b26d642bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:43:32 +0000 Subject: [PATCH 026/109] #40 get_tt_by_spn_spv(): * => TTCN. --- src/nwtimetracking.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 2a0c94a..4552d3c 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1410,9 +1410,7 @@ def get_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software ''' tt_df : DataFrame = self.__get_raw_tt_by_spn_spv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) - - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df def get_tts_by_month(self, sessions_df : DataFrame, years : list) -> DataFrame: From ac15f742fe69c77f5f57c96d531c3d7d66697443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:52:20 +0000 Subject: [PATCH 027/109] #40 get_tt_by_hashtag(): * => TTCN. --- src/nwtimetracking.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 4552d3c..37c97a7 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1453,9 +1453,7 @@ def get_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int]) -> ''' tt_df : DataFrame = self.__get_raw_tt_by_year_hashtag(sessions_df = sessions_df, years = years) - - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df def get_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: @@ -1469,9 +1467,7 @@ def get_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: ''' tt_df : DataFrame = self.__get_raw_tt_by_hashtag(sessions_df = sessions_df) - - cn_effort : str = "Effort" - tt_df[cn_effort] = tt_df[cn_effort].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) return tt_df From 2de1fdcd3a2768bddb1a05d26e6c30aea4b33aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:53:02 +0000 Subject: [PATCH 028/109] #40 update_future_months_to_empty(): * => TTCN. --- src/nwtimetracking.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 37c97a7..657a852 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1508,10 +1508,9 @@ def update_future_months_to_empty(self, tts_by_month_df : DataFrame, now : datet now_year : int = now.year now_month : int = now.month cn_year : str = str(now_year) - cn_month : str = "Month" new_value : str = "" - condition : Series = (tts_by_month_upd_df[cn_month] > now_month) + condition : Series = (tts_by_month_upd_df[TTCN.MONTH] > now_month) tts_by_month_upd_df[cn_year] = np.where(condition, new_value, tts_by_month_upd_df[cn_year]) idx_year : int = cast(int, tts_by_month_upd_df.columns.get_loc(cn_year)) From b764c040d1b7a93623b7df7ba88552120ecd8385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:57:37 +0000 Subject: [PATCH 029/109] #40 add_effort_status(): * => TTCN. --- src/nwtimetracking.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 657a852..d2f215d 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -95,6 +95,10 @@ class TTCN(StrEnum): TE = "TE" PERCDE = "%_DE" PERCTE = "%_TE" + EFFORTSTATUS = "EffortStatus" + ESISCORRECT = "ES_IsCorrect" + ESEXPECTED = "ES_Expected" + ESMESSAGE = "ES_Message" # DTOs @dataclass(frozen=True) @@ -1528,28 +1532,19 @@ def add_effort_status(self, sessions_df : DataFrame) -> DataFrame: es_df : DataFrame = sessions_df.copy(deep = True) - cn_start_time : str = "StartTime" - cn_end_time : str = "EndTime" - cn_effort : str = "Effort" - cn_effort_status : str = "EffortStatus" - - es_df[cn_effort_status] = es_df.apply( + es_df[TTCN.EFFORTSTATUS] = es_df.apply( lambda x : self.__create_effort_status_and_cast_to_any( idx = x.name, - start_time_str = x[cn_start_time], - end_time_str = x[cn_end_time], - effort_str = x[cn_effort]), + start_time_str = x[TTCN.STARTTIME], + end_time_str = x[TTCN.ENDTIME], + effort_str = x[TTCN.EFFORT]), axis = 1) - cn_es_is_correct : str = "ES_IsCorrect" - cn_es_expected : str = "ES_Expected" - cn_es_message : str = "ES_Message" - - es_df[cn_es_is_correct] = es_df[cn_effort_status].apply(lambda x : x.is_correct) - es_df[cn_es_expected] = es_df[cn_effort_status].apply(lambda x : x.expected_str) - es_df[cn_es_message] = es_df[cn_effort_status].apply(lambda x : x.message) + es_df[TTCN.ESISCORRECT] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.is_correct) + es_df[TTCN.ESEXPECTED] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.expected_str) + es_df[TTCN.ESMESSAGE] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.message) - es_df = es_df[[cn_start_time, cn_end_time, cn_effort, cn_es_is_correct, cn_es_expected, cn_es_message]] + es_df = es_df[[TTCN.STARTTIME, TTCN.ENDTIME, TTCN.EFFORT, TTCN.ESISCORRECT, TTCN.ESEXPECTED, TTCN.ESMESSAGE]] return es_df def filter_by_is_correct(self, es_df : DataFrame, is_correct : bool) -> DataFrame: From 95ec2fbbbfb4facab51d2f0c1e931cfad5e76ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 14:58:05 +0000 Subject: [PATCH 030/109] #40 filter_by_is_correct(): * => TTCN. --- src/nwtimetracking.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index d2f215d..9738370 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1553,9 +1553,7 @@ def filter_by_is_correct(self, es_df : DataFrame, is_correct : bool) -> DataFram filtered_df : DataFrame = es_df.copy(deep = True) - cn_es_is_correct : str = "ES_IsCorrect" - - condition : Series = (filtered_df[cn_es_is_correct] == is_correct) + condition : Series = (filtered_df[TTCN.ESISCORRECT] == is_correct) filtered_df = es_df.loc[condition] return filtered_df From 99ac7150dba3ff85da67ed1f372fa7ffa936cd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 15:06:46 +0000 Subject: [PATCH 031/109] #40 create_time_ranges_df(): * => TTCN. --- src/nwtimetracking.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 9738370..e5466fe 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -99,6 +99,8 @@ class TTCN(StrEnum): ESISCORRECT = "ES_IsCorrect" ESEXPECTED = "ES_Expected" ESMESSAGE = "ES_Message" + TIMERANGEID = "TimeRangeId" + OCCURRENCES = "Occurrences" # DTOs @dataclass(frozen=True) @@ -1568,24 +1570,18 @@ def create_time_ranges_df(self, sessions_df : DataFrame, unknown_id : str) -> Da ''' time_ranges_df : DataFrame = sessions_df.copy(deep = True) - - cn_start_time : str = "StartTime" - cn_end_time : str = "EndTime" - cn_time_range_id : str = "TimeRangeId" - time_ranges_df = time_ranges_df[[cn_start_time, cn_end_time]] - time_ranges_df[cn_time_range_id] = time_ranges_df.apply( + time_ranges_df = time_ranges_df[[TTCN.STARTTIME, TTCN.ENDTIME]] + time_ranges_df[TTCN.TIMERANGEID] = time_ranges_df.apply( lambda x : self.__create_time_range_id( - start_time = x[cn_start_time], - end_time = x[cn_end_time], + start_time = x[TTCN.STARTTIME], + end_time = x[TTCN.ENDTIME], unknown_id = unknown_id), axis = 1) - cn_occurrences : str = "Occurrences" - - time_ranges_df = time_ranges_df[[cn_time_range_id]].groupby(by = [cn_time_range_id], as_index=False).agg( - count = pd.NamedAgg(column = cn_time_range_id, aggfunc = "count")) - time_ranges_df.rename(columns={"count" : cn_occurrences}, inplace = True) - time_ranges_df = time_ranges_df.sort_values(by = [cn_occurrences], ascending = False).reset_index(drop = True) + time_ranges_df = time_ranges_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg( + count = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count")) + time_ranges_df.rename(columns={"count" : TTCN.OCCURRENCES}, inplace = True) + time_ranges_df = time_ranges_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) return time_ranges_df def remove_unknown_id(self, time_ranges_df : DataFrame, unknown_id : str) -> DataFrame: From a772744dd818069d827d8bf343d9ecb6f3d1931b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 15:07:12 +0000 Subject: [PATCH 032/109] #40 remove_unknown_id(): * => TTCN. --- src/nwtimetracking.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index e5466fe..9ecb6b8 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1588,9 +1588,7 @@ def remove_unknown_id(self, time_ranges_df : DataFrame, unknown_id : str) -> Dat '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' - cn_time_range_id : str = "TimeRangeId" - - condition : Series = (time_ranges_df[cn_time_range_id] != unknown_id) + condition : Series = (time_ranges_df[TTCN.TIMERANGEID] != unknown_id) time_ranges_df = time_ranges_df.loc[condition] time_ranges_df.reset_index(drop = True, inplace = True) From 0ef4dd3aa70c83f3f0af752ac01c848a18076ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 15:07:47 +0000 Subject: [PATCH 033/109] #40 filter_by_top_n_occurrences(): * => TTCN. --- src/nwtimetracking.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 9ecb6b8..2020aff 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1597,9 +1597,7 @@ def filter_by_top_n_occurrences(self, time_ranges_df : DataFrame, n : int, ascen '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' - cn_occurrences : str = "Occurrences" - - time_ranges_df.sort_values(by = cn_occurrences, ascending = [ascending], inplace = True) + time_ranges_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) time_ranges_df = time_ranges_df.iloc[0:n] time_ranges_df.reset_index(drop = True, inplace = True) From fb7618ae49fab5e8064cda4cf53f4514093272c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 15:25:43 +0000 Subject: [PATCH 034/109] #40 TTCN: moved. --- src/nwtimetracking.py | 81 ++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 2020aff..6e7bf15 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -20,46 +20,6 @@ from nwshared import Formatter, FilePathManager, FileManager, LambdaProvider, MarkdownHelper # CONSTANTS -# STATIC CLASSES -class _MessageCollection(): - - '''Collects all the messages used for logging and for the exceptions.''' - - @staticmethod - def effort_status_mismatching_effort(idx : int, start_time_str : str, end_time_str : str, actual_str : str, expected_str : str) -> str: - - ''' - "The provided row contains a mismatching effort (idx: '4', start_time: '20:00', end_time: '00:00', actual_effort: '3h 00m', expected_effort: '4h 00m')." - ''' - - message : str = "The provided row contains a mismatching effort " - message += f"(idx: '{idx}', start_time: '{start_time_str}', end_time: '{end_time_str}', actual_effort: '{actual_str}', expected_effort: '{expected_str}')." - - return message - - @staticmethod - def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_time_str : str, effort_str : str): - - ''' - "It has not been possible to create an EffortStatus for the provided parameters - (idx: '770', start_time_str: '22:00', end_time_str: '00:00 ', effort_str: '2h 00m')." - ''' - - message : str = "It has not been possible to create an EffortStatus for the provided parameters " - message += f"(idx: '{idx}', start_time_str: '{start_time_str}', end_time_str: '{end_time_str}', effort_str: '{effort_str}')." - - return message - - @staticmethod - def effort_status_not_among_expected_time_values(time : str) -> str: - return f"The provided time ('{time}') is not among the expected time values." - - @staticmethod - def starttime_endtime_are_empty() -> str: - return "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." - @staticmethod - def effort_is_correct() -> str: - return "The effort is correct." class TTCN(StrEnum): '''Collects all the column names used by ...''' @@ -102,6 +62,47 @@ class TTCN(StrEnum): TIMERANGEID = "TimeRangeId" OCCURRENCES = "Occurrences" +# STATIC CLASSES +class _MessageCollection(): + + '''Collects all the messages used for logging and for the exceptions.''' + + @staticmethod + def effort_status_mismatching_effort(idx : int, start_time_str : str, end_time_str : str, actual_str : str, expected_str : str) -> str: + + ''' + "The provided row contains a mismatching effort (idx: '4', start_time: '20:00', end_time: '00:00', actual_effort: '3h 00m', expected_effort: '4h 00m')." + ''' + + message : str = "The provided row contains a mismatching effort " + message += f"(idx: '{idx}', start_time: '{start_time_str}', end_time: '{end_time_str}', actual_effort: '{actual_str}', expected_effort: '{expected_str}')." + + return message + + @staticmethod + def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_time_str : str, effort_str : str): + + ''' + "It has not been possible to create an EffortStatus for the provided parameters + (idx: '770', start_time_str: '22:00', end_time_str: '00:00 ', effort_str: '2h 00m')." + ''' + + message : str = "It has not been possible to create an EffortStatus for the provided parameters " + message += f"(idx: '{idx}', start_time_str: '{start_time_str}', end_time_str: '{end_time_str}', effort_str: '{effort_str}')." + + return message + + @staticmethod + def effort_status_not_among_expected_time_values(time : str) -> str: + return f"The provided time ('{time}') is not among the expected time values." + + @staticmethod + def starttime_endtime_are_empty() -> str: + return "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + @staticmethod + def effort_is_correct() -> str: + return "The effort is correct." + # DTOs @dataclass(frozen=True) class YearlyTarget(): From 3d32c5de2c113b7714256ac7f85d636aff84e129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 18:44:50 +0000 Subject: [PATCH 035/109] #40 nwtimetracking: * => sessions_df, Iteration 1. --- src/nwtimetracking.py | 528 +++++++++++++++++------------------ tests/nwtimetrackingtests.py | 20 +- 2 files changed, 274 insertions(+), 274 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 6e7bf15..cdba748 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -375,7 +375,7 @@ class TimeTrackingManager(): '''Collects all the logic related to the management of "Time Tracking.xlsx".''' - def __enforce_dataframe_definition_for_sessions_df(self, sessions_df : DataFrame) -> DataFrame: + def __enforce_dataframe_definition_for_tt_df(self, tt_df : DataFrame) -> DataFrame: '''Enforces definition for the provided dataframe.''' @@ -391,26 +391,26 @@ def __enforce_dataframe_definition_for_sessions_df(self, sessions_df : DataFrame column_names.append(TTCN.YEAR) # [8], int column_names.append(TTCN.MONTH) # [9], int - sessions_df = sessions_df[column_names] + tt_df = tt_df[column_names] - sessions_df[column_names[0]] = pd.to_datetime(sessions_df[column_names[0]], format="%Y-%m-%d") - sessions_df[column_names[0]] = sessions_df[column_names[0]].apply(lambda x: x.date()) - - sessions_df = sessions_df.astype({column_names[1]: str}) - sessions_df = sessions_df.astype({column_names[2]: str}) - sessions_df = sessions_df.astype({column_names[3]: str}) - sessions_df = sessions_df.astype({column_names[4]: str}) - sessions_df = sessions_df.astype({column_names[5]: str}) - sessions_df = sessions_df.astype({column_names[6]: bool}) - sessions_df = sessions_df.astype({column_names[7]: bool}) - sessions_df = sessions_df.astype({column_names[8]: int}) - sessions_df = sessions_df.astype({column_names[9]: int}) - - sessions_df[column_names[1]] = sessions_df[column_names[1]].replace('nan', '') - sessions_df[column_names[2]] = sessions_df[column_names[2]].replace('nan', '') - sessions_df[column_names[5]] = sessions_df[column_names[5]].replace('nan', '') - - return sessions_df + tt_df[column_names[0]] = pd.to_datetime(tt_df[column_names[0]], format="%Y-%m-%d") + tt_df[column_names[0]] = tt_df[column_names[0]].apply(lambda x: x.date()) + + tt_df = tt_df.astype({column_names[1]: str}) + tt_df = tt_df.astype({column_names[2]: str}) + tt_df = tt_df.astype({column_names[3]: str}) + tt_df = tt_df.astype({column_names[4]: str}) + tt_df = tt_df.astype({column_names[5]: str}) + tt_df = tt_df.astype({column_names[6]: bool}) + tt_df = tt_df.astype({column_names[7]: bool}) + tt_df = tt_df.astype({column_names[8]: int}) + tt_df = tt_df.astype({column_names[9]: int}) + + tt_df[column_names[1]] = tt_df[column_names[1]].replace('nan', '') + tt_df[column_names[2]] = tt_df[column_names[2]].replace('nan', '') + tt_df[column_names[5]] = tt_df[column_names[5]].replace('nan', '') + + return tt_df def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataFrame: '''Ensures that the columns of the provided dataframe have the expected data types.''' @@ -508,7 +508,7 @@ def __calculate_percentage(self, part : float, whole : float, rounding_digits : prct = round(number = prct, ndigits = rounding_digits) return prct - def __get_raw_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year Month ProjectName ProjectVersion Effort @@ -517,24 +517,24 @@ def __get_raw_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[ ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) - tt_df = tt_df.loc[condition_three] + condition_three : Series = (tts_df[TTCN.PROJECTNAME].isin(values = software_project_names)) + tts_df = tts_df.loc[condition_three] - return tt_df - def __get_raw_dme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + return tts_df + def __get_raw_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month DME @@ -545,22 +545,22 @@ def __get_raw_dme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame DME = DevelopmentMonthlyEffort ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) - tt_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) + tts_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) - return tt_df - def __get_raw_tme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + return tts_df + def __get_raw_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month TME @@ -571,18 +571,18 @@ def __get_raw_tme(self, sessions_df : DataFrame, years : list[int]) -> DataFrame TME = TotalMonthlyEffort ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition] + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) - tt_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) + tts_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) - return tt_df - def __get_raw_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + return tts_df + def __get_raw_tt_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year ProjectName ProjectVersion Effort @@ -591,25 +591,25 @@ def __get_raw_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) - tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) + condition_three : Series = (tts_df[TTCN.PROJECTNAME].isin(values = software_project_names)) + tts_df = tts_df.loc[condition_three] + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - return tt_df - def __get_raw_dye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + return tts_df + def __get_raw_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year DYE @@ -620,22 +620,22 @@ def __get_raw_dye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame DYE = DevelopmentYearlyEffort ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) - tt_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) + tts_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) - return tt_df - def __get_raw_tye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + return tts_df + def __get_raw_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year TYE @@ -646,18 +646,18 @@ def __get_raw_tye(self, sessions_df : DataFrame, years : list[int]) -> DataFrame TYE = TotalYearlyEffort ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition] + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) - tt_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) + tts_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) - return tt_df - def __get_raw_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + return tts_df + def __get_raw_tt_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Hashtag ProjectName Effort @@ -668,56 +668,56 @@ def __get_raw_tt_by_spn(self, sessions_df : DataFrame, years : list[int], softwa ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.PROJECTNAME, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME]).reset_index(drop = True) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME]).reset_index(drop = True) - condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) - tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [TTCN.HASHTAG, TTCN.EFFORT], ascending = [False, False]).reset_index(drop = True) + condition_three : Series = (tts_df[TTCN.PROJECTNAME].isin(values = software_project_names)) + tts_df = tts_df.loc[condition_three] + tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.EFFORT], ascending = [False, False]).reset_index(drop = True) - tt_df = tt_df[[TTCN.HASHTAG, TTCN.PROJECTNAME, TTCN.EFFORT]] + tts_df = tts_df[[TTCN.HASHTAG, TTCN.PROJECTNAME, TTCN.EFFORT]] - return tt_df - def __get_raw_de(self, sessions_df : DataFrame, years : list[int]) -> timedelta: + return tts_df + def __get_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: '''3 days 21:15:00''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - summarized : timedelta = tt_df[TTCN.EFFORT].sum() + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized - def __get_raw_te(self, sessions_df : DataFrame, years : list[int], remove_untagged : bool) -> timedelta: + def __get_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : bool) -> timedelta: '''186 days 11:15:00''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition_one] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition_one] if remove_untagged: - condition_two : Series = (sessions_df[TTCN.HASHTAG] != "#untagged") - tt_df = tt_df.loc[condition_two] + condition_two : Series = (tt_df[TTCN.HASHTAG] != "#untagged") + tts_df = tts_df.loc[condition_two] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - summarized : timedelta = tt_df[TTCN.EFFORT].sum() + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized - def __get_raw_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tt_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -727,24 +727,24 @@ def __get_raw_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], so ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition_one : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - condition_two : Series = (sessions_df[TTCN.ISSOFTWAREPROJECT] == True) - tt_df = tt_df.loc[condition_one & condition_two] + condition_one : Series = (tt_df[TTCN.YEAR].isin(values = years)) + condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) + tts_df = tts_df.loc[condition_one & condition_two] - tt_df[TTCN.PROJECTNAME] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tt_df[TTCN.PROJECTVERSION] = tt_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - condition_three : Series = (tt_df[TTCN.PROJECTNAME].isin(values = software_project_names)) - tt_df = tt_df.loc[condition_three] - tt_df = tt_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) + condition_three : Series = (tts_df[TTCN.PROJECTNAME].isin(values = software_project_names)) + tts_df = tts_df.loc[condition_three] + tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) - return tt_df + return tts_df def __get_default_raw_ttm(self, year : int) -> DataFrame: ''' @@ -822,7 +822,7 @@ def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: return completed_df return ttm_df - def __get_raw_ttm(self, sessions_df : DataFrame, year : int) -> DataFrame: + def __get_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: ''' ttm_df: @@ -849,10 +849,10 @@ def __get_raw_ttm(self, sessions_df : DataFrame, year : int) -> DataFrame: 11 12 0 days 00:00:00 ''' - ttm_df : DataFrame = sessions_df.copy(deep=True) + ttm_df : DataFrame = tt_df.copy(deep=True) ttm_df = ttm_df[[TTCN.YEAR, TTCN.MONTH, TTCN.EFFORT]] - condition : Series = (sessions_df[TTCN.YEAR] == year) + condition : Series = (tt_df[TTCN.YEAR] == year) ttm_df = ttm_df.loc[condition] ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) @@ -883,7 +883,7 @@ def __get_trend_by_timedelta(self, td_1 : timedelta, td_2 : timedelta) -> str: trend = "=" return trend - def __expand_raw_ttm_by_year(self, sessions_df : DataFrame, years : list, tts_by_month_df : DataFrame, i : int, add_trend : bool) -> DataFrame: + def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month_df : DataFrame, i : int, add_trend : bool) -> DataFrame: ''' actual_df: @@ -932,7 +932,7 @@ def __expand_raw_ttm_by_year(self, sessions_df : DataFrame, years : list, tts_by ''' actual_df : DataFrame = tts_by_month_df.copy(deep = True) - ttm_df : DataFrame = self.__get_raw_ttm(sessions_df = sessions_df, year = years[i]) + ttm_df : DataFrame = self.__get_raw_ttm(tt_df = tt_df, year = years[i]) expansion_df = pd.merge( left = actual_df, @@ -1131,7 +1131,7 @@ def __create_time_range_id(self, start_time : str, end_time : str, unknown_id : time_range_id = unknown_id return time_range_id - def __get_raw_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tt_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1141,17 +1141,17 @@ def __get_raw_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition] + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) - return tt_df - def __get_raw_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: + return tts_df + def __get_raw_tt_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ''' Hashtag Effort Effort% @@ -1161,33 +1161,62 @@ def __get_raw_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - summarized : float = tt_df[TTCN.EFFORT].sum() - tt_df[TTCN.EFFORTPRC] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) + summarized : float = tts_df[TTCN.EFFORT].sum() + tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) - return tt_df + return tts_df - def get_sessions_dataset(self, setting_bag : SettingBag) -> DataFrame: + def get_tt(self, setting_bag : SettingBag) -> DataFrame: ''' Retrieves the content of the "Sessions" tab and returns it as a Dataframe. ''' - sessions_df : DataFrame = pd.read_excel( + tt_df : DataFrame = pd.read_excel( io = setting_bag.excel_path, skiprows = setting_bag.excel_books_skiprows, nrows = setting_bag.excel_books_nrows, sheet_name = setting_bag.excel_books_tabname, engine = 'openpyxl' ) - sessions_df = self.__enforce_dataframe_definition_for_sessions_df(sessions_df = sessions_df) + tt_df = self.__enforce_dataframe_definition_for_tt_df(tt_df = tt_df) - return sessions_df - def get_tt_by_year(self, sessions_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + return tt_df + def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: + + ''' + Month 2016 ↕ 2017 ↕ 2018 ... + 0 1 0h 00m ↑ 13h 00m ↓ 0h 00m + 1 2 0h 00m ↑ 1h 00m ↓ 0h 00m + ... + ''' + + tts_df : DataFrame = pd.DataFrame() + + for i in range(len(years)): + + if i == 0: + tts_df = self.__get_raw_ttm(tt_df = tt_df, year = years[i]) + else: + tts_df = self.__expand_raw_ttm_by_year( + tt_df = tt_df, + years = years, + tts_by_month_df = tts_df, + i = i, + add_trend = True) + + for year in years: + tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + + tts_df.rename(columns = (lambda x : self.__try_consolidate_trend_column_name(column_name = x)), inplace = True) + + return tts_df + def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' [0] @@ -1213,27 +1242,27 @@ def get_tt_by_year(self, sessions_df : DataFrame, years : list[int], yearly_targ ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition] + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby([TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = TTCN.YEAR).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby([TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = TTCN.YEAR).reset_index(drop = True) - tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEAR].apply( + tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) - tt_df[TTCN.TARGETDIFF] = tt_df[TTCN.EFFORT] - tt_df[TTCN.YEARLYTARGET] - tt_df[TTCN.ISTARGETMET] = tt_df.apply( + tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.EFFORT] - tts_df[TTCN.YEARLYTARGET] + tts_df[TTCN.ISTARGETMET] = tts_df.apply( lambda x : self.__is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEARLYTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.TARGETDIFF] = tt_df[TTCN.TARGETDIFF].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEARLYTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) - return tt_df - def get_tt_by_year_month(self, sessions_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + return tts_df + def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' [0] @@ -1273,29 +1302,29 @@ def get_tt_by_year_month(self, sessions_df : DataFrame, years : list[int], yearl ... ''' - tt_df : DataFrame = sessions_df.copy(deep = True) + tts_df : DataFrame = tt_df.copy(deep = True) - condition : Series = (sessions_df[TTCN.YEAR].isin(values = years)) - tt_df = tt_df.loc[condition] + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) - tt_df = tt_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tt_df = tt_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) - tt_df[TTCN.YEARLYTOTAL] = tt_df[TTCN.EFFORT].groupby(by = tt_df[TTCN.YEAR]).cumsum() + tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.EFFORT].groupby(by = tts_df[TTCN.YEAR]).cumsum() - tt_df[TTCN.YEARLYTARGET] = tt_df[TTCN.YEAR].apply( + tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) - tt_df[TTCN.TOTARGET] = tt_df[TTCN.YEARLYTOTAL] - tt_df[TTCN.YEARLYTARGET] - tt_df.drop(columns = [TTCN.YEARLYTARGET], axis = 1, inplace = True) + tts_df[TTCN.TOTARGET] = tts_df[TTCN.YEARLYTOTAL] - tts_df[TTCN.YEARLYTARGET] + tts_df.drop(columns = [TTCN.YEARLYTARGET], axis = 1, inplace = True) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.YEARLYTOTAL] = tt_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.TOTARGET] = tt_df[TTCN.TOTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) - return tt_df - def get_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + return tts_df + def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' [0] ... @@ -1307,11 +1336,11 @@ def get_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], ... ''' - spnv_df : DataFrame = self.__get_raw_tt_by_year_month_spnv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) - dme_df : DataFrame = self.__get_raw_dme(sessions_df = sessions_df, years = years) - tme_df : DataFrame = self.__get_raw_tme(sessions_df = sessions_df, years = years) + spnv_df : DataFrame = self.__get_raw_tt_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dme_df : DataFrame = self.__get_raw_dme(tt_df = tt_df, years = years) + tme_df : DataFrame = self.__get_raw_tme(tt_df = tt_df, years = years) - tt_df : DataFrame = pd.merge( + tts_df : DataFrame = pd.merge( left = spnv_df, right = dme_df, how = "inner", @@ -1319,23 +1348,23 @@ def get_tt_by_year_month_spnv(self, sessions_df : DataFrame, years : list[int], right_on = [TTCN.YEAR, TTCN.MONTH] ) - tt_df[TTCN.PERCDME] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) + tts_df[TTCN.PERCDME] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) - tt_df = pd.merge( - left = tt_df, + tts_df = pd.merge( + left = tts_df, right = tme_df, how = "inner", left_on = [TTCN.YEAR, TTCN.MONTH], right_on = [TTCN.YEAR, TTCN.MONTH] ) - tt_df[TTCN.PERCTME] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.DME] = tt_df[TTCN.DME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.TME] = tt_df[TTCN.TME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTME] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df - def get_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + return tts_df + def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' [0] ... @@ -1347,11 +1376,11 @@ def get_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], softwa ... ''' - spnv_df : DataFrame = self.__get_raw_tt_by_year_spnv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) - dye_df : DataFrame = self.__get_raw_dye(sessions_df = sessions_df, years = years) - tye_df : DataFrame = self.__get_raw_tye(sessions_df = sessions_df, years = years) + spnv_df : DataFrame = self.__get_raw_tt_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dye_df : DataFrame = self.__get_raw_dye(tt_df = tt_df, years = years) + tye_df : DataFrame = self.__get_raw_tye(tt_df = tt_df, years = years) - tt_df : DataFrame = pd.merge( + tts_df : DataFrame = pd.merge( left = spnv_df, right = dye_df, how = "inner", @@ -1359,23 +1388,23 @@ def get_tt_by_year_spnv(self, sessions_df : DataFrame, years : list[int], softwa right_on = [TTCN.YEAR] ) - tt_df[TTCN.PERCDYE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) + tts_df[TTCN.PERCDYE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) - tt_df = pd.merge( - left = tt_df, + tts_df = pd.merge( + left = tts_df, right = tye_df, how = "inner", left_on = [TTCN.YEAR], right_on = [TTCN.YEAR] ) - tt_df[TTCN.PERCTYE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.DYE] = tt_df[TTCN.DYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.TYE] = tt_df[TTCN.TYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTYE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df - def get_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: + return tts_df + def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: ''' Hashtag ProjectName Effort DE %_DE TE %_TE @@ -1391,22 +1420,22 @@ def get_tt_by_spn(self, sessions_df : DataFrame, years : list[int], software_pro ... ''' - tt_df : DataFrame = self.__get_raw_tt_by_spn(sessions_df = sessions_df, years = years, software_project_names = software_project_names) - de : timedelta = self.__get_raw_de(sessions_df = sessions_df, years = years) - te : timedelta = self.__get_raw_te(sessions_df = sessions_df, years = years, remove_untagged = remove_untagged) + tts_df : DataFrame = self.__get_raw_tt_by_spn(tt_df = tt_df, years = years, software_project_names = software_project_names) + de : timedelta = self.__get_raw_de(tt_df = tt_df, years = years) + te : timedelta = self.__get_raw_te(tt_df = tt_df, years = years, remove_untagged = remove_untagged) - tt_df[TTCN.DE] = de - tt_df[TTCN.PERCDE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) + tts_df[TTCN.DE] = de + tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) - tt_df[TTCN.TE] = te - tt_df[TTCN.PERCTE] = tt_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) + tts_df[TTCN.TE] = te + tts_df[TTCN.PERCTE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.DE] = tt_df[TTCN.DE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tt_df[TTCN.TE] = tt_df[TTCN.TE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DE] = tts_df[TTCN.DE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df - def get_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + return tts_df + def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -1416,40 +1445,11 @@ def get_tt_by_spn_spv(self, sessions_df : DataFrame, years : list[int], software ... ''' - tt_df : DataFrame = self.__get_raw_tt_by_spn_spv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tt_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df - def get_tts_by_month(self, sessions_df : DataFrame, years : list) -> DataFrame: - - ''' - Month 2016 ↕ 2017 ↕ 2018 ... - 0 1 0h 00m ↑ 13h 00m ↓ 0h 00m - 1 2 0h 00m ↑ 1h 00m ↓ 0h 00m - ... - ''' - - tts_by_month_df : DataFrame = pd.DataFrame() - - for i in range(len(years)): - - if i == 0: - tts_by_month_df = self.__get_raw_ttm(sessions_df = sessions_df, year = years[i]) - else: - tts_by_month_df = self.__expand_raw_ttm_by_year( - sessions_df = sessions_df, - years = years, - tts_by_month_df = tts_by_month_df, - i = i, - add_trend = True) - - for year in years: - tts_by_month_df[str(year)] = tts_by_month_df[str(year)].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - - tts_by_month_df.rename(columns = (lambda x : self.__try_consolidate_trend_column_name(column_name = x)), inplace = True) - - return tts_by_month_df - def get_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int]) -> DataFrame: + return tts_df + def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1459,11 +1459,11 @@ def get_tt_by_year_hashtag(self, sessions_df : DataFrame, years : list[int]) -> ... ''' - tt_df : DataFrame = self.__get_raw_tt_by_year_hashtag(sessions_df = sessions_df, years = years) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tt_by_year_hashtag(tt_df = tt_df, years = years) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df - def get_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: + return tts_df + def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ''' Hashtag Effort Effort% @@ -1473,10 +1473,10 @@ def get_tt_by_hashtag(self, sessions_df : DataFrame) -> DataFrame: ... ''' - tt_df : DataFrame = self.__get_raw_tt_by_hashtag(sessions_df = sessions_df) - tt_df[TTCN.EFFORT] = tt_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tt_by_hashtag(tt_df = tt_df) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - return tt_df + return tts_df def try_print_definitions(self, df : DataFrame, definitions : dict[str, str]) -> None: diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 1cea68b..bb73bb7 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1043,7 +1043,7 @@ def test_getsessionsdataset_shouldreturnexpecteddataframe_wheninvoked(self): # Act with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: - actual : DataFrame = TimeTrackingManager().get_sessions_dataset(setting_bag = setting_bag) + actual : DataFrame = TimeTrackingManager().get_tt(setting_bag = setting_bag) # Assert self.assertEqual(expected_column_names, actual.columns.tolist()) @@ -1060,7 +1060,7 @@ def test_getttbyyear_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_year(sessions_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1073,7 +1073,7 @@ def test_getttbyyearmonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_year_month(sessions_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1086,7 +1086,7 @@ def test_getttbyyearmonthspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_spnv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_year_month_spnv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1099,7 +1099,7 @@ def test_getttbyyearspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_spnv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_year_spnv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1112,7 +1112,7 @@ def test_getttbyspnspv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_spn_spv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_spn_spv(sessions_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1124,7 +1124,7 @@ def test_getttsbymonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tts_by_month_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_month(sessions_df = sessions_df, years = years) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_month(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df, actual_df) @@ -1189,7 +1189,7 @@ def test_getttbyyearhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_hashtag_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_year_hashtag(sessions_df = sessions_df, years = years) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_hashtag(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df , actual_df) @@ -1200,7 +1200,7 @@ def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_hashtag_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_hashtag(sessions_df = sessions_df) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_hashtag(tt_df = sessions_df) # Assert assert_frame_equal(expected_df , actual_df) @@ -1218,7 +1218,7 @@ def test_getttbyspn_shouldreturnexpecteddataframe_wheninvoked(self, remove_untag expected_df : DataFrame = ObjectMother().create_tt_by_spn_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tt_by_spn(sessions_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) # Assert assert_frame_equal(expected_df , actual_df) From 55a23e9021c3f5a204981f4fc22e3a9311a0b51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 18:52:37 +0000 Subject: [PATCH 036/109] #40 nwtimetracking: * => sessions_df, Iteration 2. --- src/nwtimetracking.py | 126 +++++++++++++++++------------------ tests/nwtimetrackingtests.py | 6 +- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index cdba748..1928209 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1476,6 +1476,56 @@ def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df : DataFrame = self.__get_raw_tt_by_hashtag(tt_df = tt_df) tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + return tts_df + def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: + + ''' + TimeRangeId Occurrences + 0 Unknown 44 + 1 18:00-20:00 19 + 2 08:00-08:30 16 + ... + ''' + + tts_df : DataFrame = tt_df.copy(deep = True) + + tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME]] + tts_df[TTCN.TIMERANGEID] = tts_df.apply( + lambda x : self.__create_time_range_id( + start_time = x[TTCN.STARTTIME], + end_time = x[TTCN.ENDTIME], + unknown_id = unknown_id), axis = 1) + + tts_df = tts_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg( + count = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count")) + tts_df.rename(columns={"count" : TTCN.OCCURRENCES}, inplace = True) + tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) + + return tts_df + def get_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: + + ''' + StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message + 21:00 23:00 1h 00m False 2h 00m ... + ... + ''' + + tts_df : DataFrame = tt_df.copy(deep = True) + + tts_df[TTCN.EFFORTSTATUS] = tts_df.apply( + lambda x : self.__create_effort_status_and_cast_to_any( + idx = x.name, + start_time_str = x[TTCN.STARTTIME], + end_time_str = x[TTCN.ENDTIME], + effort_str = x[TTCN.EFFORT]), + axis = 1) + + tts_df[TTCN.ESISCORRECT] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.is_correct) + tts_df[TTCN.ESEXPECTED] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.expected_str) + tts_df[TTCN.ESMESSAGE] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.message) + + tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME, TTCN.EFFORT, TTCN.ESISCORRECT, TTCN.ESEXPECTED, TTCN.ESMESSAGE]] + return tts_df def try_print_definitions(self, df : DataFrame, definitions : dict[str, str]) -> None: @@ -1525,84 +1575,34 @@ def update_future_months_to_empty(self, tts_by_month_df : DataFrame, now : datet tts_by_month_upd_df.iloc[:, idx_trend] = np.where(condition, new_value, tts_by_month_upd_df.iloc[:, idx_trend]) return tts_by_month_upd_df - def add_effort_status(self, sessions_df : DataFrame) -> DataFrame: - - ''' - StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message - 21:00 23:00 1h 00m False 2h 00m ... - ... - ''' - - es_df : DataFrame = sessions_df.copy(deep = True) - - es_df[TTCN.EFFORTSTATUS] = es_df.apply( - lambda x : self.__create_effort_status_and_cast_to_any( - idx = x.name, - start_time_str = x[TTCN.STARTTIME], - end_time_str = x[TTCN.ENDTIME], - effort_str = x[TTCN.EFFORT]), - axis = 1) - - es_df[TTCN.ESISCORRECT] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.is_correct) - es_df[TTCN.ESEXPECTED] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.expected_str) - es_df[TTCN.ESMESSAGE] = es_df[TTCN.EFFORTSTATUS].apply(lambda x : x.message) - - es_df = es_df[[TTCN.STARTTIME, TTCN.ENDTIME, TTCN.EFFORT, TTCN.ESISCORRECT, TTCN.ESEXPECTED, TTCN.ESMESSAGE]] - - return es_df - def filter_by_is_correct(self, es_df : DataFrame, is_correct : bool) -> DataFrame: + def filter_by_is_correct(self, tts_by_effort_status_df : DataFrame, is_correct : bool) -> DataFrame: '''Returns a DataFrame that contains only rows that match the provided is_correct.''' - filtered_df : DataFrame = es_df.copy(deep = True) + filtered_df : DataFrame = tts_by_effort_status_df.copy(deep = True) condition : Series = (filtered_df[TTCN.ESISCORRECT] == is_correct) - filtered_df = es_df.loc[condition] + filtered_df = tts_by_effort_status_df.loc[condition] return filtered_df - def create_time_ranges_df(self, sessions_df : DataFrame, unknown_id : str) -> DataFrame: - - ''' - TimeRangeId Occurrences - 0 Unknown 44 - 1 18:00-20:00 19 - 2 08:00-08:30 16 - ... - ''' - - time_ranges_df : DataFrame = sessions_df.copy(deep = True) - - time_ranges_df = time_ranges_df[[TTCN.STARTTIME, TTCN.ENDTIME]] - time_ranges_df[TTCN.TIMERANGEID] = time_ranges_df.apply( - lambda x : self.__create_time_range_id( - start_time = x[TTCN.STARTTIME], - end_time = x[TTCN.ENDTIME], - unknown_id = unknown_id), axis = 1) - - time_ranges_df = time_ranges_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg( - count = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count")) - time_ranges_df.rename(columns={"count" : TTCN.OCCURRENCES}, inplace = True) - time_ranges_df = time_ranges_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) - - return time_ranges_df - def remove_unknown_id(self, time_ranges_df : DataFrame, unknown_id : str) -> DataFrame: + def remove_unknown_id(self, tts_by_time_ranges_df : DataFrame, unknown_id : str) -> DataFrame: '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' - condition : Series = (time_ranges_df[TTCN.TIMERANGEID] != unknown_id) - time_ranges_df = time_ranges_df.loc[condition] - time_ranges_df.reset_index(drop = True, inplace = True) + condition : Series = (tts_by_time_ranges_df[TTCN.TIMERANGEID] != unknown_id) + tts_by_time_ranges_df = tts_by_time_ranges_df.loc[condition] + tts_by_time_ranges_df.reset_index(drop = True, inplace = True) - return time_ranges_df - def filter_by_top_n_occurrences(self, time_ranges_df : DataFrame, n : int, ascending : bool = False) -> DataFrame: + return tts_by_time_ranges_df + def filter_by_top_n_occurrences(self, tts_by_time_ranges_df : DataFrame, n : int, ascending : bool = False) -> DataFrame: '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' - time_ranges_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) - time_ranges_df = time_ranges_df.iloc[0:n] - time_ranges_df.reset_index(drop = True, inplace = True) + tts_by_time_ranges_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) + tts_by_time_ranges_df = tts_by_time_ranges_df.iloc[0:n] + tts_by_time_ranges_df.reset_index(drop = True, inplace = True) - return time_ranges_df + return tts_by_time_ranges_df class MarkdownProcessor(): '''Collects all the logic related to the processing of Markdown content.''' diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index bb73bb7..00943d1 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1150,7 +1150,7 @@ def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): expected_df.reset_index(drop = True, inplace = True) # Act - actual_df : DataFrame = TimeTrackingManager().create_time_ranges_df(sessions_df = sessions_df, unknown_id = unknown_id) + actual_df : DataFrame = TimeTrackingManager().get_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) actual_df.reset_index(drop = True, inplace = True) @@ -1165,7 +1165,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whencontainsunknownid(sel time_ranges_df.loc[len(time_ranges_df.index)] = [unknown_id, 3] # Act - actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) @@ -1177,7 +1177,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whendoesnotcontainunknown time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() # Act - actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) From 4426f04f4cf3d196f5740eb052596a770b2278ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 19:31:00 +0000 Subject: [PATCH 037/109] #40 nwtimetracking, re-factoring for scale up - Iteration 1. --- src/nwtimetracking.py | 603 ++++++++++++++++++----------------- tests/nwtimetrackingtests.py | 82 ++--- 2 files changed, 348 insertions(+), 337 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 1928209..522b25d 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -371,62 +371,19 @@ def get_all_software_project_names_by_spv(self) -> list[str]: ] return software_project_names_by_spv -class TimeTrackingManager(): - '''Collects all the logic related to the management of "Time Tracking.xlsx".''' +class TTDataFrameHelper(): - def __enforce_dataframe_definition_for_tt_df(self, tt_df : DataFrame) -> DataFrame: - - '''Enforces definition for the provided dataframe.''' - - column_names : list[str] = [] - column_names.append(TTCN.DATE) # [0], date - column_names.append(TTCN.STARTTIME) # [1], str - column_names.append(TTCN.ENDTIME) # [2], str - column_names.append(TTCN.EFFORT) # [3], str - column_names.append(TTCN.HASHTAG) # [4], str - column_names.append(TTCN.DESCRIPTOR) # [5], str - column_names.append(TTCN.ISSOFTWAREPROJECT) # [6], bool - column_names.append(TTCN.ISRELEASEDAY) # [7], bool - column_names.append(TTCN.YEAR) # [8], int - column_names.append(TTCN.MONTH) # [9], int - - tt_df = tt_df[column_names] - - tt_df[column_names[0]] = pd.to_datetime(tt_df[column_names[0]], format="%Y-%m-%d") - tt_df[column_names[0]] = tt_df[column_names[0]].apply(lambda x: x.date()) - - tt_df = tt_df.astype({column_names[1]: str}) - tt_df = tt_df.astype({column_names[2]: str}) - tt_df = tt_df.astype({column_names[3]: str}) - tt_df = tt_df.astype({column_names[4]: str}) - tt_df = tt_df.astype({column_names[5]: str}) - tt_df = tt_df.astype({column_names[6]: bool}) - tt_df = tt_df.astype({column_names[7]: bool}) - tt_df = tt_df.astype({column_names[8]: int}) - tt_df = tt_df.astype({column_names[9]: int}) - - tt_df[column_names[1]] = tt_df[column_names[1]].replace('nan', '') - tt_df[column_names[2]] = tt_df[column_names[2]].replace('nan', '') - tt_df[column_names[5]] = tt_df[column_names[5]].replace('nan', '') + '''Collects helper functions for TTDataFrameFactory.''' - return tt_df - def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataFrame: - - '''Ensures that the columns of the provided dataframe have the expected data types.''' - - df = df.astype({TTCN.MONTH: int}) - # can't enforce the year column as "timedelta" - - return df - def __convert_string_to_timedelta(self, td_str : str) -> timedelta: + def convert_string_to_timedelta(self, td_str : str) -> timedelta: '''"5h 30m" => 5:30:00''' td : timedelta = pd.Timedelta(value = td_str).to_pytimedelta() return td - def __get_yearly_target(self, yearly_targets : list[YearlyTarget], year : int) -> Optional[YearlyTarget]: + def get_yearly_target(self, yearly_targets : list[YearlyTarget], year : int) -> Optional[YearlyTarget]: '''Retrieves the YearlyTarget object for the provided "year" or None.''' @@ -435,13 +392,13 @@ def __get_yearly_target(self, yearly_targets : list[YearlyTarget], year : int) - return yearly_target return None - def __is_yearly_target_met(self, effort : timedelta, yearly_target : timedelta) -> bool: + def is_yearly_target_met(self, effort : timedelta, yearly_target : timedelta) -> bool: if effort >= yearly_target: return True return False - def __format_timedelta(self, td : timedelta, add_plus_sign : bool) -> str: + def format_timedelta(self, td : timedelta, add_plus_sign : bool) -> str: ''' 4 days 19:15:00 => "115h 15m" (or +115h 15m) @@ -460,7 +417,7 @@ def __format_timedelta(self, td : timedelta, add_plus_sign : bool) -> str: formatted = f"+{formatted}" return formatted - def __extract_software_project_name(self, descriptor : str) -> str: + def extract_software_project_name(self, descriptor : str) -> str: ''' "NW.AutoProffLibrary v1.0.0" => "NW.AutoProffLibrary" @@ -476,7 +433,7 @@ def __extract_software_project_name(self, descriptor : str) -> str: return matches[0] return "ERROR" - def __extract_software_project_version(self, descriptor : str) -> str: + def extract_software_project_version(self, descriptor : str) -> str: ''' "NW.AutoProffLibrary v1.0.0" => "1.0.0" @@ -492,7 +449,7 @@ def __extract_software_project_version(self, descriptor : str) -> str: return matches[0] return "ERROR" - def __calculate_percentage(self, part : float, whole : float, rounding_digits : int = 2) -> float: + def calculate_percentage(self, part : float, whole : float, rounding_digits : int = 2) -> float: '''Calculates a percentage.''' @@ -508,6 +465,244 @@ def __calculate_percentage(self, part : float, whole : float, rounding_digits : prct = round(number = prct, ndigits = rounding_digits) return prct + def get_trend_by_timedelta(self, td_1 : timedelta, td_2 : timedelta) -> str: + + ''' + 0h 30m, 1h 00m => "↑" + 1h 00m, 0h 30m => "↓" + 0, 0 => "=" + ''' + trend : Optional[str] = None + + if td_1 < td_2: + trend = "↑" + elif td_1 > td_2: + trend = "↓" + else: + trend = "=" + + return trend + def try_consolidate_trend_column_name(self, column_name : str) -> str: + + ''' + "2016" => "2016" + "↕1" => "↕" + ''' + + if column_name.startswith(TTCN.TREND): + return TTCN.TREND + + return column_name + def create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: + + '''Creates effort status for None values.''' + + actual_str : str = effort_str + actual_td : timedelta = self.convert_string_to_timedelta(td_str = effort_str) + is_correct : bool = True + + effort_status : EffortStatus = EffortStatus( + idx = idx, + start_time_str = None, + start_time_dt = None, + end_time_str = None, + end_time_dt = None, + actual_str = actual_str, + actual_td = actual_td, + expected_td = None, + expected_str = None, + is_correct = is_correct, + message = _MessageCollection.starttime_endtime_are_empty() + ) + + return effort_status + def create_time_object(self, time : str) -> datetime: + + '''It creates a datetime object suitable for timedelta calculation out of the provided time.''' + + day_1_times : list[str] = [ + "07:00", "07:15", "07:30", "07:45", + "08:00", "08:15", "08:30", "08:45", + "09:00", "09:15", "09:30", "09:45", + "10:00", "10:15", "10:30", "10:45", + "11:00", "11:15", "11:30", "11:45", + "12:00", "12:15", "12:30", "12:45", + "13:00", "13:15", "13:30", "13:45", + "14:00", "14:15", "14:30", "14:45", + "15:00", "15:15", "15:30", "15:45", + "16:00", "16:15", "16:30", "16:45", + "17:00", "17:15", "17:30", "17:45", + "18:00", "18:15", "18:30", "18:45", + "19:00", "19:15", "19:30", "19:45", + "20:00", "20:15", "20:30", "20:45", + "21:00", "21:15", "21:30", "21:45", + "22:00", "22:15", "22:30", "22:45", + "23:00", "23:15", "23:30", "23:45" + ] + day_2_times : list[str] = [ + "00:00", "00:15", "00:30", "00:45", + "01:00", "01:15", "01:30", "01:45", + "02:00", "02:15", "02:30", "02:45", + "03:00", "03:15", "03:30", "03:45", + "04:00", "04:15", "04:30", "04:45", + "05:00", "05:15", "05:30", "05:45", + "06:00", "06:15", "06:30", "06:45" + ] + + strp_format : str = "%Y-%m-%d %H:%M" + + dt_str : Optional[str] = None + if time in day_1_times: + dt_str = f"1900-01-01 {time}" + elif time in day_2_times: + dt_str = f"1900-01-02 {time}" + else: + raise ValueError(_MessageCollection.effort_status_not_among_expected_time_values(time = time)) + + dt : datetime = datetime.strptime(dt_str, strp_format) + + return dt + def create_effort_status(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> EffortStatus: + + ''' + start_time_str, end_time_str: + - Expects time values in the "%H:%M" format - for ex. 20:00. + + is_correct: + start_time_str = "20:00", end_time_str = "00:00", effort_str = "4h 00m" => True + start_time_str = "20:00", end_time_str = "00:00", effort_str = "5h 00m" => False + ''' + + try: + + if len(start_time_str) == 0 or len(end_time_str) == 0: + return self.create_effort_status_for_none_values(idx = idx, effort_str = effort_str) + + start_time_dt : datetime = self.create_time_object(time = start_time_str) + end_time_dt : datetime = self.create_time_object(time = end_time_str) + + actual_str : str = effort_str + actual_td : timedelta = self.convert_string_to_timedelta(td_str = effort_str) + + expected_td : timedelta = (end_time_dt - start_time_dt) + expected_str : str = self.format_timedelta(td = expected_td, add_plus_sign = False) + + is_correct : bool = True + if actual_td != expected_td: + is_correct = False + + message : str = _MessageCollection.effort_is_correct() + + if actual_td != expected_td: + message = _MessageCollection.effort_status_mismatching_effort( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + actual_str = actual_str, + expected_str = expected_str + ) + + effort_status : EffortStatus = EffortStatus( + idx = idx, + start_time_str = start_time_str, + start_time_dt = start_time_dt, + end_time_str = end_time_str, + end_time_dt = end_time_dt, + actual_str = actual_str, + actual_td = actual_td, + expected_td = expected_td, + expected_str = expected_str, + is_correct = is_correct, + message = message + ) + + return effort_status + + except: + + error : str = _MessageCollection.effort_status_not_possible_to_create( + idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) + + raise ValueError(error) + def create_effort_status_and_cast_to_any(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> Any: + + ''' + Wrapper method created to overcome the following error raised by df.apply(): + + Argument of type "(x: Unknown) -> EffortStatus" cannot be assigned to parameter "f" of type "(...) -> Series[Any]" in function "apply" + Type "(x: Unknown) -> EffortStatus" is not assignable to type "(...) -> Series[Any]" + Function return type "EffortStatus" is incompatible with type "Series[Any]" + "EffortStatus" is not assignable to "Series[Any]" + ''' + + return cast(Any, self.create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str)) + def create_time_range_id(self, start_time : str, end_time : str, unknown_id : str) -> str: + + ''' + Creates a unique time range identifier out of the provided parameters. + If parameters are empty, it returns unknown_id. + ''' + + time_range_id : str = f"{start_time}-{end_time}" + + if len(start_time) == 0 or len(end_time) == 0: + time_range_id = unknown_id + + return time_range_id +class TTDataFrameFactory(): + + '''Collects all the logic related to dataframe creation out of "Time Tracking.xlsx".''' + + __tt_helper : TTDataFrameHelper + + def __init__(self, df_helper : TTDataFrameHelper) -> None: + + self.__tt_helper = df_helper + + def __enforce_dataframe_definition_for_tt_df(self, tt_df : DataFrame) -> DataFrame: + + '''Enforces definition for the provided dataframe.''' + + column_names : list[str] = [] + column_names.append(TTCN.DATE) # [0], date + column_names.append(TTCN.STARTTIME) # [1], str + column_names.append(TTCN.ENDTIME) # [2], str + column_names.append(TTCN.EFFORT) # [3], str + column_names.append(TTCN.HASHTAG) # [4], str + column_names.append(TTCN.DESCRIPTOR) # [5], str + column_names.append(TTCN.ISSOFTWAREPROJECT) # [6], bool + column_names.append(TTCN.ISRELEASEDAY) # [7], bool + column_names.append(TTCN.YEAR) # [8], int + column_names.append(TTCN.MONTH) # [9], int + + tt_df = tt_df[column_names] + + tt_df[column_names[0]] = pd.to_datetime(tt_df[column_names[0]], format="%Y-%m-%d") + tt_df[column_names[0]] = tt_df[column_names[0]].apply(lambda x: x.date()) + + tt_df = tt_df.astype({column_names[1]: str}) + tt_df = tt_df.astype({column_names[2]: str}) + tt_df = tt_df.astype({column_names[3]: str}) + tt_df = tt_df.astype({column_names[4]: str}) + tt_df = tt_df.astype({column_names[5]: str}) + tt_df = tt_df.astype({column_names[6]: bool}) + tt_df = tt_df.astype({column_names[7]: bool}) + tt_df = tt_df.astype({column_names[8]: int}) + tt_df = tt_df.astype({column_names[9]: int}) + + tt_df[column_names[1]] = tt_df[column_names[1]].replace('nan', '') + tt_df[column_names[2]] = tt_df[column_names[2]].replace('nan', '') + tt_df[column_names[5]] = tt_df[column_names[5]].replace('nan', '') + + return tt_df + def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataFrame: + + '''Ensures that the columns of the provided dataframe have the expected data types.''' + + df = df.astype({TTCN.MONTH: int}) + # can't enforce the year column as "timedelta" + + return df def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' @@ -523,10 +718,10 @@ def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -551,10 +746,10 @@ def __get_raw_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) @@ -576,7 +771,7 @@ def __get_raw_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) @@ -597,10 +792,10 @@ def __get_raw_tt_by_year_spnv(self, tt_df : DataFrame, years : list[int], softwa condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -626,10 +821,10 @@ def __get_raw_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) @@ -651,7 +846,7 @@ def __get_raw_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) @@ -674,8 +869,8 @@ def __get_raw_tt_by_spn(self, tt_df : DataFrame, years : list[int], software_pro condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME]).reset_index(drop = True) @@ -696,7 +891,7 @@ def __get_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized @@ -713,7 +908,7 @@ def __get_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : b condition_two : Series = (tt_df[TTCN.HASHTAG] != "#untagged") tts_df = tts_df.loc[condition_two] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized @@ -733,10 +928,10 @@ def __get_raw_tt_by_spn_spv(self, tt_df : DataFrame, years : list[int], software condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -755,7 +950,7 @@ def __get_default_raw_ttm(self, year : int) -> DataFrame: ... ''' - td : timedelta = self.__convert_string_to_timedelta(td_str = "0h 00m") + td : timedelta = self.convert_string_to_timedelta(td_str = "0h 00m") default_df : DataFrame = pd.DataFrame( { @@ -855,7 +1050,7 @@ def __get_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR] == year) ttm_df = ttm_df.loc[condition] - ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) ttm_df[str(year)] = ttm_df[TTCN.EFFORT] cn_effort = str(year) @@ -866,23 +1061,6 @@ def __get_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: ttm_df = self.__enforce_dataframe_definition_for_raw_ttm_df(df = ttm_df) return ttm_df - def __get_trend_by_timedelta(self, td_1 : timedelta, td_2 : timedelta) -> str: - - ''' - 0h 30m, 1h 00m => "↑" - 1h 00m, 0h 30m => "↓" - 0, 0 => "=" - ''' - trend : Optional[str] = None - - if td_1 < td_2: - trend = "↑" - elif td_1 > td_2: - trend = "↓" - else: - trend = "=" - - return trend def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month_df : DataFrame, i : int, add_trend : bool) -> DataFrame: ''' @@ -947,7 +1125,7 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month cn_trend_1 : str = str(years[i-1]) # for ex. "2016" cn_trend_2 : str = str(years[i]) # for ex. "2017" - expansion_df[cn_trend] = expansion_df.apply(lambda x : self.__get_trend_by_timedelta(td_1 = x[cn_trend_1], td_2 = x[cn_trend_2]), axis = 1) + expansion_df[cn_trend] = expansion_df.apply(lambda x : self.get_trend_by_timedelta(td_1 = x[cn_trend_1], td_2 = x[cn_trend_2]), axis = 1) new_column_names : list = [TTCN.MONTH, cn_trend_1, cn_trend, cn_trend_2] # for ex. ["Month", "2016", "↕", "2017"] expansion_df = expansion_df.reindex(columns = new_column_names) @@ -964,173 +1142,6 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month actual_df = expansion_df return actual_df - def __try_consolidate_trend_column_name(self, column_name : str) -> str: - - ''' - "2016" => "2016" - "↕1" => "↕" - ''' - - if column_name.startswith(TTCN.TREND): - return TTCN.TREND - - return column_name - def __create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: - - '''Creates effort status for None values.''' - - actual_str : str = effort_str - actual_td : timedelta = self.__convert_string_to_timedelta(td_str = effort_str) - is_correct : bool = True - - effort_status : EffortStatus = EffortStatus( - idx = idx, - start_time_str = None, - start_time_dt = None, - end_time_str = None, - end_time_dt = None, - actual_str = actual_str, - actual_td = actual_td, - expected_td = None, - expected_str = None, - is_correct = is_correct, - message = _MessageCollection.starttime_endtime_are_empty() - ) - - return effort_status - def __create_time_object(self, time : str) -> datetime: - - '''It creates a datetime object suitable for timedelta calculation out of the provided time.''' - - day_1_times : list[str] = [ - "07:00", "07:15", "07:30", "07:45", - "08:00", "08:15", "08:30", "08:45", - "09:00", "09:15", "09:30", "09:45", - "10:00", "10:15", "10:30", "10:45", - "11:00", "11:15", "11:30", "11:45", - "12:00", "12:15", "12:30", "12:45", - "13:00", "13:15", "13:30", "13:45", - "14:00", "14:15", "14:30", "14:45", - "15:00", "15:15", "15:30", "15:45", - "16:00", "16:15", "16:30", "16:45", - "17:00", "17:15", "17:30", "17:45", - "18:00", "18:15", "18:30", "18:45", - "19:00", "19:15", "19:30", "19:45", - "20:00", "20:15", "20:30", "20:45", - "21:00", "21:15", "21:30", "21:45", - "22:00", "22:15", "22:30", "22:45", - "23:00", "23:15", "23:30", "23:45" - ] - day_2_times : list[str] = [ - "00:00", "00:15", "00:30", "00:45", - "01:00", "01:15", "01:30", "01:45", - "02:00", "02:15", "02:30", "02:45", - "03:00", "03:15", "03:30", "03:45", - "04:00", "04:15", "04:30", "04:45", - "05:00", "05:15", "05:30", "05:45", - "06:00", "06:15", "06:30", "06:45" - ] - - strp_format : str = "%Y-%m-%d %H:%M" - - dt_str : Optional[str] = None - if time in day_1_times: - dt_str = f"1900-01-01 {time}" - elif time in day_2_times: - dt_str = f"1900-01-02 {time}" - else: - raise ValueError(_MessageCollection.effort_status_not_among_expected_time_values(time = time)) - - dt : datetime = datetime.strptime(dt_str, strp_format) - - return dt - def __create_effort_status(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> EffortStatus: - - ''' - start_time_str, end_time_str: - - Expects time values in the "%H:%M" format - for ex. 20:00. - - is_correct: - start_time_str = "20:00", end_time_str = "00:00", effort_str = "4h 00m" => True - start_time_str = "20:00", end_time_str = "00:00", effort_str = "5h 00m" => False - ''' - - try: - - if len(start_time_str) == 0 or len(end_time_str) == 0: - return self.__create_effort_status_for_none_values(idx = idx, effort_str = effort_str) - - start_time_dt : datetime = self.__create_time_object(time = start_time_str) - end_time_dt : datetime = self.__create_time_object(time = end_time_str) - - actual_str : str = effort_str - actual_td : timedelta = self.__convert_string_to_timedelta(td_str = effort_str) - - expected_td : timedelta = (end_time_dt - start_time_dt) - expected_str : str = self.__format_timedelta(td = expected_td, add_plus_sign = False) - - is_correct : bool = True - if actual_td != expected_td: - is_correct = False - - message : str = _MessageCollection.effort_is_correct() - - if actual_td != expected_td: - message = _MessageCollection.effort_status_mismatching_effort( - idx = idx, - start_time_str = start_time_str, - end_time_str = end_time_str, - actual_str = actual_str, - expected_str = expected_str - ) - - effort_status : EffortStatus = EffortStatus( - idx = idx, - start_time_str = start_time_str, - start_time_dt = start_time_dt, - end_time_str = end_time_str, - end_time_dt = end_time_dt, - actual_str = actual_str, - actual_td = actual_td, - expected_td = expected_td, - expected_str = expected_str, - is_correct = is_correct, - message = message - ) - - return effort_status - - except: - - error : str = _MessageCollection.effort_status_not_possible_to_create( - idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) - - raise ValueError(error) - def __create_effort_status_and_cast_to_any(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> Any: - - ''' - Wrapper method created to overcome the following error raised by df.apply(): - - Argument of type "(x: Unknown) -> EffortStatus" cannot be assigned to parameter "f" of type "(...) -> Series[Any]" in function "apply" - Type "(x: Unknown) -> EffortStatus" is not assignable to type "(...) -> Series[Any]" - Function return type "EffortStatus" is incompatible with type "Series[Any]" - "EffortStatus" is not assignable to "Series[Any]" - ''' - - return cast(Any, self.__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str)) - def __create_time_range_id(self, start_time : str, end_time : str, unknown_id : str) -> str: - - ''' - Creates a unique time range identifier out of the provided parameters. - If parameters are empty, it returns unknown_id. - ''' - - time_range_id : str = f"{start_time}-{end_time}" - - if len(start_time) == 0 or len(end_time) == 0: - time_range_id = unknown_id - - return time_range_id def __get_raw_tt_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' @@ -1146,7 +1157,7 @@ def __get_raw_tt_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) @@ -1163,11 +1174,11 @@ def __get_raw_tt_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df : DataFrame = tt_df.copy(deep = True) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) summarized : float = tts_df[TTCN.EFFORT].sum() - tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) + tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) return tts_df @@ -1211,9 +1222,9 @@ def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: add_trend = True) for year in years: - tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df.rename(columns = (lambda x : self.__try_consolidate_trend_column_name(column_name = x)), inplace = True) + tts_df.rename(columns = (lambda x : self.try_consolidate_trend_column_name(column_name = x)), inplace = True) return tts_df def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: @@ -1247,19 +1258,19 @@ def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby([TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = TTCN.YEAR).reset_index(drop = True) tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( - lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) + lambda x : cast(YearlyTarget, self.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.EFFORT] - tts_df[TTCN.YEARLYTARGET] tts_df[TTCN.ISTARGETMET] = tts_df.apply( - lambda x : self.__is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) + lambda x : self.is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEARLYTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEARLYTARGET].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = True)) return tts_df def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: @@ -1307,21 +1318,21 @@ def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_tar condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.EFFORT].groupby(by = tts_df[TTCN.YEAR]).cumsum() tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( - lambda x : cast(YearlyTarget, self.__get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) + lambda x : cast(YearlyTarget, self.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) tts_df[TTCN.TOTARGET] = tts_df[TTCN.YEARLYTOTAL] - tts_df[TTCN.YEARLYTARGET] tts_df.drop(columns = [TTCN.YEARLYTARGET], axis = 1, inplace = True) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = True)) return tts_df def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1348,7 +1359,7 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw right_on = [TTCN.YEAR, TTCN.MONTH] ) - tts_df[TTCN.PERCDME] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) + tts_df[TTCN.PERCDME] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) tts_df = pd.merge( left = tts_df, @@ -1358,10 +1369,10 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw right_on = [TTCN.YEAR, TTCN.MONTH] ) - tts_df[TTCN.PERCTME] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTME] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1388,7 +1399,7 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr right_on = [TTCN.YEAR] ) - tts_df[TTCN.PERCDYE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) + tts_df[TTCN.PERCDYE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) tts_df = pd.merge( left = tts_df, @@ -1398,10 +1409,10 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr right_on = [TTCN.YEAR] ) - tts_df[TTCN.PERCTYE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTYE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: @@ -1425,14 +1436,14 @@ def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_ te : timedelta = self.__get_raw_te(tt_df = tt_df, years = years, remove_untagged = remove_untagged) tts_df[TTCN.DE] = de - tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) + tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) tts_df[TTCN.TE] = te - tts_df[TTCN.PERCTE] = tts_df.apply(lambda x : self.__calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) + tts_df[TTCN.PERCTE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DE] = tts_df[TTCN.DE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DE] = tts_df[TTCN.DE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1446,7 +1457,7 @@ def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_proj ''' tts_df : DataFrame = self.__get_raw_tt_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: @@ -1460,7 +1471,7 @@ def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataF ''' tts_df : DataFrame = self.__get_raw_tt_by_year_hashtag(tt_df = tt_df, years = years) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: @@ -1474,7 +1485,7 @@ def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ''' tts_df : DataFrame = self.__get_raw_tt_by_hashtag(tt_df = tt_df) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: @@ -1491,7 +1502,7 @@ def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFra tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME]] tts_df[TTCN.TIMERANGEID] = tts_df.apply( - lambda x : self.__create_time_range_id( + lambda x : self.create_time_range_id( start_time = x[TTCN.STARTTIME], end_time = x[TTCN.ENDTIME], unknown_id = unknown_id), axis = 1) @@ -1513,7 +1524,7 @@ def get_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: tts_df : DataFrame = tt_df.copy(deep = True) tts_df[TTCN.EFFORTSTATUS] = tts_df.apply( - lambda x : self.__create_effort_status_and_cast_to_any( + lambda x : self.create_effort_status_and_cast_to_any( idx = x.name, start_time_str = x[TTCN.STARTTIME], end_time_str = x[TTCN.ENDTIME], diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 00943d1..f5da951 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -17,7 +17,7 @@ import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) from nwtimetracking import ComponentBag, MarkdownProcessor, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection -from nwtimetracking import DefaultPathProvider, YearProvider, TimeTrackingManager +from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager # SUPPORT METHODS @@ -591,7 +591,7 @@ def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring expected_td : timedelta = pd.Timedelta(hours = 5, minutes = 30).to_pytimedelta() # Act - actual_td : str = TimeTrackingManager()._TimeTrackingManager__convert_string_to_timedelta(td_str = td_str) # type: ignore + actual_td : str = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = td_str) # type: ignore # Assert self.assertEqual(expected_td, actual_td) @@ -603,7 +603,7 @@ def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): expected_hours : timedelta = timedelta(hours = 250) # Act - actual_hours : timedelta = TimeTrackingManager()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year).hours # type: ignore + actual_hours : timedelta = TTDataFrameFactory()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year).hours # type: ignore # Assert self.assertEqual(expected_hours, actual_hours) @@ -614,7 +614,7 @@ def test_getyearlytarget_shouldreturnnone_whenyearnotinlist(self): year : int = 2010 # Act - yearly_target : YearlyTarget = TimeTrackingManager()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year) # type: ignore + yearly_target : YearlyTarget = TTDataFrameFactory()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year) # type: ignore # Assert self.assertIsNone(yearly_target) @@ -625,7 +625,7 @@ def test_isyearlytargetmet_shouldreturntrue_whenyearlytargetismet(self): yearly_target : timedelta = pd.Timedelta(hours = 250) # Act - actual : bool = TimeTrackingManager()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore + actual : bool = TTDataFrameFactory()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore # Assert self.assertTrue(actual) @@ -636,7 +636,7 @@ def test_isyearlytargetmet_shouldreturnfalse_whenyearlytargetisnotmet(self): yearly_target : timedelta = pd.Timedelta(hours = 250) # Act - actual : bool = TimeTrackingManager()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore + actual : bool = TTDataFrameFactory()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore # Assert self.assertFalse(actual) @@ -647,7 +647,7 @@ def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussi expected : str = "255h 30m" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = False) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = False) # type: ignore # Assert self.assertEqual(expected, actual) @@ -658,7 +658,7 @@ def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussi expected : str = "+255h 30m" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = True) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = True) # type: ignore # Assert self.assertEqual(expected, actual) @@ -669,7 +669,7 @@ def test_extractsoftwareprojectname_shouldreturnexpectedstring_whenproperstring( expected : str = "NW.AutoProffLibrary" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore # Assert self.assertEqual(expected, actual) @@ -680,7 +680,7 @@ def test_extractsoftwareprojectname_shouldreturnerrorstring_whenunproperstring(s expected : str = "ERROR" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore # Assert self.assertEqual(expected, actual) @@ -691,7 +691,7 @@ def test_extractsoftwareprojectversion_shouldreturnexpectedstring_whenproperstri expected : str = "1.0.0" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore # Assert self.assertEqual(expected, actual) @@ -702,7 +702,7 @@ def test_extractsoftwareprojectversion_shouldreturnerrorstring_whenunproperstrin expected : str = "ERROR" # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore # Assert self.assertEqual(expected, actual) @@ -715,7 +715,7 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when0and16(self): expected : float = 0.00 # Act - actual : float = TimeTrackingManager()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore + actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore # Assert self.assertEqual(expected, actual) @@ -728,7 +728,7 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when4and0(self): expected : float = 0.00 # Act - actual : float = TimeTrackingManager()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore + actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore # Assert self.assertEqual(expected, actual) @@ -741,7 +741,7 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when4and16(self): expected : float = 25.00 # Act - actual : float = TimeTrackingManager()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore + actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore # Assert self.assertEqual(expected, actual) @@ -754,7 +754,7 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when16and16(self): expected : float = 100.00 # Act - actual : float = TimeTrackingManager()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore + actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore # Assert self.assertEqual(expected, actual) @@ -767,7 +767,7 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): expected : float = 33.3333 # Act - actual : float = TimeTrackingManager()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore + actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore # Assert self.assertEqual(expected, actual) @@ -804,7 +804,7 @@ def test_createeffortstatus_shouldreturnexpectobject_wheneffortiscorrect(self): ) # Act - actual : EffortStatus = TimeTrackingManager()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str,end_time_str = end_time_str,effort_str = effort_str) # type: ignore + actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str,end_time_str = end_time_str,effort_str = effort_str) # type: ignore # Assert comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) @@ -849,7 +849,7 @@ def test_createeffortstatus_shouldreturnexpectobject_wheneffortisnotcorrect(self ) # Act - actual : EffortStatus = TimeTrackingManager()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore + actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore # Assert comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) @@ -881,7 +881,7 @@ def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( ) # Act - actual : EffortStatus = TimeTrackingManager()._TimeTrackingManager__create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore + actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore # Assert comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) @@ -913,7 +913,7 @@ def test_createtimeobject_shouldreturnexpecteddatatime_whenday1time(self, time : expected : datetime = datetime.strptime(dt_str, strp_format) # Act - actual : datetime = TimeTrackingManager()._TimeTrackingManager__create_time_object(time = time) # type: ignore + actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore # Assert self.assertEqual(expected, actual) @@ -934,7 +934,7 @@ def test_createtimeobject_shouldreturnexpecteddatatime_whenday2time(self, time : expected : datetime = datetime.strptime(dt_str, strp_format) # Act - actual : datetime = TimeTrackingManager()._TimeTrackingManager__create_time_object(time = time) # type: ignore + actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore # Assert self.assertEqual(expected, actual) @@ -950,7 +950,7 @@ def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues( # Act with self.assertRaises(ValueError) as context: - actual : datetime = TimeTrackingManager()._TimeTrackingManager__create_time_object(time = time) # type: ignore + actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore # Assert self.assertTrue(expected_message in str(context.exception)) @@ -966,7 +966,7 @@ def test_createeffortstatus_shouldreturnexpectobject_whenstarttimeorendtimeareem effort_str : str): # Arrange - actual_td : timedelta = TimeTrackingManager()._TimeTrackingManager__convert_string_to_timedelta(td_str = effort_str) # type: ignore + actual_td : timedelta = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = effort_str) # type: ignore expected : EffortStatus = EffortStatus( idx = idx, start_time_str = None, @@ -982,7 +982,7 @@ def test_createeffortstatus_shouldreturnexpectobject_whenstarttimeorendtimeareem ) # Act - actual : EffortStatus = TimeTrackingManager()._TimeTrackingManager__create_effort_status( + actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status( idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, @@ -1009,7 +1009,7 @@ def test_createeffortstatus_shouldraisevalueerrorexception_whenunproperparameter # Act with self.assertRaises(ValueError) as context: - actual : EffortStatus = TimeTrackingManager()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore + actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore # Assert self.assertTrue(expected_message in str(context.exception)) @@ -1027,7 +1027,7 @@ def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( # Arrange # Act - actual : str = TimeTrackingManager()._TimeTrackingManager__create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) # type: ignore + actual : str = TTDataFrameFactory()._TimeTrackingManager__create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) # type: ignore # Assert self.assertEqual(expected, actual) @@ -1043,7 +1043,7 @@ def test_getsessionsdataset_shouldreturnexpecteddataframe_wheninvoked(self): # Act with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: - actual : DataFrame = TimeTrackingManager().get_tt(setting_bag = setting_bag) + actual : DataFrame = TTDataFrameFactory().get_tt(setting_bag = setting_bag) # Assert self.assertEqual(expected_column_names, actual.columns.tolist()) @@ -1060,7 +1060,7 @@ def test_getttbyyear_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1073,7 +1073,7 @@ def test_getttbyyearmonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1086,7 +1086,7 @@ def test_getttbyyearmonthspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_spnv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1099,7 +1099,7 @@ def test_getttbyyearspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_spnv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1112,7 +1112,7 @@ def test_getttbyspnspv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_spn_spv_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1124,7 +1124,7 @@ def test_getttsbymonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tts_by_month_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_month(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_month(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df, actual_df) @@ -1136,7 +1136,7 @@ def test_updatefuturemonthstoempty_shouldreturnexpecteddataframe_wheninvoked(sel expected_df : DataFrame = ObjectMother().create_tts_by_month_upd_df() # Act - actual_df : DataFrame = TimeTrackingManager().update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = now) + actual_df : DataFrame = TTDataFrameFactory().update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = now) # Assert assert_frame_equal(expected_df, actual_df) @@ -1150,7 +1150,7 @@ def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): expected_df.reset_index(drop = True, inplace = True) # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) actual_df.reset_index(drop = True, inplace = True) @@ -1165,7 +1165,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whencontainsunknownid(sel time_ranges_df.loc[len(time_ranges_df.index)] = [unknown_id, 3] # Act - actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) @@ -1177,7 +1177,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whendoesnotcontainunknown time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() # Act - actual_df : DataFrame = TimeTrackingManager().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) @@ -1189,7 +1189,7 @@ def test_getttbyyearhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_hashtag_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_year_hashtag(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_hashtag(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df , actual_df) @@ -1200,7 +1200,7 @@ def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_hashtag_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_hashtag(tt_df = sessions_df) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_hashtag(tt_df = sessions_df) # Assert assert_frame_equal(expected_df , actual_df) @@ -1218,7 +1218,7 @@ def test_getttbyspn_shouldreturnexpecteddataframe_wheninvoked(self, remove_untag expected_df : DataFrame = ObjectMother().create_tt_by_spn_df() # Act - actual_df : DataFrame = TimeTrackingManager().get_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) + actual_df : DataFrame = TTDataFrameFactory().get_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) # Assert assert_frame_equal(expected_df , actual_df) From c27aa78515ca1f0d43994b06b31e51ca155e57de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 19:43:38 +0000 Subject: [PATCH 038/109] #40 nwtimetracking, re-factoring for scale up - Iteration 2. --- src/nwtimetracking.py | 164 +++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 522b25d..08ca093 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -653,11 +653,11 @@ class TTDataFrameFactory(): '''Collects all the logic related to dataframe creation out of "Time Tracking.xlsx".''' - __tt_helper : TTDataFrameHelper + __df_helper : TTDataFrameHelper def __init__(self, df_helper : TTDataFrameHelper) -> None: - self.__tt_helper = df_helper + self.__df_helper = df_helper def __enforce_dataframe_definition_for_tt_df(self, tt_df : DataFrame) -> DataFrame: @@ -703,7 +703,7 @@ def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataF # can't enforce the year column as "timedelta" return df - def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year Month ProjectName ProjectVersion Effort @@ -718,10 +718,10 @@ def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -729,7 +729,7 @@ def __get_raw_tt_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], tts_df = tts_df.loc[condition_three] return tts_df - def __get_raw_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tts_by_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month DME @@ -746,16 +746,16 @@ def __get_raw_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) return tts_df - def __get_raw_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tts_by_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month TME @@ -771,13 +771,13 @@ def __get_raw_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) return tts_df - def __get_raw_tt_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year ProjectName ProjectVersion Effort @@ -792,10 +792,10 @@ def __get_raw_tt_by_year_spnv(self, tt_df : DataFrame, years : list[int], softwa condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -804,7 +804,7 @@ def __get_raw_tt_by_year_spnv(self, tt_df : DataFrame, years : list[int], softwa tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) return tts_df - def __get_raw_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tts_by_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year DYE @@ -821,16 +821,16 @@ def __get_raw_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) return tts_df - def __get_raw_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tts_by_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year TYE @@ -846,13 +846,13 @@ def __get_raw_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR]).reset_index(drop = True) tts_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) return tts_df - def __get_raw_tt_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Hashtag ProjectName Effort @@ -869,8 +869,8 @@ def __get_raw_tt_by_spn(self, tt_df : DataFrame, years : list[int], software_pro condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME]).reset_index(drop = True) @@ -891,7 +891,7 @@ def __get_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized @@ -908,11 +908,11 @@ def __get_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : b condition_two : Series = (tt_df[TTCN.HASHTAG] != "#untagged") tts_df = tts_df.loc[condition_two] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized - def __get_raw_tt_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __get_raw_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -928,10 +928,10 @@ def __get_raw_tt_by_spn_spv(self, tt_df : DataFrame, years : list[int], software condition_two : Series = (tt_df[TTCN.ISSOFTWAREPROJECT] == True) tts_df = tts_df.loc[condition_one & condition_two] - tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_name(descriptor = x)) - tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.extract_software_project_version(descriptor = x)) + tts_df[TTCN.PROJECTNAME] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_name(descriptor = x)) + tts_df[TTCN.PROJECTVERSION] = tts_df[TTCN.DESCRIPTOR].apply(lambda x : self.__df_helper.extract_software_project_version(descriptor = x)) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) @@ -950,7 +950,7 @@ def __get_default_raw_ttm(self, year : int) -> DataFrame: ... ''' - td : timedelta = self.convert_string_to_timedelta(td_str = "0h 00m") + td : timedelta = self.__df_helper.convert_string_to_timedelta(td_str = "0h 00m") default_df : DataFrame = pd.DataFrame( { @@ -1050,7 +1050,7 @@ def __get_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: condition : Series = (tt_df[TTCN.YEAR] == year) ttm_df = ttm_df.loc[condition] - ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) ttm_df[str(year)] = ttm_df[TTCN.EFFORT] cn_effort = str(year) @@ -1125,7 +1125,7 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month cn_trend_1 : str = str(years[i-1]) # for ex. "2016" cn_trend_2 : str = str(years[i]) # for ex. "2017" - expansion_df[cn_trend] = expansion_df.apply(lambda x : self.get_trend_by_timedelta(td_1 = x[cn_trend_1], td_2 = x[cn_trend_2]), axis = 1) + expansion_df[cn_trend] = expansion_df.apply(lambda x : self.__df_helper.get_trend_by_timedelta(td_1 = x[cn_trend_1], td_2 = x[cn_trend_2]), axis = 1) new_column_names : list = [TTCN.MONTH, cn_trend_1, cn_trend, cn_trend_2] # for ex. ["Month", "2016", "↕", "2017"] expansion_df = expansion_df.reindex(columns = new_column_names) @@ -1142,7 +1142,7 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month actual_df = expansion_df return actual_df - def __get_raw_tt_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __get_raw_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1157,12 +1157,12 @@ def __get_raw_tt_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) return tts_df - def __get_raw_tt_by_hashtag(self, tt_df : DataFrame) -> DataFrame: + def __get_raw_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ''' Hashtag Effort Effort% @@ -1174,11 +1174,11 @@ def __get_raw_tt_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df : DataFrame = tt_df.copy(deep = True) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) summarized : float = tts_df[TTCN.EFFORT].sum() - tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) + tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) return tts_df @@ -1222,9 +1222,9 @@ def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: add_trend = True) for year in years: - tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) - tts_df.rename(columns = (lambda x : self.try_consolidate_trend_column_name(column_name = x)), inplace = True) + tts_df.rename(columns = (lambda x : self.__df_helper.try_consolidate_trend_column_name(column_name = x)), inplace = True) return tts_df def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: @@ -1258,19 +1258,19 @@ def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby([TTCN.YEAR])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = TTCN.YEAR).reset_index(drop = True) tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( - lambda x : cast(YearlyTarget, self.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) + lambda x : cast(YearlyTarget, self.__df_helper.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.EFFORT] - tts_df[TTCN.YEARLYTARGET] tts_df[TTCN.ISTARGETMET] = tts_df.apply( - lambda x : self.is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) + lambda x : self.__df_helper.is_yearly_target_met(effort = x[TTCN.EFFORT], yearly_target = x[TTCN.YEARLYTARGET]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEARLYTARGET].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEARLYTARGET].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: @@ -1318,21 +1318,21 @@ def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_tar condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) tts_df = tts_df.loc[condition] - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.convert_string_to_timedelta(td_str = x)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.MONTH])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.MONTH]).reset_index(drop = True) tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.EFFORT].groupby(by = tts_df[TTCN.YEAR]).cumsum() tts_df[TTCN.YEARLYTARGET] = tts_df[TTCN.YEAR].apply( - lambda x : cast(YearlyTarget, self.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) + lambda x : cast(YearlyTarget, self.__df_helper.get_yearly_target(yearly_targets = yearly_targets, year = x)).hours) tts_df[TTCN.TOTARGET] = tts_df[TTCN.YEARLYTOTAL] - tts_df[TTCN.YEARLYTARGET] tts_df.drop(columns = [TTCN.YEARLYTARGET], axis = 1, inplace = True) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = True)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1347,9 +1347,9 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw ... ''' - spnv_df : DataFrame = self.__get_raw_tt_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) - dme_df : DataFrame = self.__get_raw_dme(tt_df = tt_df, years = years) - tme_df : DataFrame = self.__get_raw_tme(tt_df = tt_df, years = years) + spnv_df : DataFrame = self.__get_raw_tts_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dme_df : DataFrame = self.__get_raw_tts_by_dme(tt_df = tt_df, years = years) + tme_df : DataFrame = self.__get_raw_tts_by_tme(tt_df = tt_df, years = years) tts_df : DataFrame = pd.merge( left = spnv_df, @@ -1359,7 +1359,7 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw right_on = [TTCN.YEAR, TTCN.MONTH] ) - tts_df[TTCN.PERCDME] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) + tts_df[TTCN.PERCDME] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DME]), axis = 1) tts_df = pd.merge( left = tts_df, @@ -1369,10 +1369,10 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw right_on = [TTCN.YEAR, TTCN.MONTH] ) - tts_df[TTCN.PERCTME] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTME] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TME]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1387,9 +1387,9 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr ... ''' - spnv_df : DataFrame = self.__get_raw_tt_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) - dye_df : DataFrame = self.__get_raw_dye(tt_df = tt_df, years = years) - tye_df : DataFrame = self.__get_raw_tye(tt_df = tt_df, years = years) + spnv_df : DataFrame = self.__get_raw_tts_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dye_df : DataFrame = self.__get_raw_tts_by_dye(tt_df = tt_df, years = years) + tye_df : DataFrame = self.__get_raw_tts_by_tye(tt_df = tt_df, years = years) tts_df : DataFrame = pd.merge( left = spnv_df, @@ -1399,7 +1399,7 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr right_on = [TTCN.YEAR] ) - tts_df[TTCN.PERCDYE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) + tts_df[TTCN.PERCDYE] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DYE]), axis = 1) tts_df = pd.merge( left = tts_df, @@ -1409,10 +1409,10 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr right_on = [TTCN.YEAR] ) - tts_df[TTCN.PERCTYE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.PERCTYE] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TYE]), axis = 1) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: @@ -1431,19 +1431,19 @@ def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_ ... ''' - tts_df : DataFrame = self.__get_raw_tt_by_spn(tt_df = tt_df, years = years, software_project_names = software_project_names) + tts_df : DataFrame = self.__get_raw_tts_by_spn(tt_df = tt_df, years = years, software_project_names = software_project_names) de : timedelta = self.__get_raw_de(tt_df = tt_df, years = years) te : timedelta = self.__get_raw_te(tt_df = tt_df, years = years, remove_untagged = remove_untagged) tts_df[TTCN.DE] = de - tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) + tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) tts_df[TTCN.TE] = te - tts_df[TTCN.PERCTE] = tts_df.apply(lambda x : self.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) + tts_df[TTCN.PERCTE] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.TE]), axis = 1) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.DE] = tts_df[TTCN.DE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) - tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.DE] = tts_df[TTCN.DE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) + tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: @@ -1456,8 +1456,8 @@ def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_proj ... ''' - tts_df : DataFrame = self.__get_raw_tt_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tts_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: @@ -1470,8 +1470,8 @@ def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataF ... ''' - tts_df : DataFrame = self.__get_raw_tt_by_year_hashtag(tt_df = tt_df, years = years) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tts_by_year_hashtag(tt_df = tt_df, years = years) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: @@ -1484,8 +1484,8 @@ def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ... ''' - tts_df : DataFrame = self.__get_raw_tt_by_hashtag(tt_df = tt_df) - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.format_timedelta(td = x, add_plus_sign = False)) + tts_df : DataFrame = self.__get_raw_tts_by_hashtag(tt_df = tt_df) + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: @@ -1502,7 +1502,7 @@ def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFra tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME]] tts_df[TTCN.TIMERANGEID] = tts_df.apply( - lambda x : self.create_time_range_id( + lambda x : self.__df_helper.create_time_range_id( start_time = x[TTCN.STARTTIME], end_time = x[TTCN.ENDTIME], unknown_id = unknown_id), axis = 1) @@ -1524,7 +1524,7 @@ def get_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: tts_df : DataFrame = tt_df.copy(deep = True) tts_df[TTCN.EFFORTSTATUS] = tts_df.apply( - lambda x : self.create_effort_status_and_cast_to_any( + lambda x : self.__df_helper.create_effort_status_and_cast_to_any( idx = x.name, start_time_str = x[TTCN.STARTTIME], end_time_str = x[TTCN.ENDTIME], From 32b66ff10152b9a36c20147b2c586678616ee563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 20:07:19 +0000 Subject: [PATCH 039/109] #40 nwtimetracking, re-factoring for scale up - Iteration 3. --- src/nwtimetracking.py | 242 +++++++++++++++++------------------ tests/nwtimetrackingtests.py | 22 ++-- 2 files changed, 132 insertions(+), 132 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 08ca093..1fcf2f7 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -703,7 +703,7 @@ def __enforce_dataframe_definition_for_raw_ttm_df(self, df : DataFrame) -> DataF # can't enforce the year column as "timedelta" return df - def __get_raw_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __create_raw_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year Month ProjectName ProjectVersion Effort @@ -729,7 +729,7 @@ def __get_raw_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], tts_df = tts_df.loc[condition_three] return tts_df - def __get_raw_tts_by_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __create_raw_tts_by_dme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month DME @@ -755,7 +755,7 @@ def __get_raw_tts_by_dme(self, tt_df : DataFrame, years : list[int]) -> DataFram tts_df.rename(columns = {TTCN.EFFORT : TTCN.DME}, inplace = True) return tts_df - def __get_raw_tts_by_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __create_raw_tts_by_tme(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Month TME @@ -777,7 +777,7 @@ def __get_raw_tts_by_tme(self, tt_df : DataFrame, years : list[int]) -> DataFram tts_df.rename(columns = {TTCN.EFFORT : TTCN.TME}, inplace = True) return tts_df - def __get_raw_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __create_raw_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Year ProjectName ProjectVersion Effort @@ -804,7 +804,7 @@ def __get_raw_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], softw tts_df = tts_df.sort_values(by = [TTCN.YEAR, TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) return tts_df - def __get_raw_tts_by_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __create_raw_tts_by_dye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year DYE @@ -830,7 +830,7 @@ def __get_raw_tts_by_dye(self, tt_df : DataFrame, years : list[int]) -> DataFram tts_df.rename(columns = {TTCN.EFFORT : TTCN.DYE}, inplace = True) return tts_df - def __get_raw_tts_by_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def __create_raw_tts_by_tye(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year TYE @@ -852,7 +852,7 @@ def __get_raw_tts_by_tye(self, tt_df : DataFrame, years : list[int]) -> DataFram tts_df.rename(columns = {TTCN.EFFORT : TTCN.TYE}, inplace = True) return tts_df - def __get_raw_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __create_raw_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' Hashtag ProjectName Effort @@ -881,7 +881,7 @@ def __get_raw_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_pr tts_df = tts_df[[TTCN.HASHTAG, TTCN.PROJECTNAME, TTCN.EFFORT]] return tts_df - def __get_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: + def __create_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: '''3 days 21:15:00''' @@ -895,7 +895,7 @@ def __get_raw_de(self, tt_df : DataFrame, years : list[int]) -> timedelta: summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized - def __get_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : bool) -> timedelta: + def __create_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : bool) -> timedelta: '''186 days 11:15:00''' @@ -912,7 +912,7 @@ def __get_raw_te(self, tt_df : DataFrame, years : list[int], remove_untagged : b summarized : timedelta = tts_df[TTCN.EFFORT].sum() return summarized - def __get_raw_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def __create_raw_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -940,7 +940,7 @@ def __get_raw_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], softwar tts_df = tts_df.sort_values(by = [TTCN.PROJECTNAME, TTCN.PROJECTVERSION]).reset_index(drop = True) return tts_df - def __get_default_raw_ttm(self, year : int) -> DataFrame: + def __create_default_raw_ttm(self, year : int) -> DataFrame: ''' default_df: @@ -963,6 +963,89 @@ def __get_default_raw_ttm(self, year : int) -> DataFrame: default_df = self.__enforce_dataframe_definition_for_raw_ttm_df(df = default_df) return default_df + def __create_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: + + ''' + ttm_df: + + Year Month Effort + 0 2015 10 8h 00m + 1 2015 11 10h 00m + 2 2015 12 0h 00m + + ttm_df: + + Year Month 2015 + 0 2015 10 0 days 08:00:00 + 1 2015 11 0 days 10:00:00 + 2 2015 12 0 days 00:00:00 + + ttm_df: + + Month 2015 + 0 1 0 days 00:00:00 + ... + 9 10 0 days 08:00:00 + 10 11 0 days 10:00:00 + 11 12 0 days 00:00:00 + ''' + + ttm_df : DataFrame = tt_df.copy(deep=True) + ttm_df = ttm_df[[TTCN.YEAR, TTCN.MONTH, TTCN.EFFORT]] + + condition : Series = (tt_df[TTCN.YEAR] == year) + ttm_df = ttm_df.loc[condition] + + ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) + ttm_df[str(year)] = ttm_df[TTCN.EFFORT] + cn_effort = str(year) + + ttm_df = ttm_df.groupby([TTCN.MONTH])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) + ttm_df = ttm_df.sort_values(by = TTCN.MONTH).reset_index(drop = True) + + ttm_df = self.__try_complete_raw_ttm(ttm_df = ttm_df, year = year) + ttm_df = self.__enforce_dataframe_definition_for_raw_ttm_df(df = ttm_df) + + return ttm_df + def __create_raw_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + + ''' + Year Hashtag Effort + 0 2023 #csharp 0 days 15:15:00 + 1 2023 #maintenance 0 days 02:30:00 + 2 2023 #powershell 3 days 02:15:00 + ... + ''' + + tts_df : DataFrame = tt_df.copy(deep = True) + + condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) + tts_df = tts_df.loc[condition] + + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) + + return tts_df + def __create_raw_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: + + ''' + Hashtag Effort Effort% + 0 #csharp 0 days 15:15:00 56.49 + 1 #maintenance 0 days 02:30:00 23.97 + 2 #powershell 3 days 02:15:00 6.43 + ... + ''' + + tts_df : DataFrame = tt_df.copy(deep = True) + + tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) + tts_df = tts_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) + + summarized : float = tts_df[TTCN.EFFORT].sum() + tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) + + return tts_df def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: ''' @@ -1007,7 +1090,7 @@ def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: if ttm_df[TTCN.MONTH].count() != 12: - default_df : DataFrame = self.__get_default_raw_ttm(year = year) + default_df : DataFrame = self.__create_default_raw_ttm(year = year) missing_df : DataFrame = default_df.loc[~default_df[TTCN.MONTH].astype(str).isin(ttm_df[TTCN.MONTH].astype(str))] completed_df : DataFrame = pd.concat([ttm_df, missing_df], ignore_index = True) @@ -1016,50 +1099,6 @@ def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: return completed_df - return ttm_df - def __get_raw_ttm(self, tt_df : DataFrame, year : int) -> DataFrame: - - ''' - ttm_df: - - Year Month Effort - 0 2015 10 8h 00m - 1 2015 11 10h 00m - 2 2015 12 0h 00m - - ttm_df: - - Year Month 2015 - 0 2015 10 0 days 08:00:00 - 1 2015 11 0 days 10:00:00 - 2 2015 12 0 days 00:00:00 - - ttm_df: - - Month 2015 - 0 1 0 days 00:00:00 - ... - 9 10 0 days 08:00:00 - 10 11 0 days 10:00:00 - 11 12 0 days 00:00:00 - ''' - - ttm_df : DataFrame = tt_df.copy(deep=True) - ttm_df = ttm_df[[TTCN.YEAR, TTCN.MONTH, TTCN.EFFORT]] - - condition : Series = (tt_df[TTCN.YEAR] == year) - ttm_df = ttm_df.loc[condition] - - ttm_df[TTCN.EFFORT] = ttm_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) - ttm_df[str(year)] = ttm_df[TTCN.EFFORT] - cn_effort = str(year) - - ttm_df = ttm_df.groupby([TTCN.MONTH])[cn_effort].sum().sort_values(ascending = [False]).reset_index(name = cn_effort) - ttm_df = ttm_df.sort_values(by = TTCN.MONTH).reset_index(drop = True) - - ttm_df = self.__try_complete_raw_ttm(ttm_df = ttm_df, year = year) - ttm_df = self.__enforce_dataframe_definition_for_raw_ttm_df(df = ttm_df) - return ttm_df def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month_df : DataFrame, i : int, add_trend : bool) -> DataFrame: @@ -1110,7 +1149,7 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month ''' actual_df : DataFrame = tts_by_month_df.copy(deep = True) - ttm_df : DataFrame = self.__get_raw_ttm(tt_df = tt_df, year = years[i]) + ttm_df : DataFrame = self.__create_raw_ttm(tt_df = tt_df, year = years[i]) expansion_df = pd.merge( left = actual_df, @@ -1142,47 +1181,8 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month actual_df = expansion_df return actual_df - def __get_raw_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: - - ''' - Year Hashtag Effort - 0 2023 #csharp 0 days 15:15:00 - 1 2023 #maintenance 0 days 02:30:00 - 2 2023 #powershell 3 days 02:15:00 - ... - ''' - - tts_df : DataFrame = tt_df.copy(deep = True) - - condition : Series = (tt_df[TTCN.YEAR].isin(values = years)) - tts_df = tts_df.loc[condition] - - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) - tts_df = tts_df.groupby(by = [TTCN.YEAR, TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - tts_df = tts_df.sort_values(by = [TTCN.HASHTAG, TTCN.YEAR]).reset_index(drop = True) - - return tts_df - def __get_raw_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: - - ''' - Hashtag Effort Effort% - 0 #csharp 0 days 15:15:00 56.49 - 1 #maintenance 0 days 02:30:00 23.97 - 2 #powershell 3 days 02:15:00 6.43 - ... - ''' - - tts_df : DataFrame = tt_df.copy(deep = True) - - tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.convert_string_to_timedelta(td_str = x)) - tts_df = tts_df.groupby(by = [TTCN.HASHTAG])[TTCN.EFFORT].sum().sort_values(ascending = [False]).reset_index(name = TTCN.EFFORT) - - summarized : float = tts_df[TTCN.EFFORT].sum() - tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) - - return tts_df - def get_tt(self, setting_bag : SettingBag) -> DataFrame: + def create_tt(self, setting_bag : SettingBag) -> DataFrame: ''' Retrieves the content of the "Sessions" tab and returns it as a Dataframe. @@ -1198,7 +1198,7 @@ def get_tt(self, setting_bag : SettingBag) -> DataFrame: tt_df = self.__enforce_dataframe_definition_for_tt_df(tt_df = tt_df) return tt_df - def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: + def create_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: ''' Month 2016 ↕ 2017 ↕ 2018 ... @@ -1212,7 +1212,7 @@ def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: for i in range(len(years)): if i == 0: - tts_df = self.__get_raw_ttm(tt_df = tt_df, year = years[i]) + tts_df = self.__create_raw_ttm(tt_df = tt_df, year = years[i]) else: tts_df = self.__expand_raw_ttm_by_year( tt_df = tt_df, @@ -1227,7 +1227,7 @@ def get_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: tts_df.rename(columns = (lambda x : self.__df_helper.try_consolidate_trend_column_name(column_name = x)), inplace = True) return tts_df - def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + def create_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' [0] @@ -1273,7 +1273,7 @@ def get_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df - def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' [0] @@ -1335,7 +1335,7 @@ def get_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_tar tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df - def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' [0] ... @@ -1347,9 +1347,9 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw ... ''' - spnv_df : DataFrame = self.__get_raw_tts_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) - dme_df : DataFrame = self.__get_raw_tts_by_dme(tt_df = tt_df, years = years) - tme_df : DataFrame = self.__get_raw_tts_by_tme(tt_df = tt_df, years = years) + spnv_df : DataFrame = self.__create_raw_tts_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dme_df : DataFrame = self.__create_raw_tts_by_dme(tt_df = tt_df, years = years) + tme_df : DataFrame = self.__create_raw_tts_by_tme(tt_df = tt_df, years = years) tts_df : DataFrame = pd.merge( left = spnv_df, @@ -1375,7 +1375,7 @@ def get_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], softw tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' [0] ... @@ -1387,9 +1387,9 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr ... ''' - spnv_df : DataFrame = self.__get_raw_tts_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) - dye_df : DataFrame = self.__get_raw_tts_by_dye(tt_df = tt_df, years = years) - tye_df : DataFrame = self.__get_raw_tts_by_tye(tt_df = tt_df, years = years) + spnv_df : DataFrame = self.__create_raw_tts_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) + dye_df : DataFrame = self.__create_raw_tts_by_dye(tt_df = tt_df, years = years) + tye_df : DataFrame = self.__create_raw_tts_by_tye(tt_df = tt_df, years = years) tts_df : DataFrame = pd.merge( left = spnv_df, @@ -1415,7 +1415,7 @@ def get_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_pr tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: + def create_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: ''' Hashtag ProjectName Effort DE %_DE TE %_TE @@ -1431,9 +1431,9 @@ def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_ ... ''' - tts_df : DataFrame = self.__get_raw_tts_by_spn(tt_df = tt_df, years = years, software_project_names = software_project_names) - de : timedelta = self.__get_raw_de(tt_df = tt_df, years = years) - te : timedelta = self.__get_raw_te(tt_df = tt_df, years = years, remove_untagged = remove_untagged) + tts_df : DataFrame = self.__create_raw_tts_by_spn(tt_df = tt_df, years = years, software_project_names = software_project_names) + de : timedelta = self.__create_raw_de(tt_df = tt_df, years = years) + te : timedelta = self.__create_raw_te(tt_df = tt_df, years = years, remove_untagged = remove_untagged) tts_df[TTCN.DE] = de tts_df[TTCN.PERCDE] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = x[TTCN.DE]), axis = 1) @@ -1446,7 +1446,7 @@ def get_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_ tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -1456,11 +1456,11 @@ def get_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_proj ... ''' - tts_df : DataFrame = self.__get_raw_tts_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) + tts_df : DataFrame = self.__create_raw_tts_by_spn_spv(tt_df = tt_df, years = years, software_project_names = software_project_names) tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def create_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1470,11 +1470,11 @@ def get_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataF ... ''' - tts_df : DataFrame = self.__get_raw_tts_by_year_hashtag(tt_df = tt_df, years = years) + tts_df : DataFrame = self.__create_raw_tts_by_year_hashtag(tt_df = tt_df, years = years) tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ''' Hashtag Effort Effort% @@ -1484,11 +1484,11 @@ def get_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: ... ''' - tts_df : DataFrame = self.__get_raw_tts_by_hashtag(tt_df = tt_df) + tts_df : DataFrame = self.__create_raw_tts_by_hashtag(tt_df = tt_df) tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: + def create_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: ''' TimeRangeId Occurrences @@ -1513,7 +1513,7 @@ def get_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFra tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) return tts_df - def get_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: ''' StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index f5da951..b67cfc4 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1043,7 +1043,7 @@ def test_getsessionsdataset_shouldreturnexpecteddataframe_wheninvoked(self): # Act with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: - actual : DataFrame = TTDataFrameFactory().get_tt(setting_bag = setting_bag) + actual : DataFrame = TTDataFrameFactory().create_tt(setting_bag = setting_bag) # Assert self.assertEqual(expected_column_names, actual.columns.tolist()) @@ -1060,7 +1060,7 @@ def test_getttbyyear_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1073,7 +1073,7 @@ def test_getttbyyearmonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1086,7 +1086,7 @@ def test_getttbyyearmonthspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_spnv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1099,7 +1099,7 @@ def test_getttbyyearspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_spnv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1112,7 +1112,7 @@ def test_getttbyspnspv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_spn_spv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1124,7 +1124,7 @@ def test_getttsbymonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tts_by_month_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_month(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_month(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df, actual_df) @@ -1150,7 +1150,7 @@ def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): expected_df.reset_index(drop = True, inplace = True) # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) actual_df.reset_index(drop = True, inplace = True) @@ -1189,7 +1189,7 @@ def test_getttbyyearhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_hashtag_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_year_hashtag(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_hashtag(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df , actual_df) @@ -1200,7 +1200,7 @@ def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_hashtag_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_hashtag(tt_df = sessions_df) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_hashtag(tt_df = sessions_df) # Assert assert_frame_equal(expected_df , actual_df) @@ -1218,7 +1218,7 @@ def test_getttbyspn_shouldreturnexpecteddataframe_wheninvoked(self, remove_untag expected_df : DataFrame = ObjectMother().create_tt_by_spn_df() # Act - actual_df : DataFrame = TTDataFrameFactory().get_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) # Assert assert_frame_equal(expected_df , actual_df) From a2584041fbd0fa377f505d31b682ccc12d3caf5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 20:16:30 +0000 Subject: [PATCH 040/109] #40 nwtimetracking, re-factoring for scale up - Iteration 4. --- src/nwtimetracking.py | 158 ++++++++++++++++++----------------- tests/nwtimetrackingtests.py | 4 +- 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 1fcf2f7..e86da7d 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -14,7 +14,7 @@ from datetime import datetime, timedelta from enum import StrEnum from pandas import DataFrame, Series -from typing import Any, Callable, Optional, cast +from typing import Any, Callable, Optional, Tuple, cast # LOCAL MODULES from nwshared import Formatter, FilePathManager, FileManager, LambdaProvider, MarkdownHelper @@ -1181,6 +1181,53 @@ def __expand_raw_ttm_by_year(self, tt_df : DataFrame, years : list, tts_by_month actual_df = expansion_df return actual_df + def __update_future_months_to_empty(self, tts_by_month_df : DataFrame, now : datetime) -> DataFrame: + + ''' + If now is 2023-08-09: + + Month 2022 ↕ 2023 + ... + 8 0h 00m = 0h 00m + 9 1h 00m ↓ 0h 00m + 10 0h 00m = 0h 00m + 11 0h 00m = 0h 00m + 12 0h 00m = 0h 00m + + Month 2022 ↕ 2023 + ... + 8 0h 00m = 0h 00m + 9 1h 00m + 10 0h 00m + 11 0h 00m + 12 0h 00m + ''' + + tts_by_month_upd_df : DataFrame = tts_by_month_df.copy(deep = True) + + now_year : int = now.year + now_month : int = now.month + cn_year : str = str(now_year) + new_value : str = "" + + condition : Series = (tts_by_month_upd_df[TTCN.MONTH] > now_month) + tts_by_month_upd_df[cn_year] = np.where(condition, new_value, tts_by_month_upd_df[cn_year]) + + idx_year : int = cast(int, tts_by_month_upd_df.columns.get_loc(cn_year)) + idx_trend : int = (idx_year - 1) + tts_by_month_upd_df.iloc[:, idx_trend] = np.where(condition, new_value, tts_by_month_upd_df.iloc[:, idx_trend]) + + return tts_by_month_upd_df + def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) -> DataFrame: + + '''Returns a DataFrame that contains only rows that match the provided is_correct.''' + + filtered_df : DataFrame = tts_by_efs_df.copy(deep = True) + + condition : Series = (filtered_df[TTCN.ESISCORRECT] == is_correct) + filtered_df = tts_by_efs_df.loc[condition] + + return filtered_df def create_tt(self, setting_bag : SettingBag) -> DataFrame: @@ -1198,13 +1245,15 @@ def create_tt(self, setting_bag : SettingBag) -> DataFrame: tt_df = self.__enforce_dataframe_definition_for_tt_df(tt_df = tt_df) return tt_df - def create_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: + def create_tts_by_month(self, tt_df : DataFrame, years : list, now : datetime) -> Tuple[DataFrame, DataFrame]: ''' Month 2016 ↕ 2017 ↕ 2018 ... 0 1 0h 00m ↑ 13h 00m ↓ 0h 00m 1 2 0h 00m ↑ 1h 00m ↓ 0h 00m ... + + Returns: (tts_by_month_df, tts_by_month_upd_df). ''' tts_df : DataFrame = pd.DataFrame() @@ -1225,8 +1274,9 @@ def create_tts_by_month(self, tt_df : DataFrame, years : list) -> DataFrame: tts_df[str(year)] = tts_df[str(year)].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) tts_df.rename(columns = (lambda x : self.__df_helper.try_consolidate_trend_column_name(column_name = x)), inplace = True) + tts_upd_df : DataFrame = self.__update_future_months_to_empty(tts_by_month_df = tts_df, now = now) - return tts_df + return (tts_df, tts_upd_df) def create_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' @@ -1488,7 +1538,35 @@ def create_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: + def create_tts_by_efs(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataFrame, DataFrame]: + + ''' + StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message + 21:00 23:00 1h 00m False 2h 00m ... + ... + + Returns (tts_by_efs_df, tts_by_efs_flt_df). + ''' + + tts_df : DataFrame = tt_df.copy(deep = True) + + tts_df[TTCN.EFFORTSTATUS] = tts_df.apply( + lambda x : self.__df_helper.create_effort_status_and_cast_to_any( + idx = x.name, + start_time_str = x[TTCN.STARTTIME], + end_time_str = x[TTCN.ENDTIME], + effort_str = x[TTCN.EFFORT]), + axis = 1) + + tts_df[TTCN.ESISCORRECT] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.is_correct) + tts_df[TTCN.ESEXPECTED] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.expected_str) + tts_df[TTCN.ESMESSAGE] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.message) + tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME, TTCN.EFFORT, TTCN.ESISCORRECT, TTCN.ESEXPECTED, TTCN.ESMESSAGE]] + + tts_flt_df : DataFrame = self.__filter_by_is_correct(tts_by_efs_df = tts_df, is_correct = is_correct) + + return (tts_df, tts_flt_df) + def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: ''' TimeRangeId Occurrences @@ -1513,31 +1591,6 @@ def create_tts_by_time_ranges(self, tt_df : DataFrame, unknown_id : str) -> Data tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) return tts_df - def create_tts_by_effort_status(self, tt_df : DataFrame) -> DataFrame: - - ''' - StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message - 21:00 23:00 1h 00m False 2h 00m ... - ... - ''' - - tts_df : DataFrame = tt_df.copy(deep = True) - - tts_df[TTCN.EFFORTSTATUS] = tts_df.apply( - lambda x : self.__df_helper.create_effort_status_and_cast_to_any( - idx = x.name, - start_time_str = x[TTCN.STARTTIME], - end_time_str = x[TTCN.ENDTIME], - effort_str = x[TTCN.EFFORT]), - axis = 1) - - tts_df[TTCN.ESISCORRECT] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.is_correct) - tts_df[TTCN.ESEXPECTED] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.expected_str) - tts_df[TTCN.ESMESSAGE] = tts_df[TTCN.EFFORTSTATUS].apply(lambda x : x.message) - - tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME, TTCN.EFFORT, TTCN.ESISCORRECT, TTCN.ESEXPECTED, TTCN.ESMESSAGE]] - - return tts_df def try_print_definitions(self, df : DataFrame, definitions : dict[str, str]) -> None: @@ -1549,53 +1602,6 @@ def try_print_definitions(self, df : DataFrame, definitions : dict[str, str]) -> for column_name in df.columns: if definitions.get(column_name) != None: print(f"{column_name}: {definitions[column_name]}") - def update_future_months_to_empty(self, tts_by_month_df : DataFrame, now : datetime) -> DataFrame: - - ''' - If now is 2023-08-09: - - Month 2022 ↕ 2023 - ... - 8 0h 00m = 0h 00m - 9 1h 00m ↓ 0h 00m - 10 0h 00m = 0h 00m - 11 0h 00m = 0h 00m - 12 0h 00m = 0h 00m - - Month 2022 ↕ 2023 - ... - 8 0h 00m = 0h 00m - 9 1h 00m - 10 0h 00m - 11 0h 00m - 12 0h 00m - ''' - - tts_by_month_upd_df : DataFrame = tts_by_month_df.copy(deep = True) - - now_year : int = now.year - now_month : int = now.month - cn_year : str = str(now_year) - new_value : str = "" - - condition : Series = (tts_by_month_upd_df[TTCN.MONTH] > now_month) - tts_by_month_upd_df[cn_year] = np.where(condition, new_value, tts_by_month_upd_df[cn_year]) - - idx_year : int = cast(int, tts_by_month_upd_df.columns.get_loc(cn_year)) - idx_trend : int = (idx_year - 1) - tts_by_month_upd_df.iloc[:, idx_trend] = np.where(condition, new_value, tts_by_month_upd_df.iloc[:, idx_trend]) - - return tts_by_month_upd_df - def filter_by_is_correct(self, tts_by_effort_status_df : DataFrame, is_correct : bool) -> DataFrame: - - '''Returns a DataFrame that contains only rows that match the provided is_correct.''' - - filtered_df : DataFrame = tts_by_effort_status_df.copy(deep = True) - - condition : Series = (filtered_df[TTCN.ESISCORRECT] == is_correct) - filtered_df = tts_by_effort_status_df.loc[condition] - - return filtered_df def remove_unknown_id(self, tts_by_time_ranges_df : DataFrame, unknown_id : str) -> DataFrame: '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index b67cfc4..2f7ca63 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1136,7 +1136,7 @@ def test_updatefuturemonthstoempty_shouldreturnexpecteddataframe_wheninvoked(sel expected_df : DataFrame = ObjectMother().create_tts_by_month_upd_df() # Act - actual_df : DataFrame = TTDataFrameFactory().update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = now) + actual_df : DataFrame = TTDataFrameFactory().__update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = now) # Assert assert_frame_equal(expected_df, actual_df) @@ -1150,7 +1150,7 @@ def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): expected_df.reset_index(drop = True, inplace = True) # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_time_ranges(tt_df = sessions_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_tr(tt_df = sessions_df, unknown_id = unknown_id) actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) actual_df.reset_index(drop = True, inplace = True) From 69f7caf535cecbe288267b45d6edf825cc06966b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 21:06:43 +0000 Subject: [PATCH 041/109] #40 nwtimetracking, re-factoring for scale up - Iteration 5. --- src/nwtimetracking.ipynb | 8 ++-- src/nwtimetracking.py | 82 +++++++++++------------------------- tests/nwtimetrackingtests.py | 10 ++--- 3 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 0f66222..aae8a54 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -41,12 +41,12 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from nwtimetracking import SettingBag, DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, TimeTrackingManager\n", - "from nwtimetracking import ComponentBag, MarkdownProcessor\n", + "from nwtimetracking import ComponentBag, TTMarkdownFactory\n", "from nwpackageversions import LanguageChecker, RequirementChecker, RequirementSummary\n", "from nwshared import Displayer" ] @@ -1543,12 +1543,12 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "component_bag : ComponentBag = ComponentBag()\n", - "markdown_processor : MarkdownProcessor = MarkdownProcessor(component_bag = component_bag, setting_bag = setting_bag)\n", + "markdown_processor : TTMarkdownFactory = TTMarkdownFactory(component_bag = component_bag, setting_bag = setting_bag)\n", "\n", "markdown_processor.process_tts_by_month_md(tts_by_month_upd_df = tts_by_month_upd_df)" ] diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index e86da7d..e46807c 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1228,6 +1228,24 @@ def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) - filtered_df = tts_by_efs_df.loc[condition] return filtered_df + def __remove_unknown_id(self, tts_by_tr_df : DataFrame, unknown_id : str) -> DataFrame: + + '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' + + condition : Series = (tts_by_tr_df[TTCN.TIMERANGEID] != unknown_id) + tts_by_tr_df = tts_by_tr_df.loc[condition] + tts_by_tr_df.reset_index(drop = True, inplace = True) + + return tts_by_tr_df + def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : int, ascending : bool = False) -> DataFrame: + + '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' + + tts_by_tr_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) + tts_by_tr_df = tts_by_tr_df.iloc[0:n] + tts_by_tr_df.reset_index(drop = True, inplace = True) + + return tts_by_tr_df def create_tt(self, setting_bag : SettingBag) -> DataFrame: @@ -1591,54 +1609,23 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) return tts_df +class TTMarkdownFactory(): - def try_print_definitions(self, df : DataFrame, definitions : dict[str, str]) -> None: - - ''' - "DE" => print("DE: Development Effort") - "Year" => do nothing - ''' - - for column_name in df.columns: - if definitions.get(column_name) != None: - print(f"{column_name}: {definitions[column_name]}") - def remove_unknown_id(self, tts_by_time_ranges_df : DataFrame, unknown_id : str) -> DataFrame: - - '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' - - condition : Series = (tts_by_time_ranges_df[TTCN.TIMERANGEID] != unknown_id) - tts_by_time_ranges_df = tts_by_time_ranges_df.loc[condition] - tts_by_time_ranges_df.reset_index(drop = True, inplace = True) - - return tts_by_time_ranges_df - def filter_by_top_n_occurrences(self, tts_by_time_ranges_df : DataFrame, n : int, ascending : bool = False) -> DataFrame: - - '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' - - tts_by_time_ranges_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) - tts_by_time_ranges_df = tts_by_time_ranges_df.iloc[0:n] - tts_by_time_ranges_df.reset_index(drop = True, inplace = True) - - return tts_by_time_ranges_df -class MarkdownProcessor(): + '''Collects all the logic related to Markdown creation out of Time Tracking dataframes.''' - '''Collects all the logic related to the processing of Markdown content.''' + __markdown_helper : MarkdownHelper - __component_bag : ComponentBag - __setting_bag : SettingBag + def __init__(self, markdown_helper : MarkdownHelper) -> None: - def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> None: + self.__markdown_helper = markdown_helper - self.__component_bag = component_bag - self.__setting_bag = setting_bag - - def __get_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : DataFrame) -> str: + def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : DataFrame) -> str: '''Creates the Markdown content for a "Time Tracking By Month" file out of the provided dataframe.''' md_paragraph_title : str = "Time Tracking By Month" - markdown_header : str = self.__component_bag.markdown_helper.get_markdown_header(last_update = last_update, paragraph_title = md_paragraph_title) + markdown_header : str = self.__markdown_helper.get_markdown_header(last_update = last_update, paragraph_title = md_paragraph_title) tts_by_month_upd_md : str = tts_by_month_upd_df.to_markdown(index = False) md_content : str = markdown_header @@ -1648,25 +1635,6 @@ def __get_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : Da return md_content - def process_tts_by_month_md(self, tts_by_month_upd_df : DataFrame) -> None: - - '''Performs all the tasks related to the "Time Tracking By Month" file.''' - - content : str = self.__get_tts_by_month_md( - last_update = self.__setting_bag.last_update, - tts_by_month_upd_df = tts_by_month_upd_df) - - if self.__setting_bag.show_tts_by_month_md: - file_name_content : str = self.__component_bag.markdown_helper.format_file_name_as_content(file_name = self.__setting_bag.tts_by_month_file_name) - self.__component_bag.logging_function(file_name_content) - self.__component_bag.logging_function(content) - - if self.__setting_bag.save_tts_by_month_md: - file_path : str = self.__component_bag.file_path_manager.create_file_path( - folder_path = self.__setting_bag.working_folder_path, - file_name = self.__setting_bag.tts_by_month_file_name) - - self.__component_bag.file_manager.save_content(content = content, file_path = file_path) # MAIN if __name__ == "__main__": diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 2f7ca63..9c5d8ad 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -16,7 +16,7 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import ComponentBag, MarkdownProcessor, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import ComponentBag, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager @@ -452,7 +452,7 @@ def create_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: return (df, expected) @staticmethod - def create_service_objects_for_ttsbymonthmd() -> Tuple[ComponentBag, SettingBag, MarkdownProcessor]: + def create_service_objects_for_ttsbymonthmd() -> Tuple[ComponentBag, SettingBag, TTMarkdownFactory]: component_bag : Mock = Mock() component_bag.logging_function = Mock() @@ -467,7 +467,7 @@ def create_service_objects_for_ttsbymonthmd() -> Tuple[ComponentBag, SettingBag, setting_bag.show_tts_by_month_md = True setting_bag.save_tts_by_month_md = True - markdown_processor : MarkdownProcessor = MarkdownProcessor( + markdown_processor : TTMarkdownFactory = TTMarkdownFactory( component_bag = component_bag, setting_bag = setting_bag ) @@ -1165,7 +1165,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whencontainsunknownid(sel time_ranges_df.loc[len(time_ranges_df.index)] = [unknown_id, 3] # Act - actual_df : DataFrame = TTDataFrameFactory().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_id(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) @@ -1177,7 +1177,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whendoesnotcontainunknown time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() # Act - actual_df : DataFrame = TTDataFrameFactory().remove_unknown_id(tts_by_time_ranges_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_id(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) From f8c1f67d389aa4ab99a1631d6e34acc911c5af65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 21:13:44 +0000 Subject: [PATCH 042/109] #40 nwtimetracking, added: TTSummary. --- src/nwtimetracking.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index e46807c..56a16b0 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -131,6 +131,24 @@ class EffortStatus(): is_correct : bool message : str +@dataclass(frozen = True) +class TTSummary(): + + '''Collects all the dataframes and markdowns.''' + + tt_df : DataFrame + tts_by_month_tpl : Tuple[DataFrame, DataFrame] + tts_by_year_df : DataFrame + tts_by_year_month_df : DataFrame + tts_by_year_month_spnv_df : DataFrame + tts_by_year_spnv_df : DataFrame + tts_by_spn_df : DataFrame + tts_by_spn_spv_df : DataFrame + tts_by_year_hashtag_df : DataFrame + tts_by_hashtag_df : DataFrame + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] + tts_by_tr_df : DataFrame + tts_by_month_md : str # CLASSES class SettingBag(): @@ -1635,7 +1653,6 @@ def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : D return md_content - # MAIN if __name__ == "__main__": pass \ No newline at end of file From 5c9530c4dbeb7a3e6e28f1fcd47fe0120dce242d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Mon, 2 Dec 2024 21:28:30 +0000 Subject: [PATCH 043/109] #40 TimeTrackingProcessor - Iteration 1. --- src/nwtimetracking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 56a16b0..39cc894 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1652,6 +1652,18 @@ def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : D md_content += "\n" return md_content +class TimeTrackingProcessor(): + + '''Collects all the logic related to the processing of "Time Tracking.xlsx".''' + + __component_bag : ComponentBag + __setting_bag : SettingBag + __rl_summary : TTSummary + + def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> None: + + self.__component_bag = component_bag + self.__setting_bag = setting_bag # MAIN if __name__ == "__main__": From 8b5ef212566f2e67dc8e9df47040e8d11c6345c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 08:34:54 +0000 Subject: [PATCH 044/109] #40 TimeTrackingProcessor - Iteration 2. --- src/nwtimetracking.py | 91 ++++++++++++++++++++++-------------- tests/nwtimetrackingtests.py | 4 +- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 39cc894..c213dbf 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -158,7 +158,7 @@ class SettingBag(): years : list[int] yearly_targets : list[YearlyTarget] excel_path : str - excel_books_nrows : int + excel_nrows : int software_project_names : list[str] software_project_names_by_spv : list[str] tt_by_year_hashtag_years : list[int] @@ -175,8 +175,8 @@ class SettingBag(): show_tts_by_month_df : bool show_effort_status_df : bool show_time_ranges_df : bool - excel_books_skiprows : int - excel_books_tabname : str + excel_skiprows : int + excel_tabname : str n_generic : int n_by_month : int now : datetime @@ -217,8 +217,8 @@ def __init__( show_tts_by_month_df : bool = True, show_effort_status_df : bool = True, show_time_ranges_df : bool = True, - excel_books_skiprows : int = 0, - excel_books_tabname : str = "Sessions", + excel_skiprows : int = 0, + excel_tabname : str = "Sessions", n_generic : int = 5, n_by_month : int = 12, now : datetime = datetime.now(), @@ -248,7 +248,7 @@ def __init__( self.years = years self.yearly_targets = yearly_targets self.excel_path = excel_path - self.excel_books_nrows = excel_books_nrows + self.excel_nrows = excel_books_nrows self.software_project_names = software_project_names self.software_project_names_by_spv = software_project_names_by_spv self.tt_by_year_hashtag_years = tt_by_year_hashtag_years @@ -265,8 +265,8 @@ def __init__( self.show_tts_by_month_df = show_tts_by_month_df self.show_effort_status_df = show_effort_status_df self.show_time_ranges_df = show_time_ranges_df - self.excel_books_skiprows = excel_books_skiprows - self.excel_books_tabname = excel_books_tabname + self.excel_skiprows = excel_skiprows + self.excel_tabname = excel_tabname self.n_generic = n_generic self.n_by_month = n_by_month self.now = now @@ -284,26 +284,6 @@ def __init__( self.tts_by_month_file_name = tts_by_month_file_name self.show_tts_by_month_md = show_tts_by_month_md self.save_tts_by_month_md = save_tts_by_month_md -class ComponentBag(): - - '''Represents a collection of components.''' - - file_path_manager : FilePathManager - file_manager : FileManager - logging_function : Callable[[str], None] - markdown_helper : MarkdownHelper - - def __init__( - self, - file_path_manager : FilePathManager = FilePathManager(), - file_manager : FileManager = FileManager(file_path_manager = FilePathManager()), - logging_function : Callable[[str], None] = LambdaProvider().get_default_logging_function(), - markdown_helper : MarkdownHelper = MarkdownHelper(formatter = Formatter())) -> None: - - self.file_path_manager = file_path_manager - self.file_manager = file_manager - self.logging_function = logging_function - self.markdown_helper = markdown_helper class DefaultPathProvider(): '''Responsible for proviving the default path to the dataset.''' @@ -1265,17 +1245,17 @@ def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : int, ascen return tts_by_tr_df - def create_tt(self, setting_bag : SettingBag) -> DataFrame: + def create_tt(self, excel_path : str, excel_skiprows : int, excel_nrows : int, excel_tabname : str) -> DataFrame: ''' Retrieves the content of the "Sessions" tab and returns it as a Dataframe. ''' tt_df : DataFrame = pd.read_excel( - io = setting_bag.excel_path, - skiprows = setting_bag.excel_books_skiprows, - nrows = setting_bag.excel_books_nrows, - sheet_name = setting_bag.excel_books_tabname, + io = excel_path, + skiprows = excel_skiprows, + nrows = excel_nrows, + sheet_name = excel_tabname, engine = 'openpyxl' ) tt_df = self.__enforce_dataframe_definition_for_tt_df(tt_df = tt_df) @@ -1652,19 +1632,62 @@ def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : D md_content += "\n" return md_content +class ComponentBag(): + + '''Represents a collection of components.''' + + file_path_manager : FilePathManager + file_manager : FileManager + df_factory : TTDataFrameFactory + md_factory : TTMarkdownFactory + logging_function : Callable[[str], None] + markdown_helper : MarkdownHelper + + def __init__( + self, + file_path_manager : FilePathManager = FilePathManager(), + file_manager : FileManager = FileManager( + file_path_manager = FilePathManager()), + df_factory : TTDataFrameFactory = TTDataFrameFactory( + df_helper = TTDataFrameHelper()), + md_factory : TTMarkdownFactory = TTMarkdownFactory( + markdown_helper = MarkdownHelper( + formatter = Formatter()) + ), + logging_function : Callable[[str], None] = LambdaProvider().get_default_logging_function()) -> None: + + self.file_path_manager = file_path_manager + self.file_manager = file_manager + self.df_factory = df_factory + self.md_factory = md_factory + self.logging_function = logging_function + class TimeTrackingProcessor(): '''Collects all the logic related to the processing of "Time Tracking.xlsx".''' __component_bag : ComponentBag __setting_bag : SettingBag - __rl_summary : TTSummary + __tt_summary : TTSummary def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> None: self.__component_bag = component_bag self.__setting_bag = setting_bag + def __create_tt_df(self) -> DataFrame: + + '''Creates the expected dataframe using __setting_bag.''' + + tt_df : DataFrame = self.__component_bag.df_factory.create_tt( + excel_path = self.__setting_bag.excel_path, + excel_skiprows = self.__setting_bag.excel_skiprows, + excel_nrows = self.__setting_bag.excel_nrows, + excel_tabname = self.__setting_bag.excel_tabname + ) + + return tt_df + # MAIN if __name__ == "__main__": pass \ No newline at end of file diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 9c5d8ad..84a6f9e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -102,9 +102,9 @@ def create_setting_bag() -> SettingBag: YearlyTarget(year = 2015, hours = timedelta(hours = 0)) ], excel_path = DefaultPathProvider().get_default_time_tracking_path(), - excel_books_skiprows = 0, + excel_skiprows = 0, excel_books_nrows = 920, - excel_books_tabname = "Sessions", + excel_tabname = "Sessions", n_generic = 5, n_by_month = 12, now = datetime.now(), From 1e29860ef2791b564a58936f3db7a293c59fed2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 09:04:19 +0000 Subject: [PATCH 045/109] #40 TimeTrackingProcessor - Iteration 3. --- src/nwtimetracking.py | 212 ++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 134 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index c213dbf..c6f461e 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -10,7 +10,7 @@ import pandas as pd import re import openpyxl -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime, timedelta from enum import StrEnum from pandas import DataFrame, Series @@ -151,139 +151,6 @@ class TTSummary(): tts_by_month_md : str # CLASSES -class SettingBag(): - - '''Represents a collection of settings.''' - - years : list[int] - yearly_targets : list[YearlyTarget] - excel_path : str - excel_nrows : int - software_project_names : list[str] - software_project_names_by_spv : list[str] - tt_by_year_hashtag_years : list[int] - - show_sessions_df : bool - show_tt_by_year_df : bool - show_tt_by_year_month_df : bool - show_tt_by_year_month_spnv_df : bool - show_tt_by_year_spnv_df : bool - show_tt_by_spn_df : bool - show_tt_by_spn_spv_df : bool - show_tt_by_year_hashtag : bool - show_tt_by_hashtag : bool - show_tts_by_month_df : bool - show_effort_status_df : bool - show_time_ranges_df : bool - excel_skiprows : int - excel_tabname : str - n_generic : int - n_by_month : int - now : datetime - remove_untagged_from_de : bool - definitions : dict[str, str] - effort_status_n : int - effort_status_is_correct : bool - tts_by_month_update_future_values_to_empty : bool - time_ranges_unknown_id : str - time_ranges_top_n : int - time_ranges_remove_unknown_id : bool - time_ranges_filter_by_top_n : bool - working_folder_path : str - last_update : datetime - tts_by_month_file_name : str - show_tts_by_month_md : bool - save_tts_by_month_md : bool - - def __init__( - self, - years : list[int], - yearly_targets : list[YearlyTarget], - excel_path : str, - excel_books_nrows : int, - software_project_names : list[str], - software_project_names_by_spv : list[str], - tt_by_year_hashtag_years : list[int], - - show_sessions_df : bool = False, - show_tt_by_year_df : bool = True, - show_tt_by_year_month_df : bool = True, - show_tt_by_year_month_spnv_df : bool = False, - show_tt_by_year_spnv_df : bool = False, - show_tt_by_spn_df : bool = True, - show_tt_by_spn_spv_df : bool = True, - show_tt_by_year_hashtag : bool = True, - show_tt_by_hashtag : bool = True, - show_tts_by_month_df : bool = True, - show_effort_status_df : bool = True, - show_time_ranges_df : bool = True, - excel_skiprows : int = 0, - excel_tabname : str = "Sessions", - n_generic : int = 5, - n_by_month : int = 12, - now : datetime = datetime.now(), - remove_untagged_from_de : bool = True, - definitions : dict[str, str] = { - "DME": "Development Monthly Effort", - "TME": "Total Monthly Effort", - "DYE": "Development Yearly Effort", - "TYE": "Total Yearly Effort", - "DE": "Development Effort", - "TE": "Total Effort" - }, - effort_status_n : int = 25, - effort_status_is_correct : bool = False, - tts_by_month_update_future_values_to_empty : bool = True, - time_ranges_unknown_id : str = "Unknown", - time_ranges_top_n : int = 5, - time_ranges_remove_unknown_id : bool = True, - time_ranges_filter_by_top_n : bool = True, - working_folder_path : str = "/home/nwtimetracking/", - last_update : datetime = datetime.now(), - tts_by_month_file_name : str = "TIMETRACKINGBYMONTH.md", - show_tts_by_month_md : bool = False, - save_tts_by_month_md : bool = False - ) -> None: - - self.years = years - self.yearly_targets = yearly_targets - self.excel_path = excel_path - self.excel_nrows = excel_books_nrows - self.software_project_names = software_project_names - self.software_project_names_by_spv = software_project_names_by_spv - self.tt_by_year_hashtag_years = tt_by_year_hashtag_years - - self.show_sessions_df = show_sessions_df - self.show_tt_by_year_df = show_tt_by_year_df - self.show_tt_by_year_month_df = show_tt_by_year_month_df - self.show_tt_by_year_month_spnv_df = show_tt_by_year_month_spnv_df - self.show_tt_by_year_spnv_df = show_tt_by_year_spnv_df - self.show_tt_by_spn_df = show_tt_by_spn_df - self.show_tt_by_spn_spv_df = show_tt_by_spn_spv_df - self.show_tt_by_year_hashtag = show_tt_by_year_hashtag - self.show_tt_by_hashtag = show_tt_by_hashtag - self.show_tts_by_month_df = show_tts_by_month_df - self.show_effort_status_df = show_effort_status_df - self.show_time_ranges_df = show_time_ranges_df - self.excel_skiprows = excel_skiprows - self.excel_tabname = excel_tabname - self.n_generic = n_generic - self.n_by_month = n_by_month - self.now = now - self.remove_untagged_from_de = remove_untagged_from_de - self.definitions = definitions - self.effort_status_n = effort_status_n - self.effort_status_is_correct = effort_status_is_correct - self.tts_by_month_update_future_values_to_empty = tts_by_month_update_future_values_to_empty - self.time_ranges_unknown_id = time_ranges_unknown_id - self.time_ranges_top_n = time_ranges_top_n - self.time_ranges_remove_unknown_id = time_ranges_remove_unknown_id - self.time_ranges_filter_by_top_n = time_ranges_filter_by_top_n - self.working_folder_path = working_folder_path - self.last_update = last_update - self.tts_by_month_file_name = tts_by_month_file_name - self.show_tts_by_month_md = show_tts_by_month_md - self.save_tts_by_month_md = save_tts_by_month_md class DefaultPathProvider(): '''Responsible for proviving the default path to the dataset.''' @@ -369,6 +236,56 @@ def get_all_software_project_names_by_spv(self) -> list[str]: ] return software_project_names_by_spv +class SettingBag(): + + '''Represents a collection of settings.''' + + # Non-Defaults + excel_nrows : int + software_project_names : list[str] + software_project_names_by_spv : list[str] + tt_by_year_hashtag_years : list[int] + + # Defaults + excel_path : str = field(default = DefaultPathProvider().get_default_time_tracking_path()) + excel_skiprows : int = field(default = 0) + excel_tabname : str = field(default = "Sessions") + years : list[int] = field(default = YearProvider().get_all_years()) + yearly_targets : list[YearlyTarget] = field(default = YearProvider().get_all_yearly_targets()) + now : datetime = field(default = datetime.now()) + + + + show_sessions_df : bool + show_tt_by_year_df : bool + show_tt_by_year_month_df : bool + show_tt_by_year_month_spnv_df : bool + show_tt_by_year_spnv_df : bool + show_tt_by_spn_df : bool + show_tt_by_spn_spv_df : bool + show_tt_by_year_hashtag : bool + show_tt_by_hashtag : bool + show_tts_by_month_df : bool + show_effort_status_df : bool + show_time_ranges_df : bool + + n_generic : int + n_by_month : int + remove_untagged_from_de : bool + definitions : dict[str, str] + effort_status_n : int + effort_status_is_correct : bool + tts_by_month_update_future_values_to_empty : bool + time_ranges_unknown_id : str + time_ranges_top_n : int + time_ranges_remove_unknown_id : bool + time_ranges_filter_by_top_n : bool + working_folder_path : str + last_update : datetime + tts_by_month_file_name : str + show_tts_by_month_md : bool + save_tts_by_month_md : bool + class TTDataFrameHelper(): @@ -1687,6 +1604,33 @@ def __create_tt_df(self) -> DataFrame: ) return tt_df + def __create_tts_by_month_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_month( + tt_df = tt_df, + years = self.__setting_bag.years, + now = self.__setting_bag.now + ) + + return tts_by_month_tpl + def __create_tts_by_year_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_year_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year( + tt_df = tt_df, + years = self.__setting_bag.years, + yearly_targets = self.__setting_bag.yearly_targets, + ) + + return tts_by_year_df + + + + + # MAIN if __name__ == "__main__": From 51966deb82432ac2556e0df1f94bbee3ef7d83bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 09:13:57 +0000 Subject: [PATCH 046/109] #40 TimeTrackingProcessor - Iteration 4. --- src/nwtimetracking.py | 65 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index c6f461e..22d696a 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -241,10 +241,7 @@ class SettingBag(): '''Represents a collection of settings.''' # Non-Defaults - excel_nrows : int - software_project_names : list[str] - software_project_names_by_spv : list[str] - tt_by_year_hashtag_years : list[int] + excel_nrows : int # Defaults excel_path : str = field(default = DefaultPathProvider().get_default_time_tracking_path()) @@ -253,9 +250,12 @@ class SettingBag(): years : list[int] = field(default = YearProvider().get_all_years()) yearly_targets : list[YearlyTarget] = field(default = YearProvider().get_all_yearly_targets()) now : datetime = field(default = datetime.now()) + software_project_names : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names()) + software_project_names_by_spv : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names_by_spv()) + tts_by_spn_remove_untagged : bool = field(default = True) - + tt_by_year_hashtag_years : list[int] show_sessions_df : bool show_tt_by_year_df : bool show_tt_by_year_month_df : bool @@ -271,7 +271,7 @@ class SettingBag(): n_generic : int n_by_month : int - remove_untagged_from_de : bool + definitions : dict[str, str] effort_status_n : int effort_status_is_correct : bool @@ -1626,9 +1626,62 @@ def __create_tts_by_year_df(self, tt_df : DataFrame) -> DataFrame: ) return tts_by_year_df + def __create_tts_by_year_month_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + tts_by_year_month_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_month( + tt_df = tt_df, + years = self.__setting_bag.years, + yearly_targets = self.__setting_bag.yearly_targets, + ) + return tts_by_year_month_df + def __create_tts_by_year_month_spnv_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_year_month_spnv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_month_spnv( + tt_df = tt_df, + years = self.__setting_bag.years, + software_project_names = self.__setting_bag.software_project_names, + ) + + return tts_by_year_month_spnv_df + def __create_tts_by_year_spnv_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_year_spnv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_spnv( + tt_df = tt_df, + years = self.__setting_bag.years, + software_project_names = self.__setting_bag.software_project_names, + ) + + return tts_by_year_spnv_df + def __create_tts_by_spn_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_spn_df : DataFrame = self.__component_bag.df_factory.create_tts_by_spn( + tt_df = tt_df, + years = self.__setting_bag.years, + software_project_names = self.__setting_bag.software_project_names, + remove_untagged = self.__setting_bag.tts_by_spn_remove_untagged + ) + + return tts_by_spn_df + def __create_tts_by_spn_spv_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_spn_spv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_spn_spv( + tt_df = tt_df, + years = self.__setting_bag.years, + software_project_names = self.__setting_bag.software_project_names + ) + return tts_by_spn_spv_df From 3c0e8c4583fc693469f8ae600480f077b45f386f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 09:22:25 +0000 Subject: [PATCH 047/109] #40 TimeTrackingProcessor - Iteration 5. --- src/nwtimetracking.ipynb | 16 +++++++------- src/nwtimetracking.py | 45 +++++++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index aae8a54..30effdb 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -377,7 +377,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -523,7 +523,7 @@ " sessions_df = sessions_df, \n", " years = setting_bag.years, \n", " software_project_names = setting_bag.software_project_names,\n", - " remove_untagged = setting_bag.remove_untagged_from_de\n", + " remove_untagged = setting_bag.tts_by_spn_remove_untagged\n", " )\n", " \n", " displayer.display(df = tt_by_spn_df, formatters = { \"%_DE\" : \"{:.2f}\", \"%_TE\" : \"{:.2f}\" }) \n", @@ -1422,7 +1422,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1457,17 +1457,17 @@ "if (setting_bag.show_effort_status_df):\n", " \n", " es_df : DataFrame = tt_manager.add_effort_status(sessions_df = sessions_df)\n", - " es_df = tt_manager.filter_by_is_correct(es_df = es_df, is_correct = setting_bag.effort_status_is_correct)\n", + " es_df = tt_manager.filter_by_is_correct(es_df = es_df, is_correct = setting_bag.tts_by_efs_is_correct)\n", " \n", " if es_df.empty:\n", " displayer.display(df = es_df)\n", " else:\n", - " displayer.display(df = es_df.head(n = setting_bag.effort_status_n))\n" + " displayer.display(df = es_df.head(n = setting_bag.tts_by_efs_n))\n" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1519,12 +1519,12 @@ "\n", " time_ranges_df : DataFrame = tt_manager.create_time_ranges_df(\n", " sessions_df = sessions_df, \n", - " unknown_id = setting_bag.time_ranges_unknown_id)\n", + " unknown_id = setting_bag.tts_by_tr_unknown_id)\n", " \n", " if setting_bag.show_time_ranges_df:\n", " time_ranges_df = tt_manager.remove_unknown_id(\n", " time_ranges_df = time_ranges_df, \n", - " unknown_id = setting_bag.time_ranges_unknown_id)\n", + " unknown_id = setting_bag.tts_by_tr_unknown_id)\n", " \n", " if setting_bag.time_ranges_filter_by_top_n:\n", " time_ranges_df = tt_manager.filter_by_top_n_occurrences(\n", diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 22d696a..fc7c22b 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -253,6 +253,9 @@ class SettingBag(): software_project_names : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names()) software_project_names_by_spv : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names_by_spv()) tts_by_spn_remove_untagged : bool = field(default = True) + tts_by_efs_is_correct : bool = field(default = False) + tts_by_efs_n : int = field(default = 25) + tts_by_tr_unknown_id : str = field(default = "Unknown") tt_by_year_hashtag_years : list[int] @@ -268,15 +271,10 @@ class SettingBag(): show_tts_by_month_df : bool show_effort_status_df : bool show_time_ranges_df : bool - n_generic : int n_by_month : int - definitions : dict[str, str] - effort_status_n : int - effort_status_is_correct : bool tts_by_month_update_future_values_to_empty : bool - time_ranges_unknown_id : str time_ranges_top_n : int time_ranges_remove_unknown_id : bool time_ranges_filter_by_top_n : bool @@ -1682,8 +1680,45 @@ def __create_tts_by_spn_spv_df(self, tt_df : DataFrame) -> DataFrame: ) return tts_by_spn_spv_df + def __create_tts_by_year_hashtag_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_year_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_hashtag( + tt_df = tt_df, + years = self.__setting_bag.years + ) + + return tts_by_year_hashtag_df + def __create_tts_by_hashtag_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_hashtag( + tt_df = tt_df + ) + return tts_by_hashtag_df + def __create_tts_by_efs_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_efs( + tt_df = tt_df, + is_correct = self.__setting_bag.tts_by_efs_is_correct + ) + + return tts_by_efs_tpl + def __create_tts_by_tr_df(self, tt_df : DataFrame) -> DataFrame: + + '''Creates the expected dataframe using tt_df and __setting_bag.''' + + tts_by_tr_df : DataFrame = self.__component_bag.df_factory.create_tts_by_tr( + tt_df = tt_df, + unknown_id = self.__setting_bag.tts_by_tr_unknown_id + ) + + return tts_by_tr_df # MAIN if __name__ == "__main__": From 5e9c2e1e93573b363648c020855c162845551778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 09:36:09 +0000 Subject: [PATCH 048/109] #40 TimeTrackingProcessor - Iteration 6. --- src/nwtimetracking.py | 31 ++++++++++++++++++++----------- tests/nwtimetrackingtests.py | 4 ++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index fc7c22b..75c26c3 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -13,7 +13,8 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta from enum import StrEnum -from pandas import DataFrame, Series +from numpy import uint +from pandas import DataFrame, Series, NamedAgg from typing import Any, Callable, Optional, Tuple, cast # LOCAL MODULES @@ -256,6 +257,9 @@ class SettingBag(): tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : int = field(default = 25) tts_by_tr_unknown_id : str = field(default = "Unknown") + tts_by_tr_remove_unknown_occurrences : bool = field(default = True) + tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) + tt_by_year_hashtag_years : list[int] @@ -275,9 +279,6 @@ class SettingBag(): n_by_month : int definitions : dict[str, str] tts_by_month_update_future_values_to_empty : bool - time_ranges_top_n : int - time_ranges_remove_unknown_id : bool - time_ranges_filter_by_top_n : bool working_folder_path : str last_update : datetime tts_by_month_file_name : str @@ -1141,7 +1142,7 @@ def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) - filtered_df = tts_by_efs_df.loc[condition] return filtered_df - def __remove_unknown_id(self, tts_by_tr_df : DataFrame, unknown_id : str) -> DataFrame: + def __remove_unknown_occurrences(self, tts_by_tr_df : DataFrame, unknown_id : str) -> DataFrame: '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' @@ -1150,11 +1151,11 @@ def __remove_unknown_id(self, tts_by_tr_df : DataFrame, unknown_id : str) -> Dat tts_by_tr_df.reset_index(drop = True, inplace = True) return tts_by_tr_df - def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : int, ascending : bool = False) -> DataFrame: + def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : uint) -> DataFrame: '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' - tts_by_tr_df.sort_values(by = TTCN.OCCURRENCES, ascending = [ascending], inplace = True) + tts_by_tr_df.sort_values(by = TTCN.OCCURRENCES, ascending = [True], inplace = True) tts_by_tr_df = tts_by_tr_df.iloc[0:n] tts_by_tr_df.reset_index(drop = True, inplace = True) @@ -1497,7 +1498,7 @@ def create_tts_by_efs(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataF tts_flt_df : DataFrame = self.__filter_by_is_correct(tts_by_efs_df = tts_df, is_correct = is_correct) return (tts_df, tts_flt_df) - def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: + def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_occurrences : bool, filter_by_top_n : Optional[uint]) -> DataFrame: ''' TimeRangeId Occurrences @@ -1516,10 +1517,16 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str) -> DataFrame: end_time = x[TTCN.ENDTIME], unknown_id = unknown_id), axis = 1) - tts_df = tts_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg( - count = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count")) + count : NamedAgg = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count") + tts_df = tts_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg(count = count) tts_df.rename(columns={"count" : TTCN.OCCURRENCES}, inplace = True) tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) + + if remove_unknown_occurrences: + tts_df = self.__remove_unknown_occurrences(tts_by_tr_df = tts_df, unknown_id = unknown_id) + + if filter_by_top_n is not None: + tts_df = self.__filter_by_top_n_occurrences(tts_by_tr_df = tts_df, n = filter_by_top_n) return tts_df class TTMarkdownFactory(): @@ -1715,7 +1722,9 @@ def __create_tts_by_tr_df(self, tt_df : DataFrame) -> DataFrame: tts_by_tr_df : DataFrame = self.__component_bag.df_factory.create_tts_by_tr( tt_df = tt_df, - unknown_id = self.__setting_bag.tts_by_tr_unknown_id + unknown_id = self.__setting_bag.tts_by_tr_unknown_id, + remove_unknown_occurrences = self.__setting_bag.tts_by_tr_remove_unknown_occurrences, + filter_by_top_n = self.__setting_bag.tts_by_tr_filter_by_top_n ) return tts_by_tr_df diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 84a6f9e..c63605b 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1165,7 +1165,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whencontainsunknownid(sel time_ranges_df.loc[len(time_ranges_df.index)] = [unknown_id, 3] # Act - actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_id(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_occurrences(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) @@ -1177,7 +1177,7 @@ def test_removeunknownid_shouldreturnexpecteddataframe_whendoesnotcontainunknown time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() # Act - actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_id(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_occurrences(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) # Assert assert_frame_equal(expected_df, actual_df) From 02681651ae3fcf71da65bb6b34811c8ceae70d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 09:59:10 +0000 Subject: [PATCH 049/109] #40 TimeTrackingProcessor - Iteration 7. --- src/nwtimetracking.py | 100 +++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 75c26c3..aed7230 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -23,7 +23,7 @@ # CONSTANTS class TTCN(StrEnum): - '''Collects all the column names used by ...''' + '''Collects all the column names used by TTDataFrameFactory.''' DATE = "Date" STARTTIME = "StartTime" @@ -62,6 +62,11 @@ class TTCN(StrEnum): ESMESSAGE = "ES_Message" TIMERANGEID = "TimeRangeId" OCCURRENCES = "Occurrences" +class TTID(StrEnum): + + '''Collects all the ids that identify the dataframes created by TTDataFrameFactory.''' + + TTSBYMONTH = "tts_by_month" # STATIC CLASSES class _MessageCollection(): @@ -137,6 +142,7 @@ class TTSummary(): '''Collects all the dataframes and markdowns.''' + # Dataframes tt_df : DataFrame tts_by_month_tpl : Tuple[DataFrame, DataFrame] tts_by_year_df : DataFrame @@ -149,6 +155,9 @@ class TTSummary(): tts_by_hashtag_df : DataFrame tts_by_efs_tpl : Tuple[DataFrame, DataFrame] tts_by_tr_df : DataFrame + definitions_df : DataFrame + + # Markdowns tts_by_month_md : str # CLASSES @@ -237,14 +246,17 @@ def get_all_software_project_names_by_spv(self) -> list[str]: ] return software_project_names_by_spv + +@dataclass(frozen=True) class SettingBag(): '''Represents a collection of settings.''' - # Non-Defaults - excel_nrows : int + # Without Defaults + excel_nrows : int - # Defaults + # With Defaults + working_folder_path : str = field(default = "/home/nwtimetracking/") excel_path : str = field(default = DefaultPathProvider().get_default_time_tracking_path()) excel_skiprows : int = field(default = 0) excel_tabname : str = field(default = "Sessions") @@ -260,32 +272,6 @@ class SettingBag(): tts_by_tr_remove_unknown_occurrences : bool = field(default = True) tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) - - - tt_by_year_hashtag_years : list[int] - show_sessions_df : bool - show_tt_by_year_df : bool - show_tt_by_year_month_df : bool - show_tt_by_year_month_spnv_df : bool - show_tt_by_year_spnv_df : bool - show_tt_by_spn_df : bool - show_tt_by_spn_spv_df : bool - show_tt_by_year_hashtag : bool - show_tt_by_hashtag : bool - show_tts_by_month_df : bool - show_effort_status_df : bool - show_time_ranges_df : bool - n_generic : int - n_by_month : int - definitions : dict[str, str] - tts_by_month_update_future_values_to_empty : bool - working_folder_path : str - last_update : datetime - tts_by_month_file_name : str - show_tts_by_month_md : bool - save_tts_by_month_md : bool - - class TTDataFrameHelper(): '''Collects helper functions for TTDataFrameFactory.''' @@ -1529,6 +1515,27 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_o tts_df = self.__filter_by_top_n_occurrences(tts_by_tr_df = tts_df, n = filter_by_top_n) return tts_df + def create_definitions(self) -> DataFrame: + + '''Creates a dataframe containing all the definitions in use in this application.''' + + columns : list[str] = ["Term", "Definition"] + + definitions : dict[str, str] = { + "DME": "Development Monthly Effort", + "TME": "Total Monthly Effort", + "DYE": "Development Yearly Effort", + "TYE": "Total Yearly Effort", + "DE": "Development Effort", + "TE": "Total Effort" + } + + definitions_df : DataFrame = DataFrame( + data = definitions.items(), + columns = columns + ) + + return definitions_df class TTMarkdownFactory(): '''Collects all the logic related to Markdown creation out of Time Tracking dataframes.''' @@ -1539,13 +1546,11 @@ def __init__(self, markdown_helper : MarkdownHelper) -> None: self.__markdown_helper = markdown_helper - def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : DataFrame) -> str: - - '''Creates the Markdown content for a "Time Tracking By Month" file out of the provided dataframe.''' + def create_tts_by_month_md(self, paragraph_title : str, last_update : datetime, tts_by_month_upd_df : DataFrame) -> str: - md_paragraph_title : str = "Time Tracking By Month" + '''Creates the expected Markdown content for the provided arguments.''' - markdown_header : str = self.__markdown_helper.get_markdown_header(last_update = last_update, paragraph_title = md_paragraph_title) + markdown_header : str = self.__markdown_helper.get_markdown_header(last_update = last_update, paragraph_title = paragraph_title) tts_by_month_upd_md : str = tts_by_month_upd_df.to_markdown(index = False) md_content : str = markdown_header @@ -1554,6 +1559,8 @@ def create_tts_by_month_md(self, last_update : datetime, tts_by_month_upd_df : D md_content += "\n" return md_content + + class ComponentBag(): '''Represents a collection of components.''' @@ -1563,7 +1570,6 @@ class ComponentBag(): df_factory : TTDataFrameFactory md_factory : TTMarkdownFactory logging_function : Callable[[str], None] - markdown_helper : MarkdownHelper def __init__( self, @@ -1597,6 +1603,16 @@ def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> No self.__component_bag = component_bag self.__setting_bag = setting_bag + def __extract_file_name_and_paragraph_title(self, id : RLID) -> Tuple[str, str]: + + '''Returns (file_name, paragraph_title) for the provided id or raise an Exception.''' + + for md_info in self.__setting_bag.md_infos: + if md_info.id == id: + return (md_info.file_name, md_info.paragraph_title) + + raise Exception(_MessageCollection.no_mdinfo_found(id = id)) + def __create_tt_df(self) -> DataFrame: '''Creates the expected dataframe using __setting_bag.''' @@ -1728,6 +1744,18 @@ def __create_tts_by_tr_df(self, tt_df : DataFrame) -> DataFrame: ) return tts_by_tr_df + def __create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame]) -> str: + + '''Creates the expected Markdown content using __setting_bag and the provided arguments.''' + + tts_by_month_md : str = self.__component_bag.md_factory.create_tts_by_month_md( + paragraph_title = self.__extract_file_name_and_paragraph_title(id = RLID.RL)[1], + last_update = self.__setting_bag.md_last_update, + tts_by_month_upd_df = tts_by_month_tpl[1] + ) + + return tts_by_month_md + # MAIN if __name__ == "__main__": From db573de97cc904737fc10ef9a18a0e115e41deec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 10:34:31 +0000 Subject: [PATCH 050/109] #40 TimeTrackingProcessor - Iteration 8. --- src/nwtimetracking.ipynb | 4 +- src/nwtimetracking.py | 117 +++++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 30effdb..93c1de1 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -1526,10 +1526,10 @@ " time_ranges_df = time_ranges_df, \n", " unknown_id = setting_bag.tts_by_tr_unknown_id)\n", " \n", - " if setting_bag.time_ranges_filter_by_top_n:\n", + " if setting_bag.tts_by_tr_filter_by_top_n:\n", " time_ranges_df = tt_manager.filter_by_top_n_occurrences(\n", " time_ranges_df = time_ranges_df, \n", - " n = setting_bag.time_ranges_top_n)\n", + " n = setting_bag.tts_by_tr_top_n)\n", "\n", " displayer.display(df = time_ranges_df)\n" ] diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index aed7230..2fe0b24 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -83,8 +83,7 @@ def effort_status_mismatching_effort(idx : int, start_time_str : str, end_time_s message : str = "The provided row contains a mismatching effort " message += f"(idx: '{idx}', start_time: '{start_time_str}', end_time: '{end_time_str}', actual_effort: '{actual_str}', expected_effort: '{expected_str}')." - return message - + return message @staticmethod def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_time_str : str, effort_str : str): @@ -96,12 +95,10 @@ def effort_status_not_possible_to_create(idx : int, start_time_str : str, end_ti message : str = "It has not been possible to create an EffortStatus for the provided parameters " message += f"(idx: '{idx}', start_time_str: '{start_time_str}', end_time_str: '{end_time_str}', effort_str: '{effort_str}')." - return message - + return message @staticmethod def effort_status_not_among_expected_time_values(time : str) -> str: - return f"The provided time ('{time}') is not among the expected time values." - + return f"The provided time ('{time}') is not among the expected time values." @staticmethod def starttime_endtime_are_empty() -> str: return "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." @@ -109,6 +106,10 @@ def starttime_endtime_are_empty() -> str: def effort_is_correct() -> str: return "The effort is correct." + @staticmethod + def no_mdinfo_found(id : TTID) -> str: + return f"No MDInfo object found for id='{id}'." + # DTOs @dataclass(frozen=True) class YearlyTarget(): @@ -138,6 +139,14 @@ class EffortStatus(): is_correct : bool message : str @dataclass(frozen = True) +class MDInfo(): + + '''Represents a collection of information related to a Markdown file.''' + + id : TTID + file_name : str + paragraph_title : str +@dataclass(frozen = True) class TTSummary(): '''Collects all the dataframes and markdowns.''' @@ -246,6 +255,19 @@ def get_all_software_project_names_by_spv(self) -> list[str]: ] return software_project_names_by_spv +class MDInfoProvider(): + + '''Collects all the logic related to the retrieval of MDInfo objects.''' + + def get_all(self) -> list[MDInfo]: + + '''Returns a list of MDInfo objects.''' + + md_infos : list[MDInfo] = [ + MDInfo(id = TTID.TTSBYMONTH, file_name = "TIMETRACKINGBYMONTH.md", paragraph_title = "Time Tracking By Month") + ] + + return md_infos @dataclass(frozen=True) class SettingBag(): @@ -271,6 +293,8 @@ class SettingBag(): tts_by_tr_unknown_id : str = field(default = "Unknown") tts_by_tr_remove_unknown_occurrences : bool = field(default = True) tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) + md_infos : list[MDInfo] = field(default = MDInfoProvider().get_all()) + md_last_update : datetime = field(default = datetime.now()) class TTDataFrameHelper(): @@ -1560,35 +1584,16 @@ def create_tts_by_month_md(self, paragraph_title : str, last_update : datetime, return md_content - +@dataclass(frozen=True) class ComponentBag(): '''Represents a collection of components.''' - file_path_manager : FilePathManager - file_manager : FileManager - df_factory : TTDataFrameFactory - md_factory : TTMarkdownFactory - logging_function : Callable[[str], None] - - def __init__( - self, - file_path_manager : FilePathManager = FilePathManager(), - file_manager : FileManager = FileManager( - file_path_manager = FilePathManager()), - df_factory : TTDataFrameFactory = TTDataFrameFactory( - df_helper = TTDataFrameHelper()), - md_factory : TTMarkdownFactory = TTMarkdownFactory( - markdown_helper = MarkdownHelper( - formatter = Formatter()) - ), - logging_function : Callable[[str], None] = LambdaProvider().get_default_logging_function()) -> None: - - self.file_path_manager = file_path_manager - self.file_manager = file_manager - self.df_factory = df_factory - self.md_factory = md_factory - self.logging_function = logging_function + file_path_manager : FilePathManager = field(default = FilePathManager()) + file_manager : FileManager = field(default = FileManager(file_path_manager = FilePathManager())) + df_factory : TTDataFrameFactory = field(default = TTDataFrameFactory(df_helper = TTDataFrameHelper())) + md_factory : TTMarkdownFactory = field(default = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter()))) + logging_function : Callable[[str], None] = field(default = LambdaProvider().get_default_logging_function()) class TimeTrackingProcessor(): @@ -1603,7 +1608,7 @@ def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> No self.__component_bag = component_bag self.__setting_bag = setting_bag - def __extract_file_name_and_paragraph_title(self, id : RLID) -> Tuple[str, str]: + def __extract_file_name_and_paragraph_title(self, id : TTID) -> Tuple[str, str]: '''Returns (file_name, paragraph_title) for the provided id or raise an Exception.''' @@ -1713,15 +1718,6 @@ def __create_tts_by_year_hashtag_df(self, tt_df : DataFrame) -> DataFrame: ) return tts_by_year_hashtag_df - def __create_tts_by_hashtag_df(self, tt_df : DataFrame) -> DataFrame: - - '''Creates the expected dataframe using tt_df and __setting_bag.''' - - tts_by_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_hashtag( - tt_df = tt_df - ) - - return tts_by_hashtag_df def __create_tts_by_efs_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: '''Creates the expected dataframe using tt_df and __setting_bag.''' @@ -1749,13 +1745,50 @@ def __create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame '''Creates the expected Markdown content using __setting_bag and the provided arguments.''' tts_by_month_md : str = self.__component_bag.md_factory.create_tts_by_month_md( - paragraph_title = self.__extract_file_name_and_paragraph_title(id = RLID.RL)[1], + paragraph_title = self.__extract_file_name_and_paragraph_title(id = TTID.TTSBYMONTH)[1], last_update = self.__setting_bag.md_last_update, tts_by_month_upd_df = tts_by_month_tpl[1] ) return tts_by_month_md + def initialize(self) -> None: + + '''Creates a RLSummary object and assign it to __rl_summary.''' + + tt_df : DataFrame = self.__create_tt_df() + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_month_tpl(tt_df = tt_df) + tts_by_year_df : DataFrame = self.__create_tts_by_year_df(tt_df = tt_df) + tts_by_year_month_df : DataFrame = self.__create_tts_by_year_month_df(tt_df = tt_df) + tts_by_year_month_spnv_df : DataFrame = self.__create_tts_by_year_month_spnv_df(tt_df = tt_df) + tts_by_year_spnv_df : DataFrame = self.__create_tts_by_year_spnv_df(tt_df = tt_df) + tts_by_spn_df : DataFrame = self.__create_tts_by_spn_df(tt_df = tt_df) + tts_by_spn_spv_df : DataFrame = self.__create_tts_by_spn_spv_df(tt_df = tt_df) + tts_by_year_hashtag_df : DataFrame = self.__create_tts_by_year_hashtag_df(tt_df = tt_df) + tts_by_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_hashtag(tt_df = tt_df) + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_efs_tpl(tt_df = tt_df) + tts_by_tr_df : DataFrame = self.__create_tts_by_tr_df(tt_df = tt_df) + definitions_df : DataFrame = self.__component_bag.df_factory.create_definitions() + + tts_by_month_md : str = self.__create_tts_by_month_md(tts_by_month_tpl = tts_by_month_tpl) + + self.__tt_summary = TTSummary( + tt_df = tt_df, + tts_by_month_tpl = tts_by_month_tpl, + tts_by_year_df = tts_by_year_df, + tts_by_year_month_df = tts_by_year_month_df, + tts_by_year_month_spnv_df = tts_by_year_month_spnv_df, + tts_by_year_spnv_df = tts_by_year_spnv_df, + tts_by_spn_df = tts_by_spn_df, + tts_by_spn_spv_df = tts_by_spn_spv_df, + tts_by_year_hashtag_df = tts_by_year_hashtag_df, + tts_by_hashtag_df = tts_by_hashtag_df, + tts_by_efs_tpl = tts_by_efs_tpl, + tts_by_tr_df = tts_by_tr_df, + definitions_df = definitions_df, + tts_by_month_md = tts_by_month_md + ) + # MAIN if __name__ == "__main__": From a0c68b7cfbfadbd815f14f2ce4c9dfe45ecbd1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 12:14:17 +0000 Subject: [PATCH 051/109] #40 TimeTrackingProcessor - Iteration 9. --- src/nwtimetracking.ipynb | 5 +- src/nwtimetracking.py | 230 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 7 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 93c1de1..f43fce3 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -45,10 +45,9 @@ "metadata": {}, "outputs": [], "source": [ - "from nwtimetracking import SettingBag, DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, TimeTrackingManager\n", + "from nwtimetracking import SettingBag, DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, TimeTrackingProcessor\n", "from nwtimetracking import ComponentBag, TTMarkdownFactory\n", - "from nwpackageversions import LanguageChecker, RequirementChecker, RequirementSummary\n", - "from nwshared import Displayer" + "from nwpackageversions import LanguageChecker, RequirementChecker, RequirementSummary" ] }, { diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 2fe0b24..8e5caa4 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -15,10 +15,10 @@ from enum import StrEnum from numpy import uint from pandas import DataFrame, Series, NamedAgg -from typing import Any, Callable, Optional, Tuple, cast +from typing import Any, Callable, Literal, Optional, Tuple, cast # LOCAL MODULES -from nwshared import Formatter, FilePathManager, FileManager, LambdaProvider, MarkdownHelper +from nwshared import Formatter, FilePathManager, FileManager, LambdaProvider, MarkdownHelper, Displayer # CONSTANTS class TTCN(StrEnum): @@ -109,6 +109,12 @@ def effort_is_correct() -> str: @staticmethod def no_mdinfo_found(id : TTID) -> str: return f"No MDInfo object found for id='{id}'." + @staticmethod + def please_run_initialize_first() -> str: + return "Please run the 'initialize' method first." + @staticmethod + def this_content_successfully_saved_as(id : TTID, file_path : str) -> str: + return f"This content (id: '{id}') has been successfully saved as '{file_path}'." # DTOs @dataclass(frozen=True) @@ -160,8 +166,8 @@ class TTSummary(): tts_by_year_spnv_df : DataFrame tts_by_spn_df : DataFrame tts_by_spn_spv_df : DataFrame - tts_by_year_hashtag_df : DataFrame tts_by_hashtag_df : DataFrame + tts_by_hashtag_year_df : DataFrame tts_by_efs_tpl : Tuple[DataFrame, DataFrame] tts_by_tr_df : DataFrame definitions_df : DataFrame @@ -275,6 +281,18 @@ class SettingBag(): '''Represents a collection of settings.''' # Without Defaults + options_tt : list[Literal["display"]] + options_tts_by_month : list[Literal["display", "save"]] + options_tts_by_year : list[Literal["display"]] + options_tts_by_year_month : list[Literal["display"]] + options_tts_by_year_month_spnv : list[Literal["display"]] + options_tts_by_spn : list[Literal["display"]] + options_tts_by_spn_spv : list[Literal["display"]] + options_tts_by_hashtag : list[Literal["display"]] + options_tts_by_hashtag_year : list[Literal["display"]] + options_tts_by_efs : list[Literal["display"]] + options_tts_by_tr : list[Literal["display"]] + options_definitions : list[Literal["display"]] excel_nrows : int # With Defaults @@ -1594,6 +1612,7 @@ class ComponentBag(): df_factory : TTDataFrameFactory = field(default = TTDataFrameFactory(df_helper = TTDataFrameHelper())) md_factory : TTMarkdownFactory = field(default = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter()))) logging_function : Callable[[str], None] = field(default = LambdaProvider().get_default_logging_function()) + displayer : Displayer = field(default = Displayer()) class TimeTrackingProcessor(): @@ -1617,6 +1636,25 @@ def __extract_file_name_and_paragraph_title(self, id : TTID) -> Tuple[str, str]: return (md_info.file_name, md_info.paragraph_title) raise Exception(_MessageCollection.no_mdinfo_found(id = id)) + def __validate_summary(self) -> None: + + '''Raises an exception if __tt_summary is None.''' + + if not hasattr(self, '_TimeTrackingProcessor__tt_summary'): + raise Exception(_MessageCollection.please_run_initialize_first()) + def __save_and_log(self, id : TTID, content : str) -> None: + + '''Creates the provided Markdown content using __setting_bag.''' + + file_path : str = self.__component_bag.file_path_manager.create_file_path( + folder_path = self.__setting_bag.working_folder_path, + file_name = self.__extract_file_name_and_paragraph_title(id = id)[0] + ) + + self.__component_bag.file_manager.save_content(content = content, file_path = file_path) + + message : str = _MessageCollection.this_content_successfully_saved_as(id = id, file_path = file_path) + self.__component_bag.logging_function(message) def __create_tt_df(self) -> DataFrame: @@ -1781,14 +1819,198 @@ def initialize(self) -> None: tts_by_year_spnv_df = tts_by_year_spnv_df, tts_by_spn_df = tts_by_spn_df, tts_by_spn_spv_df = tts_by_spn_spv_df, - tts_by_year_hashtag_df = tts_by_year_hashtag_df, + tts_by_hashtag_year_df = tts_by_year_hashtag_df, tts_by_hashtag_df = tts_by_hashtag_df, tts_by_efs_tpl = tts_by_efs_tpl, tts_by_tr_df = tts_by_tr_df, definitions_df = definitions_df, tts_by_month_md = tts_by_month_md ) + def process_tt(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tt. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tt + df : DataFrame = self.__tt_summary.tt_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_month(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_month. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_month + df : DataFrame = self.__tt_summary.tts_by_month_tpl[1] + content : str = self.__tt_summary.tts_by_month_md + id : TTID = TTID.TTSBYMONTH + + if "display" in options: + self.__component_bag.displayer.display(df = df) + + if "save" in options: + self.__save_and_log(id = id, content = content) + def process_tts_by_year(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_year. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_year + df : DataFrame = self.__tt_summary.tts_by_year_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_year_month(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_year_month. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_year_month + df : DataFrame = self.__tt_summary.tts_by_year_month_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_year_month_spnv(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_year_month_spnv. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_year_month_spnv + df : DataFrame = self.__tt_summary.tts_by_year_month_spnv_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_spn(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_spn. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_spn + df : DataFrame = self.__tt_summary.tts_by_spn_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_spn_spv(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_spn_spv. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_spn_spv + df : DataFrame = self.__tt_summary.tts_by_spn_spv_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_hashtag(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_hashtag. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_hashtag + df : DataFrame = self.__tt_summary.tts_by_hashtag_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_hashtag_year(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_hashtag_year. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_hashtag_year + df : DataFrame = self.__tt_summary.tts_by_hashtag_year_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_efs(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_efs. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_efs + df : DataFrame = self.__tt_summary.tts_by_efs_tpl[1] + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_tts_by_tr(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_tr. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_tr + df : DataFrame = self.__tt_summary.tts_by_tr_df + + if "display" in options: + self.__component_bag.displayer.display(df = df) + def process_definitions(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_definitions. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_definitions + df : DataFrame = self.__tt_summary.definitions_df + if "display" in options: + self.__component_bag.displayer.display(df = df) # MAIN if __name__ == "__main__": From 25fc808f9b7e23496a0e412378e17b7a853ddfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 13:29:06 +0000 Subject: [PATCH 052/109] #40 TimeTrackingProcessor - Iteration 10. --- src/nwtimetracking.ipynb | 1445 +++----------------------------------- src/nwtimetracking.py | 38 +- 2 files changed, 147 insertions(+), 1336 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index f43fce3..406f908 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -59,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -110,20 +110,24 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "setting_bag : SettingBag = SettingBag(\n", - " years = YearProvider().get_all_years(),\n", - " yearly_targets = YearProvider().get_all_yearly_targets(),\n", - " excel_path = DefaultPathProvider().get_default_time_tracking_path(),\n", - " excel_books_nrows = 1301,\n", - " software_project_names = SoftwareProjectNameProvider().get_all_software_project_names(),\n", - " software_project_names_by_spv = SoftwareProjectNameProvider().get_all_software_project_names_by_spv(), \n", - " tt_by_year_hashtag_years = [2024],\n", - " show_tts_by_month_md = False,\n", - " save_tts_by_month_md = True\n", + " excel_nrows = 1301,\n", + " options_tt = [],\n", + " options_tts_by_month = [\"display\", \"save\"],\n", + " options_tts_by_year = [\"display\"],\n", + " options_tts_by_year_month = [\"display\"],\n", + " options_tts_by_year_month_spnv = [\"display\"],\n", + " options_tts_by_spn = [\"display\"],\n", + " options_tts_by_spn_spv = [\"display\"],\n", + " options_tts_by_hashtag = [\"display\"],\n", + " options_tts_by_hashtag_year = [\"display\"],\n", + " options_tts_by_efs = [\"display\"],\n", + " options_tts_by_tr = [\"display\"],\n", + " options_definitions = [\"display\"]\n", ")\n" ] }, @@ -137,21 +141,7 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "displayer : Displayer = Displayer()\n", - "tt_manager : TimeTrackingManager = TimeTrackingManager()\n", - "sessions_df : DataFrame = tt_manager.get_sessions_dataset(setting_bag = setting_bag)\n", - "\n", - "if setting_bag.show_sessions_df:\n", - " displayer.display(df = sessions_df.tail(n = setting_bag.n_generic))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -159,92 +149,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -252,1292 +237,99 @@ } ], "source": [ - "if setting_bag.show_tt_by_year_df:\n", - " \n", - " tt_by_year_df : DataFrame = tt_manager.get_tt_by_year(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " yearly_targets = setting_bag.yearly_targets)\n", - " \n", - " displayer.display(df = tt_by_year_df)\n" + "tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = ComponentBag(), setting_bag = setting_bag)\n", + "tt_processor.initialize()\n", + "tt_processor.process_tt()" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
YearMonthEffortYearlyTotalToTarget
2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tt_by_year_month_df:\n", - "\n", - " tt_by_year_month_df : DataFrame = tt_manager.get_tt_by_year_month(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " yearly_targets = setting_bag.yearly_targets)\n", - " \n", - " displayer.display(df = tt_by_year_month_df.tail(n = setting_bag.n_generic))\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "if setting_bag.show_tt_by_year_month_spnv_df:\n", - "\n", - " tt_by_year_month_spnv_df : DataFrame = tt_manager.get_tt_by_year_month_spnv(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " software_project_names = setting_bag.software_project_names)\n", - " \n", - " displayer.display(df = tt_by_year_month_spnv_df)\n", - " tt_manager.try_print_definitions(df = tt_by_year_month_spnv_df, definitions = setting_bag.definitions)\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "if setting_bag.show_tt_by_year_spnv_df:\n", - "\n", - " tt_by_year_spnv_df : DataFrame = tt_manager.get_tt_by_year_spnv(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " software_project_names = setting_bag.software_project_names)\n", - " \n", - " displayer.display(df = tt_by_year_spnv_df)\n", - " tt_manager.try_print_definitions(df = tt_by_year_spnv_df, definitions = setting_bag.definitions)\n" + "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
HashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DE: Development Effort\n", - "TE: Total Effort\n" - ] - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tt_by_spn_df:\n", - "\n", - " tt_by_spn_df : DataFrame = tt_manager.get_tt_by_spn(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " software_project_names = setting_bag.software_project_names,\n", - " remove_untagged = setting_bag.tts_by_spn_remove_untagged\n", - " )\n", - " \n", - " displayer.display(df = tt_by_spn_df, formatters = { \"%_DE\" : \"{:.2f}\", \"%_TE\" : \"{:.2f}\" }) \n", - " tt_manager.try_print_definitions(df = tt_by_spn_df, definitions = setting_bag.definitions)\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ProjectNameProjectVersionEffort
NW.MarkdownTables1.0.015h 15m
NW.MarkdownTables1.0.102h 30m
NW.MarkdownTables3.0.003h 00m
NW.NGramTextClassification1.0.073h 15m
NW.NGramTextClassification1.1.007h 30m
NW.NGramTextClassification2.0.015h 30m
NW.NGramTextClassification3.0.028h 00m
NW.NGramTextClassification3.5.039h 45m
NW.NGramTextClassification3.6.017h 30m
NW.NGramTextClassification3.7.008h 00m
NW.NGramTextClassification4.0.014h 30m
NW.NGramTextClassification4.1.002h 15m
NW.NGramTextClassification4.2.001h 15m
NW.Shared.Files1.0.005h 30m
NW.Shared.Serialization1.0.004h 15m
NW.Shared.Validation1.0.002h 45m
NW.UnivariateForecasting1.0.0116h 15m
NW.UnivariateForecasting1.0.107h 30m
NW.UnivariateForecasting1.1.004h 00m
NW.UnivariateForecasting1.1.107h 30m
NW.UnivariateForecasting2.0.006h 30m
NW.UnivariateForecasting2.0.102h 00m
NW.UnivariateForecasting2.5.016h 30m
NW.UnivariateForecasting3.0.046h 00m
NW.UnivariateForecasting4.1.001h 00m
NW.UnivariateForecasting4.2.000h 45m
nwpackageversions1.0.034h 15m
nwpackageversions1.1.002h 30m
nwpackageversions1.2.003h 00m
nwpackageversions1.6.012h 15m
nwpackageversions1.8.000h 45m
nwreadinglist1.0.045h 15m
nwreadinglist1.5.016h 15m
nwreadinglist1.6.005h 00m
nwreadinglist2.0.001h 30m
nwreadinglist2.1.002h 00m
nwreadinglist2.2.006h 45m
nwreadinglist3.0.010h 00m
nwreadinglist3.1.005h 15m
nwreadinglist3.2.002h 00m
nwreadinglist3.3.000h 30m
nwreadinglist3.4.003h 15m
nwreadinglist3.5.006h 30m
nwreadinglist3.6.001h 00m
nwreadinglist3.7.007h 45m
nwreadinglist3.8.001h 30m
nwreadinglist4.0.028h 15m
nwreadinglist4.1.000h 45m
nwshared1.1.008h 00m
nwshared1.2.001h 30m
nwshared1.3.007h 00m
nwshared1.4.006h 30m
nwshared1.5.001h 00m
nwshared1.6.000h 45m
nwshared1.7.003h 00m
nwshared1.7.101h 00m
nwshared1.8.013h 30m
nwtimetracking1.0.031h 30m
nwtimetracking1.1.010h 30m
nwtimetracking1.2.003h 30m
nwtimetracking1.3.009h 45m
nwtimetracking2.0.002h 30m
nwtimetracking2.2.007h 45m
nwtimetracking3.0.003h 30m
nwtimetracking3.2.002h 00m
nwtimetracking3.3.001h 00m
nwtimetracking3.4.001h 00m
nwtimetracking3.5.004h 00m
nwtimetracking3.7.006h 00m
nwtimetracking3.8.001h 00m
nwtimetracking3.9.000h 45m
nwtraderaanalytics1.0.062h 00m
nwtraderaanalytics2.0.012h 00m
nwtraderaanalytics3.0.052h 15m
nwtraderaanalytics4.0.064h 15m
nwtraderaanalytics4.1.001h 45m
nwtraderaanalytics4.2.025h 00m
nwtraderaanalytics4.3.000h 30m
nwtraderaanalytics4.4.002h 00m
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tt_by_spn_df:\n", - "\n", - " tt_by_spn_spv_df : DataFrame = tt_manager.get_tt_by_spn_spv(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.years, \n", - " software_project_names = setting_bag.software_project_names_by_spv)\n", - " \n", - " displayer.display(df = tt_by_spn_spv_df)\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
YearHashtagEffort
2024#csharp35h 15m
2024#maintenance108h 00m
2024#python398h 45m
2024#studying124h 45m
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tt_by_year_hashtag:\n", - "\n", - " tt_by_year_hashtag_df : DataFrame = tt_manager.get_tt_by_year_hashtag(\n", - " sessions_df = sessions_df, \n", - " years = setting_bag.tt_by_year_hashtag_years)\n", - " \n", - " displayer.display(df = tt_by_year_hashtag_df)\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
HashtagEffortEffort%
#untagged2548h 45m48.98
#csharp1116h 45m21.46
#python611h 45m11.76
#studying419h 30m8.06
#maintenance333h 45m6.41
#powershell154h 00m2.96
#overtime19h 00m0.37
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tt_by_hashtag:\n", - " tt_by_hashtag_df : DataFrame = tt_manager.get_tt_by_hashtag(sessions_df = sessions_df)\n", - " displayer.display(df = tt_by_hashtag_df, formatters = { \"Effort%\" : \"{:.2f}\" })\n" + "\n" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if setting_bag.show_tts_by_month_df:\n", - " \n", - " tts_by_month_df : DataFrame = tt_manager.get_tts_by_month(sessions_df = sessions_df, years = setting_bag.years)\n", - " tts_by_month_upd_df : DataFrame = tt_manager.update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = setting_bag.now)\n", - "\n", - " if setting_bag.tts_by_month_update_future_values_to_empty:\n", - " displayer.display(df = tts_by_month_upd_df)\n", - " else:\n", - " displayer.display(df = tts_by_month_df)\n" + "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "if (setting_bag.show_effort_status_df):\n", - " \n", - " es_df : DataFrame = tt_manager.add_effort_status(sessions_df = sessions_df)\n", - " es_df = tt_manager.filter_by_is_correct(es_df = es_df, is_correct = setting_bag.tts_by_efs_is_correct)\n", - " \n", - " if es_df.empty:\n", - " displayer.display(df = es_df)\n", - " else:\n", - " displayer.display(df = es_df.head(n = setting_bag.tts_by_efs_n))\n" + "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TimeRangeIdOccurrences
08:00-08:4543
08:00-08:3025
18:00-20:0021
17:30-18:0018
19:00-20:0017
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "if setting_bag.show_time_ranges_df:\n", - "\n", - " time_ranges_df : DataFrame = tt_manager.create_time_ranges_df(\n", - " sessions_df = sessions_df, \n", - " unknown_id = setting_bag.tts_by_tr_unknown_id)\n", - " \n", - " if setting_bag.show_time_ranges_df:\n", - " time_ranges_df = tt_manager.remove_unknown_id(\n", - " time_ranges_df = time_ranges_df, \n", - " unknown_id = setting_bag.tts_by_tr_unknown_id)\n", - " \n", - " if setting_bag.tts_by_tr_filter_by_top_n:\n", - " time_ranges_df = tt_manager.filter_by_top_n_occurrences(\n", - " time_ranges_df = time_ranges_df, \n", - " n = setting_bag.tts_by_tr_top_n)\n", - "\n", - " displayer.display(df = time_ranges_df)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, + "outputs": [], "source": [ - "#### Main : Markdown" + "\n" ] }, { @@ -1546,10 +338,7 @@ "metadata": {}, "outputs": [], "source": [ - "component_bag : ComponentBag = ComponentBag()\n", - "markdown_processor : TTMarkdownFactory = TTMarkdownFactory(component_bag = component_bag, setting_bag = setting_bag)\n", - "\n", - "markdown_processor.process_tts_by_month_md(tts_by_month_upd_df = tts_by_month_upd_df)" + "\n" ] } ], diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 8e5caa4..a991262 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -300,18 +300,21 @@ class SettingBag(): excel_path : str = field(default = DefaultPathProvider().get_default_time_tracking_path()) excel_skiprows : int = field(default = 0) excel_tabname : str = field(default = "Sessions") - years : list[int] = field(default = YearProvider().get_all_years()) - yearly_targets : list[YearlyTarget] = field(default = YearProvider().get_all_yearly_targets()) + years : list[int] = field(default_factory = lambda : YearProvider().get_all_years()) + yearly_targets : list[YearlyTarget] = field(default_factory = lambda : YearProvider().get_all_yearly_targets()) now : datetime = field(default = datetime.now()) - software_project_names : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names()) - software_project_names_by_spv : list[str] = field(default = SoftwareProjectNameProvider().get_all_software_project_names_by_spv()) + software_project_names : list[str] = field(default_factory = lambda : SoftwareProjectNameProvider().get_all_software_project_names()) + software_project_names_by_spv : list[str] = field(default_factory = lambda : SoftwareProjectNameProvider().get_all_software_project_names_by_spv()) + tt_head_n : Optional[uint] = field(default = uint(5)) + tt_display_head_n_with_tail : bool = field(default = True) + tt_hide_index : bool = field(default = True) tts_by_spn_remove_untagged : bool = field(default = True) tts_by_efs_is_correct : bool = field(default = False) - tts_by_efs_n : int = field(default = 25) + tts_by_efs_n : uint = field(default = uint(25)) tts_by_tr_unknown_id : str = field(default = "Unknown") tts_by_tr_remove_unknown_occurrences : bool = field(default = True) tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) - md_infos : list[MDInfo] = field(default = MDInfoProvider().get_all()) + md_infos : list[MDInfo] = field(default_factory = lambda : MDInfoProvider().get_all()) md_last_update : datetime = field(default = datetime.now()) class TTDataFrameHelper(): @@ -1790,6 +1793,24 @@ def __create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame return tts_by_month_md + def __optimize_for_display(self, df : DataFrame, head_n : Optional[uint], display_head_n_with_tail : bool) -> DataFrame: + + '''Prepares df for display().''' + + if head_n is None: + return df + elif head_n is not None and display_head_n_with_tail == True: + return df.tail(n = int(head_n)) + else: + return df.head(n = int(head_n)) + def __optimize_tt_for_display(self, tt_df : DataFrame) -> DataFrame: + + return self.__optimize_for_display( + df = tt_df, + head_n = self.__setting_bag.tt_head_n, + display_head_n_with_tail = self.__setting_bag.tt_display_head_n_with_tail + ) + def initialize(self) -> None: '''Creates a RLSummary object and assign it to __rl_summary.''' @@ -1837,10 +1858,11 @@ def process_tt(self) -> None: self.__validate_summary() options : list = self.__setting_bag.options_tt - df : DataFrame = self.__tt_summary.tt_df + df : DataFrame = self.__optimize_tt_for_display(tt_df = self.__tt_summary.tt_df) + hide_index : bool = self.__setting_bag.tt_hide_index if "display" in options: - self.__component_bag.displayer.display(df = df) + self.__component_bag.displayer.display(df = df, hide_index = hide_index) def process_tts_by_month(self) -> None: ''' From 5927851fa0710a3da7264dee0677aa2d325bf6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 13:45:38 +0000 Subject: [PATCH 053/109] #40 TimeTrackingProcessor - Iteration 11. --- src/nwtimetracking.ipynb | 455 +++++++++++++++++++++++++++++++++------ 1 file changed, 384 insertions(+), 71 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 406f908..6c0409f 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -59,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -110,13 +110,13 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "setting_bag : SettingBag = SettingBag(\n", " excel_nrows = 1301,\n", - " options_tt = [],\n", + " options_tt = [\"display\"],\n", " options_tts_by_month = [\"display\", \"save\"],\n", " options_tts_by_year = [\"display\"],\n", " options_tts_by_year_month = [\"display\"],\n", @@ -141,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -149,87 +149,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue2024122024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue2024122024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue2024122024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -244,11 +244,324 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This content (id: 'tts_by_month') has been successfully saved as '/home/nwtimetracking/TIMETRACKINGBYMONTH.md'.\n" + ] + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_month()\n" ] }, { From bc29c547a60209b98c6c6ef9d3f53f1dc91ef15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 15:36:45 +0000 Subject: [PATCH 054/109] #40 TimeTrackingProcessor - Iteration 12. --- src/nwtimetracking.ipynb | 965 ++++++++++++++++++++++++--------------- src/nwtimetracking.py | 83 +++- 2 files changed, 661 insertions(+), 387 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 6c0409f..02fac24 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,9 +23,32 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 32, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", + " superreload(m, reload, self.old_objects)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 500, in superreload\n", + " update_generic(old_obj, new_obj)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", + " update(a, b)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 349, in update_class\n", + " if update_generic(old_obj, new_obj):\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", + " update(a, b)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 309, in update_function\n", + " setattr(old, name, getattr(new, name))\n", + "ValueError: __init__() requires a code object with 7 free vars, not 8\n", + "]\n" + ] + } + ], "source": [ "from pandas import DataFrame\n", "from typing import Optional" @@ -41,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -59,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -110,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -141,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -149,87 +172,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -244,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -252,301 +275,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -566,20 +589,228 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
YearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_year()\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
YearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_year_month()\n" ] }, { diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index a991262..9e3cd48 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -161,7 +161,7 @@ class TTSummary(): tt_df : DataFrame tts_by_month_tpl : Tuple[DataFrame, DataFrame] tts_by_year_df : DataFrame - tts_by_year_month_df : DataFrame + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] tts_by_year_month_spnv_df : DataFrame tts_by_year_spnv_df : DataFrame tts_by_spn_df : DataFrame @@ -201,6 +201,14 @@ def get_all_years(self) -> list[int]: years : list[int] = [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024] return years + def get_most_recent_x_years(self, x : uint) -> list[int]: + + '''Returns a list of years.''' + + years : list[int] = self.get_all_years() + years = years[(len(years) - int(x)):] + + return years def get_all_yearly_targets(self) -> list[YearlyTarget]: '''Returns a list of years.''' @@ -274,7 +282,6 @@ def get_all(self) -> list[MDInfo]: ] return md_infos - @dataclass(frozen=True) class SettingBag(): @@ -308,6 +315,7 @@ class SettingBag(): tt_head_n : Optional[uint] = field(default = uint(5)) tt_display_head_n_with_tail : bool = field(default = True) tt_hide_index : bool = field(default = True) + tts_by_year_month_display_only_years : Optional[list[int]] = field(default_factory = lambda : YearProvider().get_most_recent_x_years(x = uint(1))) tts_by_spn_remove_untagged : bool = field(default = True) tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : uint = field(default = uint(25)) @@ -316,7 +324,6 @@ class SettingBag(): tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) md_infos : list[MDInfo] = field(default_factory = lambda : MDInfoProvider().get_all()) md_last_update : datetime = field(default = datetime.now()) - class TTDataFrameHelper(): '''Collects helper functions for TTDataFrameFactory.''' @@ -991,6 +998,7 @@ def __create_raw_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df[TTCN.EFFORTPRC] = tts_df.apply(lambda x : self.__df_helper.calculate_percentage(part = x[TTCN.EFFORT], whole = summarized), axis = 1) return tts_df + def __try_complete_raw_ttm(self, ttm_df : DataFrame, year : int) -> DataFrame: ''' @@ -1163,6 +1171,30 @@ def __update_future_months_to_empty(self, tts_by_month_df : DataFrame, now : dat tts_by_month_upd_df.iloc[:, idx_trend] = np.where(condition, new_value, tts_by_month_upd_df.iloc[:, idx_trend]) return tts_by_month_upd_df + def __remove_unknown_occurrences(self, tts_by_tr_df : DataFrame, unknown_id : str) -> DataFrame: + + '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' + + condition : Series = (tts_by_tr_df[TTCN.TIMERANGEID] != unknown_id) + tts_by_tr_df = tts_by_tr_df.loc[condition] + tts_by_tr_df.reset_index(drop = True, inplace = True) + + return tts_by_tr_df + def __filter_by_year(self, df : DataFrame, years : list[int]) -> DataFrame: + + ''' + Returns a DataFrame that in the "TTCN.YEAR" column has only values contained in "years". + + Returns df if years is an empty list. + ''' + + filtered_df : DataFrame = df.copy(deep = True) + + if len(years) > 0: + condition : Series = filtered_df[TTCN.YEAR].isin(years) + filtered_df = df.loc[condition] + + return filtered_df def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) -> DataFrame: '''Returns a DataFrame that contains only rows that match the provided is_correct.''' @@ -1173,15 +1205,6 @@ def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) - filtered_df = tts_by_efs_df.loc[condition] return filtered_df - def __remove_unknown_occurrences(self, tts_by_tr_df : DataFrame, unknown_id : str) -> DataFrame: - - '''Removes the provided uknown_id from the "TimeRangeId" column of the provided DataFrame.''' - - condition : Series = (tts_by_tr_df[TTCN.TIMERANGEID] != unknown_id) - tts_by_tr_df = tts_by_tr_df.loc[condition] - tts_by_tr_df.reset_index(drop = True, inplace = True) - - return tts_by_tr_df def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : uint) -> DataFrame: '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' @@ -1286,7 +1309,7 @@ def create_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_target tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df - def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget], display_only_years : list[int]) -> Tuple[DataFrame, DataFrame]: ''' [0] @@ -1324,6 +1347,8 @@ def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_ 88 2023 2 23h 00m 29h 00m -371h 00m 89 2023 3 50h 15m 79h 15m -321h 15m ... + + Returns (tts_by_year_month_df, tts_by_year_month_flt_df). ''' tts_df : DataFrame = tt_df.copy(deep = True) @@ -1347,7 +1372,9 @@ def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_ tts_df[TTCN.YEARLYTOTAL] = tts_df[TTCN.YEARLYTOTAL].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) tts_df[TTCN.TOTARGET] = tts_df[TTCN.TOTARGET].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) - return tts_df + tts_flt_df : DataFrame = self.__filter_by_year(df = tts_df, years = display_only_years) + + return (tts_df, tts_flt_df) def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' @@ -1604,7 +1631,6 @@ def create_tts_by_month_md(self, paragraph_title : str, last_update : datetime, md_content += "\n" return md_content - @dataclass(frozen=True) class ComponentBag(): @@ -1616,7 +1642,6 @@ class ComponentBag(): md_factory : TTMarkdownFactory = field(default = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter()))) logging_function : Callable[[str], None] = field(default = LambdaProvider().get_default_logging_function()) displayer : Displayer = field(default = Displayer()) - class TimeTrackingProcessor(): '''Collects all the logic related to the processing of "Time Tracking.xlsx".''' @@ -1693,14 +1718,20 @@ def __create_tts_by_year_df(self, tt_df : DataFrame) -> DataFrame: ) return tts_by_year_df - def __create_tts_by_year_month_df(self, tt_df : DataFrame) -> DataFrame: + def __create_tts_by_year_month_df(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: '''Creates the expected dataframe using tt_df and __setting_bag.''' - tts_by_year_month_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_month( + display_only_years : list[int] = [] + + if display_only_years is not None: + display_only_years = cast(list[int], self.__setting_bag.tts_by_year_month_display_only_years) + + tts_by_year_month_df : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_month( tt_df = tt_df, years = self.__setting_bag.years, yearly_targets = self.__setting_bag.yearly_targets, + display_only_years = display_only_years ) return tts_by_year_month_df @@ -1810,6 +1841,18 @@ def __optimize_tt_for_display(self, tt_df : DataFrame) -> DataFrame: head_n = self.__setting_bag.tt_head_n, display_head_n_with_tail = self.__setting_bag.tt_display_head_n_with_tail ) + def __optimize_tts_by_year_month_for_display(self, tts_by_year_month_tpl : Tuple[DataFrame, DataFrame]) -> DataFrame: + + ''' + tts_by_year_month_tpl is made of (tts_by_year_month_df, tts_by_year_month_flt_df). + + This method decides which one of the two DataFrame is to be displayed according to __setting_bag.tts_by_year_month_display_only_years. + ''' + + if self.__setting_bag.tts_by_year_month_display_only_years is None: + return tts_by_year_month_tpl[0] + + return tts_by_year_month_tpl[1] def initialize(self) -> None: @@ -1835,7 +1878,7 @@ def initialize(self) -> None: tt_df = tt_df, tts_by_month_tpl = tts_by_month_tpl, tts_by_year_df = tts_by_year_df, - tts_by_year_month_df = tts_by_year_month_df, + tts_by_year_month_tpl = tts_by_year_month_df, tts_by_year_month_spnv_df = tts_by_year_month_spnv_df, tts_by_year_spnv_df = tts_by_year_spnv_df, tts_by_spn_df = tts_by_spn_df, @@ -1909,7 +1952,7 @@ def process_tts_by_year_month(self) -> None: self.__validate_summary() options : list = self.__setting_bag.options_tts_by_year_month - df : DataFrame = self.__tt_summary.tts_by_year_month_df + df : DataFrame = self.__optimize_tts_by_year_month_for_display(tts_by_year_month_tpl = self.__tt_summary.tts_by_year_month_tpl) if "display" in options: self.__component_bag.displayer.display(df = df) From 8ee17df926c3ffe3bddb832a9550eba8c2eb8460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:05:58 +0000 Subject: [PATCH 055/109] #40 TimeTrackingProcessor - Iteration 13. --- src/nwtimetracking.ipynb | 2183 +++++++++++++++++++++++++++++--------- src/nwtimetracking.py | 4 +- 2 files changed, 1671 insertions(+), 516 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 02fac24..5cd97e2 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,32 +23,9 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 67, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", - " superreload(m, reload, self.old_objects)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 500, in superreload\n", - " update_generic(old_obj, new_obj)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", - " update(a, b)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 349, in update_class\n", - " if update_generic(old_obj, new_obj):\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", - " update(a, b)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 309, in update_function\n", - " setattr(old, name, getattr(new, name))\n", - "ValueError: __init__() requires a code object with 7 free vars, not 8\n", - "]\n" - ] - } - ], + "outputs": [], "source": [ "from pandas import DataFrame\n", "from typing import Optional" @@ -64,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -82,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 69, "metadata": {}, "outputs": [ { @@ -133,12 +110,11 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ "setting_bag : SettingBag = SettingBag(\n", - " excel_nrows = 1301,\n", " options_tt = [\"display\"],\n", " options_tts_by_month = [\"display\", \"save\"],\n", " options_tts_by_year = [\"display\"],\n", @@ -150,7 +126,9 @@ " options_tts_by_hashtag_year = [\"display\"],\n", " options_tts_by_efs = [\"display\"],\n", " options_tts_by_tr = [\"display\"],\n", - " options_definitions = [\"display\"]\n", + " options_definitions = [\"display\"],\n", + " excel_nrows = 1301,\n", + " tts_by_year_month_spnv_display_only = \"nwtimetracking\"\n", ")\n" ] }, @@ -164,7 +142,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 71, "metadata": {}, "outputs": [ { @@ -172,87 +150,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -267,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 72, "metadata": {}, "outputs": [ { @@ -275,301 +253,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -589,7 +567,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 73, "metadata": {}, "outputs": [ { @@ -597,92 +575,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -695,7 +673,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 74, "metadata": {}, "outputs": [ { @@ -703,106 +681,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -815,11 +793,1186 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 75, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20204NW.UnivariateForecasting1.0.003h 00m15h 45m19.0540h 45m7.36
20207NW.UnivariateForecasting1.0.005h 00m22h 00m22.7334h 00m14.71
20208NW.UnivariateForecasting1.0.026h 00m32h 00m81.2532h 00m81.25
20209NW.UnivariateForecasting1.0.030h 00m34h 00m88.2444h 00m68.18
202010NW.MarkdownTables1.0.013h 30m22h 00m61.3648h 00m28.12
202010NW.UnivariateForecasting1.0.005h 30m22h 00m25.0048h 00m11.46
202011NW.UnivariateForecasting1.0.007h 45m15h 45m49.2135h 30m21.83
202012NW.MarkdownTables1.0.001h 45m76h 00m2.30107h 30m1.63
202012NW.MarkdownTables1.0.102h 30m76h 00m3.29107h 30m2.33
202012NW.NGramTextClassification1.0.025h 15m76h 00m33.22107h 30m23.49
202012NW.UnivariateForecasting1.0.039h 00m76h 00m51.32107h 30m36.28
202012NW.UnivariateForecasting1.0.107h 30m76h 00m9.87107h 30m6.98
20211NW.NGramTextClassification1.0.044h 30m48h 30m91.7553h 00m83.96
20212NW.NGramTextClassification1.0.003h 30m07h 30m46.6731h 30m11.11
20214NW.NGramTextClassification1.1.007h 30m19h 00m39.4719h 00m39.47
20214NW.UnivariateForecasting1.1.004h 00m19h 00m21.0519h 00m21.05
20214NW.UnivariateForecasting1.1.107h 30m19h 00m39.4719h 00m39.47
20219NW.NGramTextClassification2.0.015h 30m43h 30m35.6343h 30m35.63
202110NW.UnivariateForecasting2.0.006h 30m22h 00m29.5535h 30m18.31
20229NW.NGramTextClassification3.0.028h 00m58h 30m47.8669h 00m40.58
20229NW.NGramTextClassification3.5.011h 30m58h 30m19.6669h 00m16.67
20229NW.UnivariateForecasting2.0.102h 00m58h 30m3.4269h 00m2.90
202210NW.NGramTextClassification3.5.028h 15m30h 30m92.6238h 30m73.38
202211NW.NGramTextClassification3.6.017h 30m58h 15m30.0458h 15m30.04
202211NW.UnivariateForecasting2.5.008h 30m58h 15m14.5958h 15m14.59
202211nwtraderaanalytics1.0.021h 15m58h 15m36.4858h 15m36.48
202212NW.NGramTextClassification3.7.008h 00m49h 45m16.0854h 15m14.75
202212NW.UnivariateForecasting2.5.001h 00m49h 45m2.0154h 15m1.84
202212nwtraderaanalytics1.0.040h 45m49h 45m81.9154h 15m75.12
20231NW.UnivariateForecasting2.5.006h 00m06h 00m100.0006h 00m100.00
20232NW.UnivariateForecasting2.5.001h 00m24h 00m4.1724h 00m4.17
20232NW.UnivariateForecasting3.0.023h 00m24h 00m95.8324h 00m95.83
20233NW.UnivariateForecasting3.0.023h 00m30h 15m76.0350h 15m45.77
20233nwtraderaanalytics2.0.002h 45m30h 15m9.0950h 15m5.47
20234nwtraderaanalytics2.0.009h 15m09h 15m100.0019h 00m48.68
20236nwreadinglist1.0.006h 45m11h 45m57.4524h 45m27.27
20237nwreadinglist1.0.016h 30m16h 30m100.0016h 30m100.00
20238nwreadinglist1.0.022h 00m22h 00m100.0041h 30m53.01
20239nwreadinglist1.5.016h 15m32h 45m49.6250h 15m32.34
20239nwreadinglist1.6.005h 00m32h 45m15.2750h 15m9.95
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241NW.MarkdownTables3.0.003h 00m20h 30m14.6345h 45m6.56
20241NW.NGramTextClassification4.0.006h 15m20h 30m30.4945h 45m13.66
20241nwreadinglist2.0.001h 30m20h 30m7.3245h 45m3.28
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242NW.NGramTextClassification4.0.008h 15m36h 45m22.4577h 45m10.61
20242NW.NGramTextClassification4.1.002h 15m36h 45m6.1277h 45m2.89
20242NW.NGramTextClassification4.2.001h 15m36h 45m3.4077h 45m1.61
20242NW.Shared.Files1.0.005h 30m36h 45m14.9777h 45m7.07
20242NW.Shared.Serialization1.0.004h 15m36h 45m11.5677h 45m5.47
20242NW.Shared.Validation1.0.002h 45m36h 45m7.4877h 45m3.54
20242NW.UnivariateForecasting4.1.001h 00m36h 45m2.7277h 45m1.29
20242NW.UnivariateForecasting4.2.000h 45m36h 45m2.0477h 45m0.96
20242nwreadinglist2.1.002h 00m36h 45m5.4477h 45m2.57
20242nwreadinglist2.2.000h 30m36h 45m1.3677h 45m0.64
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwreadinglist2.2.006h 15m77h 15m8.0977h 45m8.04
20243nwreadinglist3.0.010h 00m77h 15m12.9477h 45m12.86
20243nwreadinglist3.1.005h 15m77h 15m6.8077h 45m6.75
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20243nwtraderaanalytics3.0.052h 15m77h 15m67.6477h 45m67.20
20245nwreadinglist3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwreadinglist3.3.000h 30m35h 30m1.4143h 00m1.16
20245nwshared1.1.008h 00m35h 30m22.5443h 00m18.60
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20245nwtraderaanalytics4.0.022h 00m35h 30m61.9743h 00m51.16
20246nwtraderaanalytics4.0.042h 15m42h 15m100.0048h 00m88.02
20248nwreadinglist3.4.003h 15m10h 30m30.9532h 45m9.92
20248nwshared1.2.001h 30m10h 30m14.2932h 45m4.58
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20248nwtraderaanalytics4.1.001h 45m10h 30m16.6732h 45m5.34
20249nwreadinglist3.5.006h 30m45h 45m14.2148h 00m13.54
20249nwreadinglist3.6.001h 00m45h 45m2.1948h 00m2.08
20249nwreadinglist3.7.007h 45m45h 45m16.9448h 00m16.15
20249nwshared1.3.007h 00m45h 45m15.3048h 00m14.58
20249nwshared1.4.006h 30m45h 45m14.2148h 00m13.54
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwpackageversions1.0.034h 15m85h 00m40.29101h 30m33.74
202410nwpackageversions1.1.002h 30m85h 00m2.94101h 30m2.46
202410nwpackageversions1.2.003h 00m85h 00m3.53101h 30m2.96
202410nwpackageversions1.6.006h 45m85h 00m7.94101h 30m6.65
202410nwreadinglist3.8.001h 30m85h 00m1.76101h 30m1.48
202410nwshared1.5.001h 00m85h 00m1.18101h 30m0.99
202410nwshared1.6.000h 45m85h 00m0.88101h 30m0.74
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202410nwtraderaanalytics4.2.025h 00m85h 00m29.41101h 30m24.63
202410nwtraderaanalytics4.3.000h 30m85h 00m0.59101h 30m0.49
202411nwpackageversions1.6.005h 30m47h 45m11.5288h 00m6.25
202411nwreadinglist4.0.028h 15m47h 45m59.1688h 00m32.10
202411nwshared1.7.003h 00m47h 45m6.2888h 00m3.41
202411nwshared1.7.101h 00m47h 45m2.0988h 00m1.14
202411nwshared1.8.010h 00m47h 45m20.9488h 00m11.36
202412nwpackageversions1.8.000h 45m07h 45m9.6807h 45m9.68
202412nwreadinglist4.1.000h 45m07h 45m9.6807h 45m9.68
202412nwshared1.8.003h 30m07h 45m45.1607h 45m45.16
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
202412nwtraderaanalytics4.4.002h 00m07h 45m25.8107h 45m25.81
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_year_month_spnv()\n" ] }, { diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 9e3cd48..b9e63d7 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -301,6 +301,7 @@ class SettingBag(): options_tts_by_tr : list[Literal["display"]] options_definitions : list[Literal["display"]] excel_nrows : int + tts_by_year_month_spnv_display_only : Optional[str] # With Defaults working_folder_path : str = field(default = "/home/nwtimetracking/") @@ -316,6 +317,7 @@ class SettingBag(): tt_display_head_n_with_tail : bool = field(default = True) tt_hide_index : bool = field(default = True) tts_by_year_month_display_only_years : Optional[list[int]] = field(default_factory = lambda : YearProvider().get_most_recent_x_years(x = uint(1))) + tts_by_year_month_spnv_formatters : dict = field(default_factory = lambda : { "%_DME" : "{:.2f}", "%_TME" : "{:.2f}" }) tts_by_spn_remove_untagged : bool = field(default = True) tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : uint = field(default = uint(25)) @@ -1970,7 +1972,7 @@ def process_tts_by_year_month_spnv(self) -> None: df : DataFrame = self.__tt_summary.tts_by_year_month_spnv_df if "display" in options: - self.__component_bag.displayer.display(df = df) + self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_month_spnv_formatters) def process_tts_by_spn(self) -> None: ''' From 9fcf177a3e5f08f38e03cc1606c7e7fdb75dd338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:22:59 +0000 Subject: [PATCH 056/109] #40 TimeTrackingProcessor - Iteration 14. --- src/nwtimetracking.ipynb | 2337 ++++++++++++-------------------------- src/nwtimetracking.py | 54 +- 2 files changed, 739 insertions(+), 1652 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 5cd97e2..00fe0c3 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,9 +23,32 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 76, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", + " superreload(m, reload, self.old_objects)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 500, in superreload\n", + " update_generic(old_obj, new_obj)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", + " update(a, b)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 349, in update_class\n", + " if update_generic(old_obj, new_obj):\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", + " update(a, b)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 309, in update_function\n", + " setattr(old, name, getattr(new, name))\n", + "ValueError: __init__() requires a code object with 8 free vars, not 9\n", + "]\n" + ] + } + ], "source": [ "from pandas import DataFrame\n", "from typing import Optional" @@ -41,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -59,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 78, "metadata": {}, "outputs": [ { @@ -110,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ @@ -142,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 80, "metadata": {}, "outputs": [ { @@ -150,87 +173,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -245,7 +268,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 81, "metadata": {}, "outputs": [ { @@ -253,301 +276,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -567,7 +590,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 82, "metadata": {}, "outputs": [ { @@ -575,92 +598,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -673,7 +696,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 83, "metadata": {}, "outputs": [ { @@ -681,106 +704,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -793,7 +816,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -801,1170 +824,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20204NW.UnivariateForecasting1.0.003h 00m15h 45m19.0540h 45m7.36
20207NW.UnivariateForecasting1.0.005h 00m22h 00m22.7334h 00m14.71
20208NW.UnivariateForecasting1.0.026h 00m32h 00m81.2532h 00m81.25
20209NW.UnivariateForecasting1.0.030h 00m34h 00m88.2444h 00m68.18
202010NW.MarkdownTables1.0.013h 30m22h 00m61.3648h 00m28.12
202010NW.UnivariateForecasting1.0.005h 30m22h 00m25.0048h 00m11.46
202011NW.UnivariateForecasting1.0.007h 45m15h 45m49.2135h 30m21.83
202012NW.MarkdownTables1.0.001h 45m76h 00m2.30107h 30m1.63
202012NW.MarkdownTables1.0.102h 30m76h 00m3.29107h 30m2.33
202012NW.NGramTextClassification1.0.025h 15m76h 00m33.22107h 30m23.49
202012NW.UnivariateForecasting1.0.039h 00m76h 00m51.32107h 30m36.28
202012NW.UnivariateForecasting1.0.107h 30m76h 00m9.87107h 30m6.98
20211NW.NGramTextClassification1.0.044h 30m48h 30m91.7553h 00m83.96
20212NW.NGramTextClassification1.0.003h 30m07h 30m46.6731h 30m11.11
20214NW.NGramTextClassification1.1.007h 30m19h 00m39.4719h 00m39.47
20214NW.UnivariateForecasting1.1.004h 00m19h 00m21.0519h 00m21.05
20214NW.UnivariateForecasting1.1.107h 30m19h 00m39.4719h 00m39.47
20219NW.NGramTextClassification2.0.015h 30m43h 30m35.6343h 30m35.63
202110NW.UnivariateForecasting2.0.006h 30m22h 00m29.5535h 30m18.31
20229NW.NGramTextClassification3.0.028h 00m58h 30m47.8669h 00m40.58
20229NW.NGramTextClassification3.5.011h 30m58h 30m19.6669h 00m16.67
20229NW.UnivariateForecasting2.0.102h 00m58h 30m3.4269h 00m2.90
202210NW.NGramTextClassification3.5.028h 15m30h 30m92.6238h 30m73.38
202211NW.NGramTextClassification3.6.017h 30m58h 15m30.0458h 15m30.04
202211NW.UnivariateForecasting2.5.008h 30m58h 15m14.5958h 15m14.59
202211nwtraderaanalytics1.0.021h 15m58h 15m36.4858h 15m36.48
202212NW.NGramTextClassification3.7.008h 00m49h 45m16.0854h 15m14.75
202212NW.UnivariateForecasting2.5.001h 00m49h 45m2.0154h 15m1.84
202212nwtraderaanalytics1.0.040h 45m49h 45m81.9154h 15m75.12
20231NW.UnivariateForecasting2.5.006h 00m06h 00m100.0006h 00m100.00
20232NW.UnivariateForecasting2.5.001h 00m24h 00m4.1724h 00m4.17
20232NW.UnivariateForecasting3.0.023h 00m24h 00m95.8324h 00m95.83
20233NW.UnivariateForecasting3.0.023h 00m30h 15m76.0350h 15m45.77
20233nwtraderaanalytics2.0.002h 45m30h 15m9.0950h 15m5.47
20234nwtraderaanalytics2.0.009h 15m09h 15m100.0019h 00m48.68
20236nwreadinglist1.0.006h 45m11h 45m57.4524h 45m27.27
20237nwreadinglist1.0.016h 30m16h 30m100.0016h 30m100.00
20238nwreadinglist1.0.022h 00m22h 00m100.0041h 30m53.01
20239nwreadinglist1.5.016h 15m32h 45m49.6250h 15m32.34
20239nwreadinglist1.6.005h 00m32h 45m15.2750h 15m9.95
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241NW.MarkdownTables3.0.003h 00m20h 30m14.6345h 45m6.56
20241NW.NGramTextClassification4.0.006h 15m20h 30m30.4945h 45m13.66
20241nwreadinglist2.0.001h 30m20h 30m7.3245h 45m3.28
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242NW.NGramTextClassification4.0.008h 15m36h 45m22.4577h 45m10.61
20242NW.NGramTextClassification4.1.002h 15m36h 45m6.1277h 45m2.89
20242NW.NGramTextClassification4.2.001h 15m36h 45m3.4077h 45m1.61
20242NW.Shared.Files1.0.005h 30m36h 45m14.9777h 45m7.07
20242NW.Shared.Serialization1.0.004h 15m36h 45m11.5677h 45m5.47
20242NW.Shared.Validation1.0.002h 45m36h 45m7.4877h 45m3.54
20242NW.UnivariateForecasting4.1.001h 00m36h 45m2.7277h 45m1.29
20242NW.UnivariateForecasting4.2.000h 45m36h 45m2.0477h 45m0.96
20242nwreadinglist2.1.002h 00m36h 45m5.4477h 45m2.57
20242nwreadinglist2.2.000h 30m36h 45m1.3677h 45m0.64
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwreadinglist2.2.006h 15m77h 15m8.0977h 45m8.04
20243nwreadinglist3.0.010h 00m77h 15m12.9477h 45m12.86
20243nwreadinglist3.1.005h 15m77h 15m6.8077h 45m6.75
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20243nwtraderaanalytics3.0.052h 15m77h 15m67.6477h 45m67.20
20245nwreadinglist3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwreadinglist3.3.000h 30m35h 30m1.4143h 00m1.16
20245nwshared1.1.008h 00m35h 30m22.5443h 00m18.60
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20245nwtraderaanalytics4.0.022h 00m35h 30m61.9743h 00m51.16
20246nwtraderaanalytics4.0.042h 15m42h 15m100.0048h 00m88.02
20248nwreadinglist3.4.003h 15m10h 30m30.9532h 45m9.92
20248nwshared1.2.001h 30m10h 30m14.2932h 45m4.58
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20248nwtraderaanalytics4.1.001h 45m10h 30m16.6732h 45m5.34
20249nwreadinglist3.5.006h 30m45h 45m14.2148h 00m13.54
20249nwreadinglist3.6.001h 00m45h 45m2.1948h 00m2.08
20249nwreadinglist3.7.007h 45m45h 45m16.9448h 00m16.15
20249nwshared1.3.007h 00m45h 45m15.3048h 00m14.58
20249nwshared1.4.006h 30m45h 45m14.2148h 00m13.54
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwpackageversions1.0.034h 15m85h 00m40.29101h 30m33.74
202410nwpackageversions1.1.002h 30m85h 00m2.94101h 30m2.46
202410nwpackageversions1.2.003h 00m85h 00m3.53101h 30m2.96
202410nwpackageversions1.6.006h 45m85h 00m7.94101h 30m6.65
202410nwreadinglist3.8.001h 30m85h 00m1.76101h 30m1.48
202410nwshared1.5.001h 00m85h 00m1.18101h 30m0.99
202410nwshared1.6.000h 45m85h 00m0.88101h 30m0.74
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202410nwtraderaanalytics4.2.025h 00m85h 00m29.41101h 30m24.63
202410nwtraderaanalytics4.3.000h 30m85h 00m0.59101h 30m0.49
202411nwpackageversions1.6.005h 30m47h 45m11.5288h 00m6.25
202411nwreadinglist4.0.028h 15m47h 45m59.1688h 00m32.10
202411nwshared1.7.003h 00m47h 45m6.2888h 00m3.41
202411nwshared1.7.101h 00m47h 45m2.0988h 00m1.14
202411nwshared1.8.010h 00m47h 45m20.9488h 00m11.36
202412nwpackageversions1.8.000h 45m07h 45m9.6807h 45m9.68
202412nwreadinglist4.1.000h 45m07h 45m9.6807h 45m9.68
202412nwshared1.8.003h 30m07h 45m45.1607h 45m45.16
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
202412nwtraderaanalytics4.4.002h 00m07h 45m25.8107h 45m25.8120239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index b9e63d7..06a878c 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -162,7 +162,7 @@ class TTSummary(): tts_by_month_tpl : Tuple[DataFrame, DataFrame] tts_by_year_df : DataFrame tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] - tts_by_year_month_spnv_df : DataFrame + tts_by_year_month_spnv_df : Tuple[DataFrame, DataFrame] tts_by_year_spnv_df : DataFrame tts_by_spn_df : DataFrame tts_by_spn_spv_df : DataFrame @@ -1196,6 +1196,21 @@ def __filter_by_year(self, df : DataFrame, years : list[int]) -> DataFrame: condition : Series = filtered_df[TTCN.YEAR].isin(years) filtered_df = df.loc[condition] + return filtered_df + def __filter_by_software_project_name(self, df : DataFrame, software_project_name : Optional[str]) -> DataFrame: + + ''' + Returns a DataFrame that in the "TTCN.PROJECTNAME" column has only values that are equal to software_project_name. + + Returns df if software_project_name is None. + ''' + + filtered_df : DataFrame = df.copy(deep = True) + + if software_project_name is not None: + condition : Series = (filtered_df[TTCN.PROJECTNAME] == software_project_name) + filtered_df = df.loc[condition] + return filtered_df def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) -> DataFrame: @@ -1377,7 +1392,7 @@ def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_ tts_flt_df : DataFrame = self.__filter_by_year(df = tts_df, years = display_only_years) return (tts_df, tts_flt_df) - def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: ''' [0] ... @@ -1387,6 +1402,8 @@ def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], so 0 2023 4 nwtraderaanalytics 2.0.0 09h 15m 09h 15m 100.00 19h 00m 48.68 1 2023 6 nwreadinglistmanager 1.0.0 06h 45m 06h 45m 100.00 24h 45m 27.27 ... + + Returns (tts_by_year_month_spnv_df, tts_by_year_month_spnv_flt_df). ''' spnv_df : DataFrame = self.__create_raw_tts_by_year_month_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) @@ -1416,7 +1433,9 @@ def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], so tts_df[TTCN.DME] = tts_df[TTCN.DME].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) tts_df[TTCN.TME] = tts_df[TTCN.TME].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) - return tts_df + tts_flt_df : DataFrame = self.__filter_by_software_project_name(df = tts_df, software_project_name = software_project_name) + + return (tts_df, tts_flt_df) def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' @@ -1737,17 +1756,18 @@ def __create_tts_by_year_month_df(self, tt_df : DataFrame) -> Tuple[DataFrame, D ) return tts_by_year_month_df - def __create_tts_by_year_month_spnv_df(self, tt_df : DataFrame) -> DataFrame: + def __create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: '''Creates the expected dataframe using tt_df and __setting_bag.''' - tts_by_year_month_spnv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_month_spnv( + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_month_spnv( tt_df = tt_df, years = self.__setting_bag.years, software_project_names = self.__setting_bag.software_project_names, + software_project_name = self.__setting_bag.tts_by_year_month_spnv_display_only ) - return tts_by_year_month_spnv_df + return tts_by_year_month_spnv_tpl def __create_tts_by_year_spnv_df(self, tt_df : DataFrame) -> DataFrame: '''Creates the expected dataframe using tt_df and __setting_bag.''' @@ -1855,6 +1875,18 @@ def __optimize_tts_by_year_month_for_display(self, tts_by_year_month_tpl : Tuple return tts_by_year_month_tpl[0] return tts_by_year_month_tpl[1] + def __optimize_tts_by_year_month_spnv_for_display(self, tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame]) -> DataFrame: + + ''' + tts_by_year_month_spnv_tpl is made of (tts_by_year_month_spnv_df, tts_by_year_month_spnv_flt_df). + + This method decides which one of the two DataFrame is to be displayed according to __setting_bag.tts_by_year_month_spnv_display_only. + ''' + + if self.__setting_bag.tts_by_year_month_spnv_display_only is None: + return tts_by_year_month_spnv_tpl[0] + + return tts_by_year_month_spnv_tpl[1] def initialize(self) -> None: @@ -1863,8 +1895,8 @@ def initialize(self) -> None: tt_df : DataFrame = self.__create_tt_df() tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_month_tpl(tt_df = tt_df) tts_by_year_df : DataFrame = self.__create_tts_by_year_df(tt_df = tt_df) - tts_by_year_month_df : DataFrame = self.__create_tts_by_year_month_df(tt_df = tt_df) - tts_by_year_month_spnv_df : DataFrame = self.__create_tts_by_year_month_spnv_df(tt_df = tt_df) + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_df(tt_df = tt_df) + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_spnv_tpl(tt_df = tt_df) tts_by_year_spnv_df : DataFrame = self.__create_tts_by_year_spnv_df(tt_df = tt_df) tts_by_spn_df : DataFrame = self.__create_tts_by_spn_df(tt_df = tt_df) tts_by_spn_spv_df : DataFrame = self.__create_tts_by_spn_spv_df(tt_df = tt_df) @@ -1880,8 +1912,8 @@ def initialize(self) -> None: tt_df = tt_df, tts_by_month_tpl = tts_by_month_tpl, tts_by_year_df = tts_by_year_df, - tts_by_year_month_tpl = tts_by_year_month_df, - tts_by_year_month_spnv_df = tts_by_year_month_spnv_df, + tts_by_year_month_tpl = tts_by_year_month_tpl, + tts_by_year_month_spnv_df = tts_by_year_month_spnv_tpl, tts_by_year_spnv_df = tts_by_year_spnv_df, tts_by_spn_df = tts_by_spn_df, tts_by_spn_spv_df = tts_by_spn_spv_df, @@ -1969,7 +2001,7 @@ def process_tts_by_year_month_spnv(self) -> None: self.__validate_summary() options : list = self.__setting_bag.options_tts_by_year_month_spnv - df : DataFrame = self.__tt_summary.tts_by_year_month_spnv_df + df : DataFrame = self.__optimize_tts_by_year_month_spnv_for_display(tts_by_year_month_spnv_tpl = self.__tt_summary.tts_by_year_month_spnv_df) if "display" in options: self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_month_spnv_formatters) From 72c5215084388ff2c6b1c18ced23142c616c50fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:25:39 +0000 Subject: [PATCH 057/109] #40 TimeTrackingProcessor - Iteration 14. --- src/nwtimetracking.ipynb | 4 ++-- src/nwtimetracking.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 00fe0c3..a955705 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -133,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -151,7 +151,7 @@ " options_tts_by_tr = [\"display\"],\n", " options_definitions = [\"display\"],\n", " excel_nrows = 1301,\n", - " tts_by_year_month_spnv_display_only = \"nwtimetracking\"\n", + " tts_by_year_month_spnv_display_only_spn = \"nwtimetracking\"\n", ")\n" ] }, diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 06a878c..c653cce 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -301,7 +301,7 @@ class SettingBag(): options_tts_by_tr : list[Literal["display"]] options_definitions : list[Literal["display"]] excel_nrows : int - tts_by_year_month_spnv_display_only : Optional[str] + tts_by_year_month_spnv_display_only_spn : Optional[str] # With Defaults working_folder_path : str = field(default = "/home/nwtimetracking/") @@ -1764,7 +1764,7 @@ def __create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFr tt_df = tt_df, years = self.__setting_bag.years, software_project_names = self.__setting_bag.software_project_names, - software_project_name = self.__setting_bag.tts_by_year_month_spnv_display_only + software_project_name = self.__setting_bag.tts_by_year_month_spnv_display_only_spn ) return tts_by_year_month_spnv_tpl @@ -1880,10 +1880,10 @@ def __optimize_tts_by_year_month_spnv_for_display(self, tts_by_year_month_spnv_t ''' tts_by_year_month_spnv_tpl is made of (tts_by_year_month_spnv_df, tts_by_year_month_spnv_flt_df). - This method decides which one of the two DataFrame is to be displayed according to __setting_bag.tts_by_year_month_spnv_display_only. + This method decides which one of the two DataFrame is to be displayed according to __setting_bag.tts_by_year_month_spnv_display_only_spn. ''' - if self.__setting_bag.tts_by_year_month_spnv_display_only is None: + if self.__setting_bag.tts_by_year_month_spnv_display_only_spn is None: return tts_by_year_month_spnv_tpl[0] return tts_by_year_month_spnv_tpl[1] From ad9783d1ef2ea15c597ecb05b7c4d7b4f2fe8ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:41:00 +0000 Subject: [PATCH 058/109] #40 TimeTrackingProcessor - Iteration 15. --- src/nwtimetracking.ipynb | 1536 +++++++++++++++++++++----------------- src/nwtimetracking.py | 61 +- 2 files changed, 907 insertions(+), 690 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index a955705..b7c1038 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 105, "metadata": {}, "outputs": [ { @@ -44,7 +44,7 @@ " update(a, b)\n", " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 309, in update_function\n", " setattr(old, name, getattr(new, name))\n", - "ValueError: __init__() requires a code object with 8 free vars, not 9\n", + "ValueError: __init__() requires a code object with 9 free vars, not 10\n", "]\n" ] } @@ -64,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 106, "metadata": {}, "outputs": [], "source": [ @@ -82,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 107, "metadata": {}, "outputs": [ { @@ -133,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 108, "metadata": {}, "outputs": [], "source": [ @@ -143,6 +143,7 @@ " options_tts_by_year = [\"display\"],\n", " options_tts_by_year_month = [\"display\"],\n", " options_tts_by_year_month_spnv = [\"display\"],\n", + " options_tts_by_year_spnv = [\"display\"],\n", " options_tts_by_spn = [\"display\"],\n", " options_tts_by_spn_spv = [\"display\"],\n", " options_tts_by_hashtag = [\"display\"],\n", @@ -151,7 +152,8 @@ " options_tts_by_tr = [\"display\"],\n", " options_definitions = [\"display\"],\n", " excel_nrows = 1301,\n", - " tts_by_year_month_spnv_display_only_spn = \"nwtimetracking\"\n", + " tts_by_year_month_spnv_display_only_spn = \"nwtimetracking\",\n", + " tts_by_year_spnv_display_only_spn = \"nwtimetracking\"\n", ")\n" ] }, @@ -165,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 109, "metadata": {}, "outputs": [ { @@ -173,87 +175,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -268,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 110, "metadata": {}, "outputs": [ { @@ -276,301 +278,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -590,7 +592,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 111, "metadata": {}, "outputs": [ { @@ -598,92 +600,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -696,7 +698,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 112, "metadata": {}, "outputs": [ { @@ -704,106 +706,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -816,7 +818,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 113, "metadata": {}, "outputs": [ { @@ -824,202 +826,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1032,11 +1034,191 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 114, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_year_spnv()\n" ] }, { diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index c653cce..4d964b9 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -162,8 +162,8 @@ class TTSummary(): tts_by_month_tpl : Tuple[DataFrame, DataFrame] tts_by_year_df : DataFrame tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] - tts_by_year_month_spnv_df : Tuple[DataFrame, DataFrame] - tts_by_year_spnv_df : DataFrame + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] tts_by_spn_df : DataFrame tts_by_spn_spv_df : DataFrame tts_by_hashtag_df : DataFrame @@ -293,6 +293,7 @@ class SettingBag(): options_tts_by_year : list[Literal["display"]] options_tts_by_year_month : list[Literal["display"]] options_tts_by_year_month_spnv : list[Literal["display"]] + options_tts_by_year_spnv : list[Literal["display"]] options_tts_by_spn : list[Literal["display"]] options_tts_by_spn_spv : list[Literal["display"]] options_tts_by_hashtag : list[Literal["display"]] @@ -301,7 +302,8 @@ class SettingBag(): options_tts_by_tr : list[Literal["display"]] options_definitions : list[Literal["display"]] excel_nrows : int - tts_by_year_month_spnv_display_only_spn : Optional[str] + tts_by_year_month_spnv_display_only_spn : Optional[str] + tts_by_year_spnv_display_only_spn : Optional[str] # With Defaults working_folder_path : str = field(default = "/home/nwtimetracking/") @@ -318,6 +320,7 @@ class SettingBag(): tt_hide_index : bool = field(default = True) tts_by_year_month_display_only_years : Optional[list[int]] = field(default_factory = lambda : YearProvider().get_most_recent_x_years(x = uint(1))) tts_by_year_month_spnv_formatters : dict = field(default_factory = lambda : { "%_DME" : "{:.2f}", "%_TME" : "{:.2f}" }) + tts_by_year_spnv_formatters : dict = field(default_factory = lambda : { "%_DYE" : "{:.2f}", "%_TYE" : "{:.2f}" }) tts_by_spn_remove_untagged : bool = field(default = True) tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : uint = field(default = uint(25)) @@ -1436,7 +1439,7 @@ def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], so tts_flt_df : DataFrame = self.__filter_by_software_project_name(df = tts_df, software_project_name = software_project_name) return (tts_df, tts_flt_df) - def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: ''' [0] ... @@ -1446,6 +1449,8 @@ def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software 0 2023 nwtraderaanalytics 2.0.0 09h 15m 09h 15m 100.00 19h 00m 48.68 1 2023 nwreadinglistmanager 1.0.0 06h 45m 06h 45m 100.00 24h 45m 27.27 ... + + Returns (tts_by_year_spnv_df, tts_by_year_spnv_flt_df). ''' spnv_df : DataFrame = self.__create_raw_tts_by_year_spnv(tt_df = tt_df, years = years, software_project_names = software_project_names) @@ -1475,7 +1480,9 @@ def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software tts_df[TTCN.DYE] = tts_df[TTCN.DYE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) tts_df[TTCN.TYE] = tts_df[TTCN.TYE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) - return tts_df + tts_flt_df : DataFrame = self.__filter_by_software_project_name(df = tts_df, software_project_name = software_project_name) + + return (tts_df, tts_flt_df) def create_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: ''' @@ -1768,17 +1775,18 @@ def __create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFr ) return tts_by_year_month_spnv_tpl - def __create_tts_by_year_spnv_df(self, tt_df : DataFrame) -> DataFrame: + def __create_tts_by_year_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: '''Creates the expected dataframe using tt_df and __setting_bag.''' - tts_by_year_spnv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_spnv( + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_spnv( tt_df = tt_df, years = self.__setting_bag.years, software_project_names = self.__setting_bag.software_project_names, + software_project_name = self.__setting_bag.tts_by_year_spnv_display_only_spn ) - return tts_by_year_spnv_df + return tts_by_year_spnv_tpl def __create_tts_by_spn_df(self, tt_df : DataFrame) -> DataFrame: '''Creates the expected dataframe using tt_df and __setting_bag.''' @@ -1887,17 +1895,29 @@ def __optimize_tts_by_year_month_spnv_for_display(self, tts_by_year_month_spnv_t return tts_by_year_month_spnv_tpl[0] return tts_by_year_month_spnv_tpl[1] + def __optimize_tts_by_year_spnv_for_display(self, tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame]) -> DataFrame: + + ''' + tts_by_year_spnv_tpl is made of (tts_by_year_spnv_df, tts_by_year_spnv_flt_df). + + This method decides which one of the two DataFrame is to be displayed according to __setting_bag.tts_by_year_spnv_display_only_spn. + ''' + + if self.__setting_bag.tts_by_year_spnv_display_only_spn is None: + return tts_by_year_spnv_tpl[0] + + return tts_by_year_spnv_tpl[1] def initialize(self) -> None: - '''Creates a RLSummary object and assign it to __rl_summary.''' + '''Creates a TTSummary object and assign it to __tt_summary.''' tt_df : DataFrame = self.__create_tt_df() tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_month_tpl(tt_df = tt_df) tts_by_year_df : DataFrame = self.__create_tts_by_year_df(tt_df = tt_df) tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_df(tt_df = tt_df) tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_spnv_tpl(tt_df = tt_df) - tts_by_year_spnv_df : DataFrame = self.__create_tts_by_year_spnv_df(tt_df = tt_df) + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_spnv_tpl(tt_df = tt_df) tts_by_spn_df : DataFrame = self.__create_tts_by_spn_df(tt_df = tt_df) tts_by_spn_spv_df : DataFrame = self.__create_tts_by_spn_spv_df(tt_df = tt_df) tts_by_year_hashtag_df : DataFrame = self.__create_tts_by_year_hashtag_df(tt_df = tt_df) @@ -1913,8 +1933,8 @@ def initialize(self) -> None: tts_by_month_tpl = tts_by_month_tpl, tts_by_year_df = tts_by_year_df, tts_by_year_month_tpl = tts_by_year_month_tpl, - tts_by_year_month_spnv_df = tts_by_year_month_spnv_tpl, - tts_by_year_spnv_df = tts_by_year_spnv_df, + tts_by_year_month_spnv_tpl = tts_by_year_month_spnv_tpl, + tts_by_year_spnv_tpl = tts_by_year_spnv_tpl, tts_by_spn_df = tts_by_spn_df, tts_by_spn_spv_df = tts_by_spn_spv_df, tts_by_hashtag_year_df = tts_by_year_hashtag_df, @@ -2001,10 +2021,25 @@ def process_tts_by_year_month_spnv(self) -> None: self.__validate_summary() options : list = self.__setting_bag.options_tts_by_year_month_spnv - df : DataFrame = self.__optimize_tts_by_year_month_spnv_for_display(tts_by_year_month_spnv_tpl = self.__tt_summary.tts_by_year_month_spnv_df) + df : DataFrame = self.__optimize_tts_by_year_month_spnv_for_display(tts_by_year_month_spnv_tpl = self.__tt_summary.tts_by_year_month_spnv_tpl) if "display" in options: self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_month_spnv_formatters) + def process_tts_by_year_spnv(self) -> None: + + ''' + Performs all the actions listed in __setting_bag.options_tts_by_year_spnv. + + It raises an exception if the 'initialize' method has not been run yet. + ''' + + self.__validate_summary() + + options : list = self.__setting_bag.options_tts_by_year_spnv + df : DataFrame = self.__optimize_tts_by_year_spnv_for_display(tts_by_year_spnv_tpl = self.__tt_summary.tts_by_year_spnv_tpl) + + if "display" in options: + self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_spnv_formatters) def process_tts_by_spn(self) -> None: ''' From f5dd77104fda904e36d22e0ef47051ebc9d17c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:44:58 +0000 Subject: [PATCH 059/109] #40 TimeTrackingProcessor - Iteration 16. --- src/nwtimetracking.ipynb | 2247 +++++++++++++++++++++++--------------- src/nwtimetracking.py | 3 +- 2 files changed, 1388 insertions(+), 862 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index b7c1038..2ad0863 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,32 +23,9 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 137, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", - " superreload(m, reload, self.old_objects)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 500, in superreload\n", - " update_generic(old_obj, new_obj)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", - " update(a, b)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 349, in update_class\n", - " if update_generic(old_obj, new_obj):\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 397, in update_generic\n", - " update(a, b)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 309, in update_function\n", - " setattr(old, name, getattr(new, name))\n", - "ValueError: __init__() requires a code object with 9 free vars, not 10\n", - "]\n" - ] - } - ], + "outputs": [], "source": [ "from pandas import DataFrame\n", "from typing import Optional" @@ -64,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 138, "metadata": {}, "outputs": [], "source": [ @@ -82,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 139, "metadata": {}, "outputs": [ { @@ -133,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 140, "metadata": {}, "outputs": [], "source": [ @@ -167,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 141, "metadata": {}, "outputs": [ { @@ -175,87 +152,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -270,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 142, "metadata": {}, "outputs": [ { @@ -278,301 +255,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -592,7 +569,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 143, "metadata": {}, "outputs": [ { @@ -600,92 +577,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -698,7 +675,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 144, "metadata": {}, "outputs": [ { @@ -706,106 +683,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -818,7 +795,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 145, "metadata": {}, "outputs": [ { @@ -826,202 +803,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1034,7 +1011,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 146, "metadata": {}, "outputs": [ { @@ -1042,175 +1019,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1223,20 +1200,568 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 147, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
HashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_spn()\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 148, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ProjectNameProjectVersionEffort
NW.MarkdownTables1.0.015h 15m
NW.MarkdownTables1.0.102h 30m
NW.MarkdownTables3.0.003h 00m
NW.NGramTextClassification1.0.073h 15m
NW.NGramTextClassification1.1.007h 30m
NW.NGramTextClassification2.0.015h 30m
NW.NGramTextClassification3.0.028h 00m
NW.NGramTextClassification3.5.039h 45m
NW.NGramTextClassification3.6.017h 30m
NW.NGramTextClassification3.7.008h 00m
NW.NGramTextClassification4.0.014h 30m
NW.NGramTextClassification4.1.002h 15m
NW.NGramTextClassification4.2.001h 15m
NW.Shared.Files1.0.005h 30m
NW.Shared.Serialization1.0.004h 15m
NW.Shared.Validation1.0.002h 45m
NW.UnivariateForecasting1.0.0116h 15m
NW.UnivariateForecasting1.0.107h 30m
NW.UnivariateForecasting1.1.004h 00m
NW.UnivariateForecasting1.1.107h 30m
NW.UnivariateForecasting2.0.006h 30m
NW.UnivariateForecasting2.0.102h 00m
NW.UnivariateForecasting2.5.016h 30m
NW.UnivariateForecasting3.0.046h 00m
NW.UnivariateForecasting4.1.001h 00m
NW.UnivariateForecasting4.2.000h 45m
nwpackageversions1.0.034h 15m
nwpackageversions1.1.002h 30m
nwpackageversions1.2.003h 00m
nwpackageversions1.6.012h 15m
nwpackageversions1.8.000h 45m
nwreadinglist1.0.045h 15m
nwreadinglist1.5.016h 15m
nwreadinglist1.6.005h 00m
nwreadinglist2.0.001h 30m
nwreadinglist2.1.002h 00m
nwreadinglist2.2.006h 45m
nwreadinglist3.0.010h 00m
nwreadinglist3.1.005h 15m
nwreadinglist3.2.002h 00m
nwreadinglist3.3.000h 30m
nwreadinglist3.4.003h 15m
nwreadinglist3.5.006h 30m
nwreadinglist3.6.001h 00m
nwreadinglist3.7.007h 45m
nwreadinglist3.8.001h 30m
nwreadinglist4.0.028h 15m
nwreadinglist4.1.000h 45m
nwshared1.1.008h 00m
nwshared1.2.001h 30m
nwshared1.3.007h 00m
nwshared1.4.006h 30m
nwshared1.5.001h 00m
nwshared1.6.000h 45m
nwshared1.7.003h 00m
nwshared1.7.101h 00m
nwshared1.8.013h 30m
nwtimetracking1.0.031h 30m
nwtimetracking1.1.010h 30m
nwtimetracking1.2.003h 30m
nwtimetracking1.3.009h 45m
nwtimetracking2.0.002h 30m
nwtimetracking2.2.007h 45m
nwtimetracking3.0.003h 30m
nwtimetracking3.2.002h 00m
nwtimetracking3.3.001h 00m
nwtimetracking3.4.001h 00m
nwtimetracking3.5.004h 00m
nwtimetracking3.7.006h 00m
nwtimetracking3.8.001h 00m
nwtimetracking3.9.000h 45m
nwtraderaanalytics1.0.062h 00m
nwtraderaanalytics2.0.012h 00m
nwtraderaanalytics3.0.052h 15m
nwtraderaanalytics4.0.064h 15m
nwtraderaanalytics4.1.001h 45m
nwtraderaanalytics4.2.025h 00m
nwtraderaanalytics4.3.000h 30m
nwtraderaanalytics4.4.002h 00m
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "\n" + "tt_processor.process_tts_by_spn_spv()\n" ] }, { diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 4d964b9..38bfe8f 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -321,6 +321,7 @@ class SettingBag(): tts_by_year_month_display_only_years : Optional[list[int]] = field(default_factory = lambda : YearProvider().get_most_recent_x_years(x = uint(1))) tts_by_year_month_spnv_formatters : dict = field(default_factory = lambda : { "%_DME" : "{:.2f}", "%_TME" : "{:.2f}" }) tts_by_year_spnv_formatters : dict = field(default_factory = lambda : { "%_DYE" : "{:.2f}", "%_TYE" : "{:.2f}" }) + tts_by_spn_formatters : dict = field(default_factory = lambda : { "%_DE" : "{:.2f}", "%_TE" : "{:.2f}" }) tts_by_spn_remove_untagged : bool = field(default = True) tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : uint = field(default = uint(25)) @@ -2054,7 +2055,7 @@ def process_tts_by_spn(self) -> None: df : DataFrame = self.__tt_summary.tts_by_spn_df if "display" in options: - self.__component_bag.displayer.display(df = df) + self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_spn_formatters) def process_tts_by_spn_spv(self) -> None: ''' From 5433d663cf4a3c07731f3fc6b31121651a9ddf45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Tue, 3 Dec 2024 16:58:02 +0000 Subject: [PATCH 060/109] #40 TimeTrackingProcessor - Iteration 17. --- src/nwtimetracking.ipynb | 2512 +++++++++++++++++++------------------- src/nwtimetracking.py | 4 +- 2 files changed, 1233 insertions(+), 1283 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 2ad0863..b16dc9f 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,11 +23,10 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 245, "metadata": {}, "outputs": [], "source": [ - "from pandas import DataFrame\n", "from typing import Optional" ] }, @@ -41,13 +40,12 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 246, "metadata": {}, "outputs": [], "source": [ - "from nwtimetracking import SettingBag, DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, TimeTrackingProcessor\n", - "from nwtimetracking import ComponentBag, TTMarkdownFactory\n", - "from nwpackageversions import LanguageChecker, RequirementChecker, RequirementSummary" + "from nwpackageversions import LanguageChecker, RequirementChecker, RequirementSummary\n", + "from nwtimetracking import SettingBag, ComponentBag, TimeTrackingProcessor" ] }, { @@ -59,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 247, "metadata": {}, "outputs": [ { @@ -110,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 248, "metadata": {}, "outputs": [], "source": [ @@ -122,7 +120,7 @@ " options_tts_by_year_month_spnv = [\"display\"],\n", " options_tts_by_year_spnv = [\"display\"],\n", " options_tts_by_spn = [\"display\"],\n", - " options_tts_by_spn_spv = [\"display\"],\n", + " options_tts_by_spn_spv = [],\n", " options_tts_by_hashtag = [\"display\"],\n", " options_tts_by_hashtag_year = [\"display\"],\n", " options_tts_by_efs = [\"display\"],\n", @@ -130,7 +128,8 @@ " options_definitions = [\"display\"],\n", " excel_nrows = 1301,\n", " tts_by_year_month_spnv_display_only_spn = \"nwtimetracking\",\n", - " tts_by_year_spnv_display_only_spn = \"nwtimetracking\"\n", + " tts_by_year_spnv_display_only_spn = \"nwtimetracking\",\n", + " tts_by_spn_spv_display_only_spn = \"nwtimetracking\"\n", ")\n" ] }, @@ -144,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 249, "metadata": {}, "outputs": [ { @@ -152,87 +151,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -247,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 250, "metadata": {}, "outputs": [ { @@ -255,301 +254,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -564,12 +563,12 @@ } ], "source": [ - "tt_processor.process_tts_by_month()\n" + "tt_processor.process_tts_by_month()" ] }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 251, "metadata": {}, "outputs": [ { @@ -577,92 +576,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -670,12 +669,12 @@ } ], "source": [ - "tt_processor.process_tts_by_year()\n" + "tt_processor.process_tts_by_year()" ] }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 252, "metadata": {}, "outputs": [ { @@ -683,106 +682,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -790,12 +789,12 @@ } ], "source": [ - "tt_processor.process_tts_by_year_month()\n" + "tt_processor.process_tts_by_year_month()" ] }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 253, "metadata": {}, "outputs": [ { @@ -803,202 +802,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1006,12 +1005,12 @@ } ], "source": [ - "tt_processor.process_tts_by_year_month_spnv()\n" + "tt_processor.process_tts_by_year_month_spnv()" ] }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 254, "metadata": {}, "outputs": [ { @@ -1019,175 +1018,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1195,12 +1194,12 @@ } ], "source": [ - "tt_processor.process_tts_by_year_spnv()\n" + "tt_processor.process_tts_by_year_spnv()" ] }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 255, "metadata": {}, "outputs": [ { @@ -1208,123 +1207,123 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagProjectNameEffortDE%_DETE%_TEHashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1332,12 +1331,21 @@ } ], "source": [ - "tt_processor.process_tts_by_spn()\n" + "tt_processor.process_tts_by_spn()" ] }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 256, + "metadata": {}, + "outputs": [], + "source": [ + "tt_processor.process_tts_by_spn_spv()" + ] + }, + { + "cell_type": "code", + "execution_count": 257, "metadata": {}, "outputs": [ { @@ -1345,415 +1353,391 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", + " \n", + "
ProjectNameProjectVersionEffortHashtagEffortEffort%
NW.MarkdownTables1.0.015h 15m
NW.MarkdownTables1.0.102h 30m
NW.MarkdownTables3.0.003h 00m
NW.NGramTextClassification1.0.073h 15m
NW.NGramTextClassification1.1.007h 30m
NW.NGramTextClassification2.0.015h 30m
NW.NGramTextClassification3.0.028h 00m
NW.NGramTextClassification3.5.039h 45m
NW.NGramTextClassification3.6.017h 30m
NW.NGramTextClassification3.7.008h 00m
NW.NGramTextClassification4.0.014h 30m
NW.NGramTextClassification4.1.002h 15m
NW.NGramTextClassification4.2.001h 15m
NW.Shared.Files1.0.005h 30m
NW.Shared.Serialization1.0.004h 15m
NW.Shared.Validation1.0.002h 45m
NW.UnivariateForecasting1.0.0116h 15m
NW.UnivariateForecasting1.0.107h 30m
NW.UnivariateForecasting1.1.004h 00m
NW.UnivariateForecasting1.1.107h 30m
NW.UnivariateForecasting2.0.006h 30m
NW.UnivariateForecasting2.0.102h 00m
NW.UnivariateForecasting2.5.016h 30m
NW.UnivariateForecasting3.0.046h 00m
NW.UnivariateForecasting4.1.001h 00m
NW.UnivariateForecasting4.2.000h 45m#untagged2548h 45m48.98
nwpackageversions1.0.034h 15m#csharp1116h 45m21.46
nwpackageversions1.1.002h 30m#python611h 45m11.76
nwpackageversions1.2.003h 00m#studying419h 30m8.06
nwpackageversions1.6.012h 15m#maintenance333h 45m6.41
nwpackageversions1.8.000h 45m#powershell154h 00m2.96
nwreadinglist1.0.045h 15m
nwreadinglist1.5.016h 15m#overtime19h 00m0.37
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tt_processor.process_tts_by_hashtag()" + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", + " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", + " \n", + "
nwreadinglist1.6.005h 00mYearHashtagEffort
nwreadinglist2.0.001h 30m2019#csharp67h 00m
nwreadinglist2.1.002h 00m2020#csharp206h 30m
nwreadinglist2.2.006h 45m2021#csharp441h 45m
nwreadinglist3.0.010h 00m2022#csharp298h 45m
nwreadinglist3.1.005h 15m2023#csharp67h 30m
nwreadinglist3.2.002h 00m2024#csharp35h 15m
nwreadinglist3.3.000h 30m2019#maintenance53h 45m
nwreadinglist3.4.003h 15m2020#maintenance53h 30m
nwreadinglist3.5.006h 30m2021#maintenance29h 30m
nwreadinglist3.6.001h 00m2022#maintenance36h 30m
nwreadinglist3.7.007h 45m2023#maintenance52h 30m
nwreadinglist3.8.001h 30m2024#maintenance108h 00m
nwreadinglist4.0.028h 15m2020#overtime19h 00m
nwreadinglist4.1.000h 45m2020#powershell116h 45m
nwshared1.1.008h 00m2021#powershell11h 30m
nwshared1.2.001h 30m2022#powershell21h 15m
nwshared1.3.007h 00m2023#powershell04h 30m
nwshared1.4.006h 30m2022#python72h 00m
nwshared1.5.001h 00m2023#python141h 00m
nwshared1.6.000h 45m2024#python398h 45m
nwshared1.7.003h 00m2019#studying71h 30m
nwshared1.7.101h 00m2020#studying74h 45m
nwshared1.8.013h 30m2021#studying54h 45m
nwtimetracking1.0.031h 30m2022#studying39h 00m
nwtimetracking1.1.010h 30m2023#studying54h 45m
nwtimetracking1.2.003h 30m2024#studying124h 45m
nwtimetracking1.3.009h 45m2015#untagged18h 00m
nwtimetracking2.0.002h 30m2016#untagged615h 15m
nwtimetracking2.2.007h 45m2017#untagged762h 45m
nwtimetracking3.0.003h 30m2018#untagged829h 45m
nwtimetracking3.2.002h 00m2019#untagged323h 00m
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tt_processor.process_tts_by_hashtag_year()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 259, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", + " \n", + " \n", + " \n", + "
nwtimetracking3.3.001h 00mStartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tt_processor.process_tts_by_efs()" + ] + }, + { + "cell_type": "code", + "execution_count": 260, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", + " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", + " \n", + "
nwtimetracking3.4.001h 00mTimeRangeIdOccurrences
nwtimetracking3.5.004h 00m23:30-03:301
nwtimetracking3.7.006h 00m23:30-02:451
nwtimetracking3.8.001h 00m23:30-01:451
nwtimetracking3.9.000h 45m13:15-15:151
nwtraderaanalytics1.0.062h 00m17:15-19:001
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tt_processor.process_tts_by_tr()" + ] + }, + { + "cell_type": "code", + "execution_count": 261, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", + " \n", + " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
nwtraderaanalytics2.0.012h 00mTermDefinition
nwtraderaanalytics3.0.052h 15mDMEDevelopment Monthly Effort
nwtraderaanalytics4.0.064h 15mTMETotal Monthly Effort
nwtraderaanalytics4.1.001h 45mDYEDevelopment Yearly Effort
nwtraderaanalytics4.2.025h 00mTYETotal Yearly Effort
nwtraderaanalytics4.3.000h 30mDEDevelopment Effort
nwtraderaanalytics4.4.002h 00mTETotal Effort
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1761,43 +1745,7 @@ } ], "source": [ - "tt_processor.process_tts_by_spn_spv()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" + "tt_processor.process_definitions()\n" ] } ], diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 38bfe8f..dae95ad 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -304,6 +304,7 @@ class SettingBag(): excel_nrows : int tts_by_year_month_spnv_display_only_spn : Optional[str] tts_by_year_spnv_display_only_spn : Optional[str] + tts_by_spn_spv_display_only_spn : Optional[str] # With Defaults working_folder_path : str = field(default = "/home/nwtimetracking/") @@ -323,6 +324,7 @@ class SettingBag(): tts_by_year_spnv_formatters : dict = field(default_factory = lambda : { "%_DYE" : "{:.2f}", "%_TYE" : "{:.2f}" }) tts_by_spn_formatters : dict = field(default_factory = lambda : { "%_DE" : "{:.2f}", "%_TE" : "{:.2f}" }) tts_by_spn_remove_untagged : bool = field(default = True) + tts_by_hashtag_formatters : dict = field(default_factory = lambda : { "Effort%" : "{:.2f}" }) tts_by_efs_is_correct : bool = field(default = False) tts_by_efs_n : uint = field(default = uint(25)) tts_by_tr_unknown_id : str = field(default = "Unknown") @@ -2085,7 +2087,7 @@ def process_tts_by_hashtag(self) -> None: df : DataFrame = self.__tt_summary.tts_by_hashtag_df if "display" in options: - self.__component_bag.displayer.display(df = df) + self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_hashtag_formatters) def process_tts_by_hashtag_year(self) -> None: ''' From 601721275a1ca177fcbaf2ceb8ef7e3d457df934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 11:22:36 +0000 Subject: [PATCH 061/109] #40 TimeTrackingProcessor, process_tts_by_tr(): fixed. --- src/nwtimetracking.ipynb | 2236 +++++++++++++++++++------------------- src/nwtimetracking.py | 38 +- 2 files changed, 1146 insertions(+), 1128 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index b16dc9f..f1d87ee 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 245, + "execution_count": 394, "metadata": {}, "outputs": [], "source": [ @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 246, + "execution_count": 395, "metadata": {}, "outputs": [], "source": [ @@ -57,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 247, + "execution_count": 396, "metadata": {}, "outputs": [ { @@ -108,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 248, + "execution_count": 397, "metadata": {}, "outputs": [], "source": [ @@ -143,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 249, + "execution_count": 398, "metadata": {}, "outputs": [ { @@ -151,87 +151,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -246,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 250, + "execution_count": 399, "metadata": {}, "outputs": [ { @@ -254,301 +254,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -568,7 +568,7 @@ }, { "cell_type": "code", - "execution_count": 251, + "execution_count": 400, "metadata": {}, "outputs": [ { @@ -576,92 +576,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -674,7 +674,7 @@ }, { "cell_type": "code", - "execution_count": 252, + "execution_count": 401, "metadata": {}, "outputs": [ { @@ -682,106 +682,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -794,7 +794,7 @@ }, { "cell_type": "code", - "execution_count": 253, + "execution_count": 402, "metadata": {}, "outputs": [ { @@ -802,202 +802,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1010,7 +1010,7 @@ }, { "cell_type": "code", - "execution_count": 254, + "execution_count": 403, "metadata": {}, "outputs": [ { @@ -1018,175 +1018,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1199,7 +1199,7 @@ }, { "cell_type": "code", - "execution_count": 255, + "execution_count": 404, "metadata": {}, "outputs": [ { @@ -1207,123 +1207,123 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagProjectNameEffortDE%_DETE%_TEHashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1336,7 +1336,7 @@ }, { "cell_type": "code", - "execution_count": 256, + "execution_count": 405, "metadata": {}, "outputs": [], "source": [ @@ -1345,7 +1345,7 @@ }, { "cell_type": "code", - "execution_count": 257, + "execution_count": 406, "metadata": {}, "outputs": [ { @@ -1353,55 +1353,55 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagEffortEffort%HashtagEffortEffort%
#untagged2548h 45m48.98#untagged2548h 45m48.98
#csharp1116h 45m21.46#csharp1116h 45m21.46
#python611h 45m11.76#python611h 45m11.76
#studying419h 30m8.06#studying419h 30m8.06
#maintenance333h 45m6.41#maintenance333h 45m6.41
#powershell154h 00m2.96#powershell154h 00m2.96
#overtime19h 00m0.37#overtime19h 00m0.37
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1414,7 +1414,7 @@ }, { "cell_type": "code", - "execution_count": 258, + "execution_count": 407, "metadata": {}, "outputs": [ { @@ -1422,175 +1422,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearHashtagEffortYearHashtagEffort
2019#csharp67h 00m2019#csharp67h 00m
2020#csharp206h 30m2020#csharp206h 30m
2021#csharp441h 45m2021#csharp441h 45m
2022#csharp298h 45m2022#csharp298h 45m
2023#csharp67h 30m2023#csharp67h 30m
2024#csharp35h 15m2024#csharp35h 15m
2019#maintenance53h 45m2019#maintenance53h 45m
2020#maintenance53h 30m2020#maintenance53h 30m
2021#maintenance29h 30m2021#maintenance29h 30m
2022#maintenance36h 30m2022#maintenance36h 30m
2023#maintenance52h 30m2023#maintenance52h 30m
2024#maintenance108h 00m2024#maintenance108h 00m
2020#overtime19h 00m2020#overtime19h 00m
2020#powershell116h 45m2020#powershell116h 45m
2021#powershell11h 30m2021#powershell11h 30m
2022#powershell21h 15m2022#powershell21h 15m
2023#powershell04h 30m2023#powershell04h 30m
2022#python72h 00m2022#python72h 00m
2023#python141h 00m2023#python141h 00m
2024#python398h 45m2024#python398h 45m
2019#studying71h 30m2019#studying71h 30m
2020#studying74h 45m2020#studying74h 45m
2021#studying54h 45m2021#studying54h 45m
2022#studying39h 00m2022#studying39h 00m
2023#studying54h 45m2023#studying54h 45m
2024#studying124h 45m2024#studying124h 45m
2015#untagged18h 00m2015#untagged18h 00m
2016#untagged615h 15m2016#untagged615h 15m
2017#untagged762h 45m2017#untagged762h 45m
2018#untagged829h 45m2018#untagged829h 45m
2019#untagged323h 00m2019#untagged323h 00m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1598,12 +1598,12 @@ } ], "source": [ - "tt_processor.process_tts_by_hashtag_year()\n" + "tt_processor.process_tts_by_hashtag_year()" ] }, { "cell_type": "code", - "execution_count": 259, + "execution_count": 408, "metadata": {}, "outputs": [ { @@ -1611,15 +1611,15 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1627,7 +1627,7 @@ "
StartTimeEndTimeEffortES_IsCorrectES_ExpectedES_MessageStartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1640,7 +1640,7 @@ }, { "cell_type": "code", - "execution_count": 260, + "execution_count": 409, "metadata": {}, "outputs": [ { @@ -1648,39 +1648,59 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
TimeRangeIdOccurrencesTimeRangeIdOccurrences
23:30-03:30108:00-08:4543
23:30-02:45108:00-08:3025
23:30-01:45118:00-20:0021
13:15-15:15117:30-18:0018
17:15-19:00119:00-20:0017
17:15-17:4515
17:00-17:3015
17:00-20:0011
17:00-17:4511
18:30-20:0010
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1693,7 +1713,7 @@ }, { "cell_type": "code", - "execution_count": 261, + "execution_count": 410, "metadata": {}, "outputs": [ { @@ -1701,43 +1721,43 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TermDefinitionTermDefinition
DMEDevelopment Monthly EffortDMEDevelopment Monthly Effort
TMETotal Monthly EffortTMETotal Monthly Effort
DYEDevelopment Yearly EffortDYEDevelopment Yearly Effort
TYETotal Yearly EffortTYETotal Yearly Effort
DEDevelopment EffortDEDevelopment Effort
TETotal EffortTETotal Effort
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1745,7 +1765,7 @@ } ], "source": [ - "tt_processor.process_definitions()\n" + "tt_processor.process_definitions()" ] } ], diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index dae95ad..49511ed 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -330,6 +330,8 @@ class SettingBag(): tts_by_tr_unknown_id : str = field(default = "Unknown") tts_by_tr_remove_unknown_occurrences : bool = field(default = True) tts_by_tr_filter_by_top_n : Optional[uint] = field(default = uint(5)) + tts_by_tr_head_n : Optional[uint] = field(default = uint(10)) + tts_by_tr_display_head_n_with_tail : bool = field(default = False) md_infos : list[MDInfo] = field(default_factory = lambda : MDInfoProvider().get_all()) md_last_update : datetime = field(default = datetime.now()) class TTDataFrameHelper(): @@ -1228,15 +1230,6 @@ def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) - filtered_df = tts_by_efs_df.loc[condition] return filtered_df - def __filter_by_top_n_occurrences(self, tts_by_tr_df : DataFrame, n : uint) -> DataFrame: - - '''Returns only the top n rows by "Occurrences" of the provided DataFrame.''' - - tts_by_tr_df.sort_values(by = TTCN.OCCURRENCES, ascending = [True], inplace = True) - tts_by_tr_df = tts_by_tr_df.iloc[0:n] - tts_by_tr_df.reset_index(drop = True, inplace = True) - - return tts_by_tr_df def create_tt(self, excel_path : str, excel_skiprows : int, excel_nrows : int, excel_tabname : str) -> DataFrame: @@ -1587,7 +1580,7 @@ def create_tts_by_efs(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataF tts_flt_df : DataFrame = self.__filter_by_is_correct(tts_by_efs_df = tts_df, is_correct = is_correct) return (tts_df, tts_flt_df) - def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_occurrences : bool, filter_by_top_n : Optional[uint]) -> DataFrame: + def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_occurrences : bool) -> DataFrame: ''' TimeRangeId Occurrences @@ -1598,8 +1591,8 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_o ''' tts_df : DataFrame = tt_df.copy(deep = True) - tts_df = tts_df[[TTCN.STARTTIME, TTCN.ENDTIME]] + tts_df[TTCN.TIMERANGEID] = tts_df.apply( lambda x : self.__df_helper.create_time_range_id( start_time = x[TTCN.STARTTIME], @@ -1609,14 +1602,13 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_o count : NamedAgg = pd.NamedAgg(column = TTCN.TIMERANGEID, aggfunc = "count") tts_df = tts_df[[TTCN.TIMERANGEID]].groupby(by = [TTCN.TIMERANGEID], as_index=False).agg(count = count) tts_df.rename(columns={"count" : TTCN.OCCURRENCES}, inplace = True) - tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = False).reset_index(drop = True) + + ascending : bool = False + tts_df = tts_df.sort_values(by = [TTCN.OCCURRENCES], ascending = ascending).reset_index(drop = True) if remove_unknown_occurrences: tts_df = self.__remove_unknown_occurrences(tts_by_tr_df = tts_df, unknown_id = unknown_id) - if filter_by_top_n is not None: - tts_df = self.__filter_by_top_n_occurrences(tts_by_tr_df = tts_df, n = filter_by_top_n) - return tts_df def create_definitions(self) -> DataFrame: @@ -1840,8 +1832,7 @@ def __create_tts_by_tr_df(self, tt_df : DataFrame) -> DataFrame: tts_by_tr_df : DataFrame = self.__component_bag.df_factory.create_tts_by_tr( tt_df = tt_df, unknown_id = self.__setting_bag.tts_by_tr_unknown_id, - remove_unknown_occurrences = self.__setting_bag.tts_by_tr_remove_unknown_occurrences, - filter_by_top_n = self.__setting_bag.tts_by_tr_filter_by_top_n + remove_unknown_occurrences = self.__setting_bag.tts_by_tr_remove_unknown_occurrences ) return tts_by_tr_df @@ -1857,7 +1848,7 @@ def __create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame return tts_by_month_md - def __optimize_for_display(self, df : DataFrame, head_n : Optional[uint], display_head_n_with_tail : bool) -> DataFrame: + def __orchestrate_head_n(self, df : DataFrame, head_n : Optional[uint], display_head_n_with_tail : bool) -> DataFrame: '''Prepares df for display().''' @@ -1869,7 +1860,7 @@ def __optimize_for_display(self, df : DataFrame, head_n : Optional[uint], displa return df.head(n = int(head_n)) def __optimize_tt_for_display(self, tt_df : DataFrame) -> DataFrame: - return self.__optimize_for_display( + return self.__orchestrate_head_n( df = tt_df, head_n = self.__setting_bag.tt_head_n, display_head_n_with_tail = self.__setting_bag.tt_display_head_n_with_tail @@ -1910,6 +1901,13 @@ def __optimize_tts_by_year_spnv_for_display(self, tts_by_year_spnv_tpl : Tuple[D return tts_by_year_spnv_tpl[0] return tts_by_year_spnv_tpl[1] + def __optimize_tts_by_tr_for_display(self, tts_by_tr_df : DataFrame) -> DataFrame: + + return self.__orchestrate_head_n( + df = tts_by_tr_df, + head_n = self.__setting_bag.tts_by_tr_head_n, + display_head_n_with_tail = self.__setting_bag.tts_by_tr_display_head_n_with_tail + ) def initialize(self) -> None: @@ -2129,7 +2127,7 @@ def process_tts_by_tr(self) -> None: self.__validate_summary() options : list = self.__setting_bag.options_tts_by_tr - df : DataFrame = self.__tt_summary.tts_by_tr_df + df : DataFrame = self.__optimize_tts_by_tr_for_display(tts_by_tr_df = self.__tt_summary.tts_by_tr_df) if "display" in options: self.__component_bag.displayer.display(df = df) From 1c42da6252721e2470c73920f51d78bf80d9ce28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 11:54:08 +0000 Subject: [PATCH 062/109] #40 TimeTrackingProcessor, __try_log_definitions(): added. --- src/nwtimetracking.ipynb | 2242 +++++++++++++++++++------------------- src/nwtimetracking.py | 41 +- 2 files changed, 1159 insertions(+), 1124 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index f1d87ee..239a4d8 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 394, + "execution_count": 428, "metadata": {}, "outputs": [], "source": [ @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 395, + "execution_count": 429, "metadata": {}, "outputs": [], "source": [ @@ -57,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 396, + "execution_count": 430, "metadata": {}, "outputs": [ { @@ -108,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 397, + "execution_count": 431, "metadata": {}, "outputs": [], "source": [ @@ -119,7 +119,7 @@ " options_tts_by_year_month = [\"display\"],\n", " options_tts_by_year_month_spnv = [\"display\"],\n", " options_tts_by_year_spnv = [\"display\"],\n", - " options_tts_by_spn = [\"display\"],\n", + " options_tts_by_spn = [\"display\", \"log\"],\n", " options_tts_by_spn_spv = [],\n", " options_tts_by_hashtag = [\"display\"],\n", " options_tts_by_hashtag_year = [\"display\"],\n", @@ -143,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 398, + "execution_count": 432, "metadata": {}, "outputs": [ { @@ -151,87 +151,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -246,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 399, + "execution_count": 433, "metadata": {}, "outputs": [ { @@ -254,301 +254,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -568,7 +568,7 @@ }, { "cell_type": "code", - "execution_count": 400, + "execution_count": 434, "metadata": {}, "outputs": [ { @@ -576,92 +576,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -674,7 +674,7 @@ }, { "cell_type": "code", - "execution_count": 401, + "execution_count": 435, "metadata": {}, "outputs": [ { @@ -682,106 +682,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -794,7 +794,7 @@ }, { "cell_type": "code", - "execution_count": 402, + "execution_count": 436, "metadata": {}, "outputs": [ { @@ -802,202 +802,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1010,7 +1010,7 @@ }, { "cell_type": "code", - "execution_count": 403, + "execution_count": 437, "metadata": {}, "outputs": [ { @@ -1018,175 +1018,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1199,7 +1199,7 @@ }, { "cell_type": "code", - "execution_count": 404, + "execution_count": 438, "metadata": {}, "outputs": [ { @@ -1207,127 +1207,135 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagProjectNameEffortDE%_DETE%_TEHashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DE: Development Effort\n", + "TE: Total Effort\n" + ] } ], "source": [ @@ -1336,7 +1344,7 @@ }, { "cell_type": "code", - "execution_count": 405, + "execution_count": 439, "metadata": {}, "outputs": [], "source": [ @@ -1345,7 +1353,7 @@ }, { "cell_type": "code", - "execution_count": 406, + "execution_count": 440, "metadata": {}, "outputs": [ { @@ -1353,55 +1361,55 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagEffortEffort%HashtagEffortEffort%
#untagged2548h 45m48.98#untagged2548h 45m48.98
#csharp1116h 45m21.46#csharp1116h 45m21.46
#python611h 45m11.76#python611h 45m11.76
#studying419h 30m8.06#studying419h 30m8.06
#maintenance333h 45m6.41#maintenance333h 45m6.41
#powershell154h 00m2.96#powershell154h 00m2.96
#overtime19h 00m0.37#overtime19h 00m0.37
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1414,7 +1422,7 @@ }, { "cell_type": "code", - "execution_count": 407, + "execution_count": 441, "metadata": {}, "outputs": [ { @@ -1422,175 +1430,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearHashtagEffortYearHashtagEffort
2019#csharp67h 00m2019#csharp67h 00m
2020#csharp206h 30m2020#csharp206h 30m
2021#csharp441h 45m2021#csharp441h 45m
2022#csharp298h 45m2022#csharp298h 45m
2023#csharp67h 30m2023#csharp67h 30m
2024#csharp35h 15m2024#csharp35h 15m
2019#maintenance53h 45m2019#maintenance53h 45m
2020#maintenance53h 30m2020#maintenance53h 30m
2021#maintenance29h 30m2021#maintenance29h 30m
2022#maintenance36h 30m2022#maintenance36h 30m
2023#maintenance52h 30m2023#maintenance52h 30m
2024#maintenance108h 00m2024#maintenance108h 00m
2020#overtime19h 00m2020#overtime19h 00m
2020#powershell116h 45m2020#powershell116h 45m
2021#powershell11h 30m2021#powershell11h 30m
2022#powershell21h 15m2022#powershell21h 15m
2023#powershell04h 30m2023#powershell04h 30m
2022#python72h 00m2022#python72h 00m
2023#python141h 00m2023#python141h 00m
2024#python398h 45m2024#python398h 45m
2019#studying71h 30m2019#studying71h 30m
2020#studying74h 45m2020#studying74h 45m
2021#studying54h 45m2021#studying54h 45m
2022#studying39h 00m2022#studying39h 00m
2023#studying54h 45m2023#studying54h 45m
2024#studying124h 45m2024#studying124h 45m
2015#untagged18h 00m2015#untagged18h 00m
2016#untagged615h 15m2016#untagged615h 15m
2017#untagged762h 45m2017#untagged762h 45m
2018#untagged829h 45m2018#untagged829h 45m
2019#untagged323h 00m2019#untagged323h 00m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1603,7 +1611,7 @@ }, { "cell_type": "code", - "execution_count": 408, + "execution_count": 442, "metadata": {}, "outputs": [ { @@ -1611,15 +1619,15 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1627,7 +1635,7 @@ "
StartTimeEndTimeEffortES_IsCorrectES_ExpectedES_MessageStartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1640,7 +1648,7 @@ }, { "cell_type": "code", - "execution_count": 409, + "execution_count": 443, "metadata": {}, "outputs": [ { @@ -1648,59 +1656,59 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TimeRangeIdOccurrencesTimeRangeIdOccurrences
08:00-08:454308:00-08:4543
08:00-08:302508:00-08:3025
18:00-20:002118:00-20:0021
17:30-18:001817:30-18:0018
19:00-20:001719:00-20:0017
17:15-17:451517:15-17:4515
17:00-17:301517:00-17:3015
17:00-20:001117:00-20:0011
17:00-17:451117:00-17:4511
18:30-20:001018:30-20:0010
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1713,7 +1721,7 @@ }, { "cell_type": "code", - "execution_count": 410, + "execution_count": 444, "metadata": {}, "outputs": [ { @@ -1721,43 +1729,43 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TermDefinitionTermDefinition
DMEDevelopment Monthly EffortDMEDevelopment Monthly Effort
TMETotal Monthly EffortTMETotal Monthly Effort
DYEDevelopment Yearly EffortDYEDevelopment Yearly Effort
TYETotal Yearly EffortTYETotal Yearly Effort
DEDevelopment EffortDEDevelopment Effort
TETotal EffortTETotal Effort
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 49511ed..7efe5c6 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -67,6 +67,12 @@ class TTID(StrEnum): '''Collects all the ids that identify the dataframes created by TTDataFrameFactory.''' TTSBYMONTH = "tts_by_month" +class DEFINITIONSCN(StrEnum): + + '''Collects all the column names used by definitions.''' + + TERM = "Term" + DEFINITION = "Definition" # STATIC CLASSES class _MessageCollection(): @@ -294,8 +300,8 @@ class SettingBag(): options_tts_by_year_month : list[Literal["display"]] options_tts_by_year_month_spnv : list[Literal["display"]] options_tts_by_year_spnv : list[Literal["display"]] - options_tts_by_spn : list[Literal["display"]] - options_tts_by_spn_spv : list[Literal["display"]] + options_tts_by_spn : list[Literal["display", "log"]] + options_tts_by_spn_spv : list[Literal["display", "log"]] options_tts_by_hashtag : list[Literal["display"]] options_tts_by_hashtag_year : list[Literal["display"]] options_tts_by_efs : list[Literal["display"]] @@ -1614,7 +1620,7 @@ def create_definitions(self) -> DataFrame: '''Creates a dataframe containing all the definitions in use in this application.''' - columns : list[str] = ["Term", "Definition"] + columns : list[str] = [DEFINITIONSCN.TERM, DEFINITIONSCN.DEFINITION] definitions : dict[str, str] = { "DME": "Development Monthly Effort", @@ -1706,6 +1712,15 @@ def __save_and_log(self, id : TTID, content : str) -> None: message : str = _MessageCollection.this_content_successfully_saved_as(id = id, file_path = file_path) self.__component_bag.logging_function(message) + def __try_log_definitions(self, df : DataFrame, definitions : DataFrame) -> None: + + """Logs the definitions for matching column names in the DataFrame.""" + + definitions_dict : dict = definitions.set_index(DEFINITIONSCN.TERM)[DEFINITIONSCN.DEFINITION].to_dict() + + for column_name in df.columns: + if column_name in definitions_dict: + print(f"{column_name}: {definitions_dict[column_name]}") def __create_tt_df(self) -> DataFrame: @@ -2023,9 +2038,10 @@ def process_tts_by_year_month_spnv(self) -> None: options : list = self.__setting_bag.options_tts_by_year_month_spnv df : DataFrame = self.__optimize_tts_by_year_month_spnv_for_display(tts_by_year_month_spnv_tpl = self.__tt_summary.tts_by_year_month_spnv_tpl) + formatters : dict = self.__setting_bag.tts_by_year_month_spnv_formatters if "display" in options: - self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_month_spnv_formatters) + self.__component_bag.displayer.display(df = df, formatters = formatters) def process_tts_by_year_spnv(self) -> None: ''' @@ -2038,9 +2054,10 @@ def process_tts_by_year_spnv(self) -> None: options : list = self.__setting_bag.options_tts_by_year_spnv df : DataFrame = self.__optimize_tts_by_year_spnv_for_display(tts_by_year_spnv_tpl = self.__tt_summary.tts_by_year_spnv_tpl) + formatters : dict = self.__setting_bag.tts_by_year_spnv_formatters if "display" in options: - self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_year_spnv_formatters) + self.__component_bag.displayer.display(df = df, formatters = formatters) def process_tts_by_spn(self) -> None: ''' @@ -2053,9 +2070,14 @@ def process_tts_by_spn(self) -> None: options : list = self.__setting_bag.options_tts_by_spn df : DataFrame = self.__tt_summary.tts_by_spn_df + formatters : dict = self.__setting_bag.tts_by_spn_formatters + definitions_df : DataFrame = self.__tt_summary.definitions_df if "display" in options: - self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_spn_formatters) + self.__component_bag.displayer.display(df = df, formatters = formatters) + + if "log" in options: + self.__try_log_definitions(df = df, definitions = definitions_df) def process_tts_by_spn_spv(self) -> None: ''' @@ -2068,9 +2090,13 @@ def process_tts_by_spn_spv(self) -> None: options : list = self.__setting_bag.options_tts_by_spn_spv df : DataFrame = self.__tt_summary.tts_by_spn_spv_df + definitions_df : DataFrame = self.__tt_summary.definitions_df if "display" in options: self.__component_bag.displayer.display(df = df) + + if "log" in options: + self.__try_log_definitions(df = df, definitions = definitions_df) def process_tts_by_hashtag(self) -> None: ''' @@ -2083,9 +2109,10 @@ def process_tts_by_hashtag(self) -> None: options : list = self.__setting_bag.options_tts_by_hashtag df : DataFrame = self.__tt_summary.tts_by_hashtag_df + formatters : dict = self.__setting_bag.tts_by_hashtag_formatters if "display" in options: - self.__component_bag.displayer.display(df = df, formatters = self.__setting_bag.tts_by_hashtag_formatters) + self.__component_bag.displayer.display(df = df, formatters = formatters) def process_tts_by_hashtag_year(self) -> None: ''' From 53d47daa8fb4f66b57c8bdf0c2dbb4729ebc954a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 12:56:10 +0000 Subject: [PATCH 063/109] #40 TimeTrackingProcessor => TTAdapter - Iteration 1. --- src/nwtimetracking.ipynb | 2259 +++++++++++++++++++------------------- src/nwtimetracking.py | 316 +++--- 2 files changed, 1311 insertions(+), 1264 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 239a4d8..b559513 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,9 +23,34 @@ }, { "cell_type": "code", - "execution_count": 428, + "execution_count": 445, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", + " superreload(m, reload, self.old_objects)\n", + " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 475, in superreload\n", + " module = reload(module)\n", + " ^^^^^^^^^^^^^^\n", + " File \"/usr/local/lib/python3.12/importlib/__init__.py\", line 131, in reload\n", + " _bootstrap._exec(spec, module)\n", + " File \"\", line 866, in _exec\n", + " File \"\", line 991, in exec_module\n", + " File \"\", line 1129, in get_code\n", + " File \"\", line 1059, in source_to_code\n", + " File \"\", line 488, in _call_with_frames_removed\n", + " File \"/workspaces/nwtimetracking/src/nwtimetracking.py\", line 1709\n", + " '''Creates the expected dataframe out of the provided arguments.''''\n", + " ^\n", + "SyntaxError: unterminated string literal (detected at line 1709)\n", + "]\n" + ] + } + ], "source": [ "from typing import Optional" ] @@ -40,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 429, + "execution_count": 446, "metadata": {}, "outputs": [], "source": [ @@ -57,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 430, + "execution_count": 447, "metadata": {}, "outputs": [ { @@ -108,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 431, + "execution_count": 448, "metadata": {}, "outputs": [], "source": [ @@ -143,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 432, + "execution_count": 449, "metadata": {}, "outputs": [ { @@ -151,87 +176,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -246,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 433, + "execution_count": 450, "metadata": {}, "outputs": [ { @@ -254,301 +279,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -568,7 +593,7 @@ }, { "cell_type": "code", - "execution_count": 434, + "execution_count": 451, "metadata": {}, "outputs": [ { @@ -576,92 +601,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -674,7 +699,7 @@ }, { "cell_type": "code", - "execution_count": 435, + "execution_count": 452, "metadata": {}, "outputs": [ { @@ -682,106 +707,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -794,7 +819,7 @@ }, { "cell_type": "code", - "execution_count": 436, + "execution_count": 453, "metadata": {}, "outputs": [ { @@ -802,202 +827,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1010,7 +1035,7 @@ }, { "cell_type": "code", - "execution_count": 437, + "execution_count": 454, "metadata": {}, "outputs": [ { @@ -1018,175 +1043,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1199,7 +1224,7 @@ }, { "cell_type": "code", - "execution_count": 438, + "execution_count": 455, "metadata": {}, "outputs": [ { @@ -1207,123 +1232,123 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagProjectNameEffortDE%_DETE%_TEHashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1344,7 +1369,7 @@ }, { "cell_type": "code", - "execution_count": 439, + "execution_count": 456, "metadata": {}, "outputs": [], "source": [ @@ -1353,7 +1378,7 @@ }, { "cell_type": "code", - "execution_count": 440, + "execution_count": 457, "metadata": {}, "outputs": [ { @@ -1361,55 +1386,55 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagEffortEffort%HashtagEffortEffort%
#untagged2548h 45m48.98#untagged2548h 45m48.98
#csharp1116h 45m21.46#csharp1116h 45m21.46
#python611h 45m11.76#python611h 45m11.76
#studying419h 30m8.06#studying419h 30m8.06
#maintenance333h 45m6.41#maintenance333h 45m6.41
#powershell154h 00m2.96#powershell154h 00m2.96
#overtime19h 00m0.37#overtime19h 00m0.37
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1422,7 +1447,7 @@ }, { "cell_type": "code", - "execution_count": 441, + "execution_count": 458, "metadata": {}, "outputs": [ { @@ -1430,175 +1455,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearHashtagEffortYearHashtagEffort
2019#csharp67h 00m2019#csharp67h 00m
2020#csharp206h 30m2020#csharp206h 30m
2021#csharp441h 45m2021#csharp441h 45m
2022#csharp298h 45m2022#csharp298h 45m
2023#csharp67h 30m2023#csharp67h 30m
2024#csharp35h 15m2024#csharp35h 15m
2019#maintenance53h 45m2019#maintenance53h 45m
2020#maintenance53h 30m2020#maintenance53h 30m
2021#maintenance29h 30m2021#maintenance29h 30m
2022#maintenance36h 30m2022#maintenance36h 30m
2023#maintenance52h 30m2023#maintenance52h 30m
2024#maintenance108h 00m2024#maintenance108h 00m
2020#overtime19h 00m2020#overtime19h 00m
2020#powershell116h 45m2020#powershell116h 45m
2021#powershell11h 30m2021#powershell11h 30m
2022#powershell21h 15m2022#powershell21h 15m
2023#powershell04h 30m2023#powershell04h 30m
2022#python72h 00m2022#python72h 00m
2023#python141h 00m2023#python141h 00m
2024#python398h 45m2024#python398h 45m
2019#studying71h 30m2019#studying71h 30m
2020#studying74h 45m2020#studying74h 45m
2021#studying54h 45m2021#studying54h 45m
2022#studying39h 00m2022#studying39h 00m
2023#studying54h 45m2023#studying54h 45m
2024#studying124h 45m2024#studying124h 45m
2015#untagged18h 00m2015#untagged18h 00m
2016#untagged615h 15m2016#untagged615h 15m
2017#untagged762h 45m2017#untagged762h 45m
2018#untagged829h 45m2018#untagged829h 45m
2019#untagged323h 00m2019#untagged323h 00m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1611,7 +1636,7 @@ }, { "cell_type": "code", - "execution_count": 442, + "execution_count": 459, "metadata": {}, "outputs": [ { @@ -1619,15 +1644,15 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1635,7 +1660,7 @@ "
StartTimeEndTimeEffortES_IsCorrectES_ExpectedES_MessageStartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1648,7 +1673,7 @@ }, { "cell_type": "code", - "execution_count": 443, + "execution_count": 460, "metadata": {}, "outputs": [ { @@ -1656,59 +1681,59 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TimeRangeIdOccurrencesTimeRangeIdOccurrences
08:00-08:454308:00-08:4543
08:00-08:302508:00-08:3025
18:00-20:002118:00-20:0021
17:30-18:001817:30-18:0018
19:00-20:001719:00-20:0017
17:15-17:451517:15-17:4515
17:00-17:301517:00-17:3015
17:00-20:001117:00-20:0011
17:00-17:451117:00-17:4511
18:30-20:001018:30-20:0010
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1721,7 +1746,7 @@ }, { "cell_type": "code", - "execution_count": 444, + "execution_count": 461, "metadata": {}, "outputs": [ { @@ -1729,43 +1754,43 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TermDefinitionTermDefinition
DMEDevelopment Monthly EffortDMEDevelopment Monthly Effort
TMETotal Monthly EffortTMETotal Monthly Effort
DYEDevelopment Yearly EffortDYEDevelopment Yearly Effort
TYETotal Yearly EffortTYETotal Yearly Effort
DEDevelopment EffortDEDevelopment Effort
TETotal EffortTETotal Effort
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 7efe5c6..f058cd4 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1660,208 +1660,261 @@ def create_tts_by_month_md(self, paragraph_title : str, last_update : datetime, md_content += "\n" return md_content -@dataclass(frozen=True) -class ComponentBag(): +class TTAdapter(): - '''Represents a collection of components.''' + '''Adapts SettingBag properties for use in TT*Factory methods.''' - file_path_manager : FilePathManager = field(default = FilePathManager()) - file_manager : FileManager = field(default = FileManager(file_path_manager = FilePathManager())) - df_factory : TTDataFrameFactory = field(default = TTDataFrameFactory(df_helper = TTDataFrameHelper())) - md_factory : TTMarkdownFactory = field(default = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter()))) - logging_function : Callable[[str], None] = field(default = LambdaProvider().get_default_logging_function()) - displayer : Displayer = field(default = Displayer()) -class TimeTrackingProcessor(): + __df_factory : TTDataFrameFactory + __md_factory : TTMarkdownFactory - '''Collects all the logic related to the processing of "Time Tracking.xlsx".''' - - __component_bag : ComponentBag - __setting_bag : SettingBag - __tt_summary : TTSummary - - def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> None: - - self.__component_bag = component_bag - self.__setting_bag = setting_bag + def __init__(self, df_factory : TTDataFrameFactory, md_factory : TTMarkdownFactory) -> None: + + self.__df_factory = df_factory + self.__md_factory = md_factory - def __extract_file_name_and_paragraph_title(self, id : TTID) -> Tuple[str, str]: + def extract_file_name_and_paragraph_title(self, id : TTID, setting_bag : SettingBag) -> Tuple[str, str]: '''Returns (file_name, paragraph_title) for the provided id or raise an Exception.''' - for md_info in self.__setting_bag.md_infos: + for md_info in setting_bag.md_infos: if md_info.id == id: return (md_info.file_name, md_info.paragraph_title) raise Exception(_MessageCollection.no_mdinfo_found(id = id)) - def __validate_summary(self) -> None: - - '''Raises an exception if __tt_summary is None.''' - - if not hasattr(self, '_TimeTrackingProcessor__tt_summary'): - raise Exception(_MessageCollection.please_run_initialize_first()) - def __save_and_log(self, id : TTID, content : str) -> None: - - '''Creates the provided Markdown content using __setting_bag.''' + def create_tt_df(self, setting_bag : SettingBag) -> DataFrame: - file_path : str = self.__component_bag.file_path_manager.create_file_path( - folder_path = self.__setting_bag.working_folder_path, - file_name = self.__extract_file_name_and_paragraph_title(id = id)[0] - ) - - self.__component_bag.file_manager.save_content(content = content, file_path = file_path) + '''Creates the expected dataframe out of the provided arguments.''' - message : str = _MessageCollection.this_content_successfully_saved_as(id = id, file_path = file_path) - self.__component_bag.logging_function(message) - def __try_log_definitions(self, df : DataFrame, definitions : DataFrame) -> None: - - """Logs the definitions for matching column names in the DataFrame.""" - - definitions_dict : dict = definitions.set_index(DEFINITIONSCN.TERM)[DEFINITIONSCN.DEFINITION].to_dict() - - for column_name in df.columns: - if column_name in definitions_dict: - print(f"{column_name}: {definitions_dict[column_name]}") - - def __create_tt_df(self) -> DataFrame: - - '''Creates the expected dataframe using __setting_bag.''' - - tt_df : DataFrame = self.__component_bag.df_factory.create_tt( - excel_path = self.__setting_bag.excel_path, - excel_skiprows = self.__setting_bag.excel_skiprows, - excel_nrows = self.__setting_bag.excel_nrows, - excel_tabname = self.__setting_bag.excel_tabname + tt_df : DataFrame = self.__df_factory.create_tt( + excel_path = setting_bag.excel_path, + excel_skiprows = setting_bag.excel_skiprows, + excel_nrows = setting_bag.excel_nrows, + excel_tabname = setting_bag.excel_tabname ) return tt_df - def __create_tts_by_month_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_month_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframes out of the provided arguments.''' - tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_month( + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_month( tt_df = tt_df, - years = self.__setting_bag.years, - now = self.__setting_bag.now + years = setting_bag.years, + now = setting_bag.now ) return tts_by_month_tpl - def __create_tts_by_year_df(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_year_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframe out of the provided arguments.''' - tts_by_year_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year( + tts_by_year_df : DataFrame = self.__df_factory.create_tts_by_year( tt_df = tt_df, - years = self.__setting_bag.years, - yearly_targets = self.__setting_bag.yearly_targets, + years = setting_bag.years, + yearly_targets = setting_bag.yearly_targets, ) return tts_by_year_df - def __create_tts_by_year_month_df(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_month_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframes out of the provided arguments.''' display_only_years : list[int] = [] if display_only_years is not None: - display_only_years = cast(list[int], self.__setting_bag.tts_by_year_month_display_only_years) + display_only_years = cast(list[int], setting_bag.tts_by_year_month_display_only_years) - tts_by_year_month_df : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_month( + tts_by_year_month_df : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month( tt_df = tt_df, - years = self.__setting_bag.years, - yearly_targets = self.__setting_bag.yearly_targets, + years = setting_bag.years, + yearly_targets = setting_bag.yearly_targets, display_only_years = display_only_years ) return tts_by_year_month_df - def __create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframes out of the provided arguments.''' - tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_month_spnv( + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month_spnv( tt_df = tt_df, - years = self.__setting_bag.years, - software_project_names = self.__setting_bag.software_project_names, - software_project_name = self.__setting_bag.tts_by_year_month_spnv_display_only_spn + years = setting_bag.years, + software_project_names = setting_bag.software_project_names, + software_project_name = setting_bag.tts_by_year_month_spnv_display_only_spn ) return tts_by_year_month_spnv_tpl - def __create_tts_by_year_spnv_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_spnv_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframes out of the provided arguments.''' - tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_year_spnv( + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_spnv( tt_df = tt_df, - years = self.__setting_bag.years, - software_project_names = self.__setting_bag.software_project_names, - software_project_name = self.__setting_bag.tts_by_year_spnv_display_only_spn + years = setting_bag.years, + software_project_names = setting_bag.software_project_names, + software_project_name = setting_bag.tts_by_year_spnv_display_only_spn ) return tts_by_year_spnv_tpl - def __create_tts_by_spn_df(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_spn_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframe out of the provided arguments.''' - tts_by_spn_df : DataFrame = self.__component_bag.df_factory.create_tts_by_spn( + tts_by_spn_df : DataFrame = self.__df_factory.create_tts_by_spn( tt_df = tt_df, - years = self.__setting_bag.years, - software_project_names = self.__setting_bag.software_project_names, - remove_untagged = self.__setting_bag.tts_by_spn_remove_untagged + years = setting_bag.years, + software_project_names = setting_bag.software_project_names, + remove_untagged = setting_bag.tts_by_spn_remove_untagged ) return tts_by_spn_df - def __create_tts_by_spn_spv_df(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_spn_spv_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframe out of the provided arguments.''' - tts_by_spn_spv_df : DataFrame = self.__component_bag.df_factory.create_tts_by_spn_spv( + tts_by_spn_spv_df : DataFrame = self.__df_factory.create_tts_by_spn_spv( tt_df = tt_df, - years = self.__setting_bag.years, - software_project_names = self.__setting_bag.software_project_names + years = setting_bag.years, + software_project_names = setting_bag.software_project_names ) return tts_by_spn_spv_df - def __create_tts_by_year_hashtag_df(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_year_hashtag_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframe out of the provided arguments.''' - tts_by_year_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_year_hashtag( + tts_by_year_hashtag_df : DataFrame = self.__df_factory.create_tts_by_year_hashtag( tt_df = tt_df, - years = self.__setting_bag.years + years = setting_bag.years ) return tts_by_year_hashtag_df - def __create_tts_by_efs_tpl(self, tt_df : DataFrame) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_efs_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframes out of the provided arguments.''' - tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__component_bag.df_factory.create_tts_by_efs( + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_efs( tt_df = tt_df, - is_correct = self.__setting_bag.tts_by_efs_is_correct + is_correct = setting_bag.tts_by_efs_is_correct ) return tts_by_efs_tpl - def __create_tts_by_tr_df(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_tr_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: - '''Creates the expected dataframe using tt_df and __setting_bag.''' + '''Creates the expected dataframe out of the provided arguments.''' - tts_by_tr_df : DataFrame = self.__component_bag.df_factory.create_tts_by_tr( + tts_by_tr_df : DataFrame = self.__df_factory.create_tts_by_tr( tt_df = tt_df, - unknown_id = self.__setting_bag.tts_by_tr_unknown_id, - remove_unknown_occurrences = self.__setting_bag.tts_by_tr_remove_unknown_occurrences + unknown_id = setting_bag.tts_by_tr_unknown_id, + remove_unknown_occurrences = setting_bag.tts_by_tr_remove_unknown_occurrences ) return tts_by_tr_df - def __create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame]) -> str: + def create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame], setting_bag : SettingBag) -> str: - '''Creates the expected Markdown content using __setting_bag and the provided arguments.''' + '''Creates the expected Markdown content out of the provided arguments.''' - tts_by_month_md : str = self.__component_bag.md_factory.create_tts_by_month_md( - paragraph_title = self.__extract_file_name_and_paragraph_title(id = TTID.TTSBYMONTH)[1], - last_update = self.__setting_bag.md_last_update, + tts_by_month_md : str = self.__md_factory.create_tts_by_month_md( + paragraph_title = self.extract_file_name_and_paragraph_title(id = TTID.TTSBYMONTH, setting_bag = setting_bag)[1], + last_update = setting_bag.md_last_update, tts_by_month_upd_df = tts_by_month_tpl[1] ) return tts_by_month_md + + def create_summary(self, setting_bag : SettingBag) -> TTSummary: + + '''Creates a TTSummary object out of setting_bag.''' + + tt_df : DataFrame = self.create_tt_df(setting_bag = setting_bag) + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_month_tpl(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_df : DataFrame = self.create_tts_by_year_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_month_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_month_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) + tts_by_spn_df : DataFrame = self.create_tts_by_spn_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_spn_spv_df : DataFrame = self.create_tts_by_spn_spv_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_hashtag_df : DataFrame = self.create_tts_by_year_hashtag_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_hashtag_df : DataFrame = self.__df_factory.create_tts_by_hashtag(tt_df = tt_df) + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_efs_tpl(tt_df = tt_df, setting_bag = setting_bag) + tts_by_tr_df : DataFrame = self.create_tts_by_tr_df(tt_df = tt_df, setting_bag = setting_bag) + definitions_df : DataFrame = self.__df_factory.create_definitions() + tts_by_month_md : str = self.create_tts_by_month_md(tts_by_month_tpl = tts_by_month_tpl, setting_bag = setting_bag) + + tt_summary : TTSummary = TTSummary( + tt_df = tt_df, + tts_by_month_tpl = tts_by_month_tpl, + tts_by_year_df = tts_by_year_df, + tts_by_year_month_tpl = tts_by_year_month_tpl, + tts_by_year_month_spnv_tpl = tts_by_year_month_spnv_tpl, + tts_by_year_spnv_tpl = tts_by_year_spnv_tpl, + tts_by_spn_df = tts_by_spn_df, + tts_by_spn_spv_df = tts_by_spn_spv_df, + tts_by_hashtag_year_df = tts_by_year_hashtag_df, + tts_by_hashtag_df = tts_by_hashtag_df, + tts_by_efs_tpl = tts_by_efs_tpl, + tts_by_tr_df = tts_by_tr_df, + definitions_df = definitions_df, + tts_by_month_md = tts_by_month_md + ) + + return tt_summary +@dataclass(frozen=True) +class ComponentBag(): + + '''Represents a collection of components.''' + + file_path_manager : FilePathManager = field(default = FilePathManager()) + file_manager : FileManager = field(default = FileManager(file_path_manager = FilePathManager())) + + tt_adapter : TTAdapter = field(default = TTAdapter( + df_factory = TTDataFrameFactory(df_helper = TTDataFrameHelper()), + md_factory = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter()) + ))) + + logging_function : Callable[[str], None] = field(default = LambdaProvider().get_default_logging_function()) + displayer : Displayer = field(default = Displayer()) +class TimeTrackingProcessor(): + + '''Collects all the logic related to the processing of "Time Tracking.xlsx".''' + + __component_bag : ComponentBag + __setting_bag : SettingBag + __tt_summary : TTSummary + + def __init__(self, component_bag : ComponentBag, setting_bag : SettingBag) -> None: + + self.__component_bag = component_bag + self.__setting_bag = setting_bag + + def __validate_summary(self) -> None: + + '''Raises an exception if __tt_summary is None.''' + + if not hasattr(self, '_TimeTrackingProcessor__tt_summary'): + raise Exception(_MessageCollection.please_run_initialize_first()) + def __save_and_log(self, id : TTID, content : str) -> None: + + '''Creates the provided Markdown content using __setting_bag.''' + + file_path : str = self.__component_bag.file_path_manager.create_file_path( + folder_path = self.__setting_bag.working_folder_path, + file_name = self.__component_bag.tt_adapter.extract_file_name_and_paragraph_title(id = id, setting_bag = self.__setting_bag)[0] + ) + + self.__component_bag.file_manager.save_content(content = content, file_path = file_path) + + message : str = _MessageCollection.this_content_successfully_saved_as(id = id, file_path = file_path) + self.__component_bag.logging_function(message) + def __try_log_definitions(self, df : DataFrame, definitions : DataFrame) -> None: + + """Logs the definitions for matching column names in the DataFrame.""" + + definitions_dict : dict = definitions.set_index(DEFINITIONSCN.TERM)[DEFINITIONSCN.DEFINITION].to_dict() + + for column_name in df.columns: + if column_name in definitions_dict: + print(f"{column_name}: {definitions_dict[column_name]}") def __orchestrate_head_n(self, df : DataFrame, head_n : Optional[uint], display_head_n_with_tail : bool) -> DataFrame: @@ -1928,38 +1981,7 @@ def initialize(self) -> None: '''Creates a TTSummary object and assign it to __tt_summary.''' - tt_df : DataFrame = self.__create_tt_df() - tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_month_tpl(tt_df = tt_df) - tts_by_year_df : DataFrame = self.__create_tts_by_year_df(tt_df = tt_df) - tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_df(tt_df = tt_df) - tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_month_spnv_tpl(tt_df = tt_df) - tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_year_spnv_tpl(tt_df = tt_df) - tts_by_spn_df : DataFrame = self.__create_tts_by_spn_df(tt_df = tt_df) - tts_by_spn_spv_df : DataFrame = self.__create_tts_by_spn_spv_df(tt_df = tt_df) - tts_by_year_hashtag_df : DataFrame = self.__create_tts_by_year_hashtag_df(tt_df = tt_df) - tts_by_hashtag_df : DataFrame = self.__component_bag.df_factory.create_tts_by_hashtag(tt_df = tt_df) - tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__create_tts_by_efs_tpl(tt_df = tt_df) - tts_by_tr_df : DataFrame = self.__create_tts_by_tr_df(tt_df = tt_df) - definitions_df : DataFrame = self.__component_bag.df_factory.create_definitions() - - tts_by_month_md : str = self.__create_tts_by_month_md(tts_by_month_tpl = tts_by_month_tpl) - - self.__tt_summary = TTSummary( - tt_df = tt_df, - tts_by_month_tpl = tts_by_month_tpl, - tts_by_year_df = tts_by_year_df, - tts_by_year_month_tpl = tts_by_year_month_tpl, - tts_by_year_month_spnv_tpl = tts_by_year_month_spnv_tpl, - tts_by_year_spnv_tpl = tts_by_year_spnv_tpl, - tts_by_spn_df = tts_by_spn_df, - tts_by_spn_spv_df = tts_by_spn_spv_df, - tts_by_hashtag_year_df = tts_by_year_hashtag_df, - tts_by_hashtag_df = tts_by_hashtag_df, - tts_by_efs_tpl = tts_by_efs_tpl, - tts_by_tr_df = tts_by_tr_df, - definitions_df = definitions_df, - tts_by_month_md = tts_by_month_md - ) + self.__tt_summary = self.__component_bag.tt_adapter.create_summary(setting_bag = self.__setting_bag) def process_tt(self) -> None: ''' From 2a7ddffba679f28d16c4477758909aa1172f70ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 13:00:34 +0000 Subject: [PATCH 064/109] #40 TimeTrackingProcessor => TTAdapter - Iteration 2. --- src/nwtimetracking.ipynb | 2259 +++++++++++++++++----------------- src/nwtimetracking.py | 53 +- tests/nwtimetrackingtests.py | 22 +- 3 files changed, 1154 insertions(+), 1180 deletions(-) diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index b559513..12aa934 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -23,34 +23,9 @@ }, { "cell_type": "code", - "execution_count": 445, + "execution_count": 479, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[autoreload of nwtimetracking failed: Traceback (most recent call last):\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 276, in check\n", - " superreload(m, reload, self.old_objects)\n", - " File \"/usr/local/lib/python3.12/site-packages/IPython/extensions/autoreload.py\", line 475, in superreload\n", - " module = reload(module)\n", - " ^^^^^^^^^^^^^^\n", - " File \"/usr/local/lib/python3.12/importlib/__init__.py\", line 131, in reload\n", - " _bootstrap._exec(spec, module)\n", - " File \"\", line 866, in _exec\n", - " File \"\", line 991, in exec_module\n", - " File \"\", line 1129, in get_code\n", - " File \"\", line 1059, in source_to_code\n", - " File \"\", line 488, in _call_with_frames_removed\n", - " File \"/workspaces/nwtimetracking/src/nwtimetracking.py\", line 1709\n", - " '''Creates the expected dataframe out of the provided arguments.''''\n", - " ^\n", - "SyntaxError: unterminated string literal (detected at line 1709)\n", - "]\n" - ] - } - ], + "outputs": [], "source": [ "from typing import Optional" ] @@ -65,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 446, + "execution_count": 480, "metadata": {}, "outputs": [], "source": [ @@ -82,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 447, + "execution_count": 481, "metadata": {}, "outputs": [ { @@ -133,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 448, + "execution_count": 482, "metadata": {}, "outputs": [], "source": [ @@ -168,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 449, + "execution_count": 483, "metadata": {}, "outputs": [ { @@ -176,87 +151,87 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
DateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonthDateStartTimeEndTimeEffortHashtagDescriptorIsSoftwareProjectIsReleaseDayYearMonth
2024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue2024122024-12-0115:0015:450h 45m#pythonnwpackageversions v1.8.0TrueTrue202412
2024-12-0115:4516:150h 30m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:1519:300h 15m#pythonnwreadinglist v4.1.0TrueTrue202412
2024-12-0119:3020:150h 45m#pythonnwtimetracking v3.9.0TrueTrue202412
2024-12-0121:0023:002h 00m#pythonnwtraderaanalytics v4.4.0TrueTrue202412
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -271,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 450, + "execution_count": 484, "metadata": {}, "outputs": [ { @@ -279,301 +254,301 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
Month2015201620172018201920202021202220232024Month2015201620172018201920202021202220232024
100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m100h 00m18h 00m88h 30m80h 15m60h 00m29h 15m53h 00m00h 00m06h 00m45h 45m
200h 00m45h 30m65h 30m90h 45m73h 00m38h 00m31h 30m03h 00m24h 00m77h 45m
300h 00m20h 45m71h 45m89h 00m75h 30m35h 00m40h 30m06h 15m50h 15m77h 45m
400h 00m37h 30m68h 00m88h 30m59h 45m40h 45m19h 00m27h 30m19h 00m29h 30m
500h 00m53h 00m83h 00m91h 15m54h 45m14h 30m112h 45m49h 45m31h 00m43h 00m
600h 00m57h 45m37h 45m62h 00m29h 15m12h 00m54h 00m73h 30m24h 45m48h 00m
700h 00m46h 45m65h 30m69h 30m24h 15m34h 00m23h 30m51h 00m16h 30m67h 00m
800h 00m25h 45m45h 45m72h 00m06h 00m32h 00m110h 00m36h 30m41h 30m32h 45m
900h 00m89h 30m43h 45m64h 00m39h 00m44h 00m43h 30m69h 00m50h 15m48h 00m
1008h 00m82h 15m64h 30m46h 45m45h 30m48h 00m35h 30m38h 30m20h 00m101h 30m
1110h 00m74h 30m50h 00m30h 00m38h 45m35h 30m13h 15m58h 15m14h 30m88h 00m
1200h 00m64h 00m78h 45m45h 45m09h 30m107h 30m01h 00m54h 15m22h 30m07h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -593,7 +568,7 @@ }, { "cell_type": "code", - "execution_count": 451, + "execution_count": 485, "metadata": {}, "outputs": [ { @@ -601,92 +576,92 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearEffortYearlyTargetTargetDiffIsTargetMetYearEffortYearlyTargetTargetDiffIsTargetMet
201518h 00m00h 00m+18h 00mTrue201518h 00m00h 00m+18h 00mTrue
2016615h 15m500h 00m+115h 15mTrue2016615h 15m500h 00m+115h 15mTrue
2017762h 45m500h 00m+262h 45mTrue2017762h 45m500h 00m+262h 45mTrue
2018829h 45m500h 00m+329h 45mTrue2018829h 45m500h 00m+329h 45mTrue
2019515h 15m500h 00m+15h 15mTrue2019515h 15m500h 00m+15h 15mTrue
2020470h 30m500h 00m-30h 30mFalse2020470h 30m500h 00m-30h 30mFalse
2021537h 30m500h 00m+37h 30mTrue2021537h 30m500h 00m+37h 30mTrue
2022467h 30m400h 00m+67h 30mTrue2022467h 30m400h 00m+67h 30mTrue
2023320h 15m250h 00m+70h 15mTrue2023320h 15m250h 00m+70h 15mTrue
2024666h 45m500h 00m+166h 45mTrue2024666h 45m500h 00m+166h 45mTrue
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -699,7 +674,7 @@ }, { "cell_type": "code", - "execution_count": 452, + "execution_count": 486, "metadata": {}, "outputs": [ { @@ -707,106 +682,106 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthEffortYearlyTotalToTargetYearMonthEffortYearlyTotalToTarget
2024145h 45m45h 45m-455h 45m2024145h 45m45h 45m-455h 45m
2024277h 45m123h 30m-377h 30m2024277h 45m123h 30m-377h 30m
2024377h 45m201h 15m-299h 15m2024377h 45m201h 15m-299h 15m
2024429h 30m230h 45m-270h 45m2024429h 30m230h 45m-270h 45m
2024543h 00m273h 45m-227h 45m2024543h 00m273h 45m-227h 45m
2024648h 00m321h 45m-179h 45m2024648h 00m321h 45m-179h 45m
2024767h 00m388h 45m-112h 45m2024767h 00m388h 45m-112h 45m
2024832h 45m421h 30m-79h 30m2024832h 45m421h 30m-79h 30m
2024948h 00m469h 30m-31h 30m2024948h 00m469h 30m-31h 30m
202410101h 30m571h 00m+71h 00m202410101h 30m571h 00m+71h 00m
20241188h 00m659h 00m+159h 00m20241188h 00m659h 00m+159h 00m
20241207h 45m666h 45m+166h 45m20241207h 45m666h 45m+166h 45m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -819,7 +794,7 @@ }, { "cell_type": "code", - "execution_count": 453, + "execution_count": 487, "metadata": {}, "outputs": [ { @@ -827,202 +802,202 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearMonthProjectNameProjectVersionEffortDME%_DMETME%_TMEYearMonthProjectNameProjectVersionEffortDME%_DMETME%_TME
20239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.6820239nwtimetracking1.0.011h 30m32h 45m35.1150h 15m22.89
202310nwtimetracking1.0.020h 00m20h 00m100.0020h 00m100.00
202311nwtimetracking1.1.010h 30m14h 00m75.0014h 30m72.41
202311nwtimetracking1.2.003h 30m14h 00m25.0014h 30m24.14
202312nwtimetracking1.3.002h 30m17h 00m14.7122h 30m11.11
20241nwtimetracking1.3.007h 15m20h 30m35.3745h 45m15.85
20241nwtimetracking2.0.002h 30m20h 30m12.2045h 45m5.46
20242nwtimetracking2.2.007h 45m36h 45m21.0977h 45m9.97
20243nwtimetracking3.0.003h 30m77h 15m4.5377h 45m4.50
20245nwtimetracking3.2.002h 00m35h 30m5.6343h 00m4.65
20245nwtimetracking3.3.001h 00m35h 30m2.8243h 00m2.33
20248nwtimetracking3.4.001h 00m10h 30m9.5232h 45m3.05
20249nwtimetracking3.5.004h 00m45h 45m8.7448h 00m8.33
202410nwtimetracking3.7.006h 00m85h 00m7.06101h 30m5.91
202410nwtimetracking3.8.001h 00m85h 00m1.18101h 30m0.99
202412nwtimetracking3.9.000h 45m07h 45m9.6807h 45m9.68
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1035,7 +1010,7 @@ }, { "cell_type": "code", - "execution_count": 454, + "execution_count": 488, "metadata": {}, "outputs": [ { @@ -1043,175 +1018,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearProjectNameProjectVersionEffortDYE%_DYETYE%_TYEYearProjectNameProjectVersionEffortDYE%_DYETYE%_TYE
2023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.112023nwtimetracking1.0.031h 30m213h 00m14.79320h 15m9.84
2023nwtimetracking1.1.010h 30m213h 00m4.93320h 15m3.28
2023nwtimetracking1.2.003h 30m213h 00m1.64320h 15m1.09
2023nwtimetracking1.3.002h 30m213h 00m1.17320h 15m0.78
2024nwtimetracking1.3.007h 15m429h 30m1.69666h 45m1.09
2024nwtimetracking2.0.002h 30m429h 30m0.58666h 45m0.37
2024nwtimetracking2.2.007h 45m429h 30m1.80666h 45m1.16
2024nwtimetracking3.0.003h 30m429h 30m0.81666h 45m0.52
2024nwtimetracking3.2.002h 00m429h 30m0.47666h 45m0.30
2024nwtimetracking3.3.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.4.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.5.004h 00m429h 30m0.93666h 45m0.60
2024nwtimetracking3.7.006h 00m429h 30m1.40666h 45m0.90
2024nwtimetracking3.8.001h 00m429h 30m0.23666h 45m0.15
2024nwtimetracking3.9.000h 45m429h 30m0.17666h 45m0.11
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1224,7 +1199,7 @@ }, { "cell_type": "code", - "execution_count": 455, + "execution_count": 489, "metadata": {}, "outputs": [ { @@ -1232,123 +1207,123 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagProjectNameEffortDE%_DETE%_TEHashtagProjectNameEffortDE%_DETE%_TE
#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10#pythonnwtraderaanalytics219h 45m1874h 30m11.722654h 45m8.28
#pythonnwreadinglist143h 30m1874h 30m7.662654h 45m5.41
#pythonnwtimetracking84h 45m1874h 30m4.522654h 45m3.19
#pythonnwpackageversions52h 45m1874h 30m2.812654h 45m1.99
#pythonnwshared42h 15m1874h 30m2.252654h 45m1.59
#csharpNW.UnivariateForecasting208h 00m1874h 30m11.102654h 45m7.84
#csharpNW.NGramTextClassification207h 30m1874h 30m11.072654h 45m7.82
#csharpNW.MarkdownTables20h 45m1874h 30m1.112654h 45m0.78
#csharpNW.Shared.Files05h 30m1874h 30m0.292654h 45m0.21
#csharpNW.Shared.Serialization04h 15m1874h 30m0.232654h 45m0.16
#csharpNW.Shared.Validation02h 45m1874h 30m0.152654h 45m0.10
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1369,7 +1344,7 @@ }, { "cell_type": "code", - "execution_count": 456, + "execution_count": 490, "metadata": {}, "outputs": [], "source": [ @@ -1378,7 +1353,7 @@ }, { "cell_type": "code", - "execution_count": 457, + "execution_count": 491, "metadata": {}, "outputs": [ { @@ -1386,55 +1361,55 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
HashtagEffortEffort%HashtagEffortEffort%
#untagged2548h 45m48.98#untagged2548h 45m48.98
#csharp1116h 45m21.46#csharp1116h 45m21.46
#python611h 45m11.76#python611h 45m11.76
#studying419h 30m8.06#studying419h 30m8.06
#maintenance333h 45m6.41#maintenance333h 45m6.41
#powershell154h 00m2.96#powershell154h 00m2.96
#overtime19h 00m0.37#overtime19h 00m0.37
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1447,7 +1422,7 @@ }, { "cell_type": "code", - "execution_count": 458, + "execution_count": 492, "metadata": {}, "outputs": [ { @@ -1455,175 +1430,175 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", " \n", " \n", "
YearHashtagEffortYearHashtagEffort
2019#csharp67h 00m2019#csharp67h 00m
2020#csharp206h 30m2020#csharp206h 30m
2021#csharp441h 45m2021#csharp441h 45m
2022#csharp298h 45m2022#csharp298h 45m
2023#csharp67h 30m2023#csharp67h 30m
2024#csharp35h 15m2024#csharp35h 15m
2019#maintenance53h 45m2019#maintenance53h 45m
2020#maintenance53h 30m2020#maintenance53h 30m
2021#maintenance29h 30m2021#maintenance29h 30m
2022#maintenance36h 30m2022#maintenance36h 30m
2023#maintenance52h 30m2023#maintenance52h 30m
2024#maintenance108h 00m2024#maintenance108h 00m
2020#overtime19h 00m2020#overtime19h 00m
2020#powershell116h 45m2020#powershell116h 45m
2021#powershell11h 30m2021#powershell11h 30m
2022#powershell21h 15m2022#powershell21h 15m
2023#powershell04h 30m2023#powershell04h 30m
2022#python72h 00m2022#python72h 00m
2023#python141h 00m2023#python141h 00m
2024#python398h 45m2024#python398h 45m
2019#studying71h 30m2019#studying71h 30m
2020#studying74h 45m2020#studying74h 45m
2021#studying54h 45m2021#studying54h 45m
2022#studying39h 00m2022#studying39h 00m
2023#studying54h 45m2023#studying54h 45m
2024#studying124h 45m2024#studying124h 45m
2015#untagged18h 00m2015#untagged18h 00m
2016#untagged615h 15m2016#untagged615h 15m
2017#untagged762h 45m2017#untagged762h 45m
2018#untagged829h 45m2018#untagged829h 45m
2019#untagged323h 00m2019#untagged323h 00m
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1636,7 +1611,7 @@ }, { "cell_type": "code", - "execution_count": 459, + "execution_count": 493, "metadata": {}, "outputs": [ { @@ -1644,15 +1619,15 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", @@ -1660,7 +1635,7 @@ "
StartTimeEndTimeEffortES_IsCorrectES_ExpectedES_MessageStartTimeEndTimeEffortES_IsCorrectES_ExpectedES_Message
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1673,7 +1648,7 @@ }, { "cell_type": "code", - "execution_count": 460, + "execution_count": 494, "metadata": {}, "outputs": [ { @@ -1681,59 +1656,59 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TimeRangeIdOccurrencesTimeRangeIdOccurrences
08:00-08:454308:00-08:4543
08:00-08:302508:00-08:3025
18:00-20:002118:00-20:0021
17:30-18:001817:30-18:0018
19:00-20:001719:00-20:0017
17:15-17:451517:15-17:4515
17:00-17:301517:00-17:3015
17:00-20:001117:00-20:0011
17:00-17:451117:00-17:4511
18:30-20:001018:30-20:0010
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1746,7 +1721,7 @@ }, { "cell_type": "code", - "execution_count": 461, + "execution_count": 495, "metadata": {}, "outputs": [ { @@ -1754,43 +1729,43 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "
TermDefinitionTermDefinition
DMEDevelopment Monthly EffortDMEDevelopment Monthly Effort
TMETotal Monthly EffortTMETotal Monthly Effort
DYEDevelopment Yearly EffortDYEDevelopment Yearly Effort
TYETotal Yearly EffortTYETotal Yearly Effort
DEDevelopment EffortDEDevelopment Effort
TETotal EffortTETotal Effort
\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index f058cd4..2a11263 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -9,7 +9,6 @@ import os import pandas as pd import re -import openpyxl from dataclasses import dataclass, field from datetime import datetime, timedelta from enum import StrEnum @@ -1237,7 +1236,7 @@ def __filter_by_is_correct(self, tts_by_efs_df : DataFrame, is_correct : bool) - return filtered_df - def create_tt(self, excel_path : str, excel_skiprows : int, excel_nrows : int, excel_tabname : str) -> DataFrame: + def create_tt_df(self, excel_path : str, excel_skiprows : int, excel_nrows : int, excel_tabname : str) -> DataFrame: ''' Retrieves the content of the "Sessions" tab and returns it as a Dataframe. @@ -1253,7 +1252,7 @@ def create_tt(self, excel_path : str, excel_skiprows : int, excel_nrows : int, e tt_df = self.__enforce_dataframe_definition_for_tt_df(tt_df = tt_df) return tt_df - def create_tts_by_month(self, tt_df : DataFrame, years : list, now : datetime) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_month_tpl(self, tt_df : DataFrame, years : list, now : datetime) -> Tuple[DataFrame, DataFrame]: ''' Month 2016 ↕ 2017 ↕ 2018 ... @@ -1285,7 +1284,7 @@ def create_tts_by_month(self, tt_df : DataFrame, years : list, now : datetime) - tts_upd_df : DataFrame = self.__update_future_months_to_empty(tts_by_month_df = tts_df, now = now) return (tts_df, tts_upd_df) - def create_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: + def create_tts_by_year_df(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget]) -> DataFrame: ''' [0] @@ -1331,7 +1330,7 @@ def create_tts_by_year(self, tt_df : DataFrame, years : list[int], yearly_target tts_df[TTCN.TARGETDIFF] = tts_df[TTCN.TARGETDIFF].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = True)) return tts_df - def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget], display_only_years : list[int]) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_month_tpl(self, tt_df : DataFrame, years : list[int], yearly_targets : list[YearlyTarget], display_only_years : list[int]) -> Tuple[DataFrame, DataFrame]: ''' [0] @@ -1397,7 +1396,7 @@ def create_tts_by_year_month(self, tt_df : DataFrame, years : list[int], yearly_ tts_flt_df : DataFrame = self.__filter_by_year(df = tts_df, years = display_only_years) return (tts_df, tts_flt_df) - def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: ''' [0] ... @@ -1441,7 +1440,7 @@ def create_tts_by_year_month_spnv(self, tt_df : DataFrame, years : list[int], so tts_flt_df : DataFrame = self.__filter_by_software_project_name(df = tts_df, software_project_name = software_project_name) return (tts_df, tts_flt_df) - def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_spnv_tpl(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], software_project_name : Optional[str]) -> Tuple[DataFrame, DataFrame]: ''' [0] ... @@ -1485,7 +1484,7 @@ def create_tts_by_year_spnv(self, tt_df : DataFrame, years : list[int], software tts_flt_df : DataFrame = self.__filter_by_software_project_name(df = tts_df, software_project_name = software_project_name) return (tts_df, tts_flt_df) - def create_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: + def create_tts_by_spn_df(self, tt_df : DataFrame, years : list[int], software_project_names : list[str], remove_untagged : bool) -> DataFrame: ''' Hashtag ProjectName Effort DE %_DE TE %_TE @@ -1516,7 +1515,7 @@ def create_tts_by_spn(self, tt_df : DataFrame, years : list[int], software_proje tts_df[TTCN.TE] = tts_df[TTCN.TE].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: + def create_tts_by_spn_spv_df(self, tt_df : DataFrame, years : list[int], software_project_names : list[str]) -> DataFrame: ''' ProjectName ProjectVersion Effort @@ -1530,7 +1529,7 @@ def create_tts_by_spn_spv(self, tt_df : DataFrame, years : list[int], software_p tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def create_tts_by_year_hashtag_df(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1544,7 +1543,7 @@ def create_tts_by_year_hashtag(self, tt_df : DataFrame, years : list[int]) -> Da tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: + def create_tts_by_hashtag_df(self, tt_df : DataFrame) -> DataFrame: ''' Hashtag Effort Effort% @@ -1558,7 +1557,7 @@ def create_tts_by_hashtag(self, tt_df : DataFrame) -> DataFrame: tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_efs(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_efs_tpl(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataFrame, DataFrame]: ''' StartTime EndTime Effort ES_IsCorrect ES_Expected ES_Message @@ -1586,7 +1585,7 @@ def create_tts_by_efs(self, tt_df : DataFrame, is_correct : bool) -> Tuple[DataF tts_flt_df : DataFrame = self.__filter_by_is_correct(tts_by_efs_df = tts_df, is_correct = is_correct) return (tts_df, tts_flt_df) - def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_occurrences : bool) -> DataFrame: + def create_tts_by_tr_df(self, tt_df : DataFrame, unknown_id : str, remove_unknown_occurrences : bool) -> DataFrame: ''' TimeRangeId Occurrences @@ -1616,7 +1615,7 @@ def create_tts_by_tr(self, tt_df : DataFrame, unknown_id : str, remove_unknown_o tts_df = self.__remove_unknown_occurrences(tts_by_tr_df = tts_df, unknown_id = unknown_id) return tts_df - def create_definitions(self) -> DataFrame: + def create_definitions_df(self) -> DataFrame: '''Creates a dataframe containing all the definitions in use in this application.''' @@ -1685,7 +1684,7 @@ def create_tt_df(self, setting_bag : SettingBag) -> DataFrame: '''Creates the expected dataframe out of the provided arguments.''' - tt_df : DataFrame = self.__df_factory.create_tt( + tt_df : DataFrame = self.__df_factory.create_tt_df( excel_path = setting_bag.excel_path, excel_skiprows = setting_bag.excel_skiprows, excel_nrows = setting_bag.excel_nrows, @@ -1697,7 +1696,7 @@ def create_tts_by_month_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) - '''Creates the expected dataframes out of the provided arguments.''' - tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_month( + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_month_tpl( tt_df = tt_df, years = setting_bag.years, now = setting_bag.now @@ -1708,7 +1707,7 @@ def create_tts_by_year_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> '''Creates the expected dataframe out of the provided arguments.''' - tts_by_year_df : DataFrame = self.__df_factory.create_tts_by_year( + tts_by_year_df : DataFrame = self.__df_factory.create_tts_by_year_df( tt_df = tt_df, years = setting_bag.years, yearly_targets = setting_bag.yearly_targets, @@ -1724,7 +1723,7 @@ def create_tts_by_year_month_df(self, tt_df : DataFrame, setting_bag : SettingBa if display_only_years is not None: display_only_years = cast(list[int], setting_bag.tts_by_year_month_display_only_years) - tts_by_year_month_df : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month( + tts_by_year_month_df : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month_tpl( tt_df = tt_df, years = setting_bag.years, yearly_targets = setting_bag.yearly_targets, @@ -1736,7 +1735,7 @@ def create_tts_by_year_month_spnv_tpl(self, tt_df : DataFrame, setting_bag : Set '''Creates the expected dataframes out of the provided arguments.''' - tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month_spnv( + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_month_spnv_tpl( tt_df = tt_df, years = setting_bag.years, software_project_names = setting_bag.software_project_names, @@ -1748,7 +1747,7 @@ def create_tts_by_year_spnv_tpl(self, tt_df : DataFrame, setting_bag : SettingBa '''Creates the expected dataframes out of the provided arguments.''' - tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_spnv( + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_year_spnv_tpl( tt_df = tt_df, years = setting_bag.years, software_project_names = setting_bag.software_project_names, @@ -1760,7 +1759,7 @@ def create_tts_by_spn_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> D '''Creates the expected dataframe out of the provided arguments.''' - tts_by_spn_df : DataFrame = self.__df_factory.create_tts_by_spn( + tts_by_spn_df : DataFrame = self.__df_factory.create_tts_by_spn_df( tt_df = tt_df, years = setting_bag.years, software_project_names = setting_bag.software_project_names, @@ -1772,7 +1771,7 @@ def create_tts_by_spn_spv_df(self, tt_df : DataFrame, setting_bag : SettingBag) '''Creates the expected dataframe out of the provided arguments.''' - tts_by_spn_spv_df : DataFrame = self.__df_factory.create_tts_by_spn_spv( + tts_by_spn_spv_df : DataFrame = self.__df_factory.create_tts_by_spn_spv_df( tt_df = tt_df, years = setting_bag.years, software_project_names = setting_bag.software_project_names @@ -1783,7 +1782,7 @@ def create_tts_by_year_hashtag_df(self, tt_df : DataFrame, setting_bag : Setting '''Creates the expected dataframe out of the provided arguments.''' - tts_by_year_hashtag_df : DataFrame = self.__df_factory.create_tts_by_year_hashtag( + tts_by_year_hashtag_df : DataFrame = self.__df_factory.create_tts_by_year_hashtag_df( tt_df = tt_df, years = setting_bag.years ) @@ -1793,7 +1792,7 @@ def create_tts_by_efs_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> '''Creates the expected dataframes out of the provided arguments.''' - tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_efs( + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.__df_factory.create_tts_by_efs_tpl( tt_df = tt_df, is_correct = setting_bag.tts_by_efs_is_correct ) @@ -1803,7 +1802,7 @@ def create_tts_by_tr_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> Da '''Creates the expected dataframe out of the provided arguments.''' - tts_by_tr_df : DataFrame = self.__df_factory.create_tts_by_tr( + tts_by_tr_df : DataFrame = self.__df_factory.create_tts_by_tr_df( tt_df = tt_df, unknown_id = setting_bag.tts_by_tr_unknown_id, remove_unknown_occurrences = setting_bag.tts_by_tr_remove_unknown_occurrences @@ -1835,10 +1834,10 @@ def create_summary(self, setting_bag : SettingBag) -> TTSummary: tts_by_spn_df : DataFrame = self.create_tts_by_spn_df(tt_df = tt_df, setting_bag = setting_bag) tts_by_spn_spv_df : DataFrame = self.create_tts_by_spn_spv_df(tt_df = tt_df, setting_bag = setting_bag) tts_by_year_hashtag_df : DataFrame = self.create_tts_by_year_hashtag_df(tt_df = tt_df, setting_bag = setting_bag) - tts_by_hashtag_df : DataFrame = self.__df_factory.create_tts_by_hashtag(tt_df = tt_df) + tts_by_hashtag_df : DataFrame = self.__df_factory.create_tts_by_hashtag_df(tt_df = tt_df) tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_efs_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_tr_df : DataFrame = self.create_tts_by_tr_df(tt_df = tt_df, setting_bag = setting_bag) - definitions_df : DataFrame = self.__df_factory.create_definitions() + definitions_df : DataFrame = self.__df_factory.create_definitions_df() tts_by_month_md : str = self.create_tts_by_month_md(tts_by_month_tpl = tts_by_month_tpl, setting_bag = setting_bag) tt_summary : TTSummary = TTSummary( diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index c63605b..f904d00 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1043,7 +1043,7 @@ def test_getsessionsdataset_shouldreturnexpecteddataframe_wheninvoked(self): # Act with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: - actual : DataFrame = TTDataFrameFactory().create_tt(setting_bag = setting_bag) + actual : DataFrame = TTDataFrameFactory().create_tt_df(setting_bag = setting_bag) # Assert self.assertEqual(expected_column_names, actual.columns.tolist()) @@ -1060,7 +1060,7 @@ def test_getttbyyear_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_df(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1073,7 +1073,7 @@ def test_getttbyyearmonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_tpl(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1086,7 +1086,7 @@ def test_getttbyyearmonthspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_month_spnv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_spnv_tpl(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1099,7 +1099,7 @@ def test_getttbyyearspnv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_spnv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_spnv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_spnv_tpl(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1112,7 +1112,7 @@ def test_getttbyspnspv_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_spn_spv_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_spv(tt_df = sessions_df, years = years, software_project_names = software_project_names) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_spv_df(tt_df = sessions_df, years = years, software_project_names = software_project_names) # Assert assert_frame_equal(expected_df , actual_df) @@ -1124,7 +1124,7 @@ def test_getttsbymonth_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tts_by_month_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_month(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_month_tpl(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df, actual_df) @@ -1150,7 +1150,7 @@ def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): expected_df.reset_index(drop = True, inplace = True) # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_tr(tt_df = sessions_df, unknown_id = unknown_id) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_tr_df(tt_df = sessions_df, unknown_id = unknown_id) actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) actual_df.reset_index(drop = True, inplace = True) @@ -1189,7 +1189,7 @@ def test_getttbyyearhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_year_hashtag_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_hashtag(tt_df = sessions_df, years = years) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_hashtag_df(tt_df = sessions_df, years = years) # Assert assert_frame_equal(expected_df , actual_df) @@ -1200,7 +1200,7 @@ def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): expected_df : DataFrame = ObjectMother().create_tt_by_hashtag_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_hashtag(tt_df = sessions_df) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_hashtag_df(tt_df = sessions_df) # Assert assert_frame_equal(expected_df , actual_df) @@ -1218,7 +1218,7 @@ def test_getttbyspn_shouldreturnexpecteddataframe_wheninvoked(self, remove_untag expected_df : DataFrame = ObjectMother().create_tt_by_spn_df() # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) + actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_df(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) # Assert assert_frame_equal(expected_df , actual_df) From e424bb7610ec1641cf1b5883fc66ccbe8f5c9cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 13:12:16 +0000 Subject: [PATCH 065/109] #40 * => v4.0.0. --- CHANGELOG | 6 ++++++ docs/docs-nwtimetracking.md | 3 ++- src/nwtimetracking.ipynb | 2 +- src/setup.py | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9efe61b..69265f9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +v4.0.0 - BREAKING CHANGES +- Application: + - Feature: re-structured the whole architecture to simplify usage and future expansions. +- Documentation: + - Feature: updated to v4.0.0. + v3.9.0 - Library: - Feature: makefile added. diff --git a/docs/docs-nwtimetracking.md b/docs/docs-nwtimetracking.md index ed7aaba..d0fce55 100644 --- a/docs/docs-nwtimetracking.md +++ b/docs/docs-nwtimetracking.md @@ -15,6 +15,7 @@ Contact: numbworks@gmail.com | 2024-10-01 | numbworks | Updated to v3.7.0. | | 2024-10-28 | numbworks | Updated to v3.8.0. | | 2024-12-01 | numbworks | Updated to v3.9.0. | +| 2024-12-05 | numbworks | Updated to v4.0.0. | ## Introduction @@ -144,7 +145,7 @@ The expected outcome for `all-concise` is: ``` MODULE_NAME: nwtimetracking -MODULE_VERSION: 3.9.0 +MODULE_VERSION: 4.0.0 COVERAGE_THRESHOLD: 70% [WARNING] type-concise: not passed! '1' error(s) found! [WARNING] changelog-concise: 'CHANGELOG' not updated to current version! diff --git a/src/nwtimetracking.ipynb b/src/nwtimetracking.ipynb index 12aa934..3f6ba50 100644 --- a/src/nwtimetracking.ipynb +++ b/src/nwtimetracking.ipynb @@ -9,7 +9,7 @@ "|---|---|\n", "|Title|nwtimetracking|\n", "|Author|numbworks|\n", - "|Version|3.9.0|\n", + "|Version|4.0.0|\n", "||Please check [docs/docs-nwtimetracking.md](../docs/docs-nwtimetracking.md) before proceeding.|" ] }, diff --git a/src/setup.py b/src/setup.py index faf4f81..0e0aa48 100644 --- a/src/setup.py +++ b/src/setup.py @@ -4,6 +4,6 @@ # INFORMATION MODULE_ALIAS : str = "nwtt" MODULE_NAME : str = "nwtimetracking" -MODULE_VERSION : str = "3.9.0" +MODULE_VERSION : str = "4.0.0" # SETUP \ No newline at end of file From 78b75f94e7fa5d6fa12f647d25ea53e4fefd02df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 13:30:05 +0000 Subject: [PATCH 066/109] #40 * => v4.0.0. --- scripts/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/makefile b/scripts/makefile index 66d12cb..aa9b5a5 100644 --- a/scripts/makefile +++ b/scripts/makefile @@ -3,7 +3,7 @@ SHELL := /bin/bash ROOT_DIR := $(shell cd .. && pwd) MODULE_NAME = "nwtimetracking" -MODULE_VERSION = "3.9.0" +MODULE_VERSION = "4.0.0" COVERAGE_THRESHOLD = 70 # TARGETS From 912f7795d3e3e1934a7ba4dc51be0848d4a1df1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:07:29 +0000 Subject: [PATCH 067/109] #40 YearProviderTestCase: expanded. --- src/nwtimetracking.py | 18 ++--- tests/nwtimetrackingtests.py | 131 ++++++++++++++++------------------- 2 files changed, 68 insertions(+), 81 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 2a11263..e158c42 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -206,14 +206,6 @@ def get_all_years(self) -> list[int]: years : list[int] = [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024] return years - def get_most_recent_x_years(self, x : uint) -> list[int]: - - '''Returns a list of years.''' - - years : list[int] = self.get_all_years() - years = years[(len(years) - int(x)):] - - return years def get_all_yearly_targets(self) -> list[YearlyTarget]: '''Returns a list of years.''' @@ -232,6 +224,16 @@ def get_all_yearly_targets(self) -> list[YearlyTarget]: ] return yearly_targets + def get_most_recent_x_years(self, x : uint) -> list[int]: + + '''Returns a list of years.''' + + years : list[int] = self.get_all_years() + + if x <= len(years): + years = years[(len(years) - int(x)):] + + return years class SoftwareProjectNameProvider(): '''Collects all the logic related to the retrieval of software project names.''' diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index f904d00..be3e425 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -5,7 +5,7 @@ from datetime import datetime from datetime import date from datetime import timedelta -from numpy import int64 +from numpy import int64, uint from pandas import DataFrame from pandas.testing import assert_frame_equal from parameterized import parameterized @@ -16,9 +16,9 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import ComponentBag, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import ComponentBag, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory -from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager +from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer # SUPPORT METHODS class SupportMethodProvider(): @@ -96,54 +96,27 @@ class ObjectMother(): @staticmethod def create_setting_bag() -> SettingBag: - return SettingBag( - years = [2015], - yearly_targets = [ - YearlyTarget(year = 2015, hours = timedelta(hours = 0)) - ], - excel_path = DefaultPathProvider().get_default_time_tracking_path(), - excel_skiprows = 0, - excel_books_nrows = 920, - excel_tabname = "Sessions", - n_generic = 5, - n_by_month = 12, - now = datetime.now(), - software_project_names = [ - "NW.MarkdownTables" - ], - software_project_names_by_spv = [ - "nwreadinglistmanager" - ], - remove_untagged_from_de = True, - definitions = { - "DME": "Development Monthly Effort", - "TME": "Total Monthly Effort", - "DYE": "Development Yearly Effort", - "TYE": "Total Yearly Effort", - "DE": "Development Effort", - "TE": "Total Effort" - }, - tt_by_year_hashtag_years = [2023], - tts_by_month_update_future_values_to_empty = True, - effort_status_n = 25, - effort_status_is_correct = False, - time_ranges_unknown_id = "Unknown", - time_ranges_top_n = 5, - time_ranges_remove_unknown_id = True, - time_ranges_filter_by_top_n = True, - show_sessions_df = False, - show_tt_by_year_df = True, - show_tt_by_year_month_df = True, - show_tt_by_year_month_spnv_df = False, - show_tt_by_year_spnv_df = False, - show_tt_by_spn_df = True, - show_tt_by_spn_spv_df = True, - show_tt_by_year_hashtag = True, - show_tt_by_hashtag = True, - show_tts_by_month_df = True, - show_effort_status_df = True, - show_time_ranges_df = True + setting_bag : SettingBag = SettingBag( + options_tt = ["display"], + options_tts_by_month = ["display", "save"], + options_tts_by_year = ["display"], + options_tts_by_year_month = ["display"], + options_tts_by_year_month_spnv = ["display"], + options_tts_by_year_spnv = ["display"], + options_tts_by_spn = ["display", "log"], + options_tts_by_spn_spv = [], + options_tts_by_hashtag = ["display"], + options_tts_by_hashtag_year = ["display"], + options_tts_by_efs = ["display"], + options_tts_by_tr = ["display"], + options_definitions = ["display"], + excel_nrows = 1301, + tts_by_year_month_spnv_display_only_spn = "nwtimetracking", + tts_by_year_spnv_display_only_spn = "nwtimetracking", + tts_by_spn_spv_display_only_spn = "nwtimetracking" ) + + return setting_bag @staticmethod def create_excel_data() -> DataFrame: @@ -451,28 +424,6 @@ def create_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: expected : str = "\n".join(lines) + "\n" return (df, expected) - @staticmethod - def create_service_objects_for_ttsbymonthmd() -> Tuple[ComponentBag, SettingBag, TTMarkdownFactory]: - - component_bag : Mock = Mock() - component_bag.logging_function = Mock() - component_bag.file_manager.save_content = Mock() - component_bag.markdown_helper = MarkdownHelper(formatter = Formatter()) - component_bag.file_path_manager = FilePathManager() - - setting_bag : Mock = Mock() - setting_bag.last_update = datetime(2024, 10, 1) - setting_bag.tts_by_month_file_name = "TIMETRACKINGBYMONTH.md" - setting_bag.working_folder_path = "/home/nwtimetracking/" - setting_bag.show_tts_by_month_md = True - setting_bag.save_tts_by_month_md = True - - markdown_processor : TTMarkdownFactory = TTMarkdownFactory( - component_bag = component_bag, - setting_bag = setting_bag - ) - - return (component_bag, setting_bag, markdown_processor) # TEST CLASSES class ComponentBagTestCase(unittest.TestCase): @@ -486,8 +437,9 @@ def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> # Assert self.assertIsInstance(component_bag.file_path_manager, FilePathManager) self.assertIsInstance(component_bag.file_manager, FileManager) + self.assertIsInstance(component_bag.tt_adapter, TTAdapter) self.assertIsInstance(component_bag.logging_function, FunctionType) - self.assertIsInstance(component_bag.markdown_helper, MarkdownHelper) + self.assertIsInstance(component_bag.displayer, Displayer) class DefaultPathProviderTestCase(unittest.TestCase): def test_getdefaulttimetrackingpath_shouldreturnexpectedpath_wheninvoked(self): @@ -536,6 +488,39 @@ def test_getallyearlytargets_shouldreturnexpectedlist_wheninvoked(self): # Assert self.assertTrue(SupportMethodProvider.are_lists_of_yearly_targets_equal(list1 = expected, list2 = actual)) + def test_getmostrecentxyears_shouldreturnlastxyears_whenxlessthantotalyears(self): + + # Arrange + x : uint = uint(5) + expected : list[int] = [2020, 2021, 2022, 2023, 2024] + + # Act + actual : list[int] = YearProvider().get_most_recent_x_years(x) + + # Assert + self.assertEqual(expected, actual) + def test_getmostrecentxyears_shouldreturnallyears_whenxgreaterthantotalyears(self): + + # Arrange + x : uint = uint(15) + expected : list[int] = [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024] + + # Act + actual : list[int] = YearProvider().get_most_recent_x_years(x) + + # Assert + self.assertEqual(expected, actual) + def test_getmostrecentxyears_shouldreturnemptylist_whenxiszero(self): + + # Arrange + x : uint = uint(0) + expected : list[int] = [] + + # Act + actual : list[int] = YearProvider().get_most_recent_x_years(x) + + # Assert + self.assertEqual(expected, actual) class SoftwareProjectNameProviderTestCase(unittest.TestCase): def test_getallsoftwareprojectnames_shouldreturnexpectedlist_wheninvoked(self): From 5fd8b7aaf4b06bfa3090a11d46e8d7b7b7895c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:21:00 +0000 Subject: [PATCH 068/109] #40 MDInfoProviderTestCase: added (100%). --- tests/nwtimetrackingtests.py | 669 +---------------------------------- 1 file changed, 15 insertions(+), 654 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index be3e425..060d5d8 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -16,8 +16,8 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import ComponentBag, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection -from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory +from nwtimetracking import ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer # SUPPORT METHODS @@ -567,666 +567,27 @@ def test_getallsoftwareprojectnamesbyspv_shouldreturnexpectedlist_wheninvoked(se # Assert self.assertEqual(expected, actual) -class TimeTrackingManagerTestCase(unittest.TestCase): - - def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring(self): - - # Arrange - td_str : str = "5h 30m" - expected_td : timedelta = pd.Timedelta(hours = 5, minutes = 30).to_pytimedelta() - - # Act - actual_td : str = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = td_str) # type: ignore - - # Assert - self.assertEqual(expected_td, actual_td) - def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): - - # Arrange - yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() - year : int = 2024 - expected_hours : timedelta = timedelta(hours = 250) - - # Act - actual_hours : timedelta = TTDataFrameFactory()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year).hours # type: ignore - - # Assert - self.assertEqual(expected_hours, actual_hours) - def test_getyearlytarget_shouldreturnnone_whenyearnotinlist(self): - - # Arrange - yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() - year : int = 2010 - - # Act - yearly_target : YearlyTarget = TTDataFrameFactory()._TimeTrackingManager__get_yearly_target(yearly_targets = yearly_targets, year = year) # type: ignore - - # Assert - self.assertIsNone(yearly_target) - def test_isyearlytargetmet_shouldreturntrue_whenyearlytargetismet(self): - - # Arrange - effort : timedelta = pd.Timedelta(hours = 255, minutes = 30) - yearly_target : timedelta = pd.Timedelta(hours = 250) - - # Act - actual : bool = TTDataFrameFactory()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore - - # Assert - self.assertTrue(actual) - def test_isyearlytargetmet_shouldreturnfalse_whenyearlytargetisnotmet(self): - - # Arrange - effort : timedelta = pd.Timedelta(hours = 249) - yearly_target : timedelta = pd.Timedelta(hours = 250) - - # Act - actual : bool = TTDataFrameFactory()._TimeTrackingManager__is_yearly_target_met(effort = effort, yearly_target = yearly_target) # type: ignore - - # Assert - self.assertFalse(actual) - def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussignfalse(self): - - # Arrange - td : timedelta = pd.Timedelta(hours = 255, minutes = 30) - expected : str = "255h 30m" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = False) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussigntrue(self): - - # Arrange - td : timedelta = pd.Timedelta(hours = 255, minutes = 30) - expected : str = "+255h 30m" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__format_timedelta(td = td, add_plus_sign = True) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_extractsoftwareprojectname_shouldreturnexpectedstring_whenproperstring(self): - - # Arrange - descriptor : str = "NW.AutoProffLibrary v1.0.0" - expected : str = "NW.AutoProffLibrary" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_extractsoftwareprojectname_shouldreturnerrorstring_whenunproperstring(self): - - # Arrange - descriptor : str = "Some gibberish" - expected : str = "ERROR" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_name(descriptor = descriptor) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_extractsoftwareprojectversion_shouldreturnexpectedstring_whenproperstring(self): - - # Arrange - descriptor : str = "NW.AutoProffLibrary v1.0.0" - expected : str = "1.0.0" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_extractsoftwareprojectversion_shouldreturnerrorstring_whenunproperstring(self): - - # Arrange - descriptor : str = "Some gibberish" - expected : str = "ERROR" - - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__extract_software_project_version(descriptor = descriptor) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when0and16(self): - - # Arrange - part : float = 0 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 0.00 - - # Act - actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when4and0(self): - - # Arrange - part : float = 4 - whole : float = 0 - rounding_digits : int = 2 - expected : float = 0.00 - - # Act - actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when4and16(self): - - # Arrange - part : float = 4 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 25.00 - - # Act - actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when16and16(self): - - # Arrange - part : float = 16 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 100.00 - - # Act - actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): - - # Arrange - part : float = 3 - whole : float = 9 - rounding_digits : int = 4 - expected : float = 33.3333 +class MDInfoProviderTestCase(unittest.TestCase): + + def test_getall_shouldreturnexpectedlist_wheninvoked(self): - # Act - actual : float = TTDataFrameFactory()._TimeTrackingManager__calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) # type: ignore - - # Assert - self.assertEqual(expected, actual) - def test_createeffortstatus_shouldreturnexpectobject_wheneffortiscorrect(self): - # Arrange - idx : int = 1 - start_time_str : str = "07:00" - end_time_str : str = "08:00" - effort_str : str = "01h 00m" - - strp_format : str = "%Y-%m-%d %H:%M" - - start_time_dt : datetime = datetime.strptime(f"1900-01-01 {start_time_str}", strp_format) - end_time_dt : datetime = datetime.strptime(f"1900-01-01 {end_time_str}", strp_format) - actual_str = effort_str - actual_td : timedelta = pd.Timedelta(value = actual_str).to_pytimedelta() - expected_str : str = actual_str - expected_td : timedelta = actual_td - is_correct : bool = True - message : str = "The effort is correct." - expected : EffortStatus = EffortStatus( - idx = idx, - start_time_str = start_time_str, - start_time_dt = start_time_dt, - end_time_str = end_time_str, - end_time_dt = end_time_dt, - actual_str = effort_str, - actual_td = actual_td, - expected_td = expected_td, - expected_str = expected_str, - is_correct = is_correct, - message = message + expected : list[MDInfo] = [ + MDInfo( + id = TTID.TTSBYMONTH, + file_name="TIMETRACKINGBYMONTH.md", + paragraph_title="Time Tracking By Month", ) + ] # Act - actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str,end_time_str = end_time_str,effort_str = effort_str) # type: ignore - - # Assert - comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) - self.assertTrue(comparison) - def test_createeffortstatus_shouldreturnexpectobject_wheneffortisnotcorrect(self): - - # Arrange - idx : int = 1 - start_time_str : str = "07:00" - end_time_str : str = "08:00" - effort_str : str = "02h 00m" - - strp_format : str = "%Y-%m-%d %H:%M" - - start_time_dt : datetime = datetime.strptime(f"1900-01-01 {start_time_str}", strp_format) - end_time_dt : datetime = datetime.strptime(f"1900-01-01 {end_time_str}", strp_format) - actual_str = effort_str - actual_td : timedelta = pd.Timedelta(value = actual_str).to_pytimedelta() - expected_str : str = "01h 00m" - expected_td : timedelta = pd.Timedelta(value = expected_str).to_pytimedelta() - is_correct : bool = False - message : str = _MessageCollection.effort_status_mismatching_effort( - idx = idx, - start_time_str = start_time_str, - end_time_str = end_time_str, - actual_str = actual_str, - expected_str = expected_str - ) - - expected : EffortStatus = EffortStatus( - idx = idx, - start_time_str = start_time_str, - start_time_dt = start_time_dt, - end_time_str = end_time_str, - end_time_dt = end_time_dt, - actual_str = effort_str, - actual_td = actual_td, - expected_td = expected_td, - expected_str = expected_str, - is_correct = is_correct, - message = message - ) - - # Act - actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore - - # Assert - comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) - self.assertTrue(comparison) - - @parameterized.expand([ - [1, "5h 30m", timedelta(hours = 5, minutes = 30)], - [2, "2h 00m", timedelta(hours = 2, minutes = 00)] - ]) - def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( - self, - idx : int, - effort_str : str, - actual_td : timedelta): - - # Arrange - expected : EffortStatus = EffortStatus( - idx = idx, - start_time_str = None, - start_time_dt = None, - end_time_str = None, - end_time_dt = None, - actual_str = effort_str, - actual_td = actual_td, - expected_td = None, - expected_str = None, - is_correct = True, - message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." - ) - - # Act - actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore - - # Assert - comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) - self.assertTrue(comparison) - @parameterized.expand([ - "07:00", "07:15", "07:30", "07:45", - "08:00", "08:15", "08:30", "08:45", - "09:00", "09:15", "09:30", "09:45", - "10:00", "10:15", "10:30", "10:45", - "11:00", "11:15", "11:30", "11:45", - "12:00", "12:15", "12:30", "12:45", - "13:00", "13:15", "13:30", "13:45", - "14:00", "14:15", "14:30", "14:45", - "15:00", "15:15", "15:30", "15:45", - "16:00", "16:15", "16:30", "16:45", - "17:00", "17:15", "17:30", "17:45", - "18:00", "18:15", "18:30", "18:45", - "19:00", "19:15", "19:30", "19:45", - "20:00", "20:15", "20:30", "20:45", - "21:00", "21:15", "21:30", "21:45", - "22:00", "22:15", "22:30", "22:45", - "23:00", "23:15", "23:30", "23:45" - ]) - def test_createtimeobject_shouldreturnexpecteddatatime_whenday1time(self, time : str): - - # Arrange - strp_format : str = "%Y-%m-%d %H:%M" - dt_str = f"1900-01-01 {time}" - expected : datetime = datetime.strptime(dt_str, strp_format) - - # Act - actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore - - # Assert - self.assertEqual(expected, actual) - @parameterized.expand([ - "00:00", "00:15", "00:30", "00:45", - "01:00", "01:15", "01:30", "01:45", - "02:00", "02:15", "02:30", "02:45", - "03:00", "03:15", "03:30", "03:45", - "04:00", "04:15", "04:30", "04:45", - "05:00", "05:15", "05:30", "05:45", - "06:00", "06:15", "06:30", "06:45" - ]) - def test_createtimeobject_shouldreturnexpecteddatatime_whenday2time(self, time : str): - - # Arrange - strp_format : str = "%Y-%m-%d %H:%M" - dt_str = f"1900-01-02 {time}" - expected : datetime = datetime.strptime(dt_str, strp_format) - - # Act - actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore - - # Assert - self.assertEqual(expected, actual) - @parameterized.expand([ - "07:04", - "00:01", - "gibberish text" - ]) - def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues(self, time : str): - - # Arrange - expected_message : str = _MessageCollection.effort_status_not_among_expected_time_values(time = time) - - # Act - with self.assertRaises(ValueError) as context: - actual : datetime = TTDataFrameFactory()._TimeTrackingManager__create_time_object(time = time) # type: ignore - - # Assert - self.assertTrue(expected_message in str(context.exception)) - @parameterized.expand([ - [1, "07:00", "", "5h 30m"], - [1, "", "07:00", "5h 30m"] - ]) - def test_createeffortstatus_shouldreturnexpectobject_whenstarttimeorendtimeareempty( - self, - idx : int, - start_time_str : str, - end_time_str : str, - effort_str : str): - - # Arrange - actual_td : timedelta = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = effort_str) # type: ignore - expected : EffortStatus = EffortStatus( - idx = idx, - start_time_str = None, - start_time_dt = None, - end_time_str = None, - end_time_dt = None, - actual_str = effort_str, - actual_td = actual_td, - expected_td = None, - expected_str = None, - is_correct = True, - message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." - ) - - # Act - actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status( - idx = idx, - start_time_str = start_time_str, - end_time_str = end_time_str, - effort_str = effort_str) - - # Assert - comparison : bool = SupportMethodProvider().are_effort_statuses_equal(ef1 = expected, ef2 = actual) - self.assertTrue(comparison) - @parameterized.expand([ - ["Some Gibberish", "08:00", "01h 00m"], - ["07:00", "Some Gibberish", "01h 00m"], - ["07:00", "08:00", "Some Gibberish"] - ]) - def test_createeffortstatus_shouldraisevalueerrorexception_whenunproperparameters( - self, - start_time_str : str, - end_time_str : str, - effort_str : str): - - # Arrange - idx : int = 1 - expected_message : str = _MessageCollection.effort_status_not_possible_to_create( - idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) - - # Act - with self.assertRaises(ValueError) as context: - actual : EffortStatus = TTDataFrameFactory()._TimeTrackingManager__create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) # type: ignore - - # Assert - self.assertTrue(expected_message in str(context.exception)) - @parameterized.expand([ - ["07:00", "08:00", "UNKNOWN", "07:00-08:00"], - ["", "08:00", "UNKNOWN", "UNKNOWN"], - ["07:00", "", "UNKNOWN", "UNKNOWN"] - ]) - def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( - self, - start_time : str, - end_time : str, - unknown_id : str, - expected : str): - - # Arrange - # Act - actual : str = TTDataFrameFactory()._TimeTrackingManager__create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) # type: ignore + actual : list[MDInfo] = MDInfoProvider().get_all() # Assert self.assertEqual(expected, actual) - - def test_getsessionsdataset_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - excel_data_df : DataFrame = ObjectMother().create_excel_data() - setting_bag : SettingBag = ObjectMother().create_setting_bag() - expected_column_names : list[str] = ObjectMother().create_sessions_df_column_names() - expected_dtype_names : list[str] = ObjectMother().create_sessions_df_dtype_names() - expected_nan : str = "" - - # Act - with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: - actual : DataFrame = TTDataFrameFactory().create_tt_df(setting_bag = setting_bag) - - # Assert - self.assertEqual(expected_column_names, actual.columns.tolist()) - self.assertEqual(expected_dtype_names, SupportMethodProvider().get_dtype_names(df = actual)) - self.assertEqual(expected_nan, actual[expected_column_names[1]][0]) - self.assertEqual(expected_nan, actual[expected_column_names[2]][0]) - self.assertEqual(expected_nan, actual[expected_column_names[5]][0]) - def test_getttbyyear_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_df(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttbyyearmonth_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_tpl(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttbyyearmonthspnv_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_month_spnv_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_month_spnv_tpl(tt_df = sessions_df, years = years, software_project_names = software_project_names) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttbyyearspnv_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_spnv_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_spnv_tpl(tt_df = sessions_df, years = years, software_project_names = software_project_names) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttbyspnspv_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_spn_spv_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_spv_df(tt_df = sessions_df, years = years, software_project_names = software_project_names) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttsbymonth_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tts_by_month_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_month_tpl(tt_df = sessions_df, years = years) - - # Assert - assert_frame_equal(expected_df, actual_df) - def test_updatefuturemonthstoempty_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - now : datetime = datetime(2024, 2, 27) - tts_by_month_df : DataFrame = ObjectMother().create_tts_by_month_df() - expected_df : DataFrame = ObjectMother().create_tts_by_month_upd_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().__update_future_months_to_empty(tts_by_month_df = tts_by_month_df, now = now) - - # Assert - assert_frame_equal(expected_df, actual_df) - def test_createtimeranges_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - unknown_id : str = "Unknown" - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_time_ranges_df() - expected_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) - expected_df.reset_index(drop = True, inplace = True) - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_tr_df(tt_df = sessions_df, unknown_id = unknown_id) - actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) - actual_df.reset_index(drop = True, inplace = True) - - # Assert - assert_frame_equal(expected_df, actual_df) - def test_removeunknownid_shouldreturnexpecteddataframe_whencontainsunknownid(self): - - # Arrange - unknown_id : str = "Unknown" - expected_df : DataFrame = ObjectMother().create_time_ranges_df() - time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() - time_ranges_df.loc[len(time_ranges_df.index)] = [unknown_id, 3] - - # Act - actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_occurrences(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) - - # Assert - assert_frame_equal(expected_df, actual_df) - def test_removeunknownid_shouldreturnexpecteddataframe_whendoesnotcontainunknownid(self): - - # Arrange - unknown_id : str = "Unknown" - expected_df : DataFrame = ObjectMother().create_time_ranges_df() - time_ranges_df : DataFrame = ObjectMother().create_time_ranges_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().__remove_unknown_occurrences(tts_by_tr_df = time_ranges_df, unknown_id = unknown_id) - - # Assert - assert_frame_equal(expected_df, actual_df) - def test_getttbyyearhashtag_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - years : list[int] = [2024] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_hashtag_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_year_hashtag_df(tt_df = sessions_df, years = years) - - # Assert - assert_frame_equal(expected_df , actual_df) - def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): - - # Arrange - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_hashtag_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_hashtag_df(tt_df = sessions_df) - - # Assert - assert_frame_equal(expected_df , actual_df) - - @parameterized.expand([ - [True], - [False] - ]) - def test_getttbyspn_shouldreturnexpecteddataframe_wheninvoked(self, remove_untagged : bool): - - # Arrange - years : list[int] = [2024] - software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_spn_df() - - # Act - actual_df : DataFrame = TTDataFrameFactory().create_tts_by_spn_df(tt_df = sessions_df, years = years, software_project_names = software_project_names, remove_untagged = remove_untagged) - - # Assert - assert_frame_equal(expected_df , actual_df) -class MarkdownProcessorTestCase(unittest.TestCase): - - def test_processttsbymonthmd_shouldlogandsave_whenshowandsavearetrue(self) -> None: - - # Arrange - file_name : str = "TIMETRACKINGBYMONTH.md" - file_path : str = f"/home/nwtimetracking/{file_name}" - tts_by_month_upd_df, expected = ObjectMother().create_dtos_for_ttsbymonthmd() - component_bag, _, markdown_processor = ObjectMother().create_service_objects_for_ttsbymonthmd() - - # Act - markdown_processor.process_tts_by_month_md(tts_by_month_upd_df = tts_by_month_upd_df) - - # Assert - self.assertEqual(component_bag.logging_function.call_count, 2) - component_bag.logging_function.assert_has_calls([ - call(file_name + "\n"), - call(expected) - ]) - component_bag.file_manager.save_content.assert_called_with(content = expected, file_path = file_path) + self.assertEqual(expected[0].id, actual[0].id) + self.assertEqual(expected[0].file_name, actual[0].file_name) + self.assertEqual(expected[0].paragraph_title, actual[0].paragraph_title) # MAIN if __name__ == "__main__": From 0ade9f9866b06a33f58e26254c0f59e88f339902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:33:18 +0000 Subject: [PATCH 069/109] #40 TTDataFrameHelperTestCase, added - Iteration 1 (20%). --- tests/nwtimetrackingtests.py | 76 ++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 060d5d8..b094dff 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -10,14 +10,14 @@ from pandas.testing import assert_frame_equal from parameterized import parameterized from types import FunctionType -from typing import Tuple +from typing import Optional, Tuple, cast from unittest.mock import Mock, call, patch # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) from nwtimetracking import ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection -from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider +from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider, TTDataFrameHelper from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer # SUPPORT METHODS @@ -67,7 +67,6 @@ def are_yearly_targets_equal(yt1 : YearlyTarget, yt2 : YearlyTarget) -> bool: ''' return (yt1.hours == yt2.hours and yt1.year == yt2.year) - @staticmethod def are_lists_of_yearly_targets_equal(list1 : list[YearlyTarget], list2 : list[YearlyTarget]) -> bool: @@ -588,7 +587,78 @@ def test_getall_shouldreturnexpectedlist_wheninvoked(self): self.assertEqual(expected[0].id, actual[0].id) self.assertEqual(expected[0].file_name, actual[0].file_name) self.assertEqual(expected[0].paragraph_title, actual[0].paragraph_title) +class TTDataFrameHelperTestCase(unittest.TestCase): + + def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring(self): + + # Arrange + td_str : str = "5h 30m" + expected_td : timedelta = pd.Timedelta(hours = 5, minutes = 30).to_pytimedelta() + + # Act + actual_td : timedelta = TTDataFrameHelper().convert_string_to_timedelta(td_str = td_str) + # Assert + self.assertEqual(expected_td, actual_td) + def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): + + # Arrange + yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() + year : int = 2024 + expected_hours : timedelta = timedelta(hours = 250) + + # Act + actual_hours : timedelta = cast(YearlyTarget, TTDataFrameHelper().get_yearly_target(yearly_targets = yearly_targets, year = year)).hours + + # Assert + self.assertEqual(expected_hours, actual_hours) + def test_getyearlytarget_shouldreturnnone_whenyearnotinlist(self): + + # Arrange + yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() + year : int = 2010 + + # Act + yearly_target : Optional[YearlyTarget] = TTDataFrameHelper().get_yearly_target(yearly_targets = yearly_targets, year = year) + + # Assert + self.assertIsNone(yearly_target) + def test_isyearlytargetmet_shouldreturntrue_whenyearlytargetismet(self): + + # Arrange + effort : timedelta = pd.Timedelta(hours = 255, minutes = 30) + yearly_target : timedelta = pd.Timedelta(hours = 250) + + # Act + actual : bool = TTDataFrameHelper().is_yearly_target_met(effort = effort, yearly_target = yearly_target) + + # Assert + self.assertTrue(actual) + def test_isyearlytargetmet_shouldreturnfalse_whenyearlytargetisnotmet(self): + + # Arrange + effort : timedelta = pd.Timedelta(hours = 249) + yearly_target : timedelta = pd.Timedelta(hours = 250) + + # Act + actual : bool = TTDataFrameHelper().is_yearly_target_met(effort = effort, yearly_target = yearly_target) + + # Assert + self.assertFalse(actual) + def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussignfalse(self): + + # Arrange + td : timedelta = pd.Timedelta(hours = 255, minutes = 30) + expected : str = "255h 30m" + + # Act + actual : str = TTDataFrameHelper().format_timedelta(td = td, add_plus_sign = False) + + # Assert + self.assertEqual(expected, actual) + + + # MAIN if __name__ == "__main__": result = unittest.main(argv=[''], verbosity=3, exit=False) \ No newline at end of file From 33588165dfae69f9157e7ed9087e37ee5be0d995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:41:31 +0000 Subject: [PATCH 070/109] #40 TTDataFrameHelperTestCase, added - Iteration 1 (41%). --- tests/nwtimetrackingtests.py | 135 +++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 6 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index b094dff..63c913e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -589,6 +589,9 @@ def test_getall_shouldreturnexpectedlist_wheninvoked(self): self.assertEqual(expected[0].paragraph_title, actual[0].paragraph_title) class TTDataFrameHelperTestCase(unittest.TestCase): + def setUp(self): + + self.df_helper = TTDataFrameHelper() def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring(self): # Arrange @@ -596,7 +599,7 @@ def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring expected_td : timedelta = pd.Timedelta(hours = 5, minutes = 30).to_pytimedelta() # Act - actual_td : timedelta = TTDataFrameHelper().convert_string_to_timedelta(td_str = td_str) + actual_td : timedelta = self.df_helper.convert_string_to_timedelta(td_str = td_str) # Assert self.assertEqual(expected_td, actual_td) @@ -608,7 +611,7 @@ def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): expected_hours : timedelta = timedelta(hours = 250) # Act - actual_hours : timedelta = cast(YearlyTarget, TTDataFrameHelper().get_yearly_target(yearly_targets = yearly_targets, year = year)).hours + actual_hours : timedelta = cast(YearlyTarget, self.df_helper.get_yearly_target(yearly_targets = yearly_targets, year = year)).hours # Assert self.assertEqual(expected_hours, actual_hours) @@ -619,7 +622,7 @@ def test_getyearlytarget_shouldreturnnone_whenyearnotinlist(self): year : int = 2010 # Act - yearly_target : Optional[YearlyTarget] = TTDataFrameHelper().get_yearly_target(yearly_targets = yearly_targets, year = year) + yearly_target : Optional[YearlyTarget] = self.df_helper.get_yearly_target(yearly_targets = yearly_targets, year = year) # Assert self.assertIsNone(yearly_target) @@ -630,7 +633,7 @@ def test_isyearlytargetmet_shouldreturntrue_whenyearlytargetismet(self): yearly_target : timedelta = pd.Timedelta(hours = 250) # Act - actual : bool = TTDataFrameHelper().is_yearly_target_met(effort = effort, yearly_target = yearly_target) + actual : bool = self.df_helper.is_yearly_target_met(effort = effort, yearly_target = yearly_target) # Assert self.assertTrue(actual) @@ -641,7 +644,7 @@ def test_isyearlytargetmet_shouldreturnfalse_whenyearlytargetisnotmet(self): yearly_target : timedelta = pd.Timedelta(hours = 250) # Act - actual : bool = TTDataFrameHelper().is_yearly_target_met(effort = effort, yearly_target = yearly_target) + actual : bool = self.df_helper.is_yearly_target_met(effort = effort, yearly_target = yearly_target) # Assert self.assertFalse(actual) @@ -652,13 +655,133 @@ def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussi expected : str = "255h 30m" # Act - actual : str = TTDataFrameHelper().format_timedelta(td = td, add_plus_sign = False) + actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = False) # Assert self.assertEqual(expected, actual) + def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussigntrue(self): + # Arrange + td : timedelta = pd.Timedelta(hours = 255, minutes = 30) + expected : str = "+255h 30m" + # Act + actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = True) + # Assert + self.assertEqual(expected, actual) + def test_extractsoftwareprojectname_shouldreturnexpectedstring_whenproperstring(self): + + # Arrange + descriptor : str = "NW.AutoProffLibrary v1.0.0" + expected : str = "NW.AutoProffLibrary" + + # Act + actual : str = self.df_helper.extract_software_project_name(descriptor = descriptor) + + # Assert + self.assertEqual(expected, actual) + def test_extractsoftwareprojectname_shouldreturnerrorstring_whenunproperstring(self): + + # Arrange + descriptor : str = "Some gibberish" + expected : str = "ERROR" + + # Act + actual : str = self.df_helper.extract_software_project_name(descriptor = descriptor) + + # Assert + self.assertEqual(expected, actual) + def test_extractsoftwareprojectversion_shouldreturnexpectedstring_whenproperstring(self): + + # Arrange + descriptor : str = "NW.AutoProffLibrary v1.0.0" + expected : str = "1.0.0" + + # Act + actual : str = self.df_helper.extract_software_project_version(descriptor = descriptor) + + # Assert + self.assertEqual(expected, actual) + def test_extractsoftwareprojectversion_shouldreturnerrorstring_whenunproperstring(self): + + # Arrange + descriptor : str = "Some gibberish" + expected : str = "ERROR" + + # Act + actual : str = self.df_helper.extract_software_project_version(descriptor = descriptor) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when0and16(self): + + # Arrange + part : float = 0 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 0.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when4and0(self): + + # Arrange + part : float = 4 + whole : float = 0 + rounding_digits : int = 2 + expected : float = 0.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when4and16(self): + + # Arrange + part : float = 4 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 25.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when16and16(self): + + # Arrange + part : float = 16 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 100.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): + + # Arrange + part : float = 3 + whole : float = 9 + rounding_digits : int = 4 + expected : float = 33.3333 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + + + # MAIN if __name__ == "__main__": result = unittest.main(argv=[''], verbosity=3, exit=False) \ No newline at end of file From 74e962666e3221d738f5209bb548a3fb12d88929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:47:32 +0000 Subject: [PATCH 071/109] #40 TTDataFrameHelperTestCase, added - Iteration 3 (75%). --- tests/nwtimetrackingtests.py | 125 +++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 63c913e..3ab16e8 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -592,6 +592,7 @@ class TTDataFrameHelperTestCase(unittest.TestCase): def setUp(self): self.df_helper = TTDataFrameHelper() + self.sm_provider = SupportMethodProvider() def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring(self): # Arrange @@ -779,7 +780,131 @@ def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): # Assert self.assertEqual(expected, actual) + def test_createeffortstatus_shouldreturnexpectobject_wheneffortiscorrect(self): + # Arrange + idx : int = 1 + start_time_str : str = "07:00" + end_time_str : str = "08:00" + effort_str : str = "01h 00m" + + strp_format : str = "%Y-%m-%d %H:%M" + + start_time_dt : datetime = datetime.strptime(f"1900-01-01 {start_time_str}", strp_format) + end_time_dt : datetime = datetime.strptime(f"1900-01-01 {end_time_str}", strp_format) + actual_str = effort_str + actual_td : timedelta = pd.Timedelta(value = actual_str).to_pytimedelta() + expected_str : str = actual_str + expected_td : timedelta = actual_td + is_correct : bool = True + message : str = "The effort is correct." + expected : EffortStatus = EffortStatus( + idx = idx, + start_time_str = start_time_str, + start_time_dt = start_time_dt, + end_time_str = end_time_str, + end_time_dt = end_time_dt, + actual_str = effort_str, + actual_td = actual_td, + expected_td = expected_td, + expected_str = expected_str, + is_correct = is_correct, + message = message + ) + + # Act + actual : EffortStatus = self.df_helper.create_effort_status( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + effort_str = effort_str + ) + + # Assert + comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) + self.assertTrue(comparison) + def test_createeffortstatus_shouldreturnexpectobject_wheneffortisnotcorrect(self): + + # Arrange + idx : int = 1 + start_time_str : str = "07:00" + end_time_str : str = "08:00" + effort_str : str = "02h 00m" + + strp_format : str = "%Y-%m-%d %H:%M" + + start_time_dt : datetime = datetime.strptime(f"1900-01-01 {start_time_str}", strp_format) + end_time_dt : datetime = datetime.strptime(f"1900-01-01 {end_time_str}", strp_format) + actual_str = effort_str + actual_td : timedelta = pd.Timedelta(value = actual_str).to_pytimedelta() + expected_str : str = "01h 00m" + expected_td : timedelta = pd.Timedelta(value = expected_str).to_pytimedelta() + is_correct : bool = False + message : str = _MessageCollection.effort_status_mismatching_effort( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + actual_str = actual_str, + expected_str = expected_str + ) + + expected : EffortStatus = EffortStatus( + idx = idx, + start_time_str = start_time_str, + start_time_dt = start_time_dt, + end_time_str = end_time_str, + end_time_dt = end_time_dt, + actual_str = effort_str, + actual_td = actual_td, + expected_td = expected_td, + expected_str = expected_str, + is_correct = is_correct, + message = message + ) + + # Act + actual : EffortStatus = self.df_helper.create_effort_status( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + effort_str = effort_str + ) + + # Assert + comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) + self.assertTrue(comparison) + + @parameterized.expand([ + [1, "5h 30m", timedelta(hours = 5, minutes = 30)], + [2, "2h 00m", timedelta(hours = 2, minutes = 00)] + ]) + def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( + self, + idx : int, + effort_str : str, + actual_td : timedelta): + + # Arrange + expected : EffortStatus = EffortStatus( + idx = idx, + start_time_str = None, + start_time_dt = None, + end_time_str = None, + end_time_dt = None, + actual_str = effort_str, + actual_td = actual_td, + expected_td = None, + expected_str = None, + is_correct = True, + message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + ) + + # Act + actual : EffortStatus = self.df_helper.create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore + + # Assert + comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) + self.assertTrue(comparison) # MAIN From a907c88358982cef6ab876922f72f9e964bf22b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 14:52:09 +0000 Subject: [PATCH 072/109] #40 TTDataFrameHelperTestCase, added - Iteration 4 (86%). --- tests/nwtimetrackingtests.py | 153 +++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 3ab16e8..8c49308 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -906,6 +906,159 @@ def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) self.assertTrue(comparison) + @parameterized.expand([ + "07:00", "07:15", "07:30", "07:45", + "08:00", "08:15", "08:30", "08:45", + "09:00", "09:15", "09:30", "09:45", + "10:00", "10:15", "10:30", "10:45", + "11:00", "11:15", "11:30", "11:45", + "12:00", "12:15", "12:30", "12:45", + "13:00", "13:15", "13:30", "13:45", + "14:00", "14:15", "14:30", "14:45", + "15:00", "15:15", "15:30", "15:45", + "16:00", "16:15", "16:30", "16:45", + "17:00", "17:15", "17:30", "17:45", + "18:00", "18:15", "18:30", "18:45", + "19:00", "19:15", "19:30", "19:45", + "20:00", "20:15", "20:30", "20:45", + "21:00", "21:15", "21:30", "21:45", + "22:00", "22:15", "22:30", "22:45", + "23:00", "23:15", "23:30", "23:45" + ]) + def test_createtimeobject_shouldreturnexpecteddatatime_whenday1time(self, time : str): + + # Arrange + strp_format : str = "%Y-%m-%d %H:%M" + dt_str = f"1900-01-01 {time}" + expected : datetime = datetime.strptime(dt_str, strp_format) + + # Act + actual : datetime = self.df_helper.create_time_object(time = time) + + # Assert + self.assertEqual(expected, actual) + + @parameterized.expand([ + "00:00", "00:15", "00:30", "00:45", + "01:00", "01:15", "01:30", "01:45", + "02:00", "02:15", "02:30", "02:45", + "03:00", "03:15", "03:30", "03:45", + "04:00", "04:15", "04:30", "04:45", + "05:00", "05:15", "05:30", "05:45", + "06:00", "06:15", "06:30", "06:45" + ]) + def test_createtimeobject_shouldreturnexpecteddatatime_whenday2time(self, time : str): + + # Arrange + strp_format : str = "%Y-%m-%d %H:%M" + dt_str = f"1900-01-02 {time}" + expected : datetime = datetime.strptime(dt_str, strp_format) + + # Act + actual : datetime = self.df_helper.create_time_object(time = time) + + # Assert + self.assertEqual(expected, actual) + + @parameterized.expand([ + "07:04", + "00:01", + "gibberish text" + ]) + def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues(self, time : str): + + # Arrange + expected_message : str = _MessageCollection.effort_status_not_among_expected_time_values(time = time) + + # Act + with self.assertRaises(ValueError) as context: + actual : datetime = self.df_helper.create_time_object(time = time) + + # Assert + self.assertTrue(expected_message in str(context.exception)) + + @parameterized.expand([ + [1, "07:00", "", "5h 30m"], + [1, "", "07:00", "5h 30m"] + ]) + def test_createeffortstatus_shouldreturnexpectobject_whenstarttimeorendtimeareempty( + self, + idx : int, + start_time_str : str, + end_time_str : str, + effort_str : str): + + # Arrange + actual_td : timedelta = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = effort_str) # type: ignore + expected : EffortStatus = EffortStatus( + idx = idx, + start_time_str = None, + start_time_dt = None, + end_time_str = None, + end_time_dt = None, + actual_str = effort_str, + actual_td = actual_td, + expected_td = None, + expected_str = None, + is_correct = True, + message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + ) + + # Act + actual : EffortStatus = self.df_helper.create_effort_status( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + effort_str = effort_str) + + # Assert + comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) + self.assertTrue(comparison) + + @parameterized.expand([ + ["Some Gibberish", "08:00", "01h 00m"], + ["07:00", "Some Gibberish", "01h 00m"], + ["07:00", "08:00", "Some Gibberish"] + ]) + def test_createeffortstatus_shouldraisevalueerrorexception_whenunproperparameters( + self, + start_time_str : str, + end_time_str : str, + effort_str : str): + + # Arrange + idx : int = 1 + expected_message : str = _MessageCollection.effort_status_not_possible_to_create( + idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) + + # Act + with self.assertRaises(ValueError) as context: + self.df_helper.create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) + + # Assert + self.assertTrue(expected_message in str(context.exception)) + + @parameterized.expand([ + ["07:00", "08:00", "UNKNOWN", "07:00-08:00"], + ["", "08:00", "UNKNOWN", "UNKNOWN"], + ["07:00", "", "UNKNOWN", "UNKNOWN"] + ]) + def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( + self, + start_time : str, + end_time : str, + unknown_id : str, + expected : str): + + # Arrange + # Act + actual : str = self.df_helper.create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) + + # Assert + self.assertEqual(expected, actual) + + + # MAIN if __name__ == "__main__": From 3acf96a27fd47a5692c773e8af4fcea1586ad3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Thu, 5 Dec 2024 15:13:32 +0000 Subject: [PATCH 073/109] #40 TTDataFrameHelperTestCase, added - Iteration 4 (87%). --- tests/nwtimetrackingtests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 8c49308..14bdf4e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -972,7 +972,7 @@ def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues( # Act with self.assertRaises(ValueError) as context: - actual : datetime = self.df_helper.create_time_object(time = time) + self.df_helper.create_time_object(time = time) # Assert self.assertTrue(expected_message in str(context.exception)) @@ -989,7 +989,7 @@ def test_createeffortstatus_shouldreturnexpectobject_whenstarttimeorendtimeareem effort_str : str): # Arrange - actual_td : timedelta = TTDataFrameFactory()._TimeTrackingManager__convert_string_to_timedelta(td_str = effort_str) # type: ignore + actual_td : timedelta = self.df_helper.convert_string_to_timedelta(td_str = effort_str) expected : EffortStatus = EffortStatus( idx = idx, start_time_str = None, From 3a9cce710bf39b06924884174b52f6bba5a99c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 10:56:37 +0000 Subject: [PATCH 074/109] #40 TTDataFrameHelperTestCase, added - Iteration 5 (99%). --- tests/nwtimetrackingtests.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 14bdf4e..7878f5a 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -16,7 +16,7 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider, TTDataFrameHelper from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer @@ -1057,7 +1057,41 @@ def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( # Assert self.assertEqual(expected, actual) + @parameterized.expand([ + [timedelta(minutes=30), timedelta(hours=1), "↑"], + [timedelta(hours=1), timedelta(minutes=30), "↓"], + [timedelta(minutes=30), timedelta(minutes=30), "="], + ]) + def test_gettrendbytimedelta_shouldreturnexpectedtrend_wheninvoked( + self, + td_1 : timedelta, + td_2 : timedelta, + expected : str + ): + + # Arrange + # Act + actual : str = self.df_helper.get_trend_by_timedelta(td_1 = td_1, td_2 = td_2) + + # Assert + self.assertEqual(expected, actual) + @parameterized.expand([ + ["↕1", TTCN.TREND], + ["2016", "2016"], + ]) + def test_tryconsolidatetrendcolumnname_shouldreturnexpectedcolumnname_wheninvoked( + self, + column_name: str, + expected: str + ): + + # Arrange + # Act + actual : str = self.df_helper.try_consolidate_trend_column_name(column_name) + + # Assert + self.assertEqual(expected, actual) # MAIN From d93d83e7ec7851b1466ce9c6b16286f5706e7dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 11:29:03 +0000 Subject: [PATCH 075/109] #40 TTDataFrameHelperTestCase, added - Iteration 5 (100%). --- tests/nwtimetrackingtests.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 7878f5a..dd2c299 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1093,6 +1093,31 @@ def test_tryconsolidatetrendcolumnname_shouldreturnexpectedcolumnname_wheninvoke # Assert self.assertEqual(expected, actual) + def test_createeffortstatusandcasttoany_shouldwork_withdfapply(self): + + # Arrange + data : list[dict] = [ + {"idx": 1, "start_time_str": "07:00", "end_time_str": "08:00", "effort_str": "01h 00m"} + ] + df : DataFrame = pd.DataFrame(data) + + # Act + try: + + df[TTCN.EFFORTSTATUS] = df.apply( + lambda x : self.df_helper.create_effort_status_and_cast_to_any( + idx = x["idx"], + start_time_str = x["start_time_str"], + end_time_str = x["end_time_str"], + effort_str = x["effort_str"] + ), axis=1) + + except Exception as e: + self.fail(str(e)) + + # Assert + self.assertTrue(TTCN.EFFORTSTATUS in df.columns) + # MAIN if __name__ == "__main__": From 8cddc840be0eab412935c509fd57c740520d327d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 11:47:48 +0000 Subject: [PATCH 076/109] #40 TTDataFrameHelper, re-arranged. --- src/nwtimetracking.py | 190 ++++++++-------- tests/nwtimetrackingtests.py | 424 ++++++++++++++++++----------------- 2 files changed, 308 insertions(+), 306 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index e158c42..723cc2e 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -345,28 +345,29 @@ class TTDataFrameHelper(): '''Collects helper functions for TTDataFrameFactory.''' - def convert_string_to_timedelta(self, td_str : str) -> timedelta: + def calculate_percentage(self, part : float, whole : float, rounding_digits : int = 2) -> float: - '''"5h 30m" => 5:30:00''' + '''Calculates a percentage.''' - td : timedelta = pd.Timedelta(value = td_str).to_pytimedelta() + prct : Optional[float] = None - return td - def get_yearly_target(self, yearly_targets : list[YearlyTarget], year : int) -> Optional[YearlyTarget]: + if part == 0: + prct = 0 + elif whole == 0: + prct = 0 + else: + prct = (100 * part) / whole - '''Retrieves the YearlyTarget object for the provided "year" or None.''' + prct = round(number = prct, ndigits = rounding_digits) - for yearly_target in yearly_targets: - if yearly_target.year == year: - return yearly_target - - return None - def is_yearly_target_met(self, effort : timedelta, yearly_target : timedelta) -> bool: + return prct + def convert_string_to_timedelta(self, td_str : str) -> timedelta: - if effort >= yearly_target: - return True + '''"5h 30m" => 5:30:00''' - return False + td : timedelta = pd.Timedelta(value = td_str).to_pytimedelta() + + return td def format_timedelta(self, td : timedelta, add_plus_sign : bool) -> str: ''' @@ -386,54 +387,6 @@ def format_timedelta(self, td : timedelta, add_plus_sign : bool) -> str: formatted = f"+{formatted}" return formatted - def extract_software_project_name(self, descriptor : str) -> str: - - ''' - "NW.AutoProffLibrary v1.0.0" => "NW.AutoProffLibrary" - "nwreadinglistmanager v1.5.0" => "nwreadinglistmanager" - - Returns "ERROR" is parsing goes wrong. - ''' - - pattern : str = r"\b[a-zA-Z\.]{2,}(?=[ v]{2}[0-9]{1}[\.]{1}[0-9]{1}[\.]{1}[0-9]{1})" - matches : list = re.findall(pattern = pattern, string = descriptor, flags = re.MULTILINE) - - if len(matches) == 1: - return matches[0] - - return "ERROR" - def extract_software_project_version(self, descriptor : str) -> str: - - ''' - "NW.AutoProffLibrary v1.0.0" => "1.0.0" - "nwreadinglistmanager v1.5.0" => "1.5.0" - - Returns "ERROR" is parsing goes wrong. - ''' - - pattern : str = r"(?<=v)[0-9\.]{5}$" - matches : list = re.findall(pattern = pattern, string = descriptor, flags = re.MULTILINE) - - if len(matches) == 1: - return matches[0] - - return "ERROR" - def calculate_percentage(self, part : float, whole : float, rounding_digits : int = 2) -> float: - - '''Calculates a percentage.''' - - prct : Optional[float] = None - - if part == 0: - prct = 0 - elif whole == 0: - prct = 0 - else: - prct = (100 * part) / whole - - prct = round(number = prct, ndigits = rounding_digits) - - return prct def get_trend_by_timedelta(self, td_1 : timedelta, td_2 : timedelta) -> str: ''' @@ -462,29 +415,53 @@ def try_consolidate_trend_column_name(self, column_name : str) -> str: return TTCN.TREND return column_name - def create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: + def get_yearly_target(self, yearly_targets : list[YearlyTarget], year : int) -> Optional[YearlyTarget]: - '''Creates effort status for None values.''' + '''Retrieves the YearlyTarget object for the provided "year" or None.''' - actual_str : str = effort_str - actual_td : timedelta = self.convert_string_to_timedelta(td_str = effort_str) - is_correct : bool = True + for yearly_target in yearly_targets: + if yearly_target.year == year: + return yearly_target + + return None + def is_yearly_target_met(self, effort : timedelta, yearly_target : timedelta) -> bool: - effort_status : EffortStatus = EffortStatus( - idx = idx, - start_time_str = None, - start_time_dt = None, - end_time_str = None, - end_time_dt = None, - actual_str = actual_str, - actual_td = actual_td, - expected_td = None, - expected_str = None, - is_correct = is_correct, - message = _MessageCollection.starttime_endtime_are_empty() - ) + if effort >= yearly_target: + return True - return effort_status + return False + def extract_software_project_name(self, descriptor : str) -> str: + + ''' + "NW.AutoProffLibrary v1.0.0" => "NW.AutoProffLibrary" + "nwreadinglistmanager v1.5.0" => "nwreadinglistmanager" + + Returns "ERROR" is parsing goes wrong. + ''' + + pattern : str = r"\b[a-zA-Z\.]{2,}(?=[ v]{2}[0-9]{1}[\.]{1}[0-9]{1}[\.]{1}[0-9]{1})" + matches : list = re.findall(pattern = pattern, string = descriptor, flags = re.MULTILINE) + + if len(matches) == 1: + return matches[0] + + return "ERROR" + def extract_software_project_version(self, descriptor : str) -> str: + + ''' + "NW.AutoProffLibrary v1.0.0" => "1.0.0" + "nwreadinglistmanager v1.5.0" => "1.5.0" + + Returns "ERROR" is parsing goes wrong. + ''' + + pattern : str = r"(?<=v)[0-9\.]{5}$" + matches : list = re.findall(pattern = pattern, string = descriptor, flags = re.MULTILINE) + + if len(matches) == 1: + return matches[0] + + return "ERROR" def create_time_object(self, time : str) -> datetime: '''It creates a datetime object suitable for timedelta calculation out of the provided time.''' @@ -531,6 +508,19 @@ def create_time_object(self, time : str) -> datetime: dt : datetime = datetime.strptime(dt_str, strp_format) return dt + def create_time_range_id(self, start_time : str, end_time : str, unknown_id : str) -> str: + + ''' + Creates a unique time range identifier out of the provided parameters. + If parameters are empty, it returns unknown_id. + ''' + + time_range_id : str = f"{start_time}-{end_time}" + + if len(start_time) == 0 or len(end_time) == 0: + time_range_id = unknown_id + + return time_range_id def create_effort_status(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> EffortStatus: ''' @@ -593,6 +583,29 @@ def create_effort_status(self, idx : int, start_time_str : str, end_time_str : s idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str) raise ValueError(error) + def create_effort_status_for_none_values(self, idx : int, effort_str : str) -> EffortStatus: + + '''Creates effort status for None values.''' + + actual_str : str = effort_str + actual_td : timedelta = self.convert_string_to_timedelta(td_str = effort_str) + is_correct : bool = True + + effort_status : EffortStatus = EffortStatus( + idx = idx, + start_time_str = None, + start_time_dt = None, + end_time_str = None, + end_time_dt = None, + actual_str = actual_str, + actual_td = actual_td, + expected_td = None, + expected_str = None, + is_correct = is_correct, + message = _MessageCollection.starttime_endtime_are_empty() + ) + + return effort_status def create_effort_status_and_cast_to_any(self, idx : int, start_time_str : str, end_time_str : str, effort_str : str) -> Any: ''' @@ -605,19 +618,6 @@ def create_effort_status_and_cast_to_any(self, idx : int, start_time_str : str, ''' return cast(Any, self.create_effort_status(idx = idx, start_time_str = start_time_str, end_time_str = end_time_str, effort_str = effort_str)) - def create_time_range_id(self, start_time : str, end_time : str, unknown_id : str) -> str: - - ''' - Creates a unique time range identifier out of the provided parameters. - If parameters are empty, it returns unknown_id. - ''' - - time_range_id : str = f"{start_time}-{end_time}" - - if len(start_time) == 0 or len(end_time) == 0: - time_range_id = unknown_id - - return time_range_id class TTDataFrameFactory(): '''Collects all the logic related to dataframe creation out of "Time Tracking.xlsx".''' diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index dd2c299..6ad4641 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -593,6 +593,71 @@ def setUp(self): self.df_helper = TTDataFrameHelper() self.sm_provider = SupportMethodProvider() + def test_calculatepercentage_shouldreturnexpectedfloat_when0and16(self): + + # Arrange + part : float = 0 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 0.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when4and0(self): + + # Arrange + part : float = 4 + whole : float = 0 + rounding_digits : int = 2 + expected : float = 0.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when4and16(self): + + # Arrange + part : float = 4 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 25.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when16and16(self): + + # Arrange + part : float = 16 + whole : float = 16 + rounding_digits : int = 2 + expected : float = 100.00 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) + def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): + + # Arrange + part : float = 3 + whole : float = 9 + rounding_digits : int = 4 + expected : float = 33.3333 + + # Act + actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + + # Assert + self.assertEqual(expected, actual) def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring(self): # Arrange @@ -604,6 +669,65 @@ def test_convertstringtotimedelta_shouldreturnexpectedtimedelta_whenproperstring # Assert self.assertEqual(expected_td, actual_td) + def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussignfalse(self): + + # Arrange + td : timedelta = pd.Timedelta(hours = 255, minutes = 30) + expected : str = "255h 30m" + + # Act + actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = False) + + # Assert + self.assertEqual(expected, actual) + def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussigntrue(self): + + # Arrange + td : timedelta = pd.Timedelta(hours = 255, minutes = 30) + expected : str = "+255h 30m" + + # Act + actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = True) + + # Assert + self.assertEqual(expected, actual) + + @parameterized.expand([ + [timedelta(minutes=30), timedelta(hours=1), "↑"], + [timedelta(hours=1), timedelta(minutes=30), "↓"], + [timedelta(minutes=30), timedelta(minutes=30), "="], + ]) + def test_gettrendbytimedelta_shouldreturnexpectedtrend_wheninvoked( + self, + td_1 : timedelta, + td_2 : timedelta, + expected : str + ): + + # Arrange + # Act + actual : str = self.df_helper.get_trend_by_timedelta(td_1 = td_1, td_2 = td_2) + + # Assert + self.assertEqual(expected, actual) + + @parameterized.expand([ + ["↕1", TTCN.TREND], + ["2016", "2016"], + ]) + def test_tryconsolidatetrendcolumnname_shouldreturnexpectedcolumnname_wheninvoked( + self, + column_name: str, + expected: str + ): + + # Arrange + # Act + actual : str = self.df_helper.try_consolidate_trend_column_name(column_name) + + # Assert + self.assertEqual(expected, actual) + def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): # Arrange @@ -649,28 +773,6 @@ def test_isyearlytargetmet_shouldreturnfalse_whenyearlytargetisnotmet(self): # Assert self.assertFalse(actual) - def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussignfalse(self): - - # Arrange - td : timedelta = pd.Timedelta(hours = 255, minutes = 30) - expected : str = "255h 30m" - - # Act - actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = False) - - # Assert - self.assertEqual(expected, actual) - def test_formattimedelta_shouldreturnexpectedstring_whenpropertimedeltaandplussigntrue(self): - - # Arrange - td : timedelta = pd.Timedelta(hours = 255, minutes = 30) - expected : str = "+255h 30m" - - # Act - actual : str = self.df_helper.format_timedelta(td = td, add_plus_sign = True) - - # Assert - self.assertEqual(expected, actual) def test_extractsoftwareprojectname_shouldreturnexpectedstring_whenproperstring(self): # Arrange @@ -715,71 +817,97 @@ def test_extractsoftwareprojectversion_shouldreturnerrorstring_whenunproperstrin # Assert self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when0and16(self): + + @parameterized.expand([ + "07:00", "07:15", "07:30", "07:45", + "08:00", "08:15", "08:30", "08:45", + "09:00", "09:15", "09:30", "09:45", + "10:00", "10:15", "10:30", "10:45", + "11:00", "11:15", "11:30", "11:45", + "12:00", "12:15", "12:30", "12:45", + "13:00", "13:15", "13:30", "13:45", + "14:00", "14:15", "14:30", "14:45", + "15:00", "15:15", "15:30", "15:45", + "16:00", "16:15", "16:30", "16:45", + "17:00", "17:15", "17:30", "17:45", + "18:00", "18:15", "18:30", "18:45", + "19:00", "19:15", "19:30", "19:45", + "20:00", "20:15", "20:30", "20:45", + "21:00", "21:15", "21:30", "21:45", + "22:00", "22:15", "22:30", "22:45", + "23:00", "23:15", "23:30", "23:45" + ]) + def test_createtimeobject_shouldreturnexpecteddatatime_whenday1time(self, time : str): # Arrange - part : float = 0 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 0.00 - + strp_format : str = "%Y-%m-%d %H:%M" + dt_str = f"1900-01-01 {time}" + expected : datetime = datetime.strptime(dt_str, strp_format) + # Act - actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + actual : datetime = self.df_helper.create_time_object(time = time) # Assert self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when4and0(self): + + @parameterized.expand([ + "00:00", "00:15", "00:30", "00:45", + "01:00", "01:15", "01:30", "01:45", + "02:00", "02:15", "02:30", "02:45", + "03:00", "03:15", "03:30", "03:45", + "04:00", "04:15", "04:30", "04:45", + "05:00", "05:15", "05:30", "05:45", + "06:00", "06:15", "06:30", "06:45" + ]) + def test_createtimeobject_shouldreturnexpecteddatatime_whenday2time(self, time : str): # Arrange - part : float = 4 - whole : float = 0 - rounding_digits : int = 2 - expected : float = 0.00 - - # Act - actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) - - # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when4and16(self): + strp_format : str = "%Y-%m-%d %H:%M" + dt_str = f"1900-01-02 {time}" + expected : datetime = datetime.strptime(dt_str, strp_format) - # Arrange - part : float = 4 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 25.00 - # Act - actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + actual : datetime = self.df_helper.create_time_object(time = time) # Assert self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when16and16(self): + + @parameterized.expand([ + "07:04", + "00:01", + "gibberish text" + ]) + def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues(self, time : str): # Arrange - part : float = 16 - whole : float = 16 - rounding_digits : int = 2 - expected : float = 100.00 + expected_message : str = _MessageCollection.effort_status_not_among_expected_time_values(time = time) # Act - actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + with self.assertRaises(ValueError) as context: + self.df_helper.create_time_object(time = time) # Assert - self.assertEqual(expected, actual) - def test_calculatepercentage_shouldreturnexpectedfloat_when3and9and4(self): + self.assertTrue(expected_message in str(context.exception)) + + @parameterized.expand([ + ["07:00", "08:00", "UNKNOWN", "07:00-08:00"], + ["", "08:00", "UNKNOWN", "UNKNOWN"], + ["07:00", "", "UNKNOWN", "UNKNOWN"] + ]) + def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( + self, + start_time : str, + end_time : str, + unknown_id : str, + expected : str): # Arrange - part : float = 3 - whole : float = 9 - rounding_digits : int = 4 - expected : float = 33.3333 - # Act - actual : float = self.df_helper.calculate_percentage(part = part, whole = whole, rounding_digits = rounding_digits) + actual : str = self.df_helper.create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) # Assert self.assertEqual(expected, actual) + def test_createeffortstatus_shouldreturnexpectobject_wheneffortiscorrect(self): # Arrange @@ -874,109 +1002,6 @@ def test_createeffortstatus_shouldreturnexpectobject_wheneffortisnotcorrect(self comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) self.assertTrue(comparison) - @parameterized.expand([ - [1, "5h 30m", timedelta(hours = 5, minutes = 30)], - [2, "2h 00m", timedelta(hours = 2, minutes = 00)] - ]) - def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( - self, - idx : int, - effort_str : str, - actual_td : timedelta): - - # Arrange - expected : EffortStatus = EffortStatus( - idx = idx, - start_time_str = None, - start_time_dt = None, - end_time_str = None, - end_time_dt = None, - actual_str = effort_str, - actual_td = actual_td, - expected_td = None, - expected_str = None, - is_correct = True, - message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." - ) - - # Act - actual : EffortStatus = self.df_helper.create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore - - # Assert - comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) - self.assertTrue(comparison) - - @parameterized.expand([ - "07:00", "07:15", "07:30", "07:45", - "08:00", "08:15", "08:30", "08:45", - "09:00", "09:15", "09:30", "09:45", - "10:00", "10:15", "10:30", "10:45", - "11:00", "11:15", "11:30", "11:45", - "12:00", "12:15", "12:30", "12:45", - "13:00", "13:15", "13:30", "13:45", - "14:00", "14:15", "14:30", "14:45", - "15:00", "15:15", "15:30", "15:45", - "16:00", "16:15", "16:30", "16:45", - "17:00", "17:15", "17:30", "17:45", - "18:00", "18:15", "18:30", "18:45", - "19:00", "19:15", "19:30", "19:45", - "20:00", "20:15", "20:30", "20:45", - "21:00", "21:15", "21:30", "21:45", - "22:00", "22:15", "22:30", "22:45", - "23:00", "23:15", "23:30", "23:45" - ]) - def test_createtimeobject_shouldreturnexpecteddatatime_whenday1time(self, time : str): - - # Arrange - strp_format : str = "%Y-%m-%d %H:%M" - dt_str = f"1900-01-01 {time}" - expected : datetime = datetime.strptime(dt_str, strp_format) - - # Act - actual : datetime = self.df_helper.create_time_object(time = time) - - # Assert - self.assertEqual(expected, actual) - - @parameterized.expand([ - "00:00", "00:15", "00:30", "00:45", - "01:00", "01:15", "01:30", "01:45", - "02:00", "02:15", "02:30", "02:45", - "03:00", "03:15", "03:30", "03:45", - "04:00", "04:15", "04:30", "04:45", - "05:00", "05:15", "05:30", "05:45", - "06:00", "06:15", "06:30", "06:45" - ]) - def test_createtimeobject_shouldreturnexpecteddatatime_whenday2time(self, time : str): - - # Arrange - strp_format : str = "%Y-%m-%d %H:%M" - dt_str = f"1900-01-02 {time}" - expected : datetime = datetime.strptime(dt_str, strp_format) - - # Act - actual : datetime = self.df_helper.create_time_object(time = time) - - # Assert - self.assertEqual(expected, actual) - - @parameterized.expand([ - "07:04", - "00:01", - "gibberish text" - ]) - def test_createtimeobject_shouldraisevalueerrorexception_whennotamongtimevalues(self, time : str): - - # Arrange - expected_message : str = _MessageCollection.effort_status_not_among_expected_time_values(time = time) - - # Act - with self.assertRaises(ValueError) as context: - self.df_helper.create_time_object(time = time) - - # Assert - self.assertTrue(expected_message in str(context.exception)) - @parameterized.expand([ [1, "07:00", "", "5h 30m"], [1, "", "07:00", "5h 30m"] @@ -1039,59 +1064,36 @@ def test_createeffortstatus_shouldraisevalueerrorexception_whenunproperparameter self.assertTrue(expected_message in str(context.exception)) @parameterized.expand([ - ["07:00", "08:00", "UNKNOWN", "07:00-08:00"], - ["", "08:00", "UNKNOWN", "UNKNOWN"], - ["07:00", "", "UNKNOWN", "UNKNOWN"] - ]) - def test_createtimerangeid_shouldreturnexpectedtimerangeid_wheninvoked( - self, - start_time : str, - end_time : str, - unknown_id : str, - expected : str): - - # Arrange - # Act - actual : str = self.df_helper.create_time_range_id(start_time = start_time, end_time = end_time, unknown_id = unknown_id) - - # Assert - self.assertEqual(expected, actual) - - @parameterized.expand([ - [timedelta(minutes=30), timedelta(hours=1), "↑"], - [timedelta(hours=1), timedelta(minutes=30), "↓"], - [timedelta(minutes=30), timedelta(minutes=30), "="], - ]) - def test_gettrendbytimedelta_shouldreturnexpectedtrend_wheninvoked( - self, - td_1 : timedelta, - td_2 : timedelta, - expected : str - ): - - # Arrange - # Act - actual : str = self.df_helper.get_trend_by_timedelta(td_1 = td_1, td_2 = td_2) - - # Assert - self.assertEqual(expected, actual) - - @parameterized.expand([ - ["↕1", TTCN.TREND], - ["2016", "2016"], + [1, "5h 30m", timedelta(hours = 5, minutes = 30)], + [2, "2h 00m", timedelta(hours = 2, minutes = 00)] ]) - def test_tryconsolidatetrendcolumnname_shouldreturnexpectedcolumnname_wheninvoked( + def test_createeffortstatusfornonevalues_shouldreturnexpectedobject_wheninvoked( self, - column_name: str, - expected: str - ): + idx : int, + effort_str : str, + actual_td : timedelta): # Arrange + expected : EffortStatus = EffortStatus( + idx = idx, + start_time_str = None, + start_time_dt = None, + end_time_str = None, + end_time_dt = None, + actual_str = effort_str, + actual_td = actual_td, + expected_td = None, + expected_str = None, + is_correct = True, + message = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + ) + # Act - actual : str = self.df_helper.try_consolidate_trend_column_name(column_name) + actual : EffortStatus = self.df_helper.create_effort_status_for_none_values(idx = idx, effort_str = effort_str) # type: ignore # Assert - self.assertEqual(expected, actual) + comparison : bool = self.sm_provider.are_effort_statuses_equal(ef1 = expected, ef2 = actual) + self.assertTrue(comparison) def test_createeffortstatusandcasttoany_shouldwork_withdfapply(self): From 15cb8abe9260e45c9a173adaef5c0e8f84f7d75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 12:12:55 +0000 Subject: [PATCH 077/109] #40 MessageCollectionTestCase, added (100%). --- tests/nwtimetrackingtests.py | 117 +++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 6ad4641..d201a0e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -425,6 +425,123 @@ def create_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: return (df, expected) # TEST CLASSES +class MessageCollectionTestCase(unittest.TestCase): + + def test_effortstatusmismatchingeffort_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + idx : int = 4 + start_time_str : str = "20:00" + end_time_str : str = "00:00" + actual_str : str = "3h 00m" + expected_str : str = "4h 00m" + + expected_message : str = ( + "The provided row contains a mismatching effort " + "(idx: '4', start_time: '20:00', end_time: '00:00', actual_effort: '3h 00m', expected_effort: '4h 00m')." + ) + + # Act + actual_message : str = _MessageCollection.effort_status_mismatching_effort( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + actual_str = actual_str, + expected_str = expected_str + ) + + # Assert + self.assertEqual(expected_message, actual_message) + def test_effortstatusnotpossibletocreate_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + idx : int = 770 + start_time_str : str = "22:00" + end_time_str : str = "00:00" + effort_str : str = "2h 00m" + + expected_message : str = ( + "It has not been possible to create an EffortStatus for the provided parameters " + "(idx: '770', start_time_str: '22:00', end_time_str: '00:00', effort_str: '2h 00m')." + ) + + # Act + actual_message : str = _MessageCollection.effort_status_not_possible_to_create( + idx = idx, + start_time_str = start_time_str, + end_time_str = end_time_str, + effort_str = effort_str + ) + + # Assert + self.assertEqual(expected_message, actual_message) + def test_effortstatusnotamongexpectedtimevalues_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + time : str = "25:00" + expected_message : str = "The provided time ('25:00') is not among the expected time values." + + # Act + actual_message : str = _MessageCollection.effort_status_not_among_expected_time_values(time = time) + + # Assert + self.assertEqual(expected_message, actual_message) + def test_starttimeendtimeareempty_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + expected : str = "''start_time' and/or 'end_time' are empty, 'effort' can't be verified. We assume that it's correct." + + # Act + actual : str = _MessageCollection.starttime_endtime_are_empty() + + # Assert + self.assertEqual(expected, actual) + def test_effortiscorrect_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + expected : str = "The effort is correct." + + # Act + actual : str = _MessageCollection.effort_is_correct() + + # Assert + self.assertEqual(expected, actual) + def test_nomdinfofound_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + id : TTID = TTID.TTSBYMONTH + expected : str = "No MDInfo object found for id='tts_by_month'." + + # Act + actual : str = _MessageCollection.no_mdinfo_found(id = id) + + # Assert + self.assertEqual(expected, actual) + def test_pleaseruninitializefirst_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + expected : str = "Please run the 'initialize' method first." + + # Act + actual : str = _MessageCollection.please_run_initialize_first() + + # Assert + self.assertEqual(expected, actual) + def test_thiscontentsuccessfullysavedas_shouldreturnexpectedmessage_wheninvoked(self): + + # Arrange + id : TTID = TTID.TTSBYMONTH + file_path : str = "/path/to/file.csv" + expected : str = ( + "This content (id: 'tts_by_month') has been successfully saved as '/path/to/file.csv'." + ) + + # Act + actual : str = _MessageCollection.this_content_successfully_saved_as(id = id, file_path = file_path) + + # Assert + self.assertEqual(expected, actual) + class ComponentBagTestCase(unittest.TestCase): def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: From 6274d347088affb0b711222937b705c465bc33ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 12:35:58 +0000 Subject: [PATCH 078/109] #40 YearlyTargetTestCase, added (100%). --- tests/nwtimetrackingtests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index d201a0e..b722055 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -541,6 +541,22 @@ def test_thiscontentsuccessfullysavedas_shouldreturnexpectedmessage_wheninvoked( # Assert self.assertEqual(expected, actual) +class YearlyTargetTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> None: + + # Arrange + year : int = 2024 + hours : timedelta = timedelta(hours = 1200) + + # Act + actual : YearlyTarget = YearlyTarget(year = year, hours = hours) + + # Assert + self.assertEqual(actual.year, year) + self.assertEqual(actual.hours, hours) + self.assertIsInstance(actual.year, int) + self.assertIsInstance(actual.hours, timedelta) class ComponentBagTestCase(unittest.TestCase): From 3ff585ea16eade0efbc229bab7d868333a701947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 12:42:09 +0000 Subject: [PATCH 079/109] #40 EffortStatusTestCase, added (100%). --- tests/nwtimetrackingtests.py | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index b722055..d9398bf 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -557,6 +557,104 @@ def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> self.assertEqual(actual.hours, hours) self.assertIsInstance(actual.year, int) self.assertIsInstance(actual.hours, timedelta) +class EffortStatusTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> None: + + # Arrange + idx : int = 1 + start_time_str : Optional[str] = "07:00" + start_time_dt : Optional[datetime] = datetime.strptime("07:00", "%H:%M") + end_time_str : Optional[str] = "08:00" + end_time_dt : Optional[datetime] = datetime.strptime("08:00", "%H:%M") + actual_str : str = "01h 00m" + actual_td : timedelta = timedelta(hours = 1) + expected_td : Optional[timedelta] = timedelta(hours = 1) + expected_str : Optional[str] = "01h 00m" + is_correct : bool = True + message : str = "Effort matches expected." + + # Act + actual : EffortStatus = EffortStatus( + idx = idx, + start_time_str = start_time_str, + start_time_dt = start_time_dt, + end_time_str = end_time_str, + end_time_dt = end_time_dt, + actual_str = actual_str, + actual_td = actual_td, + expected_td = expected_td, + expected_str = expected_str, + is_correct = is_correct, + message = message + ) + + # Assert + self.assertEqual(actual.idx, idx) + self.assertEqual(actual.start_time_str, start_time_str) + self.assertEqual(actual.start_time_dt, start_time_dt) + self.assertEqual(actual.end_time_str, end_time_str) + self.assertEqual(actual.end_time_dt, end_time_dt) + self.assertEqual(actual.actual_str, actual_str) + self.assertEqual(actual.actual_td, actual_td) + self.assertEqual(actual.expected_td, expected_td) + self.assertEqual(actual.expected_str, expected_str) + self.assertEqual(actual.is_correct, is_correct) + self.assertEqual(actual.message, message) + self.assertIsInstance(actual.idx, int) + self.assertIsInstance(actual.start_time_str, (str, type(None))) + self.assertIsInstance(actual.start_time_dt, (datetime, type(None))) + self.assertIsInstance(actual.end_time_str, (str, type(None))) + self.assertIsInstance(actual.end_time_dt, (datetime, type(None))) + self.assertIsInstance(actual.actual_str, str) + self.assertIsInstance(actual.actual_td, timedelta) + self.assertIsInstance(actual.expected_td, (timedelta, type(None))) + self.assertIsInstance(actual.expected_str, (str, type(None))) + self.assertIsInstance(actual.is_correct, bool) + self.assertIsInstance(actual.message, str) + def test_init_shouldinitializeobjectwithexpectedproperties_whenalloptionalsarenone(self) -> None: + + # Arrange + idx : int = 1 + start_time_str : Optional[str] = None + start_time_dt : Optional[datetime] = None + end_time_str : Optional[str] = None + end_time_dt : Optional[datetime] = None + actual_str : str = "01h 00m" + actual_td : timedelta = timedelta(hours = 1) + expected_td : Optional[timedelta] = None + expected_str : Optional[str] = None + is_correct : bool = True + message : str = "Effort recorded without expectation." + + # Act + actual : EffortStatus = EffortStatus( + idx = idx, + start_time_str = start_time_str, + start_time_dt = start_time_dt, + end_time_str = end_time_str, + end_time_dt = end_time_dt, + actual_str = actual_str, + actual_td = actual_td, + expected_td = expected_td, + expected_str = expected_str, + is_correct = is_correct, + message = message + ) + + # Assert + self.assertEqual(actual.idx, idx) + self.assertIsNone(actual.start_time_str) + self.assertIsNone(actual.start_time_dt) + self.assertIsNone(actual.end_time_str) + self.assertIsNone(actual.end_time_dt) + self.assertEqual(actual.actual_str, actual_str) + self.assertEqual(actual.actual_td, actual_td) + self.assertIsNone(actual.expected_td) + self.assertIsNone(actual.expected_str) + self.assertEqual(actual.is_correct, is_correct) + self.assertEqual(actual.message, message) + class ComponentBagTestCase(unittest.TestCase): From e6368e2c1843172bd437194f88f9c99592f5a45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 12:50:33 +0000 Subject: [PATCH 080/109] #40 MDInfoTestCase, added (100%). --- tests/nwtimetrackingtests.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index d9398bf..81f226a 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -654,6 +654,26 @@ def test_init_shouldinitializeobjectwithexpectedproperties_whenalloptionalsareno self.assertIsNone(actual.expected_str) self.assertEqual(actual.is_correct, is_correct) self.assertEqual(actual.message, message) +class MDInfoTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> None: + + # Arrange + id : TTID = TTID.TTSBYMONTH + file_name : str = "TIMETRACKINGBYMONTH.md" + paragraph_title : str = "Time Tracking By Month" + + # Act + actual : MDInfo = MDInfo(id = id, file_name = file_name, paragraph_title = paragraph_title) + + # Assert + self.assertEqual(actual.id, id) + self.assertEqual(actual.file_name, file_name) + self.assertEqual(actual.paragraph_title, paragraph_title) + self.assertIsInstance(actual.id, TTID) + self.assertIsInstance(actual.file_name, str) + self.assertIsInstance(actual.paragraph_title, str) + class ComponentBagTestCase(unittest.TestCase): From b08e32e78746c62ecee5fd09d234093e96f8828f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 13:00:38 +0000 Subject: [PATCH 081/109] #40 TTSummaryTestCase, added (100%). --- tests/nwtimetrackingtests.py | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 81f226a..2ed0b33 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -16,7 +16,7 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, TTSummary, YearlyTarget, SettingBag, EffortStatus, _MessageCollection from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider, TTDataFrameHelper from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer @@ -673,7 +673,49 @@ def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> self.assertIsInstance(actual.id, TTID) self.assertIsInstance(actual.file_name, str) self.assertIsInstance(actual.paragraph_title, str) +class TTSummaryTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> None: + + # Arrange + empty_df : DataFrame = DataFrame() + empty_tuple : Tuple[DataFrame, DataFrame] = (empty_df, empty_df) + markdown : str = "" + # Act + actual = TTSummary( + tt_df = empty_df, + tts_by_month_tpl = empty_tuple, + tts_by_year_df = empty_df, + tts_by_year_month_tpl = empty_tuple, + tts_by_year_month_spnv_tpl = empty_tuple, + tts_by_year_spnv_tpl = empty_tuple, + tts_by_spn_df = empty_df, + tts_by_spn_spv_df = empty_df, + tts_by_hashtag_df = empty_df, + tts_by_hashtag_year_df = empty_df, + tts_by_efs_tpl = empty_tuple, + tts_by_tr_df = empty_df, + definitions_df = empty_df, + tts_by_month_md = markdown, + ) + + # Assert + self.assertEqual(actual.tt_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_month_tpl, empty_tuple) + self.assertEqual(actual.tts_by_year_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_year_month_tpl, empty_tuple) + self.assertEqual(actual.tts_by_year_month_spnv_tpl, empty_tuple) + self.assertEqual(actual.tts_by_year_spnv_tpl, empty_tuple) + self.assertEqual(actual.tts_by_spn_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_spn_spv_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_hashtag_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_hashtag_year_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_efs_tpl, empty_tuple) + self.assertEqual(actual.tts_by_tr_df.shape, empty_df.shape) + self.assertEqual(actual.definitions_df.shape, empty_df.shape) + self.assertEqual(actual.tts_by_month_md, markdown) + self.assertIsInstance(actual.tts_by_month_md, str) class ComponentBagTestCase(unittest.TestCase): From a56a05eb453f41a73e7e17e3441f4020109af190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 13:45:59 +0000 Subject: [PATCH 082/109] #40 SettingBagTestCase, added (100%). --- tests/nwtimetrackingtests.py | 177 +++++++++++++++++++++++++++++++---- 1 file changed, 160 insertions(+), 17 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 2ed0b33..80df2e7 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -10,7 +10,7 @@ from pandas.testing import assert_frame_equal from parameterized import parameterized from types import FunctionType -from typing import Optional, Tuple, cast +from typing import Literal, Optional, Tuple, cast from unittest.mock import Mock, call, patch # LOCAL MODULES @@ -716,22 +716,6 @@ def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> self.assertEqual(actual.definitions_df.shape, empty_df.shape) self.assertEqual(actual.tts_by_month_md, markdown) self.assertIsInstance(actual.tts_by_month_md, str) - - -class ComponentBagTestCase(unittest.TestCase): - - def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: - - # Arrange - # Act - component_bag : ComponentBag = ComponentBag() - - # Assert - self.assertIsInstance(component_bag.file_path_manager, FilePathManager) - self.assertIsInstance(component_bag.file_manager, FileManager) - self.assertIsInstance(component_bag.tt_adapter, TTAdapter) - self.assertIsInstance(component_bag.logging_function, FunctionType) - self.assertIsInstance(component_bag.displayer, Displayer) class DefaultPathProviderTestCase(unittest.TestCase): def test_getdefaulttimetrackingpath_shouldreturnexpectedpath_wheninvoked(self): @@ -880,6 +864,165 @@ def test_getall_shouldreturnexpectedlist_wheninvoked(self): self.assertEqual(expected[0].id, actual[0].id) self.assertEqual(expected[0].file_name, actual[0].file_name) self.assertEqual(expected[0].paragraph_title, actual[0].paragraph_title) +class SettingBagTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> None: + + # Arrange + options_tt : list[Literal["display"]] = ["display"] + options_tts_by_month : list[Literal["display", "save"]] = ["display", "save"] + options_tts_by_year : list[Literal["display"]] = ["display"] + options_tts_by_year_month : list[Literal["display"]] = ["display"] + options_tts_by_year_month_spnv : list[Literal["display"]] = ["display"] + options_tts_by_year_spnv : list[Literal["display"]] = ["display"] + options_tts_by_spn : list[Literal["display", "log"]] = ["display", "log"] + options_tts_by_spn_spv : list[Literal["display", "log"]] = ["display", "log"] + options_tts_by_hashtag : list[Literal["display"]] = ["display"] + options_tts_by_hashtag_year : list[Literal["display"]] = ["display"] + options_tts_by_efs : list[Literal["display"]] = ["display"] + options_tts_by_tr : list[Literal["display"]] = ["display"] + options_definitions : list[Literal["display"]] = ["display"] + excel_nrows : int = 100 + tts_by_year_month_spnv_display_only_spn : Optional[str] = "SPN1" + tts_by_year_spnv_display_only_spn : Optional[str] = "SPN2" + tts_by_spn_spv_display_only_spn : Optional[str] = "SPN3" + working_folder_path : str = "/home/nwtimetracking/" + excel_path : str = "/mock/path/" + excel_skiprows : int = 0 + excel_tabname : str = "Sessions" + years : list[int] = [2020, 2021, 2022] + yearly_targets : list = [] + now : datetime = datetime.now() + software_project_names : list[str] = ["ProjectA", "ProjectB"] + software_project_names_by_spv : list[str] = ["ProjectC"] + tt_head_n : uint = uint(5) + tt_display_head_n_with_tail : bool = True + tt_hide_index : bool = True + tts_by_year_month_display_only_years : Optional[list[int]] = [2022] + tts_by_year_month_spnv_formatters : dict[str, str] = {"%_DME" : "{:.2f}", "%_TME" : "{:.2f}"} + tts_by_year_spnv_formatters : dict[str, str] = {"%_DYE" : "{:.2f}", "%_TYE" : "{:.2f}"} + tts_by_spn_formatters : dict[str, str] = {"%_DE" : "{:.2f}", "%_TE" : "{:.2f}"} + tts_by_spn_remove_untagged : bool = True + tts_by_hashtag_formatters : dict[str, str] = {"Effort%" : "{:.2f}"} + tts_by_efs_is_correct : bool = False + tts_by_efs_n : uint = uint(25) + tts_by_tr_unknown_id : str = "Unknown" + tts_by_tr_remove_unknown_occurrences : bool = True + tts_by_tr_filter_by_top_n : uint = uint(5) + tts_by_tr_head_n : uint = uint(10) + tts_by_tr_display_head_n_with_tail : bool = False + md_infos : list = [] + md_last_update : datetime = datetime.now() + + # Act + actual : SettingBag = SettingBag( + options_tt = options_tt, + options_tts_by_month = options_tts_by_month, + options_tts_by_year = options_tts_by_year, + options_tts_by_year_month = options_tts_by_year_month, + options_tts_by_year_month_spnv = options_tts_by_year_month_spnv, + options_tts_by_year_spnv = options_tts_by_year_spnv, + options_tts_by_spn = options_tts_by_spn, + options_tts_by_spn_spv = options_tts_by_spn_spv, + options_tts_by_hashtag = options_tts_by_hashtag, + options_tts_by_hashtag_year = options_tts_by_hashtag_year, + options_tts_by_efs = options_tts_by_efs, + options_tts_by_tr = options_tts_by_tr, + options_definitions = options_definitions, + excel_nrows = excel_nrows, + tts_by_year_month_spnv_display_only_spn = tts_by_year_month_spnv_display_only_spn, + tts_by_year_spnv_display_only_spn = tts_by_year_spnv_display_only_spn, + tts_by_spn_spv_display_only_spn = tts_by_spn_spv_display_only_spn, + working_folder_path = working_folder_path, + excel_path = excel_path, + excel_skiprows = excel_skiprows, + excel_tabname = excel_tabname, + years = years, + yearly_targets = yearly_targets, + now = now, + software_project_names = software_project_names, + software_project_names_by_spv = software_project_names_by_spv, + tt_head_n = tt_head_n, + tt_display_head_n_with_tail = tt_display_head_n_with_tail, + tt_hide_index = tt_hide_index, + tts_by_year_month_display_only_years = tts_by_year_month_display_only_years, + tts_by_year_month_spnv_formatters = tts_by_year_month_spnv_formatters, + tts_by_year_spnv_formatters = tts_by_year_spnv_formatters, + tts_by_spn_formatters = tts_by_spn_formatters, + tts_by_spn_remove_untagged = tts_by_spn_remove_untagged, + tts_by_hashtag_formatters = tts_by_hashtag_formatters, + tts_by_efs_is_correct = tts_by_efs_is_correct, + tts_by_efs_n = tts_by_efs_n, + tts_by_tr_unknown_id = tts_by_tr_unknown_id, + tts_by_tr_remove_unknown_occurrences = tts_by_tr_remove_unknown_occurrences, + tts_by_tr_filter_by_top_n = tts_by_tr_filter_by_top_n, + tts_by_tr_head_n = tts_by_tr_head_n, + tts_by_tr_display_head_n_with_tail = tts_by_tr_display_head_n_with_tail, + md_infos = md_infos, + md_last_update = md_last_update + ) + + # Assert + self.assertEqual(actual.options_tt, options_tt) + self.assertEqual(actual.options_tts_by_month, options_tts_by_month) + self.assertEqual(actual.options_tts_by_year, options_tts_by_year) + self.assertEqual(actual.options_tts_by_year_month, options_tts_by_year_month) + self.assertEqual(actual.options_tts_by_year_month_spnv, options_tts_by_year_month_spnv) + self.assertEqual(actual.options_tts_by_year_spnv, options_tts_by_year_spnv) + self.assertEqual(actual.options_tts_by_spn, options_tts_by_spn) + self.assertEqual(actual.options_tts_by_spn_spv, options_tts_by_spn_spv) + self.assertEqual(actual.options_tts_by_hashtag, options_tts_by_hashtag) + self.assertEqual(actual.options_tts_by_hashtag_year, options_tts_by_hashtag_year) + self.assertEqual(actual.options_tts_by_efs, options_tts_by_efs) + self.assertEqual(actual.options_tts_by_tr, options_tts_by_tr) + self.assertEqual(actual.options_definitions, options_definitions) + self.assertEqual(actual.excel_nrows, excel_nrows) + self.assertEqual(actual.tts_by_year_month_spnv_display_only_spn, tts_by_year_month_spnv_display_only_spn) + self.assertEqual(actual.tts_by_year_spnv_display_only_spn, tts_by_year_spnv_display_only_spn) + self.assertEqual(actual.tts_by_spn_spv_display_only_spn, tts_by_spn_spv_display_only_spn) + self.assertEqual(actual.working_folder_path, working_folder_path) + self.assertEqual(actual.excel_path, excel_path) + self.assertEqual(actual.excel_skiprows, excel_skiprows) + self.assertEqual(actual.excel_tabname, excel_tabname) + self.assertEqual(actual.years, years) + self.assertEqual(actual.yearly_targets, yearly_targets) + self.assertEqual(actual.now, now) + self.assertEqual(actual.software_project_names, software_project_names) + self.assertEqual(actual.software_project_names_by_spv, software_project_names_by_spv) + self.assertEqual(actual.tt_head_n, tt_head_n) + self.assertEqual(actual.tt_display_head_n_with_tail, tt_display_head_n_with_tail) + self.assertEqual(actual.tt_hide_index, tt_hide_index) + self.assertEqual(actual.tts_by_year_month_display_only_years, tts_by_year_month_display_only_years) + self.assertEqual(actual.tts_by_year_month_spnv_formatters, tts_by_year_month_spnv_formatters) + self.assertEqual(actual.tts_by_year_spnv_formatters, tts_by_year_spnv_formatters) + self.assertEqual(actual.tts_by_spn_formatters, tts_by_spn_formatters) + self.assertEqual(actual.tts_by_spn_remove_untagged, tts_by_spn_remove_untagged) + self.assertEqual(actual.tts_by_hashtag_formatters, tts_by_hashtag_formatters) + self.assertEqual(actual.tts_by_efs_is_correct, tts_by_efs_is_correct) + self.assertEqual(actual.tts_by_efs_n, tts_by_efs_n) + self.assertEqual(actual.tts_by_tr_unknown_id, tts_by_tr_unknown_id) + self.assertEqual(actual.tts_by_tr_remove_unknown_occurrences, tts_by_tr_remove_unknown_occurrences) + self.assertEqual(actual.tts_by_tr_filter_by_top_n, tts_by_tr_filter_by_top_n) + self.assertEqual(actual.tts_by_tr_head_n, tts_by_tr_head_n) + self.assertEqual(actual.tts_by_tr_display_head_n_with_tail, tts_by_tr_display_head_n_with_tail) + self.assertEqual(actual.md_infos, md_infos) + self.assertEqual(actual.md_last_update, md_last_update) + + +class ComponentBagTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: + + # Arrange + # Act + component_bag : ComponentBag = ComponentBag() + + # Assert + self.assertIsInstance(component_bag.file_path_manager, FilePathManager) + self.assertIsInstance(component_bag.file_manager, FileManager) + self.assertIsInstance(component_bag.tt_adapter, TTAdapter) + self.assertIsInstance(component_bag.logging_function, FunctionType) + self.assertIsInstance(component_bag.displayer, Displayer) class TTDataFrameHelperTestCase(unittest.TestCase): def setUp(self): From e250c14d73709b9b7ce6851f7572bd57efe4d338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 14:33:34 +0000 Subject: [PATCH 083/109] #40 TTDataFrameFactoryTestCase, added - Iteration 1 (20%). --- tests/nwtimetrackingtests.py | 98 +++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 80df2e7..7d6e07d 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -887,7 +887,7 @@ def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> tts_by_year_spnv_display_only_spn : Optional[str] = "SPN2" tts_by_spn_spv_display_only_spn : Optional[str] = "SPN3" working_folder_path : str = "/home/nwtimetracking/" - excel_path : str = "/mock/path/" + excel_path : str = "/workspaces/nwtimetracking/" excel_skiprows : int = 0 excel_tabname : str = "Sessions" years : list[int] = [2020, 2021, 2022] @@ -1007,22 +1007,6 @@ def test_init_shouldinitializeobjectwithexpectedproperties_wheninvoked(self) -> self.assertEqual(actual.tts_by_tr_display_head_n_with_tail, tts_by_tr_display_head_n_with_tail) self.assertEqual(actual.md_infos, md_infos) self.assertEqual(actual.md_last_update, md_last_update) - - -class ComponentBagTestCase(unittest.TestCase): - - def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: - - # Arrange - # Act - component_bag : ComponentBag = ComponentBag() - - # Assert - self.assertIsInstance(component_bag.file_path_manager, FilePathManager) - self.assertIsInstance(component_bag.file_manager, FileManager) - self.assertIsInstance(component_bag.tt_adapter, TTAdapter) - self.assertIsInstance(component_bag.logging_function, FunctionType) - self.assertIsInstance(component_bag.displayer, Displayer) class TTDataFrameHelperTestCase(unittest.TestCase): def setUp(self): @@ -1555,6 +1539,86 @@ def test_createeffortstatusandcasttoany_shouldwork_withdfapply(self): # Assert self.assertTrue(TTCN.EFFORTSTATUS in df.columns) +class TTDataFrameFactoryTestCase(unittest.TestCase): + + def setUp(self): + self.df_factory : TTDataFrameFactory = TTDataFrameFactory(df_helper = TTDataFrameHelper()) + def test_createttdf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + excel_path : str = "/workspaces/nwtimetracking/" + excel_skiprows : int = 0 + excel_nrows : int = 100 + excel_tabname : str = "Sessions" + excel_data_df : DataFrame = ObjectMother().create_excel_data() + expected_column_names : list[str] = ObjectMother().create_sessions_df_column_names() + expected_dtype_names : list[str] = ObjectMother().create_sessions_df_dtype_names() + expected_nan : str = "" + + # Act + with patch.object(pd, 'read_excel', return_value = excel_data_df) as mocked_context: + actual : DataFrame = self.df_factory.create_tt_df( + excel_path = excel_path, + excel_skiprows = excel_skiprows, + excel_nrows = excel_nrows, + excel_tabname = excel_tabname + ) + + # Assert + self.assertEqual(expected_column_names, actual.columns.tolist()) + self.assertEqual(expected_dtype_names, SupportMethodProvider().get_dtype_names(df = actual)) + self.assertEqual(expected_nan, actual[expected_column_names[1]][0]) + self.assertEqual(expected_nan, actual[expected_column_names[2]][0]) + self.assertEqual(expected_nan, actual[expected_column_names[5]][0]) + def test_createttsbyyeardf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] + sessions_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().create_tt_by_year_df() + + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_year_df(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + + # Assert + assert_frame_equal(expected_df , actual_df) + def test_createttsbyyearmonthtpl_shouldreturnexpectedtuple_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] + sessions_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() + expected_tpl : Tuple[DataFrame, DataFrame] = (expected_df, expected_df) + + # Act + actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_year_month_tpl( + tt_df = sessions_df, + years = years, + yearly_targets = yearly_targets, + display_only_years = years + ) + + # Assert + assert_frame_equal(expected_tpl[0] , actual_tpl[0]) + assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + + +class ComponentBagTestCase(unittest.TestCase): + + def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: + + # Arrange + # Act + component_bag : ComponentBag = ComponentBag() + + # Assert + self.assertIsInstance(component_bag.file_path_manager, FilePathManager) + self.assertIsInstance(component_bag.file_manager, FileManager) + self.assertIsInstance(component_bag.tt_adapter, TTAdapter) + self.assertIsInstance(component_bag.logging_function, FunctionType) + self.assertIsInstance(component_bag.displayer, Displayer) # MAIN From 00109407c57f4b1c5c3a1aeb7dbfb0181c3e0924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 14:57:44 +0000 Subject: [PATCH 084/109] #40 TTDataFrameFactoryTestCase, added - Iteration 1 (35%). --- tests/nwtimetrackingtests.py | 63 +++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 7d6e07d..b8ac7be 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -211,7 +211,7 @@ def create_sessions_df() -> DataFrame: }, index=pd.RangeIndex(start=980, stop=1001, step=1)) @staticmethod - def create_tt_by_year_df() -> DataFrame: + def get_tts_by_year_df() -> DataFrame: ''' Year Effort YearlyTarget TargetDiff IsTargetMet @@ -226,22 +226,27 @@ def create_tt_by_year_df() -> DataFrame: 'IsTargetMet': np.array([False], dtype=bool), }, index=pd.RangeIndex(start=0, stop=1, step=1)) @staticmethod - def create_tt_by_year_month_df() -> DataFrame: + def get_tts_by_year_month_tpl() -> Tuple[DataFrame, DataFrame]: ''' Year Month Effort YearlyTotal ToTarget 0 2024 2 36h 00m 36h 00m -214h 00m + + Year Month Effort YearlyTotal ToTarget + 0 2024 2 36h 00m 36h 00m -214h 00m ''' - return pd.DataFrame({ + df : DataFrame = pd.DataFrame({ 'Year': np.array([2024], dtype=int64), 'Month': np.array([2], dtype=int64), 'Effort': np.array(['36h 00m'], dtype=object), 'YearlyTotal': np.array(['36h 00m'], dtype=object), 'ToTarget': np.array(['-214h 00m'], dtype=object), }, index=pd.RangeIndex(start=0, stop=1, step=1)) + + return (df, df) @staticmethod - def create_tt_by_year_month_spnv_df() -> DataFrame: + def get_tts_by_year_month_spnv_tpl() -> Tuple[DataFrame, DataFrame]: ''' Year Month ProjectName ProjectVersion Effort DME %_DME TME %_TME @@ -249,9 +254,12 @@ def create_tt_by_year_month_spnv_df() -> DataFrame: 1 2024 2 NW.Shared.Serialization 1.0.0 04h 15m 08h 45m 48.57 36h 00m 11.81 2 2024 2 NW.UnivariateForecasting 4.2.0 00h 45m 08h 45m 8.57 36h 00m 2.08 3 2024 2 nwreadinglistmanager 2.1.0 02h 00m 08h 45m 22.86 36h 00m 5.56 + + Year Month ProjectName ProjectVersion Effort DME %_DME TME %_TME + 0 2024 2 NW.NGramTextClassification 4.2.0 01h 15m 08h 45m 14.29 36h 00m 3.47 ''' - return pd.DataFrame({ + df1 : DataFrame = pd.DataFrame({ 'Year': np.array([2024, 2024, 2024, 2024], dtype=int64), 'Month': np.array([2, 2, 2, 2], dtype=int64), 'ProjectName': np.array(['NW.NGramTextClassification', 'NW.Shared.Serialization', 'NW.UnivariateForecasting', 'nwreadinglistmanager'], dtype=object), @@ -262,6 +270,20 @@ def create_tt_by_year_month_spnv_df() -> DataFrame: 'TME': np.array(['36h 00m', '36h 00m', '36h 00m', '36h 00m'], dtype=object), '%_TME': np.array([3.47, 11.81, 2.08, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) + + df2 : DataFrame = pd.DataFrame({ + 'Year': np.array([2024], dtype=int64), + 'Month': np.array([2], dtype=int64), + 'ProjectName': np.array(['NW.NGramTextClassification'], dtype=object), + 'ProjectVersion': np.array(['4.2.0'], dtype=object), + 'Effort': np.array(['01h 15m'], dtype=object), + 'DME': np.array(['08h 45m'], dtype=object), + '%_DME': np.array([14.29], dtype= np.float64), + 'TME': np.array(['36h 00m'], dtype=object), + '%_TME': np.array([3.47], dtype= np.float64), + }, index=pd.RangeIndex(start=0, stop=1, step=1)) + + return (df1, df2) @staticmethod def create_tt_by_year_spnv_df() -> DataFrame: @@ -1575,11 +1597,11 @@ def test_createttsbyyeardf_shouldreturnexpecteddataframe_wheninvoked(self): # Arrange years : list[int] = [2024] yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_df() + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().get_tts_by_year_df() # Act - actual_df : DataFrame = self.df_factory.create_tts_by_year_df(tt_df = sessions_df, years = years, yearly_targets = yearly_targets) + actual_df : DataFrame = self.df_factory.create_tts_by_year_df(tt_df = tt_df, years = years, yearly_targets = yearly_targets) # Assert assert_frame_equal(expected_df , actual_df) @@ -1588,13 +1610,12 @@ def test_createttsbyyearmonthtpl_shouldreturnexpectedtuple_wheninvoked(self): # Arrange years : list[int] = [2024] yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - sessions_df : DataFrame = ObjectMother().create_sessions_df() - expected_df : DataFrame = ObjectMother().create_tt_by_year_month_df() - expected_tpl : Tuple[DataFrame, DataFrame] = (expected_df, expected_df) + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_month_tpl() # Act actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_year_month_tpl( - tt_df = sessions_df, + tt_df = tt_df, years = years, yearly_targets = yearly_targets, display_only_years = years @@ -1603,7 +1624,25 @@ def test_createttsbyyearmonthtpl_shouldreturnexpectedtuple_wheninvoked(self): # Assert assert_frame_equal(expected_tpl[0] , actual_tpl[0]) assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + def test_createttsbyyearmonthspnvtpl_shouldreturnexpectedtuple_wheninvoked(self): + # Arrange + years : list[int] = [2024] + software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_month_spnv_tpl() + + # Act + actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_year_month_spnv_tpl( + tt_df = tt_df, + years = years, + software_project_names = software_project_names, + software_project_name = software_project_names[0] + ) + + # Assert + assert_frame_equal(expected_tpl[0] , actual_tpl[0]) + assert_frame_equal(expected_tpl[1] , actual_tpl[1]) class ComponentBagTestCase(unittest.TestCase): From a61518042f10501205176b35be91bc309a2417b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 15:02:57 +0000 Subject: [PATCH 085/109] #40 TTDataFrameFactoryTestCase, added - Iteration 3 (48%). --- tests/nwtimetrackingtests.py | 40 ++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index b8ac7be..15a7f01 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -285,7 +285,7 @@ def get_tts_by_year_month_spnv_tpl() -> Tuple[DataFrame, DataFrame]: return (df1, df2) @staticmethod - def create_tt_by_year_spnv_df() -> DataFrame: + def get_tts_by_year_spnv_df() -> Tuple[DataFrame, DataFrame]: ''' Year ProjectName ProjectVersion Effort DYE %_DYE TYE %_TYE @@ -293,9 +293,12 @@ def create_tt_by_year_spnv_df() -> DataFrame: 1 2024 NW.Shared.Serialization 1.0.0 04h 15m 08h 45m 48.57 36h 00m 11.81 2 2024 NW.UnivariateForecasting 4.2.0 00h 45m 08h 45m 8.57 36h 00m 2.08 3 2024 nwreadinglistmanager 2.1.0 02h 00m 08h 45m 22.86 36h 00m 5.56 + + Year ProjectName ProjectVersion Effort DYE %_DYE TYE %_TYE + 0 2024 NW.NGramTextClassification 4.2.0 01h 15m 08h 45m 14.29 36h 00m 3.47 ''' - return pd.DataFrame({ + df1 : DataFrame = pd.DataFrame({ 'Year': np.array([2024, 2024, 2024, 2024], dtype=int64), 'ProjectName': np.array(['NW.NGramTextClassification', 'NW.Shared.Serialization', 'NW.UnivariateForecasting', 'nwreadinglistmanager'], dtype=object), 'ProjectVersion': np.array(['4.2.0', '1.0.0', '4.2.0', '2.1.0'], dtype=object), @@ -305,6 +308,19 @@ def create_tt_by_year_spnv_df() -> DataFrame: 'TYE': np.array(['36h 00m', '36h 00m', '36h 00m', '36h 00m'], dtype=object), '%_TYE': np.array([3.47, 11.81, 2.08, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) + + df2 : DataFrame = pd.DataFrame({ + 'Year': np.array([2024], dtype=int64), + 'ProjectName': np.array(['NW.NGramTextClassification'], dtype=object), + 'ProjectVersion': np.array(['4.2.0'], dtype=object), + 'Effort': np.array(['01h 15m'], dtype=object), + 'DYE': np.array(['08h 45m'], dtype=object), + '%_DYE': np.array([14.29], dtype= np.float64), + 'TYE': np.array(['36h 00m'], dtype=object), + '%_TYE': np.array([3.47], dtype= np.float64), + }, index=pd.RangeIndex(start=0, stop=1, step=1)) + + return (df1, df2) @staticmethod def create_tt_by_spn_df() -> DataFrame: @@ -1643,6 +1659,26 @@ def test_createttsbyyearmonthspnvtpl_shouldreturnexpectedtuple_wheninvoked(self) # Assert assert_frame_equal(expected_tpl[0] , actual_tpl[0]) assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + def test_createttsbyyearspnvtpl_shouldreturnexpectedtuple_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_spnv_df() + + # Act + actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_year_spnv_tpl( + tt_df = tt_df, + years = years, + software_project_names = software_project_names, + software_project_name = software_project_names[0] + ) + + # Assert + assert_frame_equal(expected_tpl[0] , actual_tpl[0]) + assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + class ComponentBagTestCase(unittest.TestCase): From ffce6e11b1b0ae2399f445d0a4b2c56ee7a2d6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 15:35:10 +0000 Subject: [PATCH 086/109] #40 TTDataFrameFactoryTestCase, added - Iteration 4 (69%). --- tests/nwtimetrackingtests.py | 112 ++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 15a7f01..403299c 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -321,6 +321,53 @@ def get_tts_by_year_spnv_df() -> Tuple[DataFrame, DataFrame]: }, index=pd.RangeIndex(start=0, stop=1, step=1)) return (df1, df2) + @staticmethod + def get_tts_by_spn_spv_df() -> DataFrame: + + ''' + ProjectName ProjectVersion Effort + 0 NW.NGramTextClassification 4.2.0 01h 15m + 1 NW.Shared.Serialization 1.0.0 04h 15m + 2 NW.UnivariateForecasting 4.2.0 00h 45m + 3 nwreadinglistmanager 2.1.0 02h 00m + ''' + + return pd.DataFrame({ + 'ProjectName': np.array(['NW.NGramTextClassification', 'NW.Shared.Serialization', 'NW.UnivariateForecasting', 'nwreadinglistmanager'], dtype=object), + 'ProjectVersion': np.array(['4.2.0', '1.0.0', '4.2.0', '2.1.0'], dtype=object), + 'Effort': np.array(['01h 15m', '04h 15m', '00h 45m', '02h 00m'], dtype=object), + }, index=pd.RangeIndex(start=0, stop=4, step=1)) + @staticmethod + def get_tts_by_month_tpl() -> Tuple[DataFrame, DataFrame]: + + ''' + Month 2024 + 0 1 00h 00m + 1 2 36h 00m + ... + 10 11 00h 00m + 11 12 00h 00m + + Month 2024 + ... + 10 11 00h 00m + + now = 2024-11-30 + ''' + + df1 : DataFrame = pd.DataFrame({ + 'Month': np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int64), + '2024': np.array(['00h 00m', '36h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m'], dtype=object) + }, index=pd.RangeIndex(start=0, stop=12, step=1)) + + df2 : DataFrame = pd.DataFrame({ + 'Month': np.array(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', ''], dtype=object), + '2024': np.array(['00h 00m', '36h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', ''], dtype=object) + }, index=pd.RangeIndex(start=0, stop=12, step=1)) + + return (df1, df2) + + @staticmethod def create_tt_by_spn_df() -> DataFrame: @@ -341,22 +388,7 @@ def create_tt_by_spn_df() -> DataFrame: 'TE': np.array(['36h 00m', '36h 00m', '36h 00m', '36h 00m'], dtype=object), '%_TE': np.array([5.56, 11.81, 3.47, 2.08], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) - @staticmethod - def create_tt_by_spn_spv_df() -> DataFrame: - - ''' - ProjectName ProjectVersion Effort - 0 NW.NGramTextClassification 4.2.0 01h 15m - 1 NW.Shared.Serialization 1.0.0 04h 15m - 2 NW.UnivariateForecasting 4.2.0 00h 45m - 3 nwreadinglistmanager 2.1.0 02h 00m - ''' - return pd.DataFrame({ - 'ProjectName': np.array(['NW.NGramTextClassification', 'NW.Shared.Serialization', 'NW.UnivariateForecasting', 'nwreadinglistmanager'], dtype=object), - 'ProjectVersion': np.array(['4.2.0', '1.0.0', '4.2.0', '2.1.0'], dtype=object), - 'Effort': np.array(['01h 15m', '04h 15m', '00h 45m', '02h 00m'], dtype=object), - }, index=pd.RangeIndex(start=0, stop=4, step=1)) @staticmethod def create_tt_by_year_hashtag_df() -> DataFrame: @@ -390,20 +422,6 @@ def create_tt_by_hashtag_df() -> DataFrame: 'Effort%': np.array([64.58, 17.36, 12.5, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) @staticmethod - def create_tts_by_month_df() -> DataFrame: - - ''' - Month 2024 - 0 1 00h 00m - 1 2 36h 00m - ... - ''' - - return pd.DataFrame({ - 'Month': np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int64), - '2024': np.array(['00h 00m', '36h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m', '00h 00m'], dtype=object) - }, index=pd.RangeIndex(start=0, stop=12, step=1)) - @staticmethod def create_tts_by_month_upd_df() -> DataFrame: ''' @@ -1678,6 +1696,42 @@ def test_createttsbyyearspnvtpl_shouldreturnexpectedtuple_wheninvoked(self): # Assert assert_frame_equal(expected_tpl[0] , actual_tpl[0]) assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + def test_createttsbyspnspvdf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().get_tts_by_spn_spv_df() + + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_spn_spv_df( + tt_df = tt_df, + years = years, + software_project_names = software_project_names + ) + + # Assert + assert_frame_equal(expected_df , actual_df) + def test_createttsbymonthtpl_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + now : datetime = datetime(2024, 11, 30) + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_month_tpl() + + # Act + actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_month_tpl( + tt_df = tt_df, + years = years, + now = now + ) + + # Assert + assert_frame_equal(expected_tpl[0] , actual_tpl[0]) + assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + class ComponentBagTestCase(unittest.TestCase): From 469833595f2083c2a723dc1b9286af8876cb469a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 15:49:46 +0000 Subject: [PATCH 087/109] #40 TTDataFrameFactoryTestCase, added - Iteration 5 (78%). --- src/nwtimetracking.py | 8 ++-- tests/nwtimetrackingtests.py | 84 ++++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 723cc2e..61bf646 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1531,7 +1531,7 @@ def create_tts_by_spn_spv_df(self, tt_df : DataFrame, years : list[int], softwar tts_df[TTCN.EFFORT] = tts_df[TTCN.EFFORT].apply(lambda x : self.__df_helper.format_timedelta(td = x, add_plus_sign = False)) return tts_df - def create_tts_by_year_hashtag_df(self, tt_df : DataFrame, years : list[int]) -> DataFrame: + def create_tts_by_hashtag_year_df(self, tt_df : DataFrame, years : list[int]) -> DataFrame: ''' Year Hashtag Effort @@ -1780,11 +1780,11 @@ def create_tts_by_spn_spv_df(self, tt_df : DataFrame, setting_bag : SettingBag) ) return tts_by_spn_spv_df - def create_tts_by_year_hashtag_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: + def create_tts_by_hashtag_year_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> DataFrame: '''Creates the expected dataframe out of the provided arguments.''' - tts_by_year_hashtag_df : DataFrame = self.__df_factory.create_tts_by_year_hashtag_df( + tts_by_year_hashtag_df : DataFrame = self.__df_factory.create_tts_by_hashtag_year_df( tt_df = tt_df, years = setting_bag.years ) @@ -1835,7 +1835,7 @@ def create_summary(self, setting_bag : SettingBag) -> TTSummary: tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_spn_df : DataFrame = self.create_tts_by_spn_df(tt_df = tt_df, setting_bag = setting_bag) tts_by_spn_spv_df : DataFrame = self.create_tts_by_spn_spv_df(tt_df = tt_df, setting_bag = setting_bag) - tts_by_year_hashtag_df : DataFrame = self.create_tts_by_year_hashtag_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_hashtag_df : DataFrame = self.create_tts_by_hashtag_year_df(tt_df = tt_df, setting_bag = setting_bag) tts_by_hashtag_df : DataFrame = self.__df_factory.create_tts_by_hashtag_df(tt_df = tt_df) tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_efs_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_tr_df : DataFrame = self.create_tts_by_tr_df(tt_df = tt_df, setting_bag = setting_bag) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 403299c..7192132 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -366,7 +366,23 @@ def get_tts_by_month_tpl() -> Tuple[DataFrame, DataFrame]: }, index=pd.RangeIndex(start=0, stop=12, step=1)) return (df1, df2) - + @staticmethod + def get_tts_by_tr_df() -> DataFrame: + + ''' + TimeRangeId Occurrences + 0 08:00-08:30 1 + 1 08:15-12:45 1 + 2 08:45-12:15 1 + 3 10:15-13:00 1 + 4 11:00-12:30 1 + ... + ''' + + return pd.DataFrame({ + 'TimeRangeId': np.array(['08:00-08:30', '15:30-16:30', '22:00-23:00', '21:00-22:00', '20:15-21:15', '20:00-20:15', '17:15-18:00', '17:15-17:45', '17:00-18:00', '15:30-18:00', '14:30-16:45', '08:15-12:45', '14:00-19:45', '13:30-15:00', '13:30-14:00', '11:15-13:00', '11:00-13:00', '11:00-12:30', '10:15-13:00', '08:45-12:15', '23:00-23:30'], dtype=object), + 'Occurrences': np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype= np.int64), + }, index=pd.RangeIndex(start=0, stop=21, step=1)) @staticmethod def create_tt_by_spn_df() -> DataFrame: @@ -388,9 +404,8 @@ def create_tt_by_spn_df() -> DataFrame: 'TE': np.array(['36h 00m', '36h 00m', '36h 00m', '36h 00m'], dtype=object), '%_TE': np.array([5.56, 11.81, 3.47, 2.08], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) - @staticmethod - def create_tt_by_year_hashtag_df() -> DataFrame: + def get_tts_by_hashtag_year_df() -> DataFrame: ''' Year Hashtag Effort @@ -406,7 +421,7 @@ def create_tt_by_year_hashtag_df() -> DataFrame: 'Effort': np.array(['06h 15m', '04h 30m', '02h 00m', '23h 15m'], dtype=object), }, index=pd.RangeIndex(start=0, stop=4, step=1)) @staticmethod - def create_tt_by_hashtag_df() -> DataFrame: + def get_tts_by_hashtag_df() -> DataFrame: ''' Hashtag Effort Effort% @@ -435,23 +450,6 @@ def create_tts_by_month_upd_df() -> DataFrame: 'Month': np.array(['1', '2', '', '', '', '', '', '', '', '', '', ''], dtype=object), '2024': np.array(['00h 00m', '36h 00m', '', '', '', '', '', '', '', '', '', ''], dtype=object) }, index=pd.RangeIndex(start=0, stop=12, step=1)) - @staticmethod - def create_time_ranges_df() -> DataFrame: - - ''' - TimeRangeId Occurrences - 0 08:00-08:30 1 - 1 08:15-12:45 1 - 2 08:45-12:15 1 - 3 10:15-13:00 1 - 4 11:00-12:30 1 - ... - ''' - - return pd.DataFrame({ - 'TimeRangeId': np.array(['08:00-08:30', '15:30-16:30', '22:00-23:00', '21:00-22:00', '20:15-21:15', '20:00-20:15', '17:15-18:00', '17:15-17:45', '17:00-18:00', '15:30-18:00', '14:30-16:45', '08:15-12:45', '14:00-19:45', '13:30-15:00', '13:30-14:00', '11:15-13:00', '11:00-13:00', '11:00-12:30', '10:15-13:00', '08:45-12:15', '23:00-23:30'], dtype=object), - 'Occurrences': np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype= np.int64), - }, index=pd.RangeIndex(start=0, stop=21, step=1)) @staticmethod def create_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: @@ -1713,7 +1711,7 @@ def test_createttsbyspnspvdf_shouldreturnexpecteddataframe_wheninvoked(self): # Assert assert_frame_equal(expected_df , actual_df) - def test_createttsbymonthtpl_shouldreturnexpecteddataframe_wheninvoked(self): + def test_createttsbymonthtpl_shouldreturnexpectedtuple_wheninvoked(self): # Arrange years : list[int] = [2024] @@ -1731,8 +1729,50 @@ def test_createttsbymonthtpl_shouldreturnexpecteddataframe_wheninvoked(self): # Assert assert_frame_equal(expected_tpl[0] , actual_tpl[0]) assert_frame_equal(expected_tpl[1] , actual_tpl[1]) + def test_createttsbytrdf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + unknown_id : str = "Unknown" + remove_unknown_occurrences : bool = True + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().get_tts_by_tr_df() + expected_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) + expected_df.reset_index(drop = True, inplace = True) + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_tr_df( + tt_df = tt_df, + unknown_id = unknown_id, + remove_unknown_occurrences = remove_unknown_occurrences + ) + actual_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) + actual_df.reset_index(drop = True, inplace = True) + # Assert + assert_frame_equal(expected_df, actual_df) + def test_createttsbyhashtagyeardf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + years : list[int] = [2024] + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().get_tts_by_hashtag_year_df() + + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_hashtag_year_df(tt_df = tt_df, years = years) + + # Assert + assert_frame_equal(expected_df , actual_df) + def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + tt_df : DataFrame = ObjectMother().create_sessions_df() + expected_df : DataFrame = ObjectMother().get_tts_by_hashtag_df() + + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_hashtag_df(tt_df = tt_df) + + # Assert + assert_frame_equal(expected_df , actual_df) class ComponentBagTestCase(unittest.TestCase): From b4f24fa3036fdabd50c58459ccf6a3abadae699b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Fri, 6 Dec 2024 15:54:56 +0000 Subject: [PATCH 088/109] #40 TTDataFrameFactoryTestCase, added - Iteration 6 (90%). --- tests/nwtimetrackingtests.py | 87 ++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 7192132..448ba23 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -93,7 +93,7 @@ class ObjectMother(): '''Collects all the DTOs required by the unit tests.''' @staticmethod - def create_setting_bag() -> SettingBag: + def get_setting_bag() -> SettingBag: setting_bag : SettingBag = SettingBag( options_tt = ["display"], @@ -117,7 +117,7 @@ def create_setting_bag() -> SettingBag: return setting_bag @staticmethod - def create_excel_data() -> DataFrame: + def get_excel_data() -> DataFrame: excel_data_dict : dict = { "Date": "2015-10-31", @@ -135,7 +135,7 @@ def create_excel_data() -> DataFrame: return excel_data_df @staticmethod - def create_sessions_df_column_names() -> list[str]: + def get_tt_df_column_names() -> list[str]: column_names : list[str] = [] column_names.append("Date") # [0], date @@ -151,7 +151,7 @@ def create_sessions_df_column_names() -> list[str]: return column_names @staticmethod - def create_sessions_df_dtype_names() -> list[str]: + def get_tt_df_dtype_names() -> list[str]: '''Note: the first one should be "date", but it's rendered by Pandas as "object".''' @@ -170,7 +170,7 @@ def create_sessions_df_dtype_names() -> list[str]: return expected_dtype_names @staticmethod - def create_yearly_targets() -> list[YearlyTarget]: + def get_yearly_targets() -> list[YearlyTarget]: yearly_targets = [ YearlyTarget(year = 2015, hours = timedelta(hours = 0)), @@ -186,8 +186,9 @@ def create_yearly_targets() -> list[YearlyTarget]: ] return yearly_targets + @staticmethod - def create_sessions_df() -> DataFrame: + def get_tt_df() -> DataFrame: ''' Date StartTime EndTime Effort Hashtag Descriptor IsSoftwareProject IsReleaseDay Year Month @@ -209,7 +210,6 @@ def create_sessions_df() -> DataFrame: 'Year': np.array([2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024], dtype=int64), 'Month': np.array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], dtype=int64), }, index=pd.RangeIndex(start=980, stop=1001, step=1)) - @staticmethod def get_tts_by_year_df() -> DataFrame: @@ -383,9 +383,8 @@ def get_tts_by_tr_df() -> DataFrame: 'TimeRangeId': np.array(['08:00-08:30', '15:30-16:30', '22:00-23:00', '21:00-22:00', '20:15-21:15', '20:00-20:15', '17:15-18:00', '17:15-17:45', '17:00-18:00', '15:30-18:00', '14:30-16:45', '08:15-12:45', '14:00-19:45', '13:30-15:00', '13:30-14:00', '11:15-13:00', '11:00-13:00', '11:00-12:30', '10:15-13:00', '08:45-12:15', '23:00-23:30'], dtype=object), 'Occurrences': np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype= np.int64), }, index=pd.RangeIndex(start=0, stop=21, step=1)) - @staticmethod - def create_tt_by_spn_df() -> DataFrame: + def get_tts_by_spn_df() -> DataFrame: ''' Hashtag ProjectName Effort DE %_DE TE %_TE @@ -436,23 +435,9 @@ def get_tts_by_hashtag_df() -> DataFrame: 'Effort': np.array(['23h 15m', '06h 15m', '04h 30m', '02h 00m'], dtype=object), 'Effort%': np.array([64.58, 17.36, 12.5, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) - @staticmethod - def create_tts_by_month_upd_df() -> DataFrame: - - ''' - Month 2024 - 0 1 00h 00m - 1 2 36h 00m - ... - ''' - - return pd.DataFrame({ - 'Month': np.array(['1', '2', '', '', '', '', '', '', '', '', '', ''], dtype=object), - '2024': np.array(['00h 00m', '36h 00m', '', '', '', '', '', '', '', '', '', ''], dtype=object) - }, index=pd.RangeIndex(start=0, stop=12, step=1)) @staticmethod - def create_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: + def get_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: data : list = [ [1, "00h 00m", "↑", "18h 00m", "↑", "88h 30m", "↓", "80h 15m", "↓", "60h 00m", "↓", "29h 15m", "↑", "53h 00m", "↓", "00h 00m", "↑", "06h 00m", "↑", "45h 45m"] @@ -1205,7 +1190,7 @@ def test_tryconsolidatetrendcolumnname_shouldreturnexpectedcolumnname_wheninvoke def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): # Arrange - yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() + yearly_targets : list[YearlyTarget] = ObjectMother.get_yearly_targets() year : int = 2024 expected_hours : timedelta = timedelta(hours = 250) @@ -1217,7 +1202,7 @@ def test_getyearlytarget_shouldreturnexpectedhours_whenyearinlist(self): def test_getyearlytarget_shouldreturnnone_whenyearnotinlist(self): # Arrange - yearly_targets : list[YearlyTarget] = ObjectMother.create_yearly_targets() + yearly_targets : list[YearlyTarget] = ObjectMother.get_yearly_targets() year : int = 2010 # Act @@ -1604,9 +1589,9 @@ def test_createttdf_shouldreturnexpecteddataframe_wheninvoked(self): excel_skiprows : int = 0 excel_nrows : int = 100 excel_tabname : str = "Sessions" - excel_data_df : DataFrame = ObjectMother().create_excel_data() - expected_column_names : list[str] = ObjectMother().create_sessions_df_column_names() - expected_dtype_names : list[str] = ObjectMother().create_sessions_df_dtype_names() + excel_data_df : DataFrame = ObjectMother().get_excel_data() + expected_column_names : list[str] = ObjectMother().get_tt_df_column_names() + expected_dtype_names : list[str] = ObjectMother().get_tt_df_dtype_names() expected_nan : str = "" # Act @@ -1629,7 +1614,7 @@ def test_createttsbyyeardf_shouldreturnexpecteddataframe_wheninvoked(self): # Arrange years : list[int] = [2024] yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_df : DataFrame = ObjectMother().get_tts_by_year_df() # Act @@ -1642,7 +1627,7 @@ def test_createttsbyyearmonthtpl_shouldreturnexpectedtuple_wheninvoked(self): # Arrange years : list[int] = [2024] yearly_targets : list[YearlyTarget] = [ YearlyTarget(year = 2024, hours = timedelta(hours = 250)) ] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_month_tpl() # Act @@ -1661,7 +1646,7 @@ def test_createttsbyyearmonthspnvtpl_shouldreturnexpectedtuple_wheninvoked(self) # Arrange years : list[int] = [2024] software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_month_spnv_tpl() # Act @@ -1680,7 +1665,7 @@ def test_createttsbyyearspnvtpl_shouldreturnexpectedtuple_wheninvoked(self): # Arrange years : list[int] = [2024] software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_spnv_df() # Act @@ -1699,7 +1684,7 @@ def test_createttsbyspnspvdf_shouldreturnexpecteddataframe_wheninvoked(self): # Arrange years : list[int] = [2024] software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_df : DataFrame = ObjectMother().get_tts_by_spn_spv_df() # Act @@ -1716,7 +1701,7 @@ def test_createttsbymonthtpl_shouldreturnexpectedtuple_wheninvoked(self): # Arrange years : list[int] = [2024] now : datetime = datetime(2024, 11, 30) - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_month_tpl() # Act @@ -1734,7 +1719,7 @@ def test_createttsbytrdf_shouldreturnexpecteddataframe_wheninvoked(self): # Arrange unknown_id : str = "Unknown" remove_unknown_occurrences : bool = True - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_df : DataFrame = ObjectMother().get_tts_by_tr_df() expected_df.sort_values(by = "TimeRangeId", ascending = True, inplace = True) expected_df.reset_index(drop = True, inplace = True) @@ -1754,7 +1739,7 @@ def test_createttsbyhashtagyeardf_shouldreturnexpecteddataframe_wheninvoked(self # Arrange years : list[int] = [2024] - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_df : DataFrame = ObjectMother().get_tts_by_hashtag_year_df() # Act @@ -1762,10 +1747,10 @@ def test_createttsbyhashtagyeardf_shouldreturnexpecteddataframe_wheninvoked(self # Assert assert_frame_equal(expected_df , actual_df) - def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): + def test_createttsbyhashtagdf_shouldreturnexpecteddataframe_wheninvoked(self): # Arrange - tt_df : DataFrame = ObjectMother().create_sessions_df() + tt_df : DataFrame = ObjectMother().get_tt_df() expected_df : DataFrame = ObjectMother().get_tts_by_hashtag_df() # Act @@ -1774,6 +1759,30 @@ def test_getttbyhashtag_shouldreturnexpecteddataframe_wheninvoked(self): # Assert assert_frame_equal(expected_df , actual_df) + @parameterized.expand([ + [True], + [False] + ]) + def test_createttsbyspndf_shouldreturnexpecteddataframe_wheninvoked(self, remove_untagged : bool): + + # Arrange + years : list[int] = [2024] + software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] + tt_df : DataFrame = ObjectMother().get_tt_df() + expected_df : DataFrame = ObjectMother().get_tts_by_spn_df() + + # Act + actual_df : DataFrame = self.df_factory.create_tts_by_spn_df( + tt_df = tt_df, + years = years, + software_project_names = software_project_names, + remove_untagged = remove_untagged + ) + + # Assert + assert_frame_equal(expected_df , actual_df) + + class ComponentBagTestCase(unittest.TestCase): def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: From a98708bdf11fafdd3edaecf96a7145983bc98db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 09:43:48 +0000 Subject: [PATCH 089/109] #40 TTMarkdownFactoryTestCase, added (100%). --- tests/nwtimetrackingtests.py | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 448ba23..a5aa643 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -436,6 +436,39 @@ def get_tts_by_hashtag_df() -> DataFrame: 'Effort%': np.array([64.58, 17.36, 12.5, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) + @staticmethod + def get_tts_by_month_md() -> str: + + lines: list[str] = [ + "## Revision History", + "", + "|Date|Author|Description|", + "|---|---|---|", + "|2020-12-22|numbworks|Created.|", + "|2024-11-30|numbworks|Last update.|", + "", + "## Time Tracking By Month", + "", + "| Month | 2024 |", + "|--------:|:--------|", + "| 1 | 00h 00m |", + "| 2 | 36h 00m |", + "| 3 | 00h 00m |", + "| 4 | 00h 00m |", + "| 5 | 00h 00m |", + "| 6 | 00h 00m |", + "| 7 | 00h 00m |", + "| 8 | 00h 00m |", + "| 9 | 00h 00m |", + "| 10 | 00h 00m |", + "| 11 | 00h 00m |", + "| 12 | 00h 00m |", + ] + + expected: str = "\n".join(lines) + "\n" + + return expected + @staticmethod def get_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: @@ -1781,6 +1814,28 @@ def test_createttsbyspndf_shouldreturnexpecteddataframe_wheninvoked(self, remove # Assert assert_frame_equal(expected_df , actual_df) +class TTMarkdownFactoryTestCase(unittest.TestCase): + + def setUp(self) -> None: + + self.md_factory : TTMarkdownFactory = TTMarkdownFactory(markdown_helper = MarkdownHelper(formatter = Formatter())) + def test_createttsbymonthmd_shouldreturnexpectedstring_wheninvoked(self) -> None: + + # Arrange + paragraph_title : str = "Time Tracking By Month" + last_update : datetime = datetime(2024, 11, 30) + tts_by_month_upd_df : DataFrame = ObjectMother().get_tts_by_month_tpl()[0] + expected : str = ObjectMother().get_tts_by_month_md() + + # Act + actual : str = self.md_factory.create_tts_by_month_md( + paragraph_title = paragraph_title, + last_update = last_update, + tts_by_month_upd_df = tts_by_month_upd_df + ) + + # Assert + self.assertEqual(expected, actual) class ComponentBagTestCase(unittest.TestCase): From 55c4e780d2e9828c1981e522b67a30451b334880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 09:47:09 +0000 Subject: [PATCH 090/109] #40 ObjectMother: cleaned. --- tests/nwtimetrackingtests.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index a5aa643..f350c64 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -469,33 +469,6 @@ def get_tts_by_month_md() -> str: return expected - @staticmethod - def get_dtos_for_ttsbymonthmd() -> Tuple[DataFrame, str]: - - data : list = [ - [1, "00h 00m", "↑", "18h 00m", "↑", "88h 30m", "↓", "80h 15m", "↓", "60h 00m", "↓", "29h 15m", "↑", "53h 00m", "↓", "00h 00m", "↑", "06h 00m", "↑", "45h 45m"] - ] - columns : list[str] = ["Month", "2015", "↕", "2016", "↕", "2017", "↕", "2018", "↕", "2019", "↕", "2020", "↕", "2021", "↕", "2022", "↕", "2023", "↕", "2024"] - df : DataFrame = pd.DataFrame(data, columns = columns) - - lines : list[str] = [ - "## Revision History", - "", - "|Date|Author|Description|", - "|---|---|---|", - "|2020-12-22|numbworks|Created.|", - "|2024-10-01|numbworks|Last update.|", - "", - "## Time Tracking By Month", - "", - "| Month | 2015 | ↕ | 2016 | ↕ | 2017 | ↕ | 2018 | ↕ | 2019 | ↕ | 2020 | ↕ | 2021 | ↕ | 2022 | ↕ | 2023 | ↕ | 2024 |", - "|--------:|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|:----|:--------|", - "| 1 | 00h 00m | ↑ | 18h 00m | ↑ | 88h 30m | ↓ | 80h 15m | ↓ | 60h 00m | ↓ | 29h 15m | ↑ | 53h 00m | ↓ | 00h 00m | ↑ | 06h 00m | ↑ | 45h 45m |" - ] - expected : str = "\n".join(lines) + "\n" - - return (df, expected) - # TEST CLASSES class MessageCollectionTestCase(unittest.TestCase): From 82968076aa90cbf638bd60d436a9e26ade52a155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 10:25:42 +0000 Subject: [PATCH 091/109] #40 TTAdapterTestCase, added - Iteration 1 (12%). --- tests/nwtimetrackingtests.py | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index f350c64..d2d486b 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1809,7 +1809,61 @@ def test_createttsbymonthmd_shouldreturnexpectedstring_wheninvoked(self) -> None # Assert self.assertEqual(expected, actual) +class TTAdapterTestCase(unittest.TestCase): + def setUp(self) -> None: + + self.excel_path : str = "/home/nwtimetracking/nwtimetrackingmanager/data/Time Tracking.xlsx" + self.excel_skiprows : int = 0 + self.excel_nrows : int = 100 + self.excel_tabname : str = "Sessions" + self.years : list[int] = [2023, 2024] + self.now : datetime = datetime(2023, 12, 1) + def test_createttdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.excel_path = self.excel_path + setting_bag.excel_skiprows = self.excel_skiprows + setting_bag.excel_nrows = 100 + setting_bag.excel_tabname = "Sessions" + + # Act + adapter.create_tt_df(setting_bag = setting_bag) + + # Assert + df_factory.create_tt_df.assert_called_once_with( + excel_path = self.excel_path, + excel_skiprows = self.excel_skiprows, + excel_nrows = self.excel_nrows, + excel_tabname = self.excel_tabname + ) + def test_createttsbymonthtpl_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.now = self.now + + tt_df : Mock = Mock() + + # Act + adapter.create_tts_by_month_tpl(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_month_tpl.assert_called_once_with( + tt_df = tt_df, + years = self.years, + now = self.now + ) class ComponentBagTestCase(unittest.TestCase): From ff5f5f5646d92ac74c9734d5a71cc31500fe4c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 10:43:52 +0000 Subject: [PATCH 092/109] #40 TTAdapterTestCase, added - Iteration 2 (37%). --- tests/nwtimetrackingtests.py | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index d2d486b..fcc2716 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1818,7 +1818,29 @@ def setUp(self) -> None: self.excel_nrows : int = 100 self.excel_tabname : str = "Sessions" self.years : list[int] = [2023, 2024] + self.yearly_targets : list[YearlyTarget] = [ + YearlyTarget(year = 2015, hours = timedelta(hours = 0)), + YearlyTarget(year = 2016, hours = timedelta(hours = 500)), + YearlyTarget(year = 2017, hours = timedelta(hours = 500)), + YearlyTarget(year = 2018, hours = timedelta(hours = 500)), + YearlyTarget(year = 2019, hours = timedelta(hours = 500)), + YearlyTarget(year = 2020, hours = timedelta(hours = 500)), + YearlyTarget(year = 2021, hours = timedelta(hours = 500)), + YearlyTarget(year = 2022, hours = timedelta(hours = 400)), + YearlyTarget(year = 2023, hours = timedelta(hours = 250)), + YearlyTarget(year = 2024, hours = timedelta(hours = 500)) + ] self.now : datetime = datetime(2023, 12, 1) + + self.tts_by_year_month_display_only_years : Optional[list[int]] = [2024] + + self.md_infos : list[MDInfo] = [ + MDInfo(id = TTID.TTSBYMONTH, file_name = "TIMETRACKINGBYMONTH.md", paragraph_title = "Time Tracking By Month") + ] + self.md_last_update : datetime = datetime(2023, 11, 25) + + self.paragraph_title : str = "Time Tracking By Month" + def test_createttdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: # Arrange @@ -1864,6 +1886,78 @@ def test_createttsbymonthtpl_shouldcalldffactorywithexpectedarguments_wheninvoke years = self.years, now = self.now ) + def test_createttsbyyeardf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.yearly_targets = self.yearly_targets + + tt_df : Mock = Mock() + + # Act + adapter.create_tts_by_year_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_year_df.assert_called_once_with( + tt_df = tt_df, + years = self.years, + yearly_targets = self.yearly_targets + ) + def test_createttsbyyearmonthdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.yearly_targets = self.yearly_targets + setting_bag.tts_by_year_month_display_only_years = self.tts_by_year_month_display_only_years + + tt_df : Mock = Mock() + + # Act + adapter.create_tts_by_year_month_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_year_month_tpl.assert_called_once_with( + tt_df = tt_df, + years = self.years, + yearly_targets = self.yearly_targets, + display_only_years = self.tts_by_year_month_display_only_years + ) + + # + + def test_createttsbymonthmd_shouldcallmdfactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.md_infos = self.md_infos + setting_bag.md_last_update = self.md_last_update + + tts_by_month_tpl : Tuple[Mock, Mock] = (Mock(), Mock()) + + # Act + adapter.create_tts_by_month_md(tts_by_month_tpl = tts_by_month_tpl, setting_bag = setting_bag) + + # Assert + md_factory.create_tts_by_month_md.assert_called_once_with( + paragraph_title = self.md_infos[0].paragraph_title, + last_update = self.md_last_update, + tts_by_month_upd_df = tts_by_month_tpl[1] + ) + class ComponentBagTestCase(unittest.TestCase): From 4ee1263c6a7762fc3f10556ae51a3381107edd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 10:58:43 +0000 Subject: [PATCH 093/109] #40 TTAdapterTestCase, added - Iteration 3 (45%). --- tests/nwtimetrackingtests.py | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index fcc2716..0a8ec14 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1813,6 +1813,10 @@ class TTAdapterTestCase(unittest.TestCase): def setUp(self) -> None: + # Without Defaults + self.tts_by_year_spnv_display_only_spn : Optional[str] = "nwshared" + + # With Defaults self.excel_path : str = "/home/nwtimetracking/nwtimetrackingmanager/data/Time Tracking.xlsx" self.excel_skiprows : int = 0 self.excel_nrows : int = 100 @@ -1831,6 +1835,8 @@ def setUp(self) -> None: YearlyTarget(year = 2024, hours = timedelta(hours = 500)) ] self.now : datetime = datetime(2023, 12, 1) + self.software_project_names : list[str] = [ "nwshared", "nwpackageversions"] + self.software_project_names_by_spv : list[str] = [ "nwshared" ] self.tts_by_year_month_display_only_years : Optional[list[int]] = [2024] @@ -1932,6 +1938,55 @@ def test_createttsbyyearmonthdf_shouldcalldffactorywithexpectedarguments_wheninv yearly_targets = self.yearly_targets, display_only_years = self.tts_by_year_month_display_only_years ) + def test_createttsbyyearmonthspnvtpl_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.software_project_names = self.software_project_names + setting_bag.tts_by_year_month_spnv_display_only_spn = self.software_project_names_by_spv + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_year_month_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_year_month_spnv_tpl.assert_called_once_with( + tt_df = tt_df, + years = self.years, + software_project_names = self.software_project_names, + software_project_name = self.software_project_names_by_spv + ) + def test_createttsbyyearspnvtpl_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.software_project_names = self.software_project_names + setting_bag.tts_by_year_spnv_display_only_spn = self.tts_by_year_spnv_display_only_spn + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_year_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_year_spnv_tpl.assert_called_once_with( + tt_df = tt_df, + years = self.years, + software_project_names = self.software_project_names, + software_project_name = self.tts_by_year_spnv_display_only_spn + ) + # From 92be19d62b89994783e31c9438b2f7e54aa40282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 11:13:01 +0000 Subject: [PATCH 094/109] #40 TTAdapterTestCase, added - Iteration 4 (65%). --- tests/nwtimetrackingtests.py | 112 ++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 0a8ec14..a63097d 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1837,9 +1837,11 @@ def setUp(self) -> None: self.now : datetime = datetime(2023, 12, 1) self.software_project_names : list[str] = [ "nwshared", "nwpackageversions"] self.software_project_names_by_spv : list[str] = [ "nwshared" ] - self.tts_by_year_month_display_only_years : Optional[list[int]] = [2024] - + self.tts_by_spn_remove_untagged : bool = True + self.tts_by_efs_is_correct : bool = True + self.tts_by_tr_unknown_id : str = "Unknown" + self.tts_by_tr_remove_unknown_occurrences : bool = True self.md_infos : list[MDInfo] = [ MDInfo(id = TTID.TTSBYMONTH, file_name = "TIMETRACKINGBYMONTH.md", paragraph_title = "Time Tracking By Month") ] @@ -1986,10 +1988,114 @@ def test_createttsbyyearspnvtpl_shouldcalldffactorywithexpectedarguments_wheninv software_project_names = self.software_project_names, software_project_name = self.tts_by_year_spnv_display_only_spn ) + def test_createttsbyspndf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.software_project_names = self.software_project_names + setting_bag.tts_by_spn_remove_untagged = self.tts_by_spn_remove_untagged + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_spn_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_spn_df.assert_called_once_with( + tt_df = tt_df, + years = self.years, + software_project_names = self.software_project_names, + remove_untagged = self.tts_by_spn_remove_untagged + ) + def test_createttsbyspnspvdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + setting_bag.software_project_names = self.software_project_names + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_spn_spv_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_spn_spv_df.assert_called_once_with( + tt_df = tt_df, + years = self.years, + software_project_names = self.software_project_names + ) + def test_createttsbyhashtagyeardf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.years = self.years + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_hashtag_year_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_hashtag_year_df.assert_called_once_with( + tt_df = tt_df, + years = self.years + ) + def test_createttsbyefstpl_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + setting_bag : Mock = Mock() + setting_bag.tts_by_efs_is_correct = self.tts_by_efs_is_correct - # + tt_df : Mock = Mock() + # Act + tt_adapter.create_tts_by_efs_tpl(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_efs_tpl.assert_called_once_with( + tt_df = tt_df, + is_correct = self.tts_by_efs_is_correct + ) + def test_createttsbytrdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: + + # Arrange + df_factory : Mock = Mock() + md_factory : Mock = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + setting_bag : Mock = Mock() + setting_bag.tts_by_tr_unknown_id = self.tts_by_tr_unknown_id + setting_bag.tts_by_tr_remove_unknown_occurrences = self.tts_by_tr_remove_unknown_occurrences + + tt_df : Mock = Mock() + + # Act + tt_adapter.create_tts_by_tr_df(tt_df = tt_df, setting_bag = setting_bag) + + # Assert + df_factory.create_tts_by_tr_df.assert_called_once_with( + tt_df = tt_df, + unknown_id = self.tts_by_tr_unknown_id, + remove_unknown_occurrences = self.tts_by_tr_remove_unknown_occurrences + ) def test_createttsbymonthmd_shouldcallmdfactorywithexpectedarguments_wheninvoked(self) -> None: # Arrange From 59585dc0ecd31e0c401aa9b98bd922534012bdab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 12:01:52 +0000 Subject: [PATCH 095/109] #40 TTAdapterTestCase, added - Iteration 5 (98%). --- src/nwtimetracking.py | 6 +-- tests/nwtimetrackingtests.py | 102 +++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/nwtimetracking.py b/src/nwtimetracking.py index 61bf646..82f8d0b 100644 --- a/src/nwtimetracking.py +++ b/src/nwtimetracking.py @@ -1682,6 +1682,7 @@ def extract_file_name_and_paragraph_title(self, id : TTID, setting_bag : Setting return (md_info.file_name, md_info.paragraph_title) raise Exception(_MessageCollection.no_mdinfo_found(id = id)) + def create_tt_df(self, setting_bag : SettingBag) -> DataFrame: '''Creates the expected dataframe out of the provided arguments.''' @@ -1716,7 +1717,7 @@ def create_tts_by_year_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> ) return tts_by_year_df - def create_tts_by_year_month_df(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: + def create_tts_by_year_month_tpl(self, tt_df : DataFrame, setting_bag : SettingBag) -> Tuple[DataFrame, DataFrame]: '''Creates the expected dataframes out of the provided arguments.''' @@ -1822,7 +1823,6 @@ def create_tts_by_month_md(self, tts_by_month_tpl : Tuple[DataFrame, DataFrame], ) return tts_by_month_md - def create_summary(self, setting_bag : SettingBag) -> TTSummary: '''Creates a TTSummary object out of setting_bag.''' @@ -1830,7 +1830,7 @@ def create_summary(self, setting_bag : SettingBag) -> TTSummary: tt_df : DataFrame = self.create_tt_df(setting_bag = setting_bag) tts_by_month_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_month_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_year_df : DataFrame = self.create_tts_by_year_df(tt_df = tt_df, setting_bag = setting_bag) - tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_month_df(tt_df = tt_df, setting_bag = setting_bag) + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_month_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_month_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = self.create_tts_by_year_spnv_tpl(tt_df = tt_df, setting_bag = setting_bag) tts_by_spn_df : DataFrame = self.create_tts_by_spn_df(tt_df = tt_df, setting_bag = setting_bag) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index a63097d..857b50c 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -16,7 +16,7 @@ # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, TTSummary, YearlyTarget, SettingBag, EffortStatus, _MessageCollection +from nwtimetracking import DEFINITIONSCN, TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, TTSummary, YearlyTarget, SettingBag, EffortStatus, _MessageCollection from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider, TTDataFrameHelper from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer @@ -285,7 +285,7 @@ def get_tts_by_year_month_spnv_tpl() -> Tuple[DataFrame, DataFrame]: return (df1, df2) @staticmethod - def get_tts_by_year_spnv_df() -> Tuple[DataFrame, DataFrame]: + def get_tts_by_year_spnv_tpl() -> Tuple[DataFrame, DataFrame]: ''' Year ProjectName ProjectVersion Effort DYE %_DYE TYE %_TYE @@ -435,6 +435,26 @@ def get_tts_by_hashtag_df() -> DataFrame: 'Effort': np.array(['23h 15m', '06h 15m', '04h 30m', '02h 00m'], dtype=object), 'Effort%': np.array([64.58, 17.36, 12.5, 5.56], dtype= np.float64), }, index=pd.RangeIndex(start=0, stop=4, step=1)) + @staticmethod + def get_definitions_df() -> DataFrame: + + columns : list[str] = [DEFINITIONSCN.TERM, DEFINITIONSCN.DEFINITION] + + definitions : dict[str, str] = { + "DME": "Development Monthly Effort", + "TME": "Total Monthly Effort", + "DYE": "Development Yearly Effort", + "TYE": "Total Yearly Effort", + "DE": "Development Effort", + "TE": "Total Effort" + } + + definitions_df : DataFrame = DataFrame( + data = definitions.items(), + columns = columns + ) + + return definitions_df @staticmethod def get_tts_by_month_md() -> str: @@ -1672,7 +1692,7 @@ def test_createttsbyyearspnvtpl_shouldreturnexpectedtuple_wheninvoked(self): years : list[int] = [2024] software_project_names : list[str] = ["NW.NGramTextClassification", "NW.Shared.Serialization", "NW.UnivariateForecasting", "nwreadinglistmanager"] tt_df : DataFrame = ObjectMother().get_tt_df() - expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_spnv_df() + expected_tpl : Tuple[DataFrame, DataFrame] = ObjectMother().get_tts_by_year_spnv_tpl() # Act actual_tpl : Tuple[DataFrame, DataFrame] = self.df_factory.create_tts_by_year_spnv_tpl( @@ -1787,6 +1807,17 @@ def test_createttsbyspndf_shouldreturnexpecteddataframe_wheninvoked(self, remove # Assert assert_frame_equal(expected_df , actual_df) + + def test_createdefinitionsdf_shouldreturnexpecteddataframe_wheninvoked(self): + + # Arrange + expected_df : DataFrame = ObjectMother().get_definitions_df() + + # Act + actual_df : DataFrame = self.df_factory.create_definitions_df() + + # Assert + assert_frame_equal(expected_df , actual_df) class TTMarkdownFactoryTestCase(unittest.TestCase): def setUp(self) -> None: @@ -1847,8 +1878,8 @@ def setUp(self) -> None: ] self.md_last_update : datetime = datetime(2023, 11, 25) + # Other self.paragraph_title : str = "Time Tracking By Month" - def test_createttdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: # Arrange @@ -1931,7 +1962,7 @@ def test_createttsbyyearmonthdf_shouldcalldffactorywithexpectedarguments_wheninv tt_df : Mock = Mock() # Act - adapter.create_tts_by_year_month_df(tt_df = tt_df, setting_bag = setting_bag) + adapter.create_tts_by_year_month_tpl(tt_df = tt_df, setting_bag = setting_bag) # Assert df_factory.create_tts_by_year_month_tpl.assert_called_once_with( @@ -2118,8 +2149,69 @@ def test_createttsbymonthmd_shouldcallmdfactorywithexpectedarguments_wheninvoked last_update = self.md_last_update, tts_by_month_upd_df = tts_by_month_tpl[1] ) + def test_createsummary_shouldreturnexpectedsummary_wheninvoked(self) -> None: + + # Arrange + tt_df : DataFrame = ObjectMother.get_tt_df() + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = ObjectMother.get_tts_by_month_tpl() + tts_by_year_df : DataFrame = ObjectMother.get_tts_by_year_df() + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = ObjectMother.get_tts_by_year_month_tpl() + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = ObjectMother.get_tts_by_year_month_spnv_tpl() + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = ObjectMother.get_tts_by_year_spnv_tpl() + tts_by_spn_df : DataFrame = ObjectMother.get_tts_by_spn_df() + tts_by_spn_spv_df : DataFrame = ObjectMother.get_tts_by_spn_spv_df() + tts_by_hashtag_df : DataFrame = ObjectMother.get_tts_by_hashtag_df() + tts_by_hashtag_year_df : DataFrame = ObjectMother.get_tts_by_hashtag_year_df() + tts_by_efs_tpl : Tuple[DataFrame, DataFrame] = Mock() # TO UPDATE + tts_by_tr_df : DataFrame = ObjectMother.get_tts_by_tr_df() + definitions_df : DataFrame = ObjectMother.get_definitions_df() + tts_by_month_md : str = ObjectMother.get_tts_by_month_md() + + df_factory : TTDataFrameFactory = Mock() + df_factory.create_tt_df.return_value = tt_df + df_factory.create_tts_by_month_tpl.return_value = tts_by_month_tpl + df_factory.create_tts_by_year_df.return_value = tts_by_year_df + df_factory.create_tts_by_year_month_tpl.return_value = tts_by_year_month_tpl + df_factory.create_tts_by_year_month_spnv_tpl.return_value = tts_by_year_month_spnv_tpl + df_factory.create_tts_by_year_spnv_tpl.return_value = tts_by_year_spnv_tpl + df_factory.create_tts_by_spn_df.return_value = tts_by_spn_df + df_factory.create_tts_by_spn_spv_df.return_value = tts_by_spn_spv_df + df_factory.create_tts_by_hashtag_df.return_value = tts_by_hashtag_df + df_factory.create_tts_by_hashtag_year_df.return_value = tts_by_hashtag_year_df + df_factory.create_tts_by_efs_tpl.return_value = tts_by_efs_tpl + df_factory.create_tts_by_tr_df.return_value = tts_by_tr_df + df_factory.create_definitions_df.return_value = definitions_df + + md_factory : TTMarkdownFactory = Mock() + md_factory.create_tts_by_month_md.return_value = tts_by_month_md + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + setting_bag : SettingBag = ObjectMother.get_setting_bag() + + # Act + actual : TTSummary = tt_adapter.create_summary(setting_bag=setting_bag) + + # Assert + assert_frame_equal(actual.tt_df, tt_df) + assert_frame_equal(actual.tts_by_month_tpl[0], tts_by_month_tpl[0]) + assert_frame_equal(actual.tts_by_month_tpl[1], tts_by_month_tpl[1]) + assert_frame_equal(actual.tts_by_year_df, tts_by_year_df) + assert_frame_equal(actual.tts_by_year_month_tpl[0], tts_by_year_month_tpl[0]) + assert_frame_equal(actual.tts_by_year_month_tpl[1], tts_by_year_month_tpl[1]) + assert_frame_equal(actual.tts_by_year_month_spnv_tpl[0], tts_by_year_month_spnv_tpl[0]) + assert_frame_equal(actual.tts_by_year_month_spnv_tpl[1], tts_by_year_month_spnv_tpl[1]) + assert_frame_equal(actual.tts_by_year_spnv_tpl[0], tts_by_year_spnv_tpl[0]) + assert_frame_equal(actual.tts_by_year_spnv_tpl[1], tts_by_year_spnv_tpl[1]) + assert_frame_equal(actual.tts_by_spn_df, tts_by_spn_df) + assert_frame_equal(actual.tts_by_spn_spv_df, tts_by_spn_spv_df) + assert_frame_equal(actual.tts_by_hashtag_df, tts_by_hashtag_df) + assert_frame_equal(actual.tts_by_hashtag_year_df, tts_by_hashtag_year_df) + # assert_frame_equal(actual.tts_by_efs_tpl[0], tts_by_efs_tpl[0]) + # assert_frame_equal(actual.tts_by_efs_tpl[1], tts_by_efs_tpl[1]) + assert_frame_equal(actual.tts_by_tr_df, tts_by_tr_df) + assert_frame_equal(actual.definitions_df, definitions_df) + self.assertEqual(actual.tts_by_month_md, tts_by_month_md) class ComponentBagTestCase(unittest.TestCase): def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> None: From c8fa5f84813e8f260de7db50e61247f24e1b89ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 12:06:11 +0000 Subject: [PATCH 096/109] #40: *tests: cleaned. --- tests/nwtimetrackingtests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 857b50c..dfb012e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -11,14 +11,15 @@ from parameterized import parameterized from types import FunctionType from typing import Literal, Optional, Tuple, cast -from unittest.mock import Mock, call, patch +from unittest.mock import Mock, patch # LOCAL MODULES import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) -from nwtimetracking import DEFINITIONSCN, TTCN, ComponentBag, MDInfo, TTAdapter, TTMarkdownFactory, SoftwareProjectNameProvider, TTSummary, YearlyTarget, SettingBag, EffortStatus, _MessageCollection -from nwtimetracking import DefaultPathProvider, YearProvider, TTDataFrameFactory, TTID, MDInfoProvider, TTDataFrameHelper from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer +from nwtimetracking import TTCN, TTID, DEFINITIONSCN, _MessageCollection, YearlyTarget, EffortStatus, MDInfo, TTSummary +from nwtimetracking import DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, MDInfoProvider, SettingBag +from nwtimetracking import TTDataFrameHelper, TTDataFrameFactory, TTMarkdownFactory, TTAdapter, ComponentBag # SUPPORT METHODS class SupportMethodProvider(): @@ -2227,7 +2228,6 @@ def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> self.assertIsInstance(component_bag.logging_function, FunctionType) self.assertIsInstance(component_bag.displayer, Displayer) - # MAIN if __name__ == "__main__": result = unittest.main(argv=[''], verbosity=3, exit=False) \ No newline at end of file From f4c269f931d53f1f935c0250d561aa8b1405832c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 13:44:28 +0000 Subject: [PATCH 097/109] #40 TTAdapterTestCase, added - Iteration 5 (100%). --- tests/nwtimetrackingtests.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index dfb012e..e9634ef 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -1881,6 +1881,41 @@ def setUp(self) -> None: # Other self.paragraph_title : str = "Time Tracking By Month" + def test_extractfilenameandparagraphtitle_shouldreturnexpectedvalues_whenidexists(self) -> None: + + # Arrange + df_factory : TTDataFrameFactory = Mock() + md_factory : TTMarkdownFactory = Mock() + tt_adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + id : TTID = TTID.TTSBYMONTH + setting_bag : SettingBag = Mock(md_infos = self.md_infos) + + # Act + actual : Tuple[str, str] = tt_adapter.extract_file_name_and_paragraph_title(id = id, setting_bag = setting_bag) + + # Assert + self.assertEqual(actual, ("TIMETRACKINGBYMONTH.md", "Time Tracking By Month")) + def test_extractfilenameandparagraphtitle_shouldraiseexception_wheniddoesnotexist(self) -> None: + + # Arrange + df_factory : TTDataFrameFactory = Mock() + md_factory : TTMarkdownFactory = Mock() + adapter : TTAdapter = TTAdapter(df_factory = df_factory, md_factory = md_factory) + + id : TTID = TTID.TTSBYMONTH + + md_infos : list[MDInfo] = [ + MDInfo(id = Mock(id = "other_id"), file_name = "OTHERFILE.md", paragraph_title = "Other Title") + ] + setting_bag : SettingBag = Mock(md_infos = md_infos) + + # Act + with self.assertRaises(Exception) as context: + adapter.extract_file_name_and_paragraph_title(id = id, setting_bag = setting_bag) + + # Assert + self.assertEqual(str(context.exception), _MessageCollection.no_mdinfo_found(id = id)) def test_createttdf_shouldcalldffactorywithexpectedarguments_wheninvoked(self) -> None: # Arrange From da3a81caa06a0a528ae6117735ae7825aa92fd1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 14:55:25 +0000 Subject: [PATCH 098/109] #40 TimeTrackingProcessorTestCase, added - Iteration 1 (13%). --- tests/nwtimetrackingtests.py | 37 +++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index e9634ef..ebc30ce 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -17,7 +17,7 @@ import sys, os sys.path.append(os.path.dirname(__file__).replace('tests', 'src')) from nwshared import MarkdownHelper, Formatter, FilePathManager, FileManager, Displayer -from nwtimetracking import TTCN, TTID, DEFINITIONSCN, _MessageCollection, YearlyTarget, EffortStatus, MDInfo, TTSummary +from nwtimetracking import TTCN, TTID, DEFINITIONSCN, _MessageCollection, TimeTrackingProcessor, YearlyTarget, EffortStatus, MDInfo, TTSummary from nwtimetracking import DefaultPathProvider, YearProvider, SoftwareProjectNameProvider, MDInfoProvider, SettingBag from nwtimetracking import TTDataFrameHelper, TTDataFrameFactory, TTMarkdownFactory, TTAdapter, ComponentBag @@ -2262,6 +2262,41 @@ def test_init_shouldinitializeobjectwithexpectedproperties_whendefault(self) -> self.assertIsInstance(component_bag.tt_adapter, TTAdapter) self.assertIsInstance(component_bag.logging_function, FunctionType) self.assertIsInstance(component_bag.displayer, Displayer) +class TimeTrackingProcessorTestCase(unittest.TestCase): + + def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tt_df : DataFrame = Mock() + + mock_summary : Mock = Mock() + mock_summary.tt_df = tt_df + + mock_displayer : Mock = Mock() + mock_tt_adapter : Mock = Mock() + mock_tt_adapter.create_summary.return_value = mock_summary + + component_bag : Mock = Mock() + component_bag.displayer = mock_displayer + component_bag.tt_adapter = mock_tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tt = ["display"] + setting_bag.tt_head_n = 5 + setting_bag.tt_display_head_n_with_tail = False + setting_bag.tt_hide_index = True + + processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + processor.initialize() + + # Act + processor.process_tt() + + # Assert + mock_displayer.display.assert_called_once_with( + df = tt_df.head(5), + hide_index = True + ) # MAIN if __name__ == "__main__": From b79068c11588e202ade269ad0e09680ec170a8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 14:56:30 +0000 Subject: [PATCH 099/109] #40 TimeTrackingProcessorTestCase, added - Iteration 1 (13%). --- tests/nwtimetrackingtests.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index ebc30ce..ef31289 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2269,16 +2269,16 @@ def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: # Arrange tt_df : DataFrame = Mock() - mock_summary : Mock = Mock() - mock_summary.tt_df = tt_df + summary : Mock = Mock() + summary.tt_df = tt_df - mock_displayer : Mock = Mock() - mock_tt_adapter : Mock = Mock() - mock_tt_adapter.create_summary.return_value = mock_summary + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary component_bag : Mock = Mock() - component_bag.displayer = mock_displayer - component_bag.tt_adapter = mock_tt_adapter + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter setting_bag : Mock = Mock() setting_bag.options_tt = ["display"] @@ -2286,14 +2286,13 @@ def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: setting_bag.tt_display_head_n_with_tail = False setting_bag.tt_hide_index = True - processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) - processor.initialize() - # Act - processor.process_tt() + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor.initialize() + tt_processor.process_tt() # Assert - mock_displayer.display.assert_called_once_with( + displayer.display.assert_called_once_with( df = tt_df.head(5), hide_index = True ) From 2793721f0e7053f94af6fc6631f08620ddbc469a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 15:03:39 +0000 Subject: [PATCH 100/109] #40 TimeTrackingProcessorTestCase, added - Iteration 1 25%). --- tests/nwtimetrackingtests.py | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index ef31289..86b38ab 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2296,6 +2296,63 @@ def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: df = tt_df.head(5), hide_index = True ) + def test_processttsbymonth_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = (Mock, Mock()) + + summary : Mock = Mock() + summary.tts_by_month_tpl = tts_by_month_tpl + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_month = ["display"] + + # Act + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_month() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_month_tpl[1] + ) + def test_processttsbyyear_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_year_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.tts_by_year_df = tts_by_year_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_year = ["display"] + + # Act + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_year() + + # Assert + displayer.display.assert_called_once_with( + df =tts_by_year_df + ) + # MAIN if __name__ == "__main__": From 942f1bf0d6e54ba31c369fc677b3bc5f68e85a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 15:33:50 +0000 Subject: [PATCH 101/109] #40 TimeTrackingProcessorTestCase, added - Iteration 3 (39%). --- tests/nwtimetrackingtests.py | 64 ++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 86b38ab..89185f3 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2287,7 +2287,7 @@ def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: setting_bag.tt_hide_index = True # Act - tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) tt_processor.initialize() tt_processor.process_tt() @@ -2316,7 +2316,7 @@ def test_processttsbymonth_shoulddisplay_whenoptionisdisplay(self) -> None: setting_bag.options_tts_by_month = ["display"] # Act - tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) tt_processor.initialize() tt_processor.process_tts_by_month() @@ -2344,7 +2344,7 @@ def test_processttsbyyear_shoulddisplay_whenoptionisdisplay(self) -> None: setting_bag.options_tts_by_year = ["display"] # Act - tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag, setting_bag) + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) tt_processor.initialize() tt_processor.process_tts_by_year() @@ -2352,6 +2352,64 @@ def test_processttsbyyear_shoulddisplay_whenoptionisdisplay(self) -> None: displayer.display.assert_called_once_with( df =tts_by_year_df ) + def test_processttsbyyearmonth_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_year_month_tpl : Tuple[DataFrame, DataFrame] = (Mock(), Mock()) + + summary : Mock = Mock() + summary.tts_by_year_month_tpl = tts_by_year_month_tpl + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_year_month = ["display"] + + # Act + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_year_month() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_year_month_tpl[1] + ) + def test_processttsbyyearmonthspnv_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_year_month_spnv_tpl : Tuple[DataFrame, DataFrame] = (Mock(), Mock()) + + summary : Mock = Mock() + summary.tts_by_year_month_spnv_tpl = tts_by_year_month_spnv_tpl + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_year_month_spnv = ["display"] + setting_bag.tts_by_year_month_spnv_formatters = {"%_DME": "{:.2f}", "%_TME": "{:.2f}"} + + # Act + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_year_month_spnv() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_year_month_spnv_tpl[1], + formatters = setting_bag.tts_by_year_month_spnv_formatters + ) # MAIN From ea8907323bea76950984a6b4d246ed266216f558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 15:40:36 +0000 Subject: [PATCH 102/109] #40 TimeTrackingProcessorTestCase, added - Iteration 4 (50%). --- tests/nwtimetrackingtests.py | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 89185f3..3a59cb3 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2410,7 +2410,64 @@ def test_processttsbyyearmonthspnv_shoulddisplay_whenoptionisdisplay(self) -> No df = tts_by_year_month_spnv_tpl[1], formatters = setting_bag.tts_by_year_month_spnv_formatters ) + def test_processttsbyyearspnv_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_year_spnv_tpl : Tuple[DataFrame, DataFrame] = (Mock(), Mock()) + + summary : Mock = Mock() + summary.tts_by_year_spnv_tpl = tts_by_year_spnv_tpl + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_year_spnv = ["display"] + setting_bag.tts_by_year_spnv_formatters = {"%_DYE": "{:.2f}", "%_TYE": "{:.2f}"} + + # Act + processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + processor.initialize() + processor.process_tts_by_year_spnv() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_year_spnv_tpl[1], + formatters = setting_bag.tts_by_year_spnv_formatters + ) + def test_processdefinitions_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + definitions_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.definitions_df = definitions_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_definitions = ["display"] + + # Act + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_definitions() + + # Assert + displayer.display.assert_called_once_with( + df = definitions_df + ) # MAIN if __name__ == "__main__": From 3724d21f3ac96947fda310852d0467700ff89325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 16:03:42 +0000 Subject: [PATCH 103/109] #40 TimeTrackingProcessorTestCase, added - Iteration 4 (57%). --- tests/nwtimetrackingtests.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index 3a59cb3..c5a134b 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2469,6 +2469,32 @@ def test_processdefinitions_shoulddisplay_whenoptionisdisplay(self) -> None: df = definitions_df ) + @parameterized.expand([ + ["process_tt"], + ["process_tts_by_month"], + ["process_tts_by_year"], + ["process_tts_by_year_month"], + ["process_tts_by_year_month_spnv"], + ["process_tts_by_year_spnv"], + ["process_tts_by_spn"], + ["process_tts_by_spn_spv"], + ["process_tts_by_hashtag"], + ["process_tts_by_hashtag_year"], + ["process_tts_by_efs"], + ["process_tts_by_tr"], + ["process_definitions"] + ]) + def test_processmethod_shouldraiseexception_wheninitializenotrun(self, method_name : str) -> None: + + # Arrange + tt_processor : TimeTrackingProcessor = TimeTrackingProcessor(component_bag = Mock(), setting_bag = Mock()) + + # Act & Assert + with self.assertRaises(Exception) as context: + getattr(tt_processor, method_name)() + + self.assertEqual(str(context.exception), "Please run the 'initialize' method first.") + # MAIN if __name__ == "__main__": result = unittest.main(argv=[''], verbosity=3, exit=False) \ No newline at end of file From c30689e7ad0e96148384479874329823fd045e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 16:20:53 +0000 Subject: [PATCH 104/109] #40 TimeTrackingProcessorTestCase, added - Iteration 6 (82%). --- tests/nwtimetrackingtests.py | 150 +++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index c5a134b..dae662e 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2440,6 +2440,156 @@ def test_processttsbyyearspnv_shoulddisplay_whenoptionisdisplay(self) -> None: df = tts_by_year_spnv_tpl[1], formatters = setting_bag.tts_by_year_spnv_formatters ) + def test_processttsbyspn_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_spn_df : DataFrame = Mock() + definitions_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.tts_by_spn_df = tts_by_spn_df + summary.definitions_df = definitions_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_spn = ["display"] + setting_bag.tts_by_spn_formatters = {"%_DE" : "{:.2f}", "%_TE" : "{:.2f}"} + + # Act + tt_processor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_spn() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_spn_df, + formatters = setting_bag.tts_by_spn_formatters + ) + def test_processttsbyspnspv_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_spn_spv_df : DataFrame = Mock() + definitions_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.tts_by_spn_spv_df = tts_by_spn_spv_df + summary.definitions_df = definitions_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_spn_spv = ["display"] + + # Act + tt_processor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_spn_spv() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_spn_spv_df + ) + def test_processttsbyhashtag_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_hashtag_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.tts_by_hashtag_df = tts_by_hashtag_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_hashtag = ["display"] + setting_bag.tts_by_hashtag_formatters = {"Effort%" : "{:.2f}"} + + # Act + tt_processor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_hashtag() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_hashtag_df, + formatters = setting_bag.tts_by_hashtag_formatters + ) + def test_processttsbyefs_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_efs_tpl : tuple = (Mock(), Mock()) + tts_by_efs_df : DataFrame = tts_by_efs_tpl[1] + + summary : Mock = Mock() + summary.tts_by_efs_tpl = tts_by_efs_tpl + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_efs = ["display"] + + # Act + tt_processor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_efs() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_efs_df + ) + def test_processttsbytr_shoulddisplay_whenoptionisdisplay(self) -> None: + + # Arrange + tts_by_tr_df : DataFrame = Mock() + + summary : Mock = Mock() + summary.tts_by_tr_df = tts_by_tr_df + + displayer : Mock = Mock() + tt_adapter : Mock = Mock() + tt_adapter.create_summary.return_value = summary + + component_bag : Mock = Mock() + component_bag.displayer = displayer + component_bag.tt_adapter = tt_adapter + + setting_bag : Mock = Mock() + setting_bag.options_tts_by_tr = ["display"] + setting_bag.tts_by_tr_head_n = uint(10) + + # Act + tt_processor = TimeTrackingProcessor(component_bag = component_bag, setting_bag = setting_bag) + tt_processor.initialize() + tt_processor.process_tts_by_tr() + + # Assert + displayer.display.assert_called_once_with( + df = tts_by_tr_df.head(10) + ) def test_processdefinitions_shoulddisplay_whenoptionisdisplay(self) -> None: # Arrange From 971372950b051eb5ad822cd13e7ff50d1f040c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 16:47:16 +0000 Subject: [PATCH 105/109] #40 makefile, added: tryinstall-verbose. --- .devcontainer/Dockerfile | 2 +- scripts/makefile | 17 +++++++++++++++++ src/setup.py | 23 ++++++++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fc5d100..1d59b6f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,11 +8,11 @@ RUN pip install pandas==2.2.3 RUN pip install requests==2.32.3 RUN pip install tabulate==0.9.0 RUN pip install 'git+https://github.com/numbworks/nwshared.git@v1.8.0#egg=nwshared&subdirectory=src' -RUN pip install 'git+https://github.com/numbworks/nwpackageversions.git@v1.8.0#egg=nwpackageversions&subdirectory=src' # JUPYTER NOTEBOOK RUN pip install ipykernel==6.29.5 RUN pip install jupyter==1.1.0 +RUN pip install 'git+https://github.com/numbworks/nwpackageversions.git@v1.8.0#egg=nwpackageversions&subdirectory=src' # UNIT TESTING RUN pip install coverage==7.6.4 diff --git a/scripts/makefile b/scripts/makefile index aa9b5a5..98c60da 100644 --- a/scripts/makefile +++ b/scripts/makefile @@ -25,6 +25,23 @@ coverage-verbose: coverage html --omit=$(MODULE_NAME)tests.py && sed -n '//,/<\/table>/p' htmlcov/class_index.html | pandoc --from html --to plain; \ sleep 3; \ rm -rf htmlcov; +tryinstall-verbose: + @clear; \ + cd /home; \ + rm -rf build; \ + rm -rf dist; \ + rm -rf $(MODULE_NAME).egg-info; \ + rm -rf venv; \ + python /workspaces/$(MODULE_NAME)/src/setup.py bdist_wheel; \ + python3 -m venv venv; \ + source venv/bin/activate; \ + pip install dist/$(MODULE_NAME)*.whl; \ + pip show $(MODULE_NAME) | grep Version; \ + deactivate; \ + rm -rf build; \ + rm -rf dist; \ + rm -rf $(MODULE_NAME).egg-info; \ + rm -rf venv; type-concise: @value=$$(mypy $(ROOT_DIR)/src/$(MODULE_NAME).py --disable-error-code=import-untyped | grep -c "error:"); \ diff --git a/src/setup.py b/src/setup.py index 0e0aa48..a95d746 100644 --- a/src/setup.py +++ b/src/setup.py @@ -1,9 +1,30 @@ '''Contains packaging information about nwtimetracking.py.''' # GLOBAL MODULES +from setuptools import setup + # INFORMATION MODULE_ALIAS : str = "nwtt" MODULE_NAME : str = "nwtimetracking" MODULE_VERSION : str = "4.0.0" -# SETUP \ No newline at end of file +# SETUP +setup( + name = MODULE_NAME, + version = MODULE_VERSION, + description = "An application to run analyses on 'Time Tracking.xlsx'.", + author = "numbworks", + url = f"https://github.com/numbworks/{MODULE_NAME}", + py_modules = [ MODULE_NAME ], + install_requires = [ + "numpy>=2.1.2", + "pyarrow>=17.0.0", + "pyarrow>=17.0.0", + "pandas>=2.2.3", + "requests>=2.32.3", + "tabulate>=0.9.0", + "nwshared @ git+https://github.com/numbworks/nwshared.git@v1.8.0#egg=nwshared&subdirectory=src" + ], + python_requires = ">=3.12", + license = "MIT" +) \ No newline at end of file From cc6e4ec337e771276323f6c6791d11c5ec177d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 19:18:05 +0000 Subject: [PATCH 106/109] #40 makefile, expanded: type-concise. --- scripts/makefile | 4 +++- tests/nwtimetrackingtests.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/makefile b/scripts/makefile index 98c60da..888c653 100644 --- a/scripts/makefile +++ b/scripts/makefile @@ -16,7 +16,8 @@ makefile-info: type-verbose: @clear; \ - mypy $(ROOT_DIR)/src/$(MODULE_NAME).py --check-untyped-defs --disable-error-code=import-untyped; + mypy $(ROOT_DIR)/src/$(MODULE_NAME).py --check-untyped-defs --disable-error-code=import-untyped; \ + mypy $(ROOT_DIR)/tests/$(MODULE_NAME)tests.py --check-untyped-defs --disable-error-code=import-untyped --disable-error-code=import-not-found; coverage-verbose: @clear; \ cd $(ROOT_DIR)/tests/; \ @@ -45,6 +46,7 @@ tryinstall-verbose: type-concise: @value=$$(mypy $(ROOT_DIR)/src/$(MODULE_NAME).py --disable-error-code=import-untyped | grep -c "error:"); \ + value+=$$(mypy $(ROOT_DIR)/tests/$(MODULE_NAME)tests.py --disable-error-code=import-untyped --disable-error-code=import-not-found | grep -c "error:"); \ if [ $$value -eq 0 ]; then echo "[OK] $@: passed!"; else echo "[WARNING] $@: not passed! '$$value' error(s) found!"; fi; changelog-concise: @value=$$(cat $(ROOT_DIR)/CHANGELOG | grep -c -e "v$(MODULE_VERSION)$$" -e "v$(MODULE_VERSION) - BREAKING CHANGES$$"); \ diff --git a/tests/nwtimetrackingtests.py b/tests/nwtimetrackingtests.py index dae662e..5384b5a 100644 --- a/tests/nwtimetrackingtests.py +++ b/tests/nwtimetrackingtests.py @@ -2299,7 +2299,7 @@ def test_processtt_shoulddisplay_whenoptionisdisplay(self) -> None: def test_processttsbymonth_shoulddisplay_whenoptionisdisplay(self) -> None: # Arrange - tts_by_month_tpl : Tuple[DataFrame, DataFrame] = (Mock, Mock()) + tts_by_month_tpl : Tuple[DataFrame, DataFrame] = (Mock(), Mock()) summary : Mock = Mock() summary.tts_by_month_tpl = tts_by_month_tpl From 856d69fb5b3db5d6e858dcb243dd4d97b6fb3d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 19:50:14 +0000 Subject: [PATCH 107/109] #40 makefile, added: tryinstall-concise. --- docs/docs-nwtimetracking.md | 9 ++++++--- scripts/makefile | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/docs-nwtimetracking.md b/docs/docs-nwtimetracking.md index d0fce55..3bd2ebd 100644 --- a/docs/docs-nwtimetracking.md +++ b/docs/docs-nwtimetracking.md @@ -139,6 +139,7 @@ The avalaible target names are: |---|---| | type-verbose | Runs a type verification task and logs everything. | | coverage-verbose | Runs a unit test coverage calculation task and logs the % per class. | +| tryinstall-verbose | Simulates a "pip install" and logs everything. | | all-concise | Runs a batch of verification tasks and logs one summary line for each of them. | The expected outcome for `all-concise` is: @@ -147,13 +148,14 @@ The expected outcome for `all-concise` is: MODULE_NAME: nwtimetracking MODULE_VERSION: 4.0.0 COVERAGE_THRESHOLD: 70% -[WARNING] type-concise: not passed! '1' error(s) found! -[WARNING] changelog-concise: 'CHANGELOG' not updated to current version! +[OK] type-concise: passed! +[OK] changelog-concise: 'CHANGELOG' updated to current version! [OK] setup-concise: 'setup.py' updated to current version! [OK] coverage-concise: unit test coverage >= 70%. +[OK] tryinstall-concise: installation process works. ``` -Considering the old-fashioned syntax adopted by `make`, here a summary of its less intuitive aspects: +Considering the old-fashioned syntax adopted by both `make` and `bash`, here a summary of its less intuitive aspects: | Aspect | Description | |---|---| @@ -162,6 +164,7 @@ Considering the old-fashioned syntax adopted by `make`, here a summary of its le | `@` | By default, `make` logs all the commands included in the target. The `@` disables this behaviour. | | `$$` | Necessary to escape `$`. | | `$@` | Variable that stores the target name. | +| `if [[ ... ]]` | Double square brackets to enable pattern matching. | ## Known Issues - nwshared diff --git a/scripts/makefile b/scripts/makefile index 888c653..390a6d2 100644 --- a/scripts/makefile +++ b/scripts/makefile @@ -1,5 +1,5 @@ # SETTINGS -.PHONY: clear makefile-info type-concise changelog-concise setup-concise coverage-concise all-concise +.PHONY: clear makefile-info type-concise changelog-concise setup-concise coverage-concise tryinstall-concise all-concise SHELL := /bin/bash ROOT_DIR := $(shell cd .. && pwd) MODULE_NAME = "nwtimetracking" @@ -59,6 +59,10 @@ coverage-concise: coverage run -m unittest $(MODULE_NAME)tests.py > /dev/null 2>&1; \ value=$$(coverage report --omit=$(MODULE_NAME)tests.py | grep -oP 'TOTAL\s+\d+\s+\d+\s+\K\d+(?=%)'); \ if [ $$value -ge $(COVERAGE_THRESHOLD) ]; then echo "[OK] $@: unit test coverage >= $(COVERAGE_THRESHOLD)%."; else echo "[WARNING] $@: unit test coverage < $(COVERAGE_THRESHOLD)%."; fi; +tryinstall-concise: + @value=$$(make tryinstall-verbose 2>&1); \ + last_chars=$$(echo "$$value" | tail -c 100); \ + if [[ "$$last_chars" == *"Version: $(MODULE_VERSION)"* ]]; then echo "[OK] $@: installation process works."; else echo "[WARNING] $@: installation process fails!"; fi; # AGGREGATE TARGETS -all-concise: clear makefile-info type-concise changelog-concise setup-concise coverage-concise \ No newline at end of file +all-concise: clear makefile-info type-concise changelog-concise setup-concise coverage-concise tryinstall-concise \ No newline at end of file From 772755268809bad30a9ffa4c4f93f4b966657c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 20:07:34 +0000 Subject: [PATCH 108/109] #40 *: application description updated. --- README.md | 2 +- docs/docs-nwtimetracking.md | 4 ++-- src/setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5e54062..fb13dcf 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Contact: numbworks@gmail.com From the documentation: -> nwtimetracking is a Jupyter Notebook designed to analyze the Excel file I use to annotate the durations of all my sessions of extra work and continuos learning, so that I can run analyses on them. This software is born to overcome the lack of support for durations (timedeltas) in Excel. +> `nwtimetracking` is an application designed to run automated data analysis tasks on `Time Tracking.xlsx`. This file is the one I use to annotate the durations of all my sessions of extra work and continuos learning. This application is born to overcome the lack of support for durations (timedeltas) in Excel. ## Getting started diff --git a/docs/docs-nwtimetracking.md b/docs/docs-nwtimetracking.md index 3bd2ebd..becfed7 100644 --- a/docs/docs-nwtimetracking.md +++ b/docs/docs-nwtimetracking.md @@ -19,9 +19,9 @@ Contact: numbworks@gmail.com ## Introduction -`nwtimetracking` is a `Jupyter Notebook` designed to analyze the Excel file I use to annotate the durations of all my sessions of extra work and continuos learning, so that I can run analyses on them. +`nwtimetracking` is an application designed to run automated data analysis tasks on `Time Tracking.xlsx`. -This software is born to overcome the lack of support for durations (timedeltas) in Excel. +This file is the one I use to annotate the durations of all my sessions of extra work and continuos learning. This application is born to overcome the lack of support for durations (timedeltas) in Excel. This project may not be useful for many (not generic enough), but I decided to upload it to `Github` anyway, in order to showcase my way of working when I face similar data analysis tasks and I decide to tackle them with `Python` and `Jupyter Notebook`. diff --git a/src/setup.py b/src/setup.py index a95d746..589d89d 100644 --- a/src/setup.py +++ b/src/setup.py @@ -12,7 +12,7 @@ setup( name = MODULE_NAME, version = MODULE_VERSION, - description = "An application to run analyses on 'Time Tracking.xlsx'.", + description = "An application designed to run automated data analysis tasks on 'Time Tracking.xlsx'.", author = "numbworks", url = f"https://github.com/numbworks/{MODULE_NAME}", py_modules = [ MODULE_NAME ], From 8f86a255e9993ad1b8634aa1226058a3cc4a8def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A8n?= Date: Sun, 8 Dec 2024 20:12:54 +0000 Subject: [PATCH 109/109] #40 CHANGE#40 CHANGELOG, codecoverage*: updated.LOG: updated. --- CHANGELOG | 1 + codecoverage.svg | 2 +- codecoverage.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 69265f9..ab85160 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ v4.0.0 - BREAKING CHANGES - Application: - Feature: re-structured the whole architecture to simplify usage and future expansions. + - Maintenance: makefile now supports "*tests.py" in "type*"" targets and "tryinstall-concise" in "all-concise". - Documentation: - Feature: updated to v4.0.0. diff --git a/codecoverage.svg b/codecoverage.svg index f3e0bcc..7f8ab10 100644 --- a/codecoverage.svg +++ b/codecoverage.svg @@ -1 +1 @@ -coverage: 93.0%coverage93.0% \ No newline at end of file +coverage: 94.0%coverage94.0% \ No newline at end of file diff --git a/codecoverage.txt b/codecoverage.txt index e5abe75..c1868ea 100644 --- a/codecoverage.txt +++ b/codecoverage.txt @@ -1 +1 @@ -https://img.shields.io/badge/coverage-93.0%25-green \ No newline at end of file +https://img.shields.io/badge/coverage-94.0%25-green \ No newline at end of file