Fixed bugs and added last comment auto completion

This commit is contained in:
Fabian 2021-11-30 23:06:05 +01:00
parent 0a01146227
commit 3189d9a7a5
2 changed files with 79 additions and 49 deletions

View File

@ -96,7 +96,8 @@ class WorklogDialog(QtWidgets.QDialog):
def __init__(self, config: Config, worklog: Worklog, parent, *args, **kwargs): def __init__(self, config: Config, worklog: Worklog, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs) super().__init__(parent, *args, **kwargs)
self.rest = WorklogRest(config) self.config = config
self.rest = WorklogRest(self.config)
self._changing_items = False self._changing_items = False
self._worklog = worklog self._worklog = worklog
@ -172,8 +173,10 @@ class WorklogDialog(QtWidgets.QDialog):
self.update_timer.timeout.connect(self.update_statuses) self.update_timer.timeout.connect(self.update_statuses)
def showEvent(self, _): def showEvent(self, _):
self.rest.purge_cache() # reinitialize to purge caches
self.rest = WorklogRest(self.config)
self.update_all() self.update_all()
self.upload_button.setEnabled(False)
def update_all(self): def update_all(self):
self.refresh_table() self.refresh_table()
@ -187,10 +190,13 @@ class WorklogDialog(QtWidgets.QDialog):
for row in self._worklog_data[:-1]: for row in self._worklog_data[:-1]:
issue_keys.append(row[0].split()[0]) issue_keys.append(row[0].split()[0])
old_statuses = self._statuses old_statuses = self._statuses
self._statuses = self.rest.get_issues_state(issue_keys) self._statuses = self.rest.get_issues_state(issue_keys, self._worklog.date)
for row, status in enumerate(self._statuses): for row, status in enumerate(self._statuses):
if len(old_statuses) != len(self._statuses) or old_statuses[row][0] != status[0]: if len(old_statuses) != len(self._statuses) or old_statuses[row][0] != status[0]:
self.update_status_view(row) self.update_status_view(row)
if not self._worklog_data[row][1] and status[2]:
self._worklog_data[row][1] = status[2]
self.tableWidget.cellWidget(row, 1).setPlainText(status[2])
all_ok = reduce(lambda acc, it: acc and it[0] is Status.OK, self._statuses, True) all_ok = reduce(lambda acc, it: acc and it[0] is Status.OK, self._statuses, True)
if all_ok: if all_ok:
self.upload_button.setEnabled(True) self.upload_button.setEnabled(True)

View File

@ -6,7 +6,7 @@ from datetime import date, datetime, timedelta, time
from functools import partial from functools import partial
from textwrap import dedent from textwrap import dedent
from threading import Lock from threading import Lock
from typing import List, Dict, Tuple from typing import List, Dict, Tuple, Optional
import requests import requests
from requests_futures.sessions import FuturesSession from requests_futures.sessions import FuturesSession
@ -25,9 +25,11 @@ class WorklogRest:
self.worklog_update_url = os.path.join(config.jira_url, "rest/api/2/issue/{issue_key}/worklog/{worklog_id}") self.worklog_update_url = os.path.join(config.jira_url, "rest/api/2/issue/{issue_key}/worklog/{worklog_id}")
self.session = FuturesSession() self.session = FuturesSession()
self._user = None self._user = None
self._user_future = self._req_user()
self._issue_state: Dict[str, Tuple[Status, str]] = dict() self._issue_state: Dict[str, Tuple[Status, str]] = dict()
self._issue_state_lock = Lock() self._issue_previous_comments: Dict[str, str] = dict()
self._req_user() self._issue_worklog_id: Dict[str, str] = dict()
self._issues_lock = Lock()
def _req_user(self): def _req_user(self):
future = self.session.get( future = self.session.get(
@ -38,6 +40,7 @@ class WorklogRest:
}, },
) )
future.add_done_callback(self._resp_user) future.add_done_callback(self._resp_user)
return future
def _resp_user(self, future): def _resp_user(self, future):
try: try:
@ -46,20 +49,21 @@ class WorklogRest:
print("Could not get user key:\n", file=sys.stderr) print("Could not get user key:\n", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr)
def get_issues_state(self, issue_keys: List[str]): def get_issues_state(self, issue_keys: List[str], pdate: date) -> List[Tuple[Status, str, Optional[str]]]:
ret = [] ret = []
with self._issue_state_lock: with self._issues_lock:
for issue_key in issue_keys: for issue_key in issue_keys:
if issue_key not in self._issue_state: if issue_key not in self._issue_state:
self._issue_state[issue_key] = (Status.PROGRESS, "Working") self._issue_state[issue_key] = (Status.PROGRESS, "Working")
self._req_issue(issue_key) self._req_issue(issue_key, pdate)
ret.append(self._issue_state[issue_key]) if issue_key in self._issue_previous_comments:
prev_comment = self._issue_previous_comments[issue_key]
else:
prev_comment = None
ret.append((*self._issue_state[issue_key], prev_comment))
return ret return ret
def purge_cache(self): def _req_issue(self, issue_key: str, pdate :date):
self._issue_state = dict()
def _req_issue(self, issue_key: str):
future = self.session.get( future = self.session.get(
self.issue_url.format(issue_key), self.issue_url.format(issue_key),
headers={ headers={
@ -67,22 +71,60 @@ class WorklogRest:
"Accept": "application/json", "Accept": "application/json",
}, },
) )
future.add_done_callback(partial(self._resp_issue, issue_key)) future.add_done_callback(partial(self._resp_issue, issue_key, pdate))
def _resp_issue(self, issue_key: str, future: Future): def _resp_issue(self, issue_key: str, pdate: date, future: Future):
resp: requests.Response = future.result() resp: requests.Response = future.result()
with self._issue_state_lock: with self._issues_lock:
if resp.status_code == 200: if resp.status_code == 200:
hint = f'{issue_key} {resp.json()["fields"]["summary"]}' issue_title = f'{issue_key} {resp.json()["fields"]["summary"]}'
self._issue_state[issue_key] = (Status.OK, hint) self._req_worklog_check(issue_key, issue_title, pdate)
else: else:
self._issue_state[issue_key] = (Status.ERROR, "Could not find specified issue") self._issue_state[issue_key] = (Status.ERROR, "Could not find specified issue")
def _req_worklog_check(self, issue_key: str, issue_title: str, pdate: date):
future = self.session.get(
self.worklog_url.format(issue_key),
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
future.add_done_callback(partial(self._resp_worklog_check, issue_key, issue_title, pdate))
def _resp_worklog_check(self, issue_key: str, issue_title: str, pdate: date, future: Future):
resp = future.result()
worklogs = resp.json()["worklogs"]
worklogs = sorted(
worklogs, key=lambda x: datetime.strptime(x["started"], "%Y-%m-%dT%H:%M:%S.%f%z"), reverse=True)
self._user_future.result()
if not self._user:
with self._issues_lock:
self._issue_state[issue_key] = (Status.OK, issue_title)
return
worklog_found = False
with self._issues_lock:
for log in worklogs:
if log["author"]["key"] == self._user:
if datetime.strptime(log["started"], "%Y-%m-%dT%H:%M:%S.%f%z").date() == pdate:
self._issue_worklog_id[issue_key] = log["id"]
worklog_found = True
else:
self._issue_previous_comments[issue_key] = log["comment"].strip()
worklog_found = True
break
if worklog_found:
print(f"Found existing worklog for issue {issue_key}")
else:
print(f"Did not find existing worklog for issue {issue_key}")
self._issue_state[issue_key] = (Status.OK, issue_title)
def _upload_sanity_check(self, issue_keys: List[str]): def _upload_sanity_check(self, issue_keys: List[str]):
if not self._user: if not self._user:
raise FimeException("Could not get user key") raise FimeException("Could not get user key")
# lock is held by caller
for issue_key in issue_keys: for issue_key in issue_keys:
if issue_key not in self._issue_state or self._issue_state[issue_key] is not Status.OK: if issue_key not in self._issue_state or self._issue_state[issue_key][0] is not Status.OK:
raise FimeException(f"Issue with key {issue_key} in unexpected state") raise FimeException(f"Issue with key {issue_key} in unexpected state")
def upload(self, issues: List[Tuple[str, str, timedelta]], pdate: date): def upload(self, issues: List[Tuple[str, str, timedelta]], pdate: date):
@ -92,33 +134,15 @@ class WorklogRest:
timedelta(seconds=300), timedelta(seconds=300),
), ... ] ), ... ]
""" """
with self._issues_lock:
self._upload_sanity_check(list(map(lambda x: x[0], issues))) self._upload_sanity_check(list(map(lambda x: x[0], issues)))
for issue in issues: for issue in issues:
future = self.session.get( issue_key, comment, time_spent = issue
self.worklog_url.format(issue[0]), if issue_key in self._issue_worklog_id:
headers={ self._worklog_update(issue_key, self._issue_worklog_id[issue_key], comment, time_spent, pdate)
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
}
)
future.add_done_callback(partial(self._worklog_check_resp, issue[0], issue[1], issue[2], pdate))
self._issue_state[issue[0]] = (Status.PROGRESS, "Working")
def _worklog_check_resp(self, issue_key: str, comment: str, time_spent: timedelta, pdate: date, future: Future):
resp = future.result()
worklogs = resp.json()["worklogs"]
worklog_id = None
for log in worklogs:
if log["author"]["key"] == self._user \
and datetime.strptime(log["started"], "%Y-%m-%dT%H:%M:%S.%f%z").date() == pdate:
worklog_id = log["id"]
break
if worklog_id:
print(f"Found existing worklog for issue {issue_key} with id {worklog_id}")
self._worklog_update(issue_key, worklog_id, comment, time_spent, pdate)
else: else:
print(f"Did not find existing worklog for issue {issue_key}")
self._worklog_create(issue_key, comment, time_spent, pdate) self._worklog_create(issue_key, comment, time_spent, pdate)
self._issue_state[issue[0]] = (Status.PROGRESS, "Working")
def _worklog_create(self, issue_key: str, comment: str, time_spent: timedelta, pdate: date): def _worklog_create(self, issue_key: str, comment: str, time_spent: timedelta, pdate: date):
future = self.session.post( future = self.session.post(
@ -156,8 +180,8 @@ class WorklogRest:
def _worklog_resp(self, issue_key: str, future: Future): def _worklog_resp(self, issue_key: str, future: Future):
resp: requests.Response = future.result() resp: requests.Response = future.result()
with self._issue_state_lock: with self._issues_lock:
if resp.status_code == 200: if resp.status_code in (200, 201):
self._issue_state[issue_key] = (Status.OK, "Successfully uploaded") self._issue_state[issue_key] = (Status.OK, "Successfully uploaded")
print(f"Successfully uploaded issue {issue_key}") print(f"Successfully uploaded issue {issue_key}")
else: else: