Implemented issue verification

This commit is contained in:
Fabian 2021-11-30 15:30:58 +01:00
parent 169d9749af
commit 5a24c0c5eb
5 changed files with 138 additions and 79 deletions

View File

@ -12,8 +12,6 @@ except ImportError:
from PySide2 import QtCore, QtWidgets from PySide2 import QtCore, QtWidgets
PYSIDE_6 = False PYSIDE_6 = False
# noinspection PyUnresolvedReferences
import fime.icons
from fime.data import Tasks, Log, Data, LogCommentsData from fime.data import Tasks, Log, Data, LogCommentsData
from fime.exceptions import FimeException from fime.exceptions import FimeException
from fime.import_task import ImportTask from fime.import_task import ImportTask

View File

@ -1,8 +1,13 @@
import enum
try: try:
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
except ImportError: except ImportError:
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
# noinspection PyUnresolvedReferences
import fime.icons
def get_screen_height(qobject): def get_screen_height(qobject):
if hasattr(qobject, "screen"): if hasattr(qobject, "screen"):
@ -11,6 +16,7 @@ def get_screen_height(qobject):
print("unable to detect screen height falling back to default value of 1080") print("unable to detect screen height falling back to default value of 1080")
return 1080 return 1080
def get_screen_width(qobject): def get_screen_width(qobject):
if hasattr(qobject, "screen"): if hasattr(qobject, "screen"):
return qobject.screen().size().width() return qobject.screen().size().width()
@ -37,3 +43,9 @@ class EditStartedDetector(QtWidgets.QStyledItemDelegate):
editor.editingFinished.connect(self.editFinished) editor.editingFinished.connect(self.editFinished)
self.editStarted.emit() self.editStarted.emit()
return editor return editor
class Status(enum.Enum):
PROGRESS = enum.auto()
OK = enum.auto()
ERROR = enum.auto()

View File

