diff --git a/.gitignore b/.gitignore index 6482967..b04e8c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ *.log *.conf *.db -.vscode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5f45ceb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "sqltools.connections": [ + { + "previewLimit": 50, + "driver": "SQLite", + "name": "TimeTrack", + "group": "Test", + "database": "${workspaceFolder:timetrack}/timetrack.db" + } + ], + "sqltools.useNodeRuntime": true +} \ No newline at end of file diff --git a/ERD_TimeTrack_Database.eddx b/ERD_TimeTrack_Database.eddx deleted file mode 100644 index 24202ef..0000000 Binary files a/ERD_TimeTrack_Database.eddx and /dev/null differ diff --git a/Mindmap_TimeTrack.emmx b/Mindmap_TimeTrack.emmx deleted file mode 100644 index 9b6fd74..0000000 Binary files a/Mindmap_TimeTrack.emmx and /dev/null differ diff --git a/PAP_TimeTrack.eddx b/PAP_TimeTrack.eddx deleted file mode 100644 index 2f7228d..0000000 Binary files a/PAP_TimeTrack.eddx and /dev/null differ diff --git a/timeTrack.py b/timeTrack.py deleted file mode 100644 index 1bc8bbd..0000000 --- a/timeTrack.py +++ /dev/null @@ -1,489 +0,0 @@ -#!/bin/python3 -######################### -# -# timeTrack.py -# by 4nima -# v.0.5.0 -# -######################### -# simple time tracking with database -######################### - -import datetime -import sqlite3 -import json -import os -import logging - -# Main class -class TimeTrack: - def __init__(self, DATABASE='timetrack.db', CONFIG='timetrack.conf'): - self.DATABASE = DATABASE - self.CONFIG = CONFIG - self.USERID = 0 - self.USERNAME = '' - self.OLDEVENT = 2 - self.LOGFILE = 'timetrack.log' - logging.basicConfig( - filename=self.LOGFILE, - level=logging.DEBUG, - format='%(asctime)s - %(process)d-%(levelname)s: %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' - ) - self.db_setup() - self.load_config() - - ## Prepartation - ### Check OS and clear screen - def clear_screen(self): - if os.name == 'posix': - logging.debug('Unix/Linux system detected') - _ = os.system('clear') - else: - logging.debug('Winwos System detected') - _ = os.system('cls') - - ### Creates a database if none is found - def db_setup(self): - if os.path.isfile(self.DATABASE): - logging.info('Database file was found') - else: - logging.info('Database file was not found, will create ') - - sql = [] - sql.append(""" - CREATE TABLE IF NOT EXISTS time_entries ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - starttime TIMESTAMP NOT NULL, - endtime TIMESTAMP NOT NULL, - user_id INTEGER NOT NULL, - activity TEXT, - catrgory_id INTEGER, - client_id INTEGER, - lock BOOLEAN - ) - """) - - sql.append(""" - CREATE TABLE IF NOT EXISTS active_events ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - starttime TIMESTAMP NOT NULL, - user_id INT NOT NULL - ) - """) - - sql.append(""" - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - worktime INT, - worktime_span INT - ) - """) - - sql.append(""" - CREATE TABLE IF NOT EXISTS clients ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL - ) - """) - - sql.append(""" - CREATE TABLE IF NOT EXISTS categories ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL - ) - """) - - connect = sqlite3.connect(self.DATABASE) - cursor = connect.cursor() - logging.debug('Create initial database tables') - for SQL in sql: - try: - cursor.execute(SQL) - except: - logging.error('Table could not be created') - logging.debug(SQL) - print('TimeTrack wird beendet: Fehler bei Datanbank Setup') - quit() - else: - logging.debug('Table was created successfully or already exists') - - connect.commit() - connect.close() - - ### Loads or creates a config file - def load_config(self): - if os.path.isfile(self.CONFIG): - logging.info('Config file was found') - try: - with open(self.CONFIG) as config_data: - data = json.load(config_data) - except ValueError: - logging.error('Config file has no valid JSON syntax') - print('TimeTrack wird beendet: Fehler in der JSON-Konfig ({})'.format(self.CONFIG)) - quit() - else: - logging.info('Config file was loaded successfully') - self.USERID = data['user'] - self.OLDEVENT = data['oldevent'] - logging.debug('UserID {} was used'.format(data['user'])) - self.set_user() - - else: - logging.warning('Config file not found') - config = { - 'default' : 'interactive', - 'user' : 1, - 'oldevent' : 2 - } - with open(self.CONFIG, "w") as outfile: - json.dump(config, outfile, indent=4, sort_keys=True) - logging.info('Config file created successfully') - - #> wenn man keine datei erstellen darf/kann, fällt das skript in einen loop ? - self.load_config() - - ## user handling - ### Creates a user who does not yet exist - def create_user(self, USER=''): - if USER == '': - username = input('Benutzername eingaben: ') - logging.debug('Selected username: {}'.format(username)) - else: - username = USER - - while self.get_users(NAME=username): - print('Der gewünsche Benutzername ({}) ist schon vergeben'.format(username)) - username = input('Wähle einen anderen Benutzernamen: ') - logging.debug('Try again: Selected username: {}'.format(username)) - - logging.debug('Accepted username: {}'.format(username)) - - connect = sqlite3.connect(self.DATABASE) - cursor = connect.cursor() - - sql = "INSERT INTO users ( name ) values ( ? )" - - try: - cursor.execute(sql, [username]) - except: - logging.error('User could not be saved in database') - logging.debug(sql) - else: - logging.info('User was saved successfully') - connect.commit() - - connect.close() - - ### Outputs existing users from the DB - def get_users(self, UID=0, NAME=''): - if not UID == 0: - logging.debug('Get user by ID: {}'.format(UID)) - data = UID - sql = "SELECT * FROM users WHERE id = ?" - elif not NAME == '': - logging.debug('Get user by username: {}'.format(NAME)) - data = NAME - sql = "SELECT * FROM users WHERE name LIKE ?" - else: - logging.debug('Get all users') - data = '' - sql = "SELECT * FROM users" - - connect = sqlite3.connect(self.DATABASE) - cursor = connect.cursor() - - try: - if data: - cursor.execute(sql, [data]) - else: - cursor.execute(sql) - except: - logging.error('Could not get user') - logging.debug(sql) - print('Fehler beim Zugriff auf die Benutzer Datenbank') - return 1 - else: - logging.debug('User database read out successfully') - connect.commit() - - data = cursor.fetchall() - connect.close() - return data - - ### Defines a user for the session - def set_user(self): - data = self.get_users() - if not data: - logging.info("No user was found") - print("Es wurde kein Benutzer gefunden, bitte legen sie einen neuen an.") - self.create_user() - - data = self.get_users(UID=self.USERID) - if data == []: - logging.error('User ID was not found') - else: - self.USERNAME = data[0][1] - - ## Time handling - ### Creates an active event if none exists for the user - def save_event(self, TIME=datetime.datetime.now()): - if not self.get_event(USERID=self.USERID): - logging.debug('No active events found for the user: {}'.format(self.USERID)) - connect = sqlite3.connect(self.DATABASE) - cursor = connect.cursor() - sql = "INSERT INTO active_events ( starttime, user_id ) VALUES ( ?, ? )" - - try: - cursor.execute(sql, [TIME, self.USERID]) - except: - logging.error('Event could not be created') - logging.debug(sql) - print('Event konnte nicht gespeichert werden.') - return False - else: - logging.info('Event was created successfully') - connect.commit() - - connect.close() - return True - else: - logging.warning('Active events found for the user, new event could not be created') - return False - - ### Deletes an active event based on a user or event ID - def delete_event(self, ENTRYID='', USERID=''): - if not ENTRYID == '': - logging.info('Deletes event based on eventid: {}'.format(ENTRYID)) - sql = "DELETE FROM active_events WHERE id = ?" - data = ENTRYID - elif not USERID == '': - logging.info('Deletes events based on userid: {}'.format(USERID)) - sql = "DELETE FROM active_events WHERE user_id = ?" - data = USERID - else: - logging.warning('No indication of what should be deleted') - print('Keine angabe was gelöscht werden soll') - return 0 - - connect = sqlite3.connect(self.DATABASE, detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) - cursor = connect.cursor() - - try: - cursor.execute(sql, [data]) - except: - logging.error('Event could not be deleted') - logging.debug(sql) - print('Fehler beim löschen des Events.') - else: - logging.debug('Event was successfully deleted') - connect.commit() - - connect.close() - - ### Get an active event based on a user or event ID - def get_event(self, ENTRYID='', USERID=''): - if not ENTRYID == '': - logging.debug('Search event based on eventid: {}'.format(ENTRYID)) - sql = "SELECT * FROM active_events WHERE id = ?" - data = ENTRYID - elif not USERID == '': - logging.debug('Search events based on userid: {}'.format(USERID)) - sql = "SELECT * FROM active_events WHERE user_id = ?" - data = USERID - else: - sql = "SELECT * FROM active_events" - data = '' - - connect = sqlite3.connect(self.DATABASE, detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) - cursor = connect.cursor() - try: - if data: - logging.debug('Search event') - cursor.execute(sql, [data]) - else: - logging.debug('Get all Events') - cursor.execute(sql) - except: - logging.error('Events could not be read') - logging.debug(sql) - print('Fehler beim auslesen der aktiven Events') - else: - logging.debug('Events could be read out successfully') - data = cursor.fetchall() - connect.close() - if data == []: - logging.debug('No active events found') - return 0 - else: - logging.debug('{} events found'.format(len(data))) - return data[0] - - def time_start(self, AUTOFORWARD=True): - self.clear_screen() - starttime = datetime.datetime.now() - logging.debug('New Event process started at {}'.format(starttime)) - if self.save_event(starttime): - print('Neues Event gestartet um {}'.format(starttime.strftime("%H:%M"))) - if AUTOFORWARD: - self.time_stop() - else: - data = self.get_event(USERID=self.USERID) - print('Es existiert bereits ein aktives Event.') - if (data[1] + datetime.timedelta(hours=self.OLDEVENT)) <= datetime.datetime.now(): - logging.info('Event exceeds allowed duration') - print('Zeiteintrag ist zu alt laut den Einstellungen (älter {} Stunden)'.format(self.OLDEVENT)) - - if datetime.date.today() == data[1].date(): - print('Start um: {}'.format(data[1].strftime("%H:%M"))) - else: - print('Start am: {}'.format(data[1].strftime("%d.%m.%Y um %H:%M"))) - - elapsed = datetime.datetime.now() - data[1] - if elapsed.days: - logging.debug('Event older than 1 day ({} days)'.format(elapsed.days)) - print('Vergangene Zeit: >{} Tage'.format(elapsed.days)) - else: - logging.debug('Event younger than 1 day') - print('Vergangene Zeit: >{} Stunden'.format(int(elapsed.seconds/3600))) - - userinput = '' - while True: - if userinput == "F" or userinput == "f" or userinput == "1" \ - or userinput == "L" or userinput == "l" or userinput == "2" \ - or userinput == "A" or userinput == "a" or userinput == "3": - break - print('Soll das Event fortgesetzt oder gelöscht werden?') - print('[1/F/f] für fortsetzen') - print('[2/L/l] für löschen') - print('[3/A/a] für abbrechen') - userinput = input('Aktion: ') - logging.debug('User input: {}'.format(userinput)) - self.clear_screen() - - if userinput == "F" or userinput == "f" or userinput == "1": - logging.debug('Event should be continued') - self.time_stop() - elif userinput == "L" or userinput == "l" or userinput == "2": - logging.info('Event should be deleted (eventid: {})'.format(data[0])) - self.delete_event(data[0]) - self.time_start() - else: - logging.debug('Terminated by the user') - exit() - - else: - logging.debug('Event continues (eventid{})'.format(data[0])) - print('Event von {} Uhr geladen'.format(data[1].strftime("%H:%M"))) - self.time_stop() - - def time_stop(self): - data = self.get_event(USERID=self.USERID) - logging.debug('Event stop progess is started') - if data: - self.clear_screen() - userinput = '' - while True: - if userinput == "B" or userinput == "b" or userinput == "1" \ - or userinput == "L" or userinput == "l" or userinput == "2" \ - or userinput == "A" or userinput == "a" or userinput == "3": - break - print('Event von {} Uhr beenden?'.format(data[1].strftime("%H:%M"))) - print('[1/B/b] für beenden') - print('[2/L/l] für löschen') - print('[3/A/a] für abbrechen') - userinput = input('Aktion: ') - logging.debug('User input: {}'.format(userinput)) - self.clear_screen() - - if userinput == "B" or userinput == "b" or userinput == "1": - logging.debug('Event is ended') - print('Eingabe beenden mittels doppelter Leerzeile.') - print('Durchgeführte Tätigkeiten:') - userinput = [] - while True: - try: - if userinput[-1] == '' and userinput[-2] == '': - break - except IndexError: - pass - userinput.append(input()) - - logging.debug('User input: {}'.format(userinput)) - del userinput[-1] - del userinput[-1] - action = '' - for i in userinput: - if not action == '': - action += "\n" - action += i - - endtime = datetime.datetime.now() - logging.debug('Event end process start at {}'.format(endtime)) - connect = sqlite3.connect(self.DATABASE) - cursor = connect.cursor() - sql = "INSERT INTO time_entries ( starttime, endtime, user_id, activity ) VALUES ( ?, ?, ?, ? )" - - try: - cursor.execute(sql, [data[1], endtime, self.USERID, action]) - except: - logging.error('Time entry could not be created') - logging.debug(sql) - print('Zeiteintrag konnte nicht gespeichert werden.') - return False - else: - logging.info('Time entry was created successfully') - connect.commit() - self.delete_event(data[0]) - self.print_time_entry(STARTTIME=data[1], ENDTIME=endtime, ACTIVITY=action) - print('Zeiteintrag wurde gespeichert.') - while True: - if userinput == "J" or userinput == "j" or userinput == "1" \ - or userinput == "N" or userinput == "n" or userinput == "2": - break - print('Nächsten Zeiteintrag begrinnen ?') - print('[1/J/j] Ja') - print('[2/N/n] Nein') - userinput = input('Aktion: ') - logging.debug('User input: {}'.format(userinput)) - self.clear_screen() - - if userinput == "J" or userinput == "j" or userinput == "1": - self.time_start() - else: - logging.debug('Terminated by the user') - exit() - - - connect.close() - - elif userinput == "L" or userinput == "l" or userinput == "2": - logging.info('Event should be deleted (eventid: {})'.format(data[0])) - self.delete_event(data[0]) - else: - logging.debug('Terminated by the user') - exit() - - def get_time(self): - pass - - def print_time_entry(self, STARTTIME='', ENDTIME='', ACTIVITY=''): - s = (ENDTIME - STARTTIME).seconds - hours, remainder = divmod(s, 3600) - minutes, seconds = divmod(remainder, 60) - - print(50*"-") - print('Start: {} Uhr'.format(STARTTIME.strftime('%H:%M'))) - print('Ende: {} Uhr'.format(ENDTIME.strftime('%H:%M'))) - print('Dauer: {:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds))) - if not ACTIVITY == '': - print('Aktivität:') - print(ACTIVITY) - print(50*"-") - - - - -test = TimeTrack() -test.time_start() \ No newline at end of file