Implemented issue verification
This commit is contained in:
parent
169d9749af
commit
5a24c0c5eb
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
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 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:
|
||||
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()
|
||||
|
@ -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
96
src/fime/worklog_rest.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user