#!/usr/bin/python # # PyTiTra_Classes.py # # Implements classes with "bussiness logic" and data modell for Time Tracking # # No GUI Implementation and not depended on IOS pythonista. # Works and is tested in python and Jupyter Notebook # # fixed error when deleting last action in calender # # - making sure there is always a task["0"] with ._id==0 and at least one project[...] # - error handling in ReadCalFomCSV added to handle unknown task # - problems when writing german umlauts solved by adding # encoding='utf8',errors="ignore" to open file # Why is this only appearing in CSV and not on JSON? # # https://realpython.com/documenting-python-code/#why-documenting-your-code-is-so-important # # ------------------------------------------------------------------------------------- # Licence & Copyright # ------------------------------------------------------------------------------------- # # Copyright 2019 ArduFox (Wolfgang Fuchs) # # This file is part of TiTraPy. # # TiTraPy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # TiTraPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with TiTraPy. If not, see . # #import pprint import datetime from datetime import timedelta from datetime import date import os, shutil #import random import json import re import csv class Task: '''Beschreibt eine Tätigkeit bzw. Teilprojekt bzw. Aufgabe, die zu konkreten Zeiten getan werden kann bzw. auf die Zeiten gebucht werden können''' # Soll ich hier eine statische Liste aller tasks führen? # Dann könnte ich die hier lesen & finden # mindestens für Debugging __task_num = 0 __all_tasks = dict() def __init__(self, name: str, emoij="X", farbe="#265B6A"): '''Neuen Task anlegen und im Dictionary aller tasks aufnehmen''' self._name = name self._emoij = emoij self._farbe = farbe self._projectName = "" self._project = None # 0 oder none ?? self._id = Task.__task_num Task.__task_num += 1 self.__all_tasks[self._id] = self def __str__(self): return "Task ([%d]: %s, %s, %s, %s)" % (self._id, self._name, self._emoij, self._farbe, self._projectName) def SetProject(self, prj): """Projektnamen eintragen oder ändern. Hier wird geprüft, ob das Projekt sich ändert und in diesem Fall wird der Task aus dem anderen Projekt entfernt werden""" # if self._project != 0 : if self._project != None: self._project.removeTask(self) self._projectName = prj._name self._project = prj self._project.addTask(self) def RemoveProject(self): self._projectName = "" self._project = None def UpdateProjectName(self, new_name: str): '''Update ONLY the projectName member''' self._projectName = new_name def SetName(self, NewName): '''Change name of task''' if self._project != None: # print("\n\n~~~~ PROBLEM Ändere Namen '{}' ~~~~\n".format(NewName)) self._project.removeTask(self) # TODO not yet tested self._name = NewName self._project.addTask(self) else: self._name = NewName @classmethod def NextID(cls): 'Gibt die nächste ID-Nummer zurück, die für den nächsten Task vergeben wird' return Task.__task_num @classmethod def FindTaskid(cls, id): '''find task with return None if found nothing ''' # https://realpython.com/python-keyerror/ return cls.__all_tasks.get(id) @classmethod def FindTaskName(cls, name): '''find first task with return None if found nothing''' for k, _t in cls.__all_tasks.items(): if name == _t._name: return _t return None def RemoveTask(self): '''Remove Task by given id. Remove it from associated Project What to do with associated actions? ''' self._project.removeTask(self) self.__all_tasks.pop(self._id, None) @classmethod def AllTasksStr(cls) -> str: """Construct beautiful string of all tasks """ s = "AllTasksStr:" for k, _t in cls.__all_tasks.items(): s = s + f"\n{k}: {_t}" return s @classmethod def GetAllTasksList(cls) -> list: """Return the List of AllTasks __all_tasks """ return cls.__all_tasks @classmethod def UITasksList(cls) -> list: """construct list of dicts of all tasks for usage in UI return list of dict of all tasks """ l = list() for k, _t in cls.__all_tasks.items(): l.append({ "titl": f"{_t._name} '{_t._emoij}' [{_t._projectName}]", 'id': f"{_t._id}", "task": _t._name, 'prj': _t._projectName, 'color': _t._farbe, 'id': _t._id }) l = sorted(l, key=lambda i: (i['prj'], i['task'])) return l def NewAction(self, time): '''create new action with this task at given