Compare commits
1 Commits
main
...
stop-threa
Author | SHA1 | Date | |
---|---|---|---|
c5ee87535a |
3
Pipfile
3
Pipfile
@ -9,7 +9,8 @@ requests = "~=2.28"
|
|||||||
requests-futures = "~=1.0"
|
requests-futures = "~=1.0"
|
||||||
packaging = "~=23.0"
|
packaging = "~=23.0"
|
||||||
loguru = "~=0.6"
|
loguru = "~=0.6"
|
||||||
browser-cookie3 = "*"
|
browser-cookie3 = "~=0.19"
|
||||||
|
pebble = "~=5.0"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
pyinstaller = "~=5.6"
|
pyinstaller = "~=5.6"
|
||||||
|
17
Pipfile.lock
generated
17
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "236b0d1f3ca2625232ac2e64bf963e188ff5fa85fc570e46f8e877bf13cb98d3"
|
"sha256": "b9f0e0869765dff4160bfe632eb361b03a8b93bd8d9b19e36d371f2ff76c73f1"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -204,6 +204,15 @@
|
|||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==23.2"
|
"version": "==23.2"
|
||||||
},
|
},
|
||||||
|
"pebble": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c8a0659b215ff6dcc974516018fcd95c5c626d8bb9a6668dcfbf85880e6390dc",
|
||||||
|
"sha256:e7f7ecfd0107ab7cec9f3bb411a856c4d7d552202d4e9a8b038e9a64ae31fd8c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==5.0.6"
|
||||||
|
},
|
||||||
"pycryptodomex": {
|
"pycryptodomex": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1",
|
"sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1",
|
||||||
@ -355,11 +364,11 @@
|
|||||||
},
|
},
|
||||||
"pyinstaller-hooks-contrib": {
|
"pyinstaller-hooks-contrib": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:131494f9cfce190aaa66ed82e82c78b2723d1720ce64d012fbaf938f4ab01d35",
|
"sha256:43f3e084ae5f826415399d72ecf2e32328fe859ad7455c7cddfc09f1a61c90b7",
|
||||||
"sha256:51a51ea9e1ae6bd5ffa7ec45eba7579624bf4f2472ff56dba0edc186f6ed46a6"
|
"sha256:8f5ac1acdafde9e553c82242aeae2d2f8fb65ec8e2b0ba416547948108a73e01"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==2024.1"
|
"version": "==2024.2"
|
||||||
},
|
},
|
||||||
"pyproject-hooks": {
|
"pyproject-hooks": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -24,6 +24,7 @@ install_requires =
|
|||||||
packaging
|
packaging
|
||||||
loguru
|
loguru
|
||||||
browser_cookie3
|
browser_cookie3
|
||||||
|
pebble
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
where = src
|
where = src
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from concurrent.futures import Executor
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -10,14 +12,14 @@ from fime.util import get_icon
|
|||||||
|
|
||||||
|
|
||||||
class ImportTask(QtWidgets.QDialog):
|
class ImportTask(QtWidgets.QDialog):
|
||||||
def __init__(self, config: Config, parent, *args, **kwargs):
|
def __init__(self, config: Config, executor: Executor, parent, *args, **kwargs):
|
||||||
super().__init__(parent, *args, **kwargs)
|
super().__init__(parent, *args, **kwargs)
|
||||||
self.setWindowTitle("New Tasks")
|
self.setWindowTitle("New Tasks")
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
self.line_edit = QtWidgets.QLineEdit(self)
|
self.line_edit = QtWidgets.QLineEdit(self)
|
||||||
self.completer = TaskCompleter(config)
|
self.completer = TaskCompleter(config, executor)
|
||||||
self.line_edit.setCompleter(self.completer)
|
self.line_edit.setCompleter(self.completer)
|
||||||
self.line_edit.textChanged.connect(self.completer.update_picker)
|
self.line_edit.textChanged.connect(self.completer.update_picker)
|
||||||
self.line_edit.setFocus()
|
self.line_edit.setFocus()
|
||||||
|
@ -23,7 +23,7 @@ from fime.import_task import ImportTask
|
|||||||
from fime.report import ReportDialog
|
from fime.report import ReportDialog
|
||||||
from fime.settings import Settings
|
from fime.settings import Settings
|
||||||
from fime.task_edit import TaskEdit
|
from fime.task_edit import TaskEdit
|
||||||
from fime.util import get_screen_height, get_icon
|
from fime.util import get_screen_height, get_icon, CompatPool
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
@ -41,7 +41,9 @@ class App:
|
|||||||
|
|
||||||
self.menu = QtWidgets.QMenu(None)
|
self.menu = QtWidgets.QMenu(None)
|
||||||
|
|
||||||
self.import_task = ImportTask(self.config, None)
|
self.executor = CompatPool()
|
||||||
|
|
||||||
|
self.import_task = ImportTask(self.config, self.executor, None)
|
||||||
self.import_task.accepted.connect(self.new_task_imported)
|
self.import_task.accepted.connect(self.new_task_imported)
|
||||||
|
|
||||||
self.taskEdit = TaskEdit(self.tasks, None)
|
self.taskEdit = TaskEdit(self.tasks, None)
|
||||||
@ -50,7 +52,7 @@ class App:
|
|||||||
self.reportDialog = ReportDialog(self.tasks, Report(lcd), None)
|
self.reportDialog = ReportDialog(self.tasks, Report(lcd), None)
|
||||||
self.reportDialog.accepted.connect(self.log_edited)
|
self.reportDialog.accepted.connect(self.log_edited)
|
||||||
|
|
||||||
self.worklogDialog = WorklogDialog(self.config, Worklog(lcd), None)
|
self.worklogDialog = WorklogDialog(self.config, self.executor, Worklog(lcd), None)
|
||||||
self.worklogDialog.accepted.connect(self.log_edited)
|
self.worklogDialog.accepted.connect(self.log_edited)
|
||||||
|
|
||||||
self.settings = Settings(self.config, None)
|
self.settings = Settings(self.config, None)
|
||||||
@ -153,7 +155,7 @@ class App:
|
|||||||
|
|
||||||
menu_items.append(("Settings", partial(self.open_new_dialog, self.settings)))
|
menu_items.append(("Settings", partial(self.open_new_dialog, self.settings)))
|
||||||
menu_items.append(("About", partial(self.open_new_dialog, self.about)))
|
menu_items.append(("About", partial(self.open_new_dialog, self.about)))
|
||||||
menu_items.append(("Close", self.app.quit))
|
menu_items.append(("Close", self.quit_handler))
|
||||||
|
|
||||||
if self.config.flip_menu:
|
if self.config.flip_menu:
|
||||||
menu_items.reverse()
|
menu_items.reverse()
|
||||||
@ -169,17 +171,23 @@ class App:
|
|||||||
action.setIcon(get_icon("go-next"))
|
action.setIcon(get_icon("go-next"))
|
||||||
action.triggered.connect(item[1])
|
action.triggered.connect(item[1])
|
||||||
|
|
||||||
def sigterm_handler(self, signo, _frame):
|
def quit_handler(self, signo=None, _frame=None):
|
||||||
logger.debug(f'handling signal "{signal.strsignal(signo)}"')
|
if signo:
|
||||||
|
logger.debug(f'handling signal "{signal.strsignal(signo)}"')
|
||||||
|
logger.debug("Quitting app")
|
||||||
self.app.quit()
|
self.app.quit()
|
||||||
|
logger.debug("Shutting down HTTP requests executor")
|
||||||
|
self.executor.stop()
|
||||||
|
self.executor.join()
|
||||||
|
logger.debug("HTTP requests executor is shutdown")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
timer = QtCore.QTimer(None)
|
timer = QtCore.QTimer(None)
|
||||||
# interrupt event loop regularly for signal handling
|
# interrupt event loop regularly for signal handling
|
||||||
timer.timeout.connect(lambda: None)
|
timer.timeout.connect(lambda: None)
|
||||||
timer.start(500)
|
timer.start(500)
|
||||||
signal.signal(signal.SIGTERM, self.sigterm_handler)
|
signal.signal(signal.SIGTERM, self.quit_handler)
|
||||||
signal.signal(signal.SIGINT, self.sigterm_handler)
|
signal.signal(signal.SIGINT, self.quit_handler)
|
||||||
if PYSIDE_6:
|
if PYSIDE_6:
|
||||||
self.app.exec()
|
self.app.exec()
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from concurrent.futures import Executor
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from functools import reduce, partial
|
from functools import reduce, partial
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
@ -27,11 +28,11 @@ class TaskCompleter(QtWidgets.QCompleter):
|
|||||||
running = QtCore.Signal()
|
running = QtCore.Signal()
|
||||||
stopped = QtCore.Signal()
|
stopped = QtCore.Signal()
|
||||||
|
|
||||||
def __init__(self, config: Config, parent=None, *args, **kwargs):
|
def __init__(self, config: Config, executor: Executor, parent=None, *args, **kwargs):
|
||||||
super().__init__([], parent, *args, **kwargs)
|
super().__init__([], parent, *args, **kwargs)
|
||||||
self.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
|
self.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
|
||||||
self.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
self.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||||
self.session = FuturesSession()
|
self.session = FuturesSession(executor=executor)
|
||||||
self.session.headers["Accept"] = "application/json"
|
self.session.headers["Accept"] = "application/json"
|
||||||
self.config = config
|
self.config = config
|
||||||
self.picker_url = None
|
self.picker_url = None
|
||||||
|
@ -2,6 +2,7 @@ import enum
|
|||||||
|
|
||||||
import browser_cookie3
|
import browser_cookie3
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from pebble import ProcessPool
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
from fime.config import Config, AuthMethods, Browsers
|
from fime.config import Config, AuthMethods, Browsers
|
||||||
@ -15,6 +16,11 @@ except ImportError:
|
|||||||
import fime.icons
|
import fime.icons
|
||||||
|
|
||||||
|
|
||||||
|
class CompatPool(ProcessPool):
|
||||||
|
def submit(self, fn, *args, **kwargs):
|
||||||
|
return self.schedule(fn, args=args, kwargs=kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_screen_height(qobject):
|
def get_screen_height(qobject):
|
||||||
if hasattr(qobject, "screen"):
|
if hasattr(qobject, "screen"):
|
||||||
return qobject.screen().size().height()
|
return qobject.screen().size().height()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from concurrent.futures import Executor
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from functools import reduce, partial
|
from functools import reduce, partial
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
@ -95,10 +96,11 @@ class WorklogDialog(QtWidgets.QDialog):
|
|||||||
if not self.return_ or self.editor.text() == self.initial_text:
|
if not self.return_ or self.editor.text() == self.initial_text:
|
||||||
self.edit_finished_row.emit(row)
|
self.edit_finished_row.emit(row)
|
||||||
|
|
||||||
def __init__(self, config: Config, worklog: Worklog, parent, *args, **kwargs):
|
def __init__(self, config: Config, executor: Executor, worklog: Worklog, parent, *args, **kwargs):
|
||||||
super().__init__(parent, *args, **kwargs)
|
super().__init__(parent, *args, **kwargs)
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.executor = executor
|
||||||
self.rest = None
|
self.rest = None
|
||||||
|
|
||||||
self._changing_items = False
|
self._changing_items = False
|
||||||
@ -176,7 +178,7 @@ class WorklogDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
def showEvent(self, _):
|
def showEvent(self, _):
|
||||||
# reinitialize to purge caches and pick up config changes
|
# reinitialize to purge caches and pick up config changes
|
||||||
self.rest = WorklogRest(self.config)
|
self.rest = WorklogRest(self.config, self.executor)
|
||||||
self._worklog.date = date.today()
|
self._worklog.date = date.today()
|
||||||
self.update_all()
|
self.update_all()
|
||||||
self.upload_button.setEnabled(False)
|
self.upload_button.setEnabled(False)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future, Executor
|
||||||
from datetime import date, datetime, timedelta, time
|
from datetime import date, datetime, timedelta, time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
@ -16,13 +16,13 @@ from fime.util import Status, add_auth
|
|||||||
|
|
||||||
|
|
||||||
class WorklogRest:
|
class WorklogRest:
|
||||||
def __init__(self, config: Config):
|
def __init__(self, config: Config, executor: Executor):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.user_url = os.path.join(config.jira_url, "rest/api/2/myself")
|
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.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_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_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(executor=executor)
|
||||||
self.session.headers["Accept"] = "application/json"
|
self.session.headers["Accept"] = "application/json"
|
||||||
add_auth(config, self.session)
|
add_auth(config, self.session)
|
||||||
self._user = None
|
self._user = None
|
||||||
|
Loading…
Reference in New Issue
Block a user