Forráskód Böngészése

added timer and tasks to time recorder

Nico Ruhnke 2 éve
szülő
commit
8a7bd1d563

+ 13 - 5
time_recoder/save_recorded_time_in_table.py

@@ -5,28 +5,36 @@ from tool_lib import create_excel_tables
 from os import path
 from time import time
 
-def create_recorded_time_dict(worked_time, income):
+
+def create_recorded_time_dict(worked_time, income, task):
     return {'Name': NAME,
             'Datum': str(datetime_functions.get_current_date()) ,
-            'Arbeitszeit' : worked_time,
+            'Arbeitszeit': worked_time,
             'Einkommen': income,
-            'Kalenderwoche': datetime_functions.get_calendarweek_from_datetime(datetime_functions.get_current_date())}
+            'Kalenderwoche': datetime_functions.get_calendarweek_from_datetime(datetime_functions.get_current_date()),
+            'Task': task}
+
 
 def table_exists(path_):
     return path.isfile(path_)
 
+
 def get_current_time_as_timestamp():
     return int(time())
 
+
 def add_to_xlsx_table(table_object):
     table_object.append_df_to_xlsx(table_object.dataframes)
 
+
 def create_xlsx_table(table_object):
     table_object.export_to_excel(table_object.create_writer())
 
-def save_recorded_time_in_table(worked_time_in_minutes):
+
+def save_recorded_time_in_table(worked_time_in_minutes, task):
+    worked_time_in_minutes = round(worked_time_in_minutes)
     income = calc_income_by_time_in_euro_without_sales_tax(worked_time_in_minutes, HOURLY_WAGE_IN_EURO)
-    table_dict = create_recorded_time_dict(worked_time_in_minutes, income)
+    table_dict = create_recorded_time_dict(worked_time_in_minutes, income, task)
     xlsx_table = create_excel_tables.CreateTable(table_dict, path=PATH)
     if table_exists(PATH):
         add_to_xlsx_table(xlsx_table)

+ 7 - 1
time_recoder/time_recoder_gui/time_recorder_gui_config.py

@@ -4,4 +4,10 @@ START_BUTTON_TEXT = 'Start Timer'
 STOP_BUTTON_TEXT = 'Stop Timer'
 STOP_BUTTON_INTRODUCTION_TEXT = 'Pause Timer: Pausiert die aktuelle Zeit'
 PAUSE_BUTTON_INTRODUCTION_TEXT = 'Stop Timer: Fügt die gearbeitete Zeit in eine Excel'
-WINDOW_SIZE = (400, 130)
+SELECT_TASK_INTRODUCTION_TEXT = 'Task auswählen'
+ADD_TASK_INTRODUCTION_TEXT = 'neuer Sub-Task'
+ADD_TASK_BUTTON_TEXT = 'hinzufügen'
+NEW_TASK_TEXT_KEY = 'sub_task_name'
+TASK_TREE_COMBO_NAME = 'task_tree'
+TIMER_KEY = 'timer'
+WINDOW_SIZE = (400, 250)

+ 62 - 27
time_recoder/time_recoder_gui/time_recorder_gui_main.py

@@ -1,7 +1,21 @@
+import time
+from threading import Thread
+
 import PySimpleGUI
-from time_recoder.time_recoder_gui.time_recorder_gui_config import PAUSE_BUTTON_TEXT, START_BUTTON_TEXT, STOP_BUTTON_TEXT, \
-    STOP_BUTTON_INTRODUCTION_TEXT, PAUSE_BUTTON_INTRODUCTION_TEXT, WINDOW_SIZE
+from sqlalchemy.orm import sessionmaker
+
+from time_recoder.time_recoder_gui.time_recorder_gui_config import PAUSE_BUTTON_TEXT, START_BUTTON_TEXT, \
+    STOP_BUTTON_TEXT, \
+    STOP_BUTTON_INTRODUCTION_TEXT, PAUSE_BUTTON_INTRODUCTION_TEXT, WINDOW_SIZE, SELECT_TASK_INTRODUCTION_TEXT, \
+    ADD_TASK_INTRODUCTION_TEXT, ADD_TASK_BUTTON_TEXT, NEW_TASK_TEXT_KEY, TASK_TREE_COMBO_NAME, TIMER_KEY
 from time_recoder import save_recorded_time_in_table