@ -1,12 +1,11 @@
import enum
from datetime import datetime
from functools import reduce, partial from functools import reduce, partial
from typing import Optional, List, Tuple from typing import Optional, List, Tuple
from fime.data import Worklog from fime.data import Worklog
from fime.progressindicator import ProgressIndicator from fime.progressindicator import ProgressIndicator
from fime.task_completer import TaskCompleter from fime.task_completer import TaskCompleter
from fime.util import get_icon, get_screen_height, EditStartedDetector from fime.util import get_icon, EditStartedDetector, Status
from fime.worklog_rest import WorklogRest
try: try:
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
@ -14,8 +13,6 @@ except ImportError:
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
from fime.util import get_screen_width from fime.util import get_screen_width
# noinspection PyUnresolvedReferences
import fime.icons
class WorklogDialog(QtWidgets.QDialog): class WorklogDialog(QtWidgets.QDialog):
@ -83,6 +80,7 @@ class WorklogDialog(QtWidgets.QDialog):
completer.setFilterMode(QtCore.Qt.MatchFlag.MatchContains) completer.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
editor.setCompleter(completer) editor.setCompleter(completer)
editor.textChanged.connect(completer.update_picker)
return editor return editor
@QtCore.Slot() @QtCore.Slot()
@ -94,23 +92,20 @@ class WorklogDialog(QtWidgets.QDialog):
if not self.return_: if not self.return_:
self.edit_finished_row.emit(row) self.edit_finished_row.emit(row)
class Status(enum.Enum):
PROGRESS = enum.auto()
OK = enum.auto()
ERROR = enum.auto()
def __init__(self, config, parent, *args, **kwargs): def __init__(self, config, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs) super().__init__(parent, *args, **kwargs)
self.setWindowTitle("Worklog") self.rest = WorklogRest(config)
self._changing_items = False self._changing_items = False
self._worklog: Optional[Worklog] = None self._worklog: Optional[Worklog] = None
self._worklog_data: List[List[str]] = [] self._worklog_data: List[List[str]] = []
self._status: List[Tuple[WorklogDialog.Status, str]] = [] self._status: List[Tuple[Status, str]] = []
self.row_height = None self.row_height = None
self._focussed = False self._focussed = False
self.setWindowTitle("Worklog")
self.tableWidget = WorklogDialog.TabTable(self) self.tableWidget = WorklogDialog.TabTable(self)
self.tableWidget.setColumnCount(3) self.tableWidget.setColumnCount(3)
self.tableWidget.setHorizontalHeaderLabels(["Task", "Comment", "Duration"]) self.tableWidget.setHorizontalHeaderLabels(["Task", "Comment", "Duration"])
@ -171,18 +166,31 @@ class WorklogDialog(QtWidgets.QDialog):
layout.addLayout(alayout) layout.addLayout(alayout)
self.setLayout(layout) self.setLayout(layout)
self.update_timer = QtCore.QTimer(self)
self.update_timer.setInterval(500)
self.update_timer.timeout.connect(self.update_statuses)
def set_data(self, worklog: Worklog): def set_data(self, worklog: Worklog):
self._worklog = worklog self._worklog = worklog
self.refresh_table() self.refresh_table()
self.update_title() self.update_title()
self.update_prev_next() self.update_prev_next()
self.update_statuses()
self.update_timer.start()
def update_status(self, row, new_status, status_text="Could not find specified issue"): def update_statuses(self):
self._status[row] = (new_status, status_text) issue_keys = []
for row in self._worklog_data[:-1]:
issue_keys.append(row[0].split()[0])
self._status = self.rest.get_issues_state(issue_keys)
for row, _ in enumerate(self._status):
self.update_status_view(row) self.update_status_view(row)
all_ok = reduce(lambda acc, it: acc and it[0] is WorklogDialog.Status.OK, self._status, True) all_ok = reduce(lambda acc, it: acc and it[0] is Status.OK, self._status, True)
if all_ok: if all_ok:
self.upload_button.setEnabled(True) self.upload_button.setEnabled(True)
all_done = reduce(lambda acc, it: acc and it[0] is not Status.PROGRESS, self._status, True)
if all_done:
self.update_timer.stop()
def save(self): def save(self):
self._worklog.worklog = self._worklog_data self._worklog.worklog = self._worklog_data
@ -193,7 +201,7 @@ class WorklogDialog(QtWidgets.QDialog):
def refresh_table(self): def refresh_table(self):
self._worklog_data = self._worklog.worklog self._worklog_data = self._worklog.worklog
for i, _ in enumerate(self._worklog_data[:-1]): for i, _ in enumerate(self._worklog_data[:-1]):
self._status.append((WorklogDialog.Status.PROGRESS, "Fetching")) self._status.append((Status.PROGRESS, "Fetching"))
self._changing_items = True self._changing_items = True
if not self.row_height: if not self.row_height:
self.tableWidget.setRowCount(1) self.tableWidget.setRowCount(1)
@ -239,15 +247,15 @@ class WorklogDialog(QtWidgets.QDialog):
self.move(x, y) self.move(x, y)
def update_status_view(self, row): def update_status_view(self, row):
if self._status[row][0] is WorklogDialog.Status.PROGRESS: if self._status[row][0] is Status.PROGRESS:
icon = ProgressIndicator(self) icon = ProgressIndicator(self)
icon.setMaximumSize(QtCore.QSize(self.row_height * 0.75, self.row_height * 0.75)) icon.setMaximumSize(QtCore.QSize(self.row_height * 0.75, self.row_height * 0.75))
icon.setAnimationDelay(70) icon.setAnimationDelay(70)
icon.startAnimation() icon.startAnimation()
else: else:
if self._status[row][0] is WorklogDialog.Status.OK: if self._status[row][0] is Status.OK:
item_name = "dialog-ok" item_name = "dialog-ok"
elif self._status[row][0] is WorklogDialog.Status.ERROR: elif self._status[row][0] is Status.ERROR:
item_name = "edit-delete-remove" item_name = "edit-delete-remove"
icon = QtWidgets.QLabel() icon = QtWidgets.QLabel()
icon.setPixmap(get_icon(item_name).pixmap(self.row_height * 0.75, self.row_height * 0.75)) icon.setPixmap(get_icon(item_name).pixmap(self.row_height * 0.75, self.row_height * 0.75))
@ -308,7 +316,8 @@ class WorklogDialog(QtWidgets.QDialog):
return return
self._worklog_data[row][0] = self.tableWidget.item(row, 0).text() self._worklog_data[row][0] = self.tableWidget.item(row, 0).text()
self.save() self.save()
self.update_status(row, WorklogDialog.Status.PROGRESS, "Fetching") self.update_statuses()
self.update_timer.start()
self.refresh_table() self.refresh_table()
@QtCore.Slot() @QtCore.Slot()

