#!/bin/python3 ######################### # # timeTrack.py # by 4nima # v.2.0.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()