+from time_recoder.time_recoder_gui.time_recorder_task_handler import TaskHandler
+from time_recoder.time_recoder_gui.time_recorder_timer import Timer
+from time_recoder.time_recorder_database.create_db import ENGINE
+
+Session = sessionmaker(bind=ENGINE)
+taskHandler = TaskHandler()
+timer = Timer()
 
 
 def create_layout():
@@ -10,7 +24,16 @@ def create_layout():
         [PySimpleGUI.Text(STOP_BUTTON_INTRODUCTION_TEXT)],
         [PySimpleGUI.Button(START_BUTTON_TEXT),
          PySimpleGUI.Button(PAUSE_BUTTON_TEXT),
-         PySimpleGUI.Button(STOP_BUTTON_TEXT)]
+         PySimpleGUI.Button(STOP_BUTTON_TEXT)],
+        [PySimpleGUI.Text('', size=(8, 1), font=('Helvetica', 20), justification='center', key=TIMER_KEY)],
+        [PySimpleGUI.Text(SELECT_TASK_INTRODUCTION_TEXT),
+         PySimpleGUI.Combo(taskHandler.get_task_tree_name_strings(taskHandler.root_task),
+                           size=(30, 1),
+                           default_value='root',
+                           key=TASK_TREE_COMBO_NAME)],
+        [PySimpleGUI.Text(ADD_TASK_INTRODUCTION_TEXT),
+         PySimpleGUI.InputText(size=(15, 1), key=NEW_TASK_TEXT_KEY),
+         PySimpleGUI.Button(ADD_TASK_BUTTON_TEXT)]
     ]
 
 
@@ -34,39 +57,50 @@ def pause_button_is_pushed(event):
     return event == PAUSE_BUTTON_TEXT
 
 
+def add_sub_task_button_is_pushed(event):
+    return event == ADD_TASK_BUTTON_TEXT
+
+
 def event_loop(window):
     while True:
-        event, values = window.read()
+        event, values = window.read(timeout=10)
+        window[TIMER_KEY].update(timer.get_value_string())
         if window_is_closed(event):
             break
         if start_button_is_pushed(event):
             print('Start Time')
-            if 'start_time' in locals():
-                if 'pause_time' in locals():
-                    print('Time updated, Pause Timer Reseted')
-                    start_time = start_time + (pause_time - save_recorded_time_in_table.get_current_time_as_timestamp())
-                    pause_time = 'reseted'
-                else:
-                    pass
-            else:
-                start_time = save_recorded_time_in_table.get_current_time_as_timestamp()
+            timer.start()
+
         if pause_button_is_pushed(event):
             print('Pause Timer Activated')
-            if 'pause_time' in locals():
-                if pause_time == 'reseted':
-                    print('New Pause Timer Activated')
-                    pause_time = save_recorded_time_in_table.get_current_time_as_timestamp()
-                else:
-                    pass
-            else:
-                pause_time = save_recorded_time_in_table.get_current_time_as_timestamp()
+            timer.pause()
+
         if stop_button_is_pushed(event):