View File

@ -1,56 +0,0 @@
import os
from datetime import date, datetime
import requests
from fime.config import Config
class Worklog:
def __init__(self, config: Config):
self.config = config
self.user_url = os.path.join(config.jira_url, "rest/api/2/myself")
self.issue_url = os.path.join(config.jira_url, "rest/api/2/issue/{}")
self.worklog_url = os.path.join(config.jira_url, "rest/api/2/issue/{}/worklog")
self.worklog_update_url = os.path.join(config.jira_url, "rest/api/2/issue/{issue}/worklog/{worklog}")
self.user = self.get_user()
def get_user(self):
resp = requests.get(self.user_url,
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
user = resp.json()["key"]
print(user)
return user
def get(self, issue_key: str, date: date):
resp = requests.get(self.issue_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
if resp.status_code != 200:
raise RuntimeError("issue does not exist")
resp = requests.get(self.worklog_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
worklogs = resp.json()["worklogs"]
found = False
for log in worklogs:
if log["author"]["key"] == self.user \
and datetime.strptime(log["started"], "%Y-%m-%dT%H:%M:%S.%f%z").date() == date:
print(log["id"])
print(log["comment"])
found = True
break
print(found)

96
src/fime/worklog_rest.py Normal file
View File

@ -0,0 +1,96 @@
import os
import traceback
from concurrent.futures import Future
from datetime import date, datetime
from functools import partial
from threading import Lock
from typing import List, Dict, Tuple
import requests
from requests_futures.sessions import FuturesSession
from fime.config import Config
from fime.exceptions import FimeException
from fime.util import Status
class WorklogRest:
def __init__(self, config: Config):
self.config = config
self.user_url = os.path.join(config.jira_url, "rest/api/2/myself")
self.issue_url = os.path.join(config.jira_url, "rest/api/2/issue/{}")
self.worklog_url = os.path.join(config.jira_url, "rest/api/2/issue/{}/worklog")
self.worklog_update_url = os.path.join(config.jira_url, "rest/api/2/issue/{issue}/worklog/{worklog}")
self.session = FuturesSession()
self.user = self._req_user()
self._issue_state: Dict[str, Tuple[Status, str]] = {}
self._issue_state_lock = Lock()
def _req_user(self):
return self.session.get(
self.user_url,
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
def _get_user(self):
try:
return self.user.result().json()["key"]
except Exception:
raise FimeException("Could not get user key:\n" + traceback.format_exc())
def get_issues_state(self, issue_keys: List[str]):
ret = []
with self._issue_state_lock:
for issue_key in issue_keys:
if issue_key not in self._issue_state:
self._issue_state[issue_key] = (Status.PROGRESS, "Fetching")
self._req_issue(issue_key)
ret.append(self._issue_state[issue_key])
return ret
def _req_issue(self, issue_key: str):
future = self.session.get(self.issue_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
future.add_done_callback(partial(self._resp_issue, issue_key))
def _resp_issue(self, issue_key: str, future: Future):
resp: requests.Response = future.result()
with self._issue_state_lock:
if resp.status_code == 200:
hint = f'{issue_key} {resp.json()["fields"]["summary"]}'
self._issue_state[issue_key] = (Status.OK, hint)
else:
self._issue_state[issue_key] = (Status.ERROR, "Could not find specified issue")
def get(self, issue_key: str, date: date):
resp = requests.get(self.issue_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
if resp.status_code != 200:
raise RuntimeError("issue does not exist")
resp = requests.get(self.worklog_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
worklogs = resp.json()["worklogs"]
found = False
for log in worklogs:
if log["author"]["key"] == self.user \
and datetime.strptime(log["started"], "%Y-%m-%dT%H:%M:%S.%f%z").date() == date:
print(log["id"])
print(log["comment"])
found = True
break
print(found)