From 5bf8f423b2a02c998d0d665839a43d7e4cdca409 Mon Sep 17 00:00:00 2001 From: Vincent A Date: Sun, 20 May 2018 19:26:37 +0200 Subject: [PATCH] qboobtracker: new CapBugTracker app --- scripts/qboobtracker | 28 +++ weboob/applications/qboobtracker/__init__.py | 3 + .../applications/qboobtracker/main_window.py | 174 ++++++++++++++++++ .../applications/qboobtracker/qboobtracker.py | 46 +++++ weboob/applications/qboobtracker/ui/Makefile | 13 ++ .../applications/qboobtracker/ui/__init__.py | 0 .../qboobtracker/ui/main_window.ui | 117 ++++++++++++ 7 files changed, 381 insertions(+) create mode 100755 scripts/qboobtracker create mode 100644 weboob/applications/qboobtracker/__init__.py create mode 100644 weboob/applications/qboobtracker/main_window.py create mode 100644 weboob/applications/qboobtracker/qboobtracker.py create mode 100644 weboob/applications/qboobtracker/ui/Makefile create mode 100644 weboob/applications/qboobtracker/ui/__init__.py create mode 100644 weboob/applications/qboobtracker/ui/main_window.ui diff --git a/scripts/qboobtracker b/scripts/qboobtracker new file mode 100755 index 0000000000..24c2366b27 --- /dev/null +++ b/scripts/qboobtracker @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai + +# Copyright(C) 2010-2012 Sébastien Monel +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from __future__ import absolute_import +from weboob.applications.qboobtracker import QBoobTracker + + +if __name__ == '__main__': + QBoobTracker.run() diff --git a/weboob/applications/qboobtracker/__init__.py b/weboob/applications/qboobtracker/__init__.py new file mode 100644 index 0000000000..49482dd316 --- /dev/null +++ b/weboob/applications/qboobtracker/__init__.py @@ -0,0 +1,3 @@ +from .qboobtracker import QBoobTracker + +__all__ = ['QBoobTracker'] diff --git a/weboob/applications/qboobtracker/main_window.py b/weboob/applications/qboobtracker/main_window.py new file mode 100644 index 0000000000..b1834cc11f --- /dev/null +++ b/weboob/applications/qboobtracker/main_window.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Sébastien Monel +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from __future__ import unicode_literals + +from PyQt5.QtWidgets import QMessageBox, QInputDialog +from PyQt5.QtCore import Qt, pyqtSlot as Slot + +from weboob.tools.application.qt5 import QtMainWindow +from weboob.tools.application.qt5.backendcfg import BackendCfg +from weboob.tools.application.qt5.search_history import HistoryCompleter +from weboob.tools.application.qt5.models import ResultModel +from weboob.capabilities.bugtracker import CapBugTracker, Query, Change, Update + +from .ui.main_window_ui import Ui_MainWindow + +import os +import re +import shlex + + +KEYWORDS = ['project', 'backend', 'category', 'title', 'author', 'assignee', 'status'] +KWD_MATCH = re.compile(r'^(%s):(.*)$' % '|'.join(KEYWORDS)) + + +def string_to_queries(s): + # TODO rewrite all when there are finer criteria + # and when more complex expressions can be written + + tokens = shlex.split(s) # raises ValueError + + criteria = {} + + for tok in tokens: + m = KWD_MATCH.match(tok) + if not m: + raise ValueError('%r is not a valid criterion' % tok) + + k, v = m.groups() + criteria.setdefault(k, []).append(v) + + queries = [Query()] + for k, values in criteria.items(): + if len(values) == 1: + v, = values + for q in queries: + setattr(q, k, v) + else: + dupq = {} + for v in values: + dupq[v] = [] + for q in queries: + q = q.copy() + setattr(q, k, v) + dupq[v].append(q) + + queries = sum(dupq.values(), []) + + return queries + + +class MainWindow(QtMainWindow): + def __init__(self, config, storage, weboob, parent=None): + super(MainWindow, self).__init__(parent) + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + self.config = config + self.storage = storage + self.weboob = weboob + self.process = None + + # search history is a list of patterns which have been searched + history_path = os.path.join(self.weboob.workdir, 'qboobtracker_history') + qc = HistoryCompleter(history_path, self) + qc.load() + qc.setCaseSensitivity(Qt.CaseInsensitive) + self.ui.searchEdit.setCompleter(qc) + + self.ui.actionBackends.triggered.connect(self.backendsConfig) + + self.ui.searchEdit.returnPressed.connect(self.doSearch) + self.ui.searchButton.clicked.connect(self.doSearch) + + if self.weboob.count_backends() == 0: + self.backendsConfig() + + self.mdl = ResultModel(self.weboob) + self.mdl.setColumnFields(['id', 'title', 'status', 'creation', 'author', 'updated', 'assignee']) + + self.ui.bugList.setModel(self.mdl) + self.ui.bugList.addAction(self.ui.actionBulk) + self.ui.actionBulk.triggered.connect(self.doBulk) + + @Slot() + def doBulk(self): + qidxes = self.ui.bugList.selectionModel().selectedIndexes() + cols = set(qidx.column() for qidx in qidxes) + if len(cols) != 1: + return + + col, = cols + colname = self.mdl.headerData(col, Qt.Horizontal, Qt.DisplayRole) + if colname not in ('status', 'assignee'): + return + + objs = [qidx.data(ResultModel.RoleObject) for qidx in qidxes] + project = objs[0].project + if colname == 'status': + vals = [status.name for status in project.statuses or []] + val, ok = QInputDialog.getItem(self, self.tr('Bulk edit'), self.tr('Select new status for selected items'), vals, 0, False) + elif colname == 'assignee': + vals = [user.name for user in project.members or []] + val, ok = QInputDialog.getItem(self, self.tr('Bulk edit'), self.tr('Select new assignee for selected items'), vals, 0, False) + if not ok: + return + + for obj in objs: + change = Change() + change.field = colname + change.new = val + + update = Update() + update.changes = [change] + + self.weboob.do('update_issue', obj, update, backends=(obj.backend,)) + + @Slot() + def doSearch(self): + pattern = self.ui.searchEdit.text() + + try: + queries = string_to_queries(pattern) + except ValueError as e: + QMessageBox.critical(self, self.tr('Error in search query'), str(e)) + return + + self.ui.searchEdit.completer().addString(pattern) + + self.mdl.clear() + self.process = self.mdl.addRootDo(self.do_search, queries) + + def do_search(self, backend, queries): + for q in queries: + if q.backend and q.backend != backend.name: + continue + for issue in backend.iter_issues(q): + yield issue + + def closeEvent(self, event): + self.ui.searchEdit.completer().save() + super(MainWindow, self).closeEvent(event) + + @Slot() + def backendsConfig(self): + bckndcfg = BackendCfg(self.weboob, (CapBugTracker,), self) + if bckndcfg.run(): + pass diff --git a/weboob/applications/qboobtracker/qboobtracker.py b/weboob/applications/qboobtracker/qboobtracker.py new file mode 100644 index 0000000000..33ba14560a --- /dev/null +++ b/weboob/applications/qboobtracker/qboobtracker.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Sébastien Monel +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from __future__ import unicode_literals + +from weboob.capabilities.bugtracker import CapBugTracker +from weboob.tools.application.qt5 import QtApplication +from weboob.tools.config.yamlconfig import YamlConfig + +from .main_window import MainWindow + + +class QBoobTracker(QtApplication): + APPNAME = 'qboobtracker' + VERSION = '1.4' + COPYRIGHT = 'Copyright(C) 2018-YEAR Vincent A' + DESCRIPTION = "Qt application to search for bugs and tasks." + SHORT_DESCRIPTION = "search for bugs/tasks" + CAPS = CapBugTracker + CONFIG = {'queries': {}} + STORAGE = {'bookmarks': [], 'read': [], 'notes': {}} + + def main(self, argv): + self.load_backends(CapBugTracker) + self.create_storage() + self.load_config(klass=YamlConfig) + + main_window = MainWindow(self.config, self.storage, self.weboob) + main_window.show() + return self.weboob.loop() diff --git a/weboob/applications/qboobtracker/ui/Makefile b/weboob/applications/qboobtracker/ui/Makefile new file mode 100644 index 0000000000..85940ca519 --- /dev/null +++ b/weboob/applications/qboobtracker/ui/Makefile @@ -0,0 +1,13 @@ +UI_FILES = $(wildcard *.ui) +UI_PY_FILES = $(UI_FILES:%.ui=%_ui.py) +PYUIC = pyuic5 + +all: $(UI_PY_FILES) + +%_ui.py: %.ui + $(PYUIC) -o $@ $^ + +clean: + rm -f *.pyc + rm -f $(UI_PY_FILES) + diff --git a/weboob/applications/qboobtracker/ui/__init__.py b/weboob/applications/qboobtracker/ui/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/weboob/applications/qboobtracker/ui/main_window.ui b/weboob/applications/qboobtracker/ui/main_window.ui new file mode 100644 index 0000000000..7122b21dd8 --- /dev/null +++ b/weboob/applications/qboobtracker/ui/main_window.ui @@ -0,0 +1,117 @@ + + + MainWindow + + + + 0 + 0 + 709 + 572 + + + + QBoobTracker + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search query + + + + + + + Go + + + + + + + + + + true + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + false + + + + + + + + + 0 + 0 + 709 + 20 + + + + + Issues + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + Backends + + + + + Bulk &edit + + + Alt+E + + + + + + -- GitLab