fime/task_completer.py

126 lines
4.7 KiB
Python
Raw Normal View History

2021-11-16 18:57:09 +00:00
import os
import sys
import traceback
from functools import reduce
from queue import Queue, Empty
from urllib.parse import urlparse, parse_qs
from PySide2 import QtCore
from PySide2.QtCore import QTimer
from PySide2.QtWidgets import QCompleter
from requests_futures.sessions import FuturesSession
from config import Config
class TaskCompleter(QCompleter):
def __init__(self, parent=None, *args, **kwargs):
super().__init__([], parent, *args, **kwargs)
self.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
self.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.session = FuturesSession()
self.config = Config()
self.picker_url = os.path.join(self.config.jira_url, "rest/api/2/issue/picker")
self.search_url = os.path.join(self.config.jira_url, "rest/api/2/search")
self.text = ""
self.response_text = ""
self.model_string_list = []
self.escalate = False
self.update_timer = QTimer(self)
self.update_timer.timeout.connect(self.process_response)
self.update_timer.setInterval(250)
self.queue = Queue()
@QtCore.Slot()
def process_response(self):
try:
while not self.queue.empty():
result_dict = self.queue.get_nowait()
if result_dict["response_text"] == self.text:
if self.text == self.response_text:
self.response_text = result_dict["response_text"]
self.model_string_list = result_dict["result"]
else:
self.model_string_list += result_dict["result"]
self.model().setStringList(self.model_string_list)
self.complete()
except Empty:
return
@QtCore.Slot(str)
def update_picker(self, text):
self.text = text
if self.text == self.currentCompletion():
# do not update, after auto completion was used
return
if self.escalate:
self.update_search()
if not self.update_timer.isActive():
self.update_timer.start()
future = self.session.get(
url=self.picker_url,
params={
"query": self.text
},
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
future.add_done_callback(self.picker_response_callback)
def picker_response_callback(self, future):
try:
result = future.result()
parsed = urlparse(result.request.url)
response_text = parse_qs(parsed.query)["query"][0]
issues = reduce(lambda x, y: x + (y["issues"]), result.json()["sections"], [])
extracted = list(map(lambda x: f"{x['key']} {x['summaryText']}", issues))
if extracted:
self.queue.put({
"response_text": response_text,
"result": extracted,
})
else:
if not self.escalate:
print("No picker results. Escalating")
self.escalate = True
self.update_search()
except Exception:
print("Ignoring exception, as it only breaks autocompletion:", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
return
def update_search(self):
for jql in [f"text = {self.text}", f"key = {self.text}"]:
future = self.session.get(
url=self.search_url,
params={
"jql": jql,
"maxResults": 10,
"fields": "key,summary",
},
headers={
"Authorization": f"Bearer {self.config.jira_token}",
"Accept": "application/json",
},
)
future.add_done_callback(self.search_response_callback)
def search_response_callback(self, future):
try:
result = future.result()
json_result = result.json()
parsed = urlparse(result.request.url)
response_text = parse_qs(parsed.query)["jql"][0].split()[2]
if "issues" in json_result:
extracted = list(map(lambda x: f"{x['key']} {x['fields']['summary']}", json_result["issues"]))
self.queue.put({
"response_text": response_text,
"result": extracted,
})
except Exception:
print("Ignoring exception, as it only breaks autocompletion:", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
return