Integrate Jira tasks
This commit is contained in:
parent
3e1f17cfa2
commit
c3cde56eb8
77
data.py
77
data.py
@ -1,10 +1,11 @@
|
|||||||
import os
|
|
||||||
import json
|
|
||||||
import base64
|
|
||||||
import atexit
|
import atexit
|
||||||
from datetime import datetime, date, time, timedelta
|
import base64
|
||||||
from threading import Thread, Event
|
import json
|
||||||
|
import os
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
|
from datetime import datetime, date, timedelta
|
||||||
|
from threading import Thread, Event
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from PySide2 import QtCore
|
from PySide2 import QtCore
|
||||||
|
|
||||||
@ -15,33 +16,67 @@ tasks_path = os.path.join(data_dir_path, "tasks.json")
|
|||||||
|
|
||||||
data_path = os.path.join(data_dir_path, "data_{}.json")
|
data_path = os.path.join(data_dir_path, "data_{}.json")
|
||||||
save_delay = 3 * 60
|
save_delay = 3 * 60
|
||||||
|
max_jira_tasks = 50
|
||||||
|
|
||||||
|
|
||||||
class Tasks:
|
class Tasks:
|
||||||
def __init__(self):
|
def __init__(self, data):
|
||||||
if not os.path.exists(data_dir_path):
|
self._data = data
|
||||||
os.mkdir(data_dir_path)
|
if "tasks" in self._data:
|
||||||
if os.path.exists(tasks_path):
|
self._tasks = list(map(lambda x: base64.b64decode(x.encode("utf-8")).decode("utf-8"), self._data["tasks"]))
|
||||||
with open(tasks_path, "r") as f:
|
|
||||||
encoded_tasks = json.loads(f.read())
|
|
||||||
self._tasks = list(map(lambda x: base64.b64decode(x.encode("utf-8")).decode("utf-8"), encoded_tasks))
|
|
||||||
else:
|
else:
|
||||||
self._tasks = []
|
self._tasks = []
|
||||||
|
if "jira_tasks" in self._data:
|
||||||
|
self._jira_tasks_usage = dict()
|
||||||
|
for k, v in self._data["jira_tasks"].items():
|
||||||
|
key = base64.b64decode(k.encode("utf-8")).decode("utf-8")
|
||||||
|
self._jira_tasks_usage[key] = datetime.fromisoformat(v)
|
||||||
|
self._jira_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
|
||||||
|
else:
|
||||||
|
self._jira_tasks_usage = dict()
|
||||||
|
self._jira_tasks = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self) -> List[str]:
|
||||||
return self._tasks
|
return self._tasks
|
||||||
|
|
||||||
@tasks.setter
|
@tasks.setter
|
||||||
def tasks(self, tasks):
|
def tasks(self, tasks):
|
||||||
self._tasks = tasks
|
self._tasks = tasks
|
||||||
self._save()
|
|
||||||
|
|
||||||
def _save(self):
|
|
||||||
print("... saving tasks ...")
|
|
||||||
encoded_tasks = list(map(lambda x: base64.b64encode(x.encode("utf-8")).decode("utf-8"), self._tasks))
|
encoded_tasks = list(map(lambda x: base64.b64encode(x.encode("utf-8")).decode("utf-8"), self._tasks))
|
||||||
with open(tasks_path, "w+") as f:
|
self._data["tasks"] = encoded_tasks
|
||||||
f.write(json.dumps(encoded_tasks))
|
|
||||||
|
@property
|
||||||
|
def jira_tasks(self):
|
||||||
|
return self._jira_tasks
|
||||||
|
|
||||||
|
def add_jira_task(self, task_name):
|
||||||
|
print(f"before: {self._jira_tasks}")
|
||||||
|
self._jira_tasks.append(task_name)
|
||||||
|
print(f"after: {self._jira_tasks}")
|
||||||
|
self._jira_tasks_usage[task_name] = datetime.now()
|
||||||
|
if len(self._jira_tasks_usage) > max_jira_tasks:
|
||||||
|
sorted_tasks = sorted(self._jira_tasks_usage.keys(), key=lambda x: self._jira_tasks_usage[x])
|
||||||
|
overhang_tasks = sorted_tasks[:len(sorted_tasks) - max_jira_tasks]
|
||||||
|
for task in overhang_tasks:
|
||||||
|
del self._jira_tasks_usage[task]
|
||||||
|
self._save_jira_tasks()
|
||||||
|
|
||||||
|
def update_jira_task_usage(self, task_name):
|
||||||
|
if task_name in self._jira_tasks_usage:
|
||||||
|
self._jira_tasks_usage[task_name] = datetime.now()
|
||||||
|
self._save_jira_tasks()
|
||||||
|
|
||||||
|
def _save_jira_tasks(self):
|
||||||
|
serialized = dict()
|
||||||
|
for k, v in self._jira_tasks_usage.items():
|
||||||
|
key = base64.b64encode(k.encode("utf-8")).decode("utf-8")
|
||||||
|
serialized[key] = datetime.isoformat(v)
|
||||||
|
self._data["jira_tasks"] = serialized
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_tasks(self):
|
||||||
|
return self.tasks + self.jira_tasks
|
||||||
|
|
||||||
|
|
||||||
class Data(MutableMapping):
|
class Data(MutableMapping):
|
||||||
@ -110,8 +145,8 @@ class Data(MutableMapping):
|
|||||||
|
|
||||||
|
|
||||||
class Log:
|
class Log:
|
||||||
def __init__(self):
|
def __init__(self, data):
|
||||||
self._data = Data()
|
self._data = data
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
self.log("End")
|
self.log("End")
|
||||||
|
38
main.py
38
main.py
@ -7,7 +7,7 @@ from functools import partial
|
|||||||
from PySide2 import QtCore, QtGui, QtWidgets
|
from PySide2 import QtCore, QtGui, QtWidgets
|
||||||
from PySide2.QtWidgets import QMessageBox
|
from PySide2.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from data import Tasks, Log
|
from data import Tasks, Log, Data
|
||||||
from exceptions import FimeFrackingException
|
from exceptions import FimeFrackingException
|
||||||
from new_task import NewTask
|
from new_task import NewTask
|
||||||
from task_edit import TaskEdit
|
from task_edit import TaskEdit
|
||||||
@ -18,8 +18,9 @@ class App:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = QtWidgets.QApplication(sys.argv)
|
self.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
self.tasks = Tasks()
|
data = Data()
|
||||||
self.log = Log()
|
self.tasks = Tasks(data)
|
||||||
|
self.log = Log(data)
|
||||||
self.active_task = self.log.last_log() or "Nothing"
|
self.active_task = self.log.last_log() or "Nothing"
|
||||||
|
|
||||||
icon = QtGui.QIcon.fromTheme("appointment-new")
|
icon = QtGui.QIcon.fromTheme("appointment-new")
|
||||||
@ -27,12 +28,12 @@ class App:
|
|||||||
self.menu = QtWidgets.QMenu()
|
self.menu = QtWidgets.QMenu()
|
||||||
|
|
||||||
self.new_task = NewTask(None)
|
self.new_task = NewTask(None)
|
||||||
self.new_task.accepted.connect(self.new_task_selected)
|
self.new_task.accepted.connect(self.new_task_imported)
|
||||||
|
|
||||||
self.taskEdit = TaskEdit(None)
|
self.taskEdit = TaskEdit(None)
|
||||||
self.taskEdit.accepted.connect(self.tasks_edited)
|
self.taskEdit.accepted.connect(self.tasks_edited)
|
||||||
|
|
||||||
self.reportDialog = Report(None)
|
self.reportDialog = Report(self.tasks, None)
|
||||||
self.reportDialog.accepted.connect(self.report_done)
|
self.reportDialog.accepted.connect(self.report_done)
|
||||||
|
|
||||||
self.tray = QtWidgets.QSystemTrayIcon()
|
self.tray = QtWidgets.QSystemTrayIcon()
|
||||||
@ -43,8 +44,10 @@ class App:
|
|||||||
self.update_tray_menu()
|
self.update_tray_menu()
|
||||||
|
|
||||||
@QtCore.Slot()
|
@QtCore.Slot()
|
||||||
def new_task_selected(self):
|
def new_task_imported(self):
|
||||||
print(f"dialog input: {self.new_task.task_text}")
|
if self.new_task.task_text:
|
||||||
|
self.tasks.add_jira_task(self.new_task.task_text)
|
||||||
|
self.update_tray_menu()
|
||||||
|
|
||||||
@QtCore.Slot()
|
@QtCore.Slot()
|
||||||
def tasks_edited(self):
|
def tasks_edited(self):
|
||||||
@ -64,21 +67,28 @@ class App:
|
|||||||
self.update_tray_menu()
|
self.update_tray_menu()
|
||||||
|
|
||||||
def update_tray_menu(self):
|
def update_tray_menu(self):
|
||||||
self.menu.clear()
|
def add_tasks(tasks):
|
||||||
tasks = list(self.tasks.tasks)
|
|
||||||
tasks.append("Pause")
|
|
||||||
if self.active_task == "Nothing":
|
|
||||||
tasks.append("Nothing")
|
|
||||||
|
|
||||||
for t in tasks:
|
for t in tasks:
|
||||||
a = self.menu.addAction(t)
|
a = self.menu.addAction(t)
|
||||||
a.triggered.connect(partial(self.change_task, t))
|
a.triggered.connect(partial(self.change_task, t))
|
||||||
if t == self.active_task:
|
if t == self.active_task:
|
||||||
a.setIcon(QtGui.QIcon.fromTheme("go-next"))
|
a.setIcon(QtGui.QIcon.fromTheme("go-next"))
|
||||||
|
|
||||||
|
self.menu.clear()
|
||||||
|
|
||||||
|
add_tasks(self.tasks.tasks)
|
||||||
|
|
||||||
|
self.menu.addSeparator()
|
||||||
|
add_tasks(self.tasks.jira_tasks)
|
||||||
|
|
||||||
|
self.menu.addSeparator()
|
||||||
|
add_tasks(["Pause"])
|
||||||
|
if self.active_task == "Nothing":
|
||||||
|
add_tasks(["Nothing"])
|
||||||
|
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
|
|
||||||
new_action = self.menu.addAction("New task")
|
new_action = self.menu.addAction("Import Jira task")
|
||||||
new_action.triggered.connect(self.new_task_slot)
|
new_action.triggered.connect(self.new_task_slot)
|
||||||
|
|
||||||
edit_action = self.menu.addAction("Edit tasks")
|
edit_action = self.menu.addAction("Edit tasks")
|
||||||
|
@ -15,7 +15,7 @@ class NewTask(QtWidgets.QDialog):
|
|||||||
|
|
||||||
ok_button = QtWidgets.QPushButton()
|
ok_button = QtWidgets.QPushButton()
|
||||||
ok_button.setText("OK")
|
ok_button.setText("OK")
|
||||||
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok-apply"))
|
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok"))
|
||||||
ok_button.pressed.connect(self.accept)
|
ok_button.pressed.connect(self.accept)
|
||||||
ok_button.setAutoDefault(True)
|
ok_button.setAutoDefault(True)
|
||||||
|
|
||||||
|
20
report.py
20
report.py
@ -2,10 +2,25 @@ from PySide2 import QtCore, QtGui, QtWidgets
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from data import Tasks
|
||||||
|
|
||||||
|
|
||||||
class Report(QtWidgets.QDialog):
|
class Report(QtWidgets.QDialog):
|
||||||
def __init__(self, parent, *args, **kwargs):
|
class TaskItemCompleter(QtWidgets.QStyledItemDelegate):
|
||||||
|
def __init__(self, tasks: Tasks, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._tasks = tasks
|
||||||
|
|
||||||
|
def createEditor(self, parent, *_):
|
||||||
|
editor = QtWidgets.QLineEdit(parent)
|
||||||
|
completer = QtWidgets.QCompleter(self._tasks.all_tasks, parent)
|
||||||
|
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||||
|
editor.setCompleter(completer)
|
||||||
|
return editor
|
||||||
|
|
||||||
|
def __init__(self, tasks: Tasks, parent, *args, **kwargs):
|
||||||
super().__init__(parent, *args, **kwargs)
|
super().__init__(parent, *args, **kwargs)
|
||||||
|
self._tasks = tasks
|
||||||
self._report = None
|
self._report = None
|
||||||
self._report_data = None
|
self._report_data = None
|
||||||
self._changing_items = False
|
self._changing_items = False
|
||||||
@ -20,6 +35,7 @@ class Report(QtWidgets.QDialog):
|
|||||||
self.tableWidget.setColumnCount(3)
|
self.tableWidget.setColumnCount(3)
|
||||||
self.tableWidget.setHorizontalHeaderLabels(["Task", "Start time", "Duration"])
|
self.tableWidget.setHorizontalHeaderLabels(["Task", "Start time", "Duration"])
|
||||||
self.tableWidget.cellChanged.connect(self.cell_changed)
|
self.tableWidget.cellChanged.connect(self.cell_changed)
|
||||||
|
self.tableWidget.setItemDelegateForColumn(0, Report.TaskItemCompleter(self._tasks, self))
|
||||||
self.header = QtWidgets.QHeaderView(QtCore.Qt.Orientation.Horizontal)
|
self.header = QtWidgets.QHeaderView(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.tableWidget.setHorizontalHeader(self.header)
|
self.tableWidget.setHorizontalHeader(self.header)
|
||||||
|
|
||||||
@ -49,7 +65,7 @@ class Report(QtWidgets.QDialog):
|
|||||||
|
|
||||||
ok_button = QtWidgets.QPushButton()
|
ok_button = QtWidgets.QPushButton()
|
||||||
ok_button.setText("OK")
|
ok_button.setText("OK")
|
||||||
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok-apply"))
|
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok"))
|
||||||
ok_button.pressed.connect(self._accept)
|
ok_button.pressed.connect(self._accept)
|
||||||
ok_button.setAutoDefault(True)
|
ok_button.setAutoDefault(True)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class TaskEdit(QtWidgets.QDialog):
|
|||||||
|
|
||||||
ok_button = QtWidgets.QPushButton()
|
ok_button = QtWidgets.QPushButton()
|
||||||
ok_button.setText("OK")
|
ok_button.setText("OK")
|
||||||
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok-apply"))
|
ok_button.setIcon(QtGui.QIcon.fromTheme("dialog-ok"))
|
||||||
ok_button.pressed.connect(self.accept)
|
ok_button.pressed.connect(self.accept)
|
||||||
ok_button.setAutoDefault(True)
|
ok_button.setAutoDefault(True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user