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
PYSIDE_6 = False
# noinspection PyUnresolvedReferences
import fime.icons
from fime.data import Tasks, Log, Data, LogCommentsData
from fime.exceptions import FimeException
from fime.import_task import ImportTask

View File

@ -1,8 +1,13 @@
import enum
try:
from PySide6 import QtCore, QtGui, QtWidgets
except ImportError:
from PySide2 import QtCore, QtGui, QtWidgets
# noinspection PyUnresolvedReferences
import fime.icons
def get_screen_height(qobject):
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")
return 1080
def get_screen_width(qobject):
if hasattr(qobject, "screen"):
return qobject.screen().size().width()
@ -37,3 +43,9 @@ class EditStartedDetector(QtWidgets.QStyledItemDelegate):
editor.editingFinished.connect(self.editFinished)
self.editStarted.emit()
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 typing import Optional, List, Tuple
from fime.data import Worklog
from fime.progressindicator import ProgressIndicator
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:
from PySide6 import QtCore, QtGui, QtWidgets
@ -14,8 +13,6 @@ except ImportError:
from PySide2 import QtCore, QtGui, QtWidgets
from fime.util import get_screen_width
# noinspection PyUnresolvedReferences
import fime.icons
class WorklogDialog(QtWidgets.QDialog):
@ -83,6 +80,7 @@ class WorklogDialog(QtWidgets.QDialog):
completer.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
editor.setCompleter(completer)
editor.textChanged.connect(completer.update_picker)
return editor
@QtCore.Slot()
@ -94,23 +92,20 @@ class WorklogDialog(QtWidgets.QDialog):
if not self.return_:
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):
super().__init__(parent, *args, **kwargs)
self.setWindowTitle("Worklog")
self.rest = WorklogRest(config)
self._changing_items = False
self._worklog: Optional[Worklog] = None
self._worklog_data: List[List[str]] = []
self._status: List[Tuple[WorklogDialog.Status, str]] = []
self._status: List[Tuple[Status, str]] = []
self.row_height = None
self._focussed = False
self.setWindowTitle("Worklog")
self.tableWidget = WorklogDialog.TabTable(self)
self.tableWidget.setColumnCount(3)
self.tableWidget.setHorizontalHeaderLabels(["Task", "Comment", "Duration"])
@ -171,18 +166,31 @@ class WorklogDialog(QtWidgets.QDialog):
layout.addLayout(alayout)
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):
self._worklog = worklog
self.refresh_table()
self.update_title()
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"):
self._status[row] = (new_status, status_text)
self.update_status_view(row)
all_ok = reduce(lambda acc, it: acc and it[0] is WorklogDialog.Status.OK, self._status, True)
def update_statuses(self):
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)
all_ok = reduce(lambda acc, it: acc and it[0] is Status.OK, self._status, True)
if all_ok:
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):
self._worklog.worklog = self._worklog_data
@ -193,7 +201,7 @@ class WorklogDialog(QtWidgets.QDialog):
def refresh_table(self):
self._worklog_data = self._worklog.worklog
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
if not self.row_height:
self.tableWidget.setRowCount(1)
@ -239,15 +247,15 @@ class WorklogDialog(QtWidgets.QDialog):
self.move(x, y)
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.setMaximumSize(QtCore.QSize(self.row_height * 0.75, self.row_height * 0.75))
icon.setAnimationDelay(70)
icon.startAnimation()
else:
if self._status[row][0] is WorklogDialog.Status.OK:
if self._status[row][0] is Status.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"
icon = QtWidgets.QLabel()
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
self._worklog_data[row][0] = self.tableWidget.item(row, 0).text()
self.save()
self.update_status(row, WorklogDialog.Status.PROGRESS, "Fetching")
self.update_statuses()
self.update_timer.start()
self.refresh_table()
@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)