-            print('Timer Stoped Recording, Close the Window')
-            if 'start_time' in locals():
-                worked_time_in_minutes = (save_recorded_time_in_table.get_current_time_as_timestamp() - start_time) / 60
-                save_recorded_time_in_table.save_recorded_time_in_table(worked_time_in_minutes)
-            else:
-                pass
+            worked_time_in_minutes = timer.get_passed_time() / 60
+            selected_task_name = window.Element(TASK_TREE_COMBO_NAME).get()
+            current_task = taskHandler.get_task_by_id(taskHandler.get_task_id(selected_task_name))
+            current_branch = taskHandler.get_task_branch_as_string(current_task)
+            try:
+                save_recorded_time_in_table.save_recorded_time_in_table(worked_time_in_minutes, current_branch)
+                timer.stop()
+                print('Timer stopped recording for task: ' + selected_task_name.strip())
+            except PermissionError:
+                print("Error: couldn't write into table file. Please close any program that uses the table" +
+                      " and press 'Stop Timer' again")
+
+        if add_sub_task_button_is_pushed(event):
+            sub_task_name = window.Element(NEW_TASK_TEXT_KEY).get()
+            sub_task_name_already_in_use = sub_task_name in taskHandler.get_task_tree_name_strings(
+                taskHandler.root_task)
+            if sub_task_name_already_in_use:
+                print('Error: Task name ' + sub_task_name + ' already used, please enter another one')
+            elif sub_task_name:
+                parent_task_name = window.Element(TASK_TREE_COMBO_NAME).get()
+                parent_task_id = taskHandler.get_task_id(parent_task_name)
+                print('Adding new Sub-Task: ' + sub_task_name + ' to ' + parent_task_name.strip())
+                taskHandler.add_task(parent_task_id, sub_task_name)
+                window.Element(TASK_TREE_COMBO_NAME).update(
+                    values=taskHandler.get_task_tree_name_strings(taskHandler.root_task))
 
 
 def gui_main():
@@ -78,3 +112,4 @@ def gui_main():
 
 if __name__ == '__main__':
     gui_main()
+    taskHandler.close_session()

+ 73 - 0
time_recoder/time_recoder_gui/time_recorder_task_handler.py

@@ -0,0 +1,73 @@
+import sqlalchemy
+from sqlalchemy import select
+from sqlalchemy.orm import sessionmaker
+from time_recoder.time_recorder_database.create_db import ENGINE, create_root_task
+from time_recoder.time_recorder_database.db_models import Task
+
+Session = sessionmaker(bind=ENGINE)
+
+
+class TaskHandler:
+
+    def __init__(self):
+        self.session = Session()
+        self.root_task = self.get_root_task()
+
+    def get_root_task(self):
+        task: Task
+        try:
+            task = self.session.query(Task).filter(Task.parent_task_id == -1).one()
+        except:
+            create_root_task()
+            task = self.session.query(Task).filter(Task.parent_task_id == -1).one()
+        return task
+
+    def add_task(self, parent_task_id: int, name: str):
+        new_task = Task(parent_task_id=parent_task_id, name=name)
+        self.session.add(new_task)
+        self.session.commit()
+
+    """ fancy recursion tree ♥ """
+    def get_task_tree(self, parent_task: Task):
+        child_tasks = self.session.query(Task).filter(Task.parent_task_id == parent_task.id).all()
+        return {'task': parent_task,
+                'child_tasks': [self.get_task_tree(task) for task in child_tasks]}
+
+    def close_session(self):
+        self.session.close()
+
+    def task_tree_as_string_list(self, task_tree, level_offset=0):
+        string_level_prefix = ''
+        for _ in range(0, level_offset):
+            string_level_prefix += '  '
+        intended_name = string_level_prefix + task_tree['task'].name
+        level_offset += 2
+        child_tree_list = [self.task_tree_as_string_list(task_tree, level_offset) for task_tree in
+                           task_tree['child_tasks']]
+        flatten_child_tree_list = [item for sublist in child_tree_list for item in sublist]
+        return [intended_name] + flatten_child_tree_list
+
+    def task_tree_as_id_list(self, task_tree):
+        child_tree_list = [self.task_tree_as_id_list(task_tree) for task_tree in task_tree['child_tasks']]
+        root_id = task_tree['task'].id
+        flatten_child_tree_list = [item for sublist in child_tree_list for item in sublist]
+        return [root_id] + flatten_child_tree_list
+
+    def get_task_tree_name_strings(self, task):
+        return self.task_tree_as_string_list(self.get_task_tree(task))
+
+    def get_task_tree_ids(self, task):
+        return self.task_tree_as_id_list(self.get_task_tree(task))
+
+    def get_task_id(self, task_name: str):
+        return self.get_task_tree_ids(self.root_task)[self.get_task_tree_name_strings(self.root_task).index(task_name)]
+
+    def get_task_by_id(self, task_id: int):
+        return self.session.query(Task).filter(Task.id == task_id).one()
+
+    def get_task_branch_as_string(self, task):
+        parent_task_id = task.parent_task_id
+        if parent_task_id == -1:
+            return task.name
+        else:
+            return self.get_task_branch_as_string(self.get_task_by_id(parent_task_id)) + '/' + task.name

+ 50 - 0
time_recoder/time_recoder_gui/time_recorder_timer.py

@@ -0,0 +1,50 @@
+from time_recoder import save_recorded_time_in_table
+
+
+def get_current_time():
+    return save_recorded_time_in_table.get_current_time_as_timestamp()
+
+
+class Timer:
+
+    def __init__(self):
+        self.start_time = -1
+        self.pause_time = 0
+        self.is_paused = False
+
+    def get_passed_time(self):
+        if self.was_started():
+            current_time = get_current_time()
+            if self.is_paused:
+                return current_time - (self.start_time + (current_time - self.pause_time))
+            else:
+                return current_time - self.start_time
+        else:
+            return 0
+
+    def get_value_string(self):
+        current_time = self.get_passed_time()
+        return '{:02d}:{:02d}:{:02d}'.format((current_time // 60) // 60,
+                                             (current_time // 60) % 60,
+                                             current_time % 60)
+
+    def start(self):
+        if self.is_paused:
+            self.start_time = self.start_time + (get_current_time() - self.pause_time)
+            self.pause_time = 0
+            self.is_paused = False
+        else:
+            self.start_time = get_current_time()
+
+    def stop(self):
+        self.start_time = -1
+        self.pause_time = 0
+        self.is_paused = False
+
+    def pause(self):
+        if not self.is_paused:
+            self.is_paused = True
+            self.pause_time = get_current_time()
+
+    def was_started(self):
+        return self.start_time > 0

+ 1 - 1
time_recoder/time_recoder_test.py

@@ -24,7 +24,7 @@ class TimeRecorderIntegrationtest(unittest.TestCase):
         start_time = get_current_time_as_timestamp()
         sleep(5)
         worked_time_in_minutes = (get_current_time_as_timestamp() - start_time) / 60
-        save_recorded_time_in_table(worked_time_in_minutes)
+        save_recorded_time_in_table(worked_time_in_minutes, 'default')
 
 
 class GUITest(unittest.TestCase):

+ 26 - 0
time_recoder/time_recorder_database/create_db.py

@@ -0,0 +1,26 @@
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from time_recoder.time_recoder_config import PATH
+from time_recoder.time_recorder_database.db_models import BASE, Task
+
+DATABASE_NAME = "time_recorder_database.db"
+ENGINE = create_engine("sqlite+pysqlite:///" + PATH + DATABASE_NAME, echo=False, future=True)
+Session = sessionmaker(bind=ENGINE)
+
+
+def main():
+    BASE.metadata.create_all(ENGINE)
+
+
+def create_root_task():
+    session = Session()
+    root_task = Task(parent_task_id=-1, name='root')
+    session.add(root_task)
+    session.commit()
+    session.close()
+
+
+if __name__ == "__main__":
+    main()
+

+ 18 - 0
time_recoder/time_recorder_database/db_models.py

@@ -0,0 +1,18 @@
+
+from sqlalchemy import ForeignKey, Float
+from sqlalchemy import Column, Integer, String
+from sqlalchemy.ext.declarative import declarative_base
+BASE = declarative_base()
+
+
+class Task(BASE):
+
+    __tablename__ = "task"
+
+    id = Column(Integer, primary_key=True)
+    parent_task_id = Column(Integer, ForeignKey('task.id'))
+    name = Column(String)
+
+    def __init__(self, parent_task_id, name):
+        self.parent_task_id = parent_task_id
+        self.name = name