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