1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing a class representing the tasks JSON file. 8""" 9 10import json 11import time 12 13from PyQt5.QtCore import QObject 14 15from E5Gui import E5MessageBox 16from E5Gui.E5OverrideCursor import E5OverridenCursor 17from E5Gui.E5Application import e5App 18 19import Preferences 20 21from .Task import TaskType, TaskPriority 22 23 24class TasksFile(QObject): 25 """ 26 Class representing the tasks JSON file. 27 """ 28 def __init__(self, isGlobal: bool, parent: QObject = None): 29 """ 30 Constructor 31 32 @param isGlobal flag indicating a file for global tasks 33 @type bool 34 @param parent reference to the parent object (defaults to None) 35 @type QObject (optional) 36 """ 37 super().__init__(parent) 38 self.__isGlobal = isGlobal 39 40 def writeFile(self, filename: str) -> bool: 41 """ 42 Public method to write the tasks data to a tasks JSON file. 43 44 @param filename name of the tasks file 45 @type str 46 @return flag indicating a successful write 47 @rtype bool 48 """ 49 # prepare the tasks data dictionary 50 # step 0: header 51 tasksDict = {} 52 if self.__isGlobal: 53 tasksDict["header"] = { 54 "comment": "eric tasks file", 55 "saved": time.strftime('%Y-%m-%d, %H:%M:%S'), 56 "warning": ( 57 "This file was generated automatically, do not edit." 58 ), 59 } 60 # step 1: project scan filter 61 tasksDict["ProjectScanFilter"] = "" 62 63 # step 2: tasks 64 tasksDict["Tasks"] = [ 65 task.toDict() 66 for task in e5App().getObject("TaskViewer").getGlobalTasks() 67 ] 68 else: 69 tasksDict["header"] = { 70 "comment": "eric tasks file for project {0}".format( 71 e5App().getObject("Project").getProjectName()), 72 "warning": ( 73 "This file was generated automatically, do not edit." 74 ), 75 } 76 if Preferences.getProject("TimestampFile"): 77 tasksDict["header"]["saved"] = ( 78 time.strftime('%Y-%m-%d, %H:%M:%S') 79 ) 80 # step 1: project scan filter 81 tasksDict["ProjectScanFilter"] = ( 82 e5App().getObject("TaskViewer").getTasksScanFilter() 83 ) 84 85 # step 2: tasks 86 tasksDict["Tasks"] = [ 87 task.toDict() 88 for task in e5App().getObject("TaskViewer").getProjectTasks() 89 ] 90 91 try: 92 jsonString = json.dumps(tasksDict, indent=2) 93 with open(filename, "w") as f: 94 f.write(jsonString) 95 except (TypeError, OSError) as err: 96 with E5OverridenCursor(): 97 E5MessageBox.critical( 98 None, 99 self.tr("Save Tasks"), 100 self.tr( 101 "<p>The tasks file <b>{0}</b> could not be" 102 " written.</p><p>Reason: {1}</p>" 103 ).format(filename, str(err)) 104 ) 105 return False 106 107 return True 108 109 def readFile(self, filename: str) -> bool: 110 """ 111 Public method to read the tasks data from a task JSON file. 112 113 @param filename name of the project file 114 @type str 115 @return flag indicating a successful read 116 @rtype bool 117 """ 118 try: 119 with open(filename, "r") as f: 120 jsonString = f.read() 121 tasksDict = json.loads(jsonString) 122 except (OSError, json.JSONDecodeError) as err: 123 E5MessageBox.critical( 124 None, 125 self.tr("Read Tasks"), 126 self.tr( 127 "<p>The tasks file <b>{0}</b> could not be read.</p>" 128 "<p>Reason: {1}</p>" 129 ).format(filename, str(err)) 130 ) 131 return False 132 133 viewer = e5App().getObject("TaskViewer") 134 if tasksDict["ProjectScanFilter"]: 135 viewer.setTasksScanFilter(tasksDict["ProjectScanFilter"]) 136 137 addedTasks = [] 138 for task in tasksDict["Tasks"]: 139 addedTask = viewer.addTask( 140 task["summary"], priority=TaskPriority(task["priority"]), 141 filename=task["filename"], lineno=task["lineno"], 142 completed=task["completed"], _time=task["created"], 143 isProjectTask=not self.__isGlobal, 144 taskType=TaskType(task["type"]), 145 description=task["description"], uid=task["uid"], 146 parentTask=task["parent_uid"]) 147 if addedTask: 148 addedTasks.append((addedTask, task["expanded"])) 149 150 for task, expanded in addedTasks: 151 task.setExpanded(expanded) 152 153 return True 154