|
| 1 | +import pkg_resources |
| 2 | +import requests |
| 3 | +import urllib.parse |
| 4 | + |
| 5 | +import dclab |
| 6 | +from PyQt5 import uic, QtWidgets |
| 7 | + |
| 8 | +from ... import settings |
| 9 | + |
| 10 | + |
| 11 | +class DCORLoader(QtWidgets.QDialog): |
| 12 | + def __init__(self, parent, *args, **kwargs): |
| 13 | + QtWidgets.QWidget.__init__(self, parent, *args, **kwargs) |
| 14 | + path_ui = pkg_resources.resource_filename( |
| 15 | + "shapeout2.gui.dcor", "dcor.ui") |
| 16 | + uic.loadUi(path_ui, self) |
| 17 | + |
| 18 | + self.main_ui = parent |
| 19 | + self.search_results = [] |
| 20 | + |
| 21 | + # Update UI |
| 22 | + self.settings = settings.SettingsFile() |
| 23 | + |
| 24 | + # hide SSL checkbox |
| 25 | + if not self.settings.get_bool("developer mode"): |
| 26 | + self.checkBox_ssl.hide() |
| 27 | + self._init_combobox() |
| 28 | + self.lineEdit_api_key.setText(self.settings.get_string("dcor api key")) |
| 29 | + |
| 30 | + # tool button |
| 31 | + self.pushButton_search.clicked.connect(self.on_search) |
| 32 | + self.pushButton_search.setDefault(True) |
| 33 | + self.buttonBox.buttons()[1].setDefault(False) |
| 34 | + self.buttonBox.buttons()[0].setDefault(False) |
| 35 | + self.lineEdit_search.setFocus() |
| 36 | + |
| 37 | + def _init_combobox(self): |
| 38 | + # update server list |
| 39 | + servs = self.settings.get_string_list("dcor servers") |
| 40 | + self.comboBox_server.clear() |
| 41 | + self.comboBox_server.addItems(servs) |
| 42 | + self.comboBox_server.setCurrentIndex(0) |
| 43 | + |
| 44 | + def done(self, r): |
| 45 | + if r: |
| 46 | + for ii in range(self.listWidget.count()): |
| 47 | + item = self.listWidget.item(ii) |
| 48 | + if item.isSelected(): |
| 49 | + self.main_ui.add_dataslot( |
| 50 | + paths=[self.search_results[ii][0]], is_dcor=True) |
| 51 | + super(DCORLoader, self).done(r) |
| 52 | + |
| 53 | + def on_search(self): |
| 54 | + # update server list |
| 55 | + servs = self.settings.get_string_list("dcor servers") |
| 56 | + serv = self.comboBox_server.currentText() |
| 57 | + if serv.count("://"): |
| 58 | + serv = serv.split("://")[1] |
| 59 | + if serv in servs: |
| 60 | + # make this server the first choice |
| 61 | + servs.remove(serv) |
| 62 | + servs = [serv] + servs |
| 63 | + api_key = self.lineEdit_api_key.text().strip() |
| 64 | + # Add this API Key to the known API Keys (dclab) |
| 65 | + dclab.rtdc_dataset.fmt_dcor.APIHandler.add_api_key(api_key) |
| 66 | + |
| 67 | + # save config |
| 68 | + self.settings.set_string("dcor api key", api_key) |
| 69 | + self.settings.set_string_list("dcor servers", servs) |
| 70 | + self._init_combobox() |
| 71 | + |
| 72 | + # ready API |
| 73 | + http = "https" if self.checkBox_ssl.isChecked() else "http" |
| 74 | + base = "{}://{}/api/3".format(http, serv) |
| 75 | + api_headers = {"Authorization": api_key} |
| 76 | + |
| 77 | + # check API availability |
| 78 | + req = requests.get(base, headers=api_headers) |
| 79 | + if not req.ok: |
| 80 | + msg = QtWidgets.QMessageBox() |
| 81 | + msg.setIcon(QtWidgets.QMessageBox.Warning) |
| 82 | + msg.setText("Failed to connect to DCOR server '{}'! ".format(serv) |
| 83 | + + "Reason: {}".format(req.reason)) |
| 84 | + msg.setWindowTitle("Connection failed") |
| 85 | + msg.exec_() |
| 86 | + return |
| 87 | + if "version" not in req.json() or req.json()["version"] != 3: |
| 88 | + raise ValueError("Invalid response: {}".format(req.json())) |
| 89 | + |
| 90 | + # perform search (limit to 20 results) |
| 91 | + if self.comboBox_search.currentIndex() == 1: |
| 92 | + stype = "dataset" |
| 93 | + else: |
| 94 | + stype = "free" |
| 95 | + search_string = urllib.parse.quote(self.lineEdit_search.text()) |
| 96 | + res = self.perform_search(search_string, stype, base, api_headers) |
| 97 | + self.listWidget.clear() |
| 98 | + for r in res: |
| 99 | + self.listWidget.addItem(r[1]) |
| 100 | + self.search_results = res |
| 101 | + |
| 102 | + def perform_search(self, string, search_type, api_base, api_headers): |
| 103 | + """Perform search |
| 104 | +
|
| 105 | + Parameters |
| 106 | + ---------- |
| 107 | + string: str |
| 108 | + Search string (already parsed using urllib.parse.quote) |
| 109 | + search_type: str |
| 110 | + "free": free text search |
| 111 | + or "dataset": resource/package name/id |
| 112 | + api_base: str |
| 113 | + Everything up until "https://server.example.org/api/3" |
| 114 | + """ |
| 115 | + if search_type == "free": |
| 116 | + url = api_base + "/action/package_search?q={}".format(string) |
| 117 | + # limit to 20 rows |
| 118 | + url += "&rows=20" |
| 119 | + urls = [url] |
| 120 | + else: |
| 121 | + urls = [ |
| 122 | + api_base + "/action/package_show?id={}".format(string), |
| 123 | + api_base + "/action/resource_show?id={}".format(string), |
| 124 | + ] |
| 125 | + |
| 126 | + pkg_res = [] |
| 127 | + for url in urls: |
| 128 | + req = requests.get(url, headers=api_headers) |
| 129 | + if not req.ok: |
| 130 | + continue |
| 131 | + resp = req.json()["result"] |
| 132 | + if "count" in resp and "results" in resp: |
| 133 | + # free search |
| 134 | + for pkg in resp["results"]: |
| 135 | + if "resources" in pkg: |
| 136 | + for res in pkg["resources"]: |
| 137 | + pkg_res.append([pkg, res]) |
| 138 | + elif "resources" in resp: |
| 139 | + # package show |
| 140 | + for res in resp["resources"]: |
| 141 | + pkg_res.append([resp, res]) |
| 142 | + else: |
| 143 | + # resource show |
| 144 | + purl = api_base + "/action/package_show?id={}".format( |
| 145 | + resp["package_id"]) |
| 146 | + pkg = requests.get(purl, headers=api_headers).json()["result"] |
| 147 | + pkg_res.append([pkg, resp]) |
| 148 | + |
| 149 | + res_list = [] |
| 150 | + failed = [] |
| 151 | + for pkg, res in pkg_res: |
| 152 | + # check availability of each resource |
| 153 | + c = api_base + "/action/dcserv?id={}&query=valid".format(res["id"]) |
| 154 | + req = requests.get(c, headers=api_headers) |
| 155 | + if req.ok: |
| 156 | + name = "{}: {} <{}@{}>".format( |
| 157 | + pkg["title"], |
| 158 | + res["name"], |
| 159 | + pkg["name"], |
| 160 | + pkg["organization"]["name"], |
| 161 | + ) |
| 162 | + ru = api_base + "/action/dcserv?id={}".format(res["id"]) |
| 163 | + res_list.append([ru, name]) |
| 164 | + else: |
| 165 | + failed.append("{}: {}".format(res["id"], req.reason)) |
| 166 | + if failed: |
| 167 | + msg = QtWidgets.QMessageBox() |
| 168 | + msg.setIcon(QtWidgets.QMessageBox.Information) |
| 169 | + msg.setText("Search found invalid data: {}".format(failed)) |
| 170 | + msg.setWindowTitle("Dataset validation") |
| 171 | + msg.exec_() |
| 172 | + return res_list |
0 commit comments