Compare commits
No commits in common. "32621bcc87ea665e1ab9972f1f7aa901c3770b32" and "169d9749af2cd48d6bb4454d196265e041d1b9fc" have entirely different histories.
32621bcc87
...
169d9749af
@ -12,6 +12,8 @@ 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,13 +1,8 @@
|
||||
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"):
|
||||
@ -16,7 +11,6 @@ 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()
|
||||
@ -43,9 +37,3 @@ 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,11 +1,12 @@
|
||||
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, EditStartedDetector, Status
|
||||
from fime.worklog_rest import WorklogRest
|
||||
from fime.util import get_icon, get_screen_height, EditStartedDetector
|
||||
|
||||
try:
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
@ -13,6 +14,8 @@ except ImportError:
|
||||
from PySide2 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from fime.util import get_screen_width
|
||||
# noinspection PyUnresolvedReferences
|
||||
import fime.icons
|
||||
|
||||
|
||||
class WorklogDialog(QtWidgets.QDialog):
|
||||
@ -80,7 +83,6 @@ 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()
|
||||
@ -92,20 +94,23 @@ 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.rest = WorklogRest(config)
|
||||
self.setWindowTitle("Worklog")
|
||||
|
||||
self._changing_items = False
|
||||
self._worklog: Optional[Worklog] = None
|
||||
self._worklog_data: List[List[str]] = []
|
||||
self._statuses: List[Tuple[Status, str]] = []
|
||||
self._status: List[Tuple[WorklogDialog.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"])
|
||||
@ -166,36 +171,18 @@ 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.update_all()
|
||||
|
||||
def update_all(self):
|
||||
self.refresh_table()
|
||||
self.update_title()
|
||||
self.update_prev_next()
|
||||
self.update_statuses()
|
||||
self.update_timer.start()
|
||||
|
||||
def update_statuses(self):
|
||||
issue_keys = []
|
||||
for row in self._worklog_data[:-1]:
|
||||
issue_keys.append(row[0].split()[0])
|
||||
old_statuses = self._statuses
|
||||
self._statuses = self.rest.get_issues_state(issue_keys)
|
||||
for row, status in enumerate(self._statuses):
|
||||
if len(old_statuses) != len(self._statuses) or old_statuses[row][0] != status[0]:
|
||||
self.update_status_view(row)
|
||||
all_ok = reduce(lambda acc, it: acc and it[0] is Status.OK, self._statuses, True)
|
||||
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)
|
||||
if all_ok:
|
||||
self.upload_button.setEnabled(True)
|
||||
all_done = reduce(lambda acc, it: acc and it[0] is not Status.PROGRESS, self._statuses, True)
|
||||
if all_done:
|
||||
self.update_timer.stop()
|
||||
|
||||
def save(self):
|
||||
self._worklog.worklog = self._worklog_data
|
||||
@ -205,6 +192,8 @@ 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._changing_items = True
|
||||
if not self.row_height:
|
||||
self.tableWidget.setRowCount(1)
|
||||
@ -223,6 +212,7 @@ class WorklogDialog(QtWidgets.QDialog):
|
||||
self.tableWidget.setItem(row, 1, item1)
|
||||
item1.setFlags(item1.flags() & QtCore.Qt.ItemIsEnabled)
|
||||
else:
|
||||
self.update_status_view(row)
|
||||
text_edit = WorklogDialog.TableTextEdit(self._worklog_data[row][1], row, self)
|
||||
text_edit.textChanged.connect(self.on_resize)
|
||||
text_edit.setFrameStyle(QtWidgets.QFrame.NoFrame)
|
||||
@ -249,19 +239,19 @@ class WorklogDialog(QtWidgets.QDialog):
|
||||
self.move(x, y)
|
||||
|
||||
def update_status_view(self, row):
|
||||
if self._statuses[row][0] is Status.PROGRESS:
|
||||
if self._status[row][0] is WorklogDialog.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._statuses[row][0] is Status.OK:
|
||||
if self._status[row][0] is WorklogDialog.Status.OK:
|
||||
item_name = "dialog-ok"
|
||||
elif self._statuses[row][0] is Status.ERROR:
|
||||
elif self._status[row][0] is WorklogDialog.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))
|
||||
icon.setToolTip(self._statuses[row][1])
|
||||
icon.setToolTip(self._status[row][1])
|
||||
layout = QtWidgets.QHBoxLayout()
|
||||
layout.addWidget(icon, alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
layout.setContentsMargins(self.row_height * 0.1, self.row_height * 0.1,
|
||||
@ -296,15 +286,21 @@ class WorklogDialog(QtWidgets.QDialog):
|
||||
def previous(self):
|
||||
self.save()
|
||||
self._worklog.previous()
|
||||
self.update_title()
|
||||
self._focussed = False
|
||||
self.update_all()
|
||||
self.refresh_table()
|
||||
self.center_dialog()
|
||||
self.update_prev_next()
|
||||
|
||||
@QtCore.Slot()
|
||||
def next(self):
|
||||
self.save()
|
||||
self._worklog.next()
|
||||
self.update_title()
|
||||
self._focussed = False
|
||||
self.update_all()
|
||||
self.refresh_table()
|
||||
self.center_dialog()
|
||||
self.update_prev_next()
|
||||
|
||||
@QtCore.Slot()
|
||||
def cell_changed(self, row, _):
|
||||
@ -312,7 +308,8 @@ class WorklogDialog(QtWidgets.QDialog):
|
||||
return
|
||||
self._worklog_data[row][0] = self.tableWidget.item(row, 0).text()
|
||||
self.save()
|
||||
self.update_all()
|
||||
self.update_status(row, WorklogDialog.Status.PROGRESS, "Fetching")
|
||||
self.refresh_table()
|
||||
|
||||
@QtCore.Slot()
|
||||
def text_edit_changed(self, text, row):
|
||||
|
56
src/fime/worklog_.py
Normal file
56
src/fime/worklog_.py
Normal file
@ -0,0 +1,56 @@
|
||||
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)
|
||||
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
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