Skip to content

Commit

Permalink
[Processing] Add proxy support for Get scripts and models (fixes #13412)
Browse files Browse the repository at this point in the history
  • Loading branch information
Médéric Ribreux committed Oct 27, 2015
1 parent fe6456f commit f2e527f
Showing 1 changed file with 117 additions and 86 deletions.
203 changes: 117 additions & 86 deletions python/plugins/processing/gui/GetScriptsAndModels.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,24 @@

__author__ = 'Victor Olaya'
__date__ = 'June 2014'
__copyright__ = '(C) 201, Victor Olaya'
__copyright__ = '(C) 2014, Victor Olaya'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
import json
import urllib2
from urllib2 import HTTPError
from functools import partial

from PyQt4 import uic
from PyQt4.QtCore import Qt, QCoreApplication
from PyQt4.QtGui import QIcon, QMessageBox, QCursor, QApplication, QTreeWidgetItem
from PyQt4.QtCore import Qt, QCoreApplication, QUrl
from PyQt4.QtGui import QIcon, QCursor, QApplication, QTreeWidgetItem, QPushButton
from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest

from qgis.utils import iface
from qgis.utils import iface, show_message_log
from qgis.core import QgsNetworkAccessManager, QgsMessageLog
from qgis.gui import QgsMessageBar

from processing.gui.ToolboxAction import ToolboxAction
from processing.script.ScriptUtils import ScriptUtils
Expand All @@ -48,7 +50,6 @@
WIDGET, BASE = uic.loadUiType(
os.path.join(pluginPath, 'ui', 'DlgGetScriptsAndModels.ui'))


class GetScriptsAction(ToolboxAction):

def __init__(self):
Expand All @@ -59,15 +60,10 @@ def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'script.png'))

def execute(self):
try:
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.SCRIPTS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('script')
except HTTPError:
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem', 'GetScriptsAction'),
self.tr('Could not connect to scripts/models repository', 'GetScriptsAction'))
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.SCRIPTS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('script')


class GetRScriptsAction(ToolboxAction):
Expand All @@ -80,15 +76,10 @@ def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'r.png'))

def execute(self):
try:
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.RSCRIPTS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('r')
except HTTPError:
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem', 'GetRScriptsAction'),
self.tr('Could not connect to scripts/models repository', 'GetRScriptsAction'))
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.RSCRIPTS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('r')


class GetModelsAction(ToolboxAction):
Expand All @@ -101,23 +92,10 @@ def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'model.png'))

def execute(self):
try:
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('model')
except (HTTPError, URLError):
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem', 'GetModelsAction'),
self.tr('Could not connect to scripts/models repository', 'GetModelsAction'))


def readUrl(url):
try:
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
return urllib2.urlopen(url).read()
finally:
QApplication.restoreOverrideCursor()
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('model')


class GetScriptsAndModelsDialog(BASE, WIDGET):
Expand All @@ -137,9 +115,14 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
SCRIPTS = 1
RSCRIPTS = 2

tr_disambiguation = { 0: 'GetModelsAction',
1: 'GetScriptsAction',
2: 'GetRScriptsAction' }

def __init__(self, resourceType):
super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow())
self.setupUi(self)
self.manager = QgsNetworkAccessManager.instance()

self.resourceType = resourceType
if self.resourceType == self.MODELS:
Expand All @@ -160,8 +143,30 @@ def __init__(self, resourceType):
self.populateTree()
self.buttonBox.accepted.connect(self.okPressed)
self.buttonBox.rejected.connect(self.cancelPressed)
self.tree.currentItemChanged .connect(self.currentItemChanged)

self.tree.currentItemChanged.connect(self.currentItemChanged)

def popupError(self, error=None, url=None):
"""Popups an Error message bar for network errors."""
disambiguation = self.tr_disambiguation[self.resourceType]
widget = iface.messageBar().createMessage(self.tr('Connection problem', disambiguation),
self.tr('Could not connect to scripts/models repository', disambiguation))
if error and url:
QgsMessageLog.logMessage(self.tr(u"Network error code: {} on URL: {}").format(error, url), u"Processing", QgsMessageLog.CRITICAL)
button = QPushButton(QCoreApplication.translate("Python", "View message log"), pressed=show_message_log)
widget.layout().addWidget(button)

iface.messageBar().pushWidget(widget, level=QgsMessageBar.CRITICAL, duration=5)

def grabHTTP(self, url, loadFunction, arguments=None):
"""Grab distant content via QGIS internal classes and QtNetwork."""
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
request = QUrl(url)
reply = self.manager.get(QNetworkRequest(request))
if arguments:
reply.finished.connect(partial(loadFunction, reply, arguments))
else:
reply.finished.connect(partial(loadFunction, reply))

def populateTree(self):
self.uptodateItem = QTreeWidgetItem()
self.uptodateItem.setText(0, self.tr('Installed'))
Expand All @@ -172,35 +177,53 @@ def populateTree(self):
self.toupdateItem.setIcon(0, self.icon)
self.uptodateItem.setIcon(0, self.icon)
self.notinstalledItem.setIcon(0, self.icon)
resources = readUrl(self.urlBase + 'list.txt').splitlines()
resources = [r.split(',') for r in resources]
self.resources = {f: (v, n) for f, v, n in resources}
for filename, version, name in sorted(resources, key=lambda kv: kv[2].lower()):
treeBranch = self.getTreeBranchForState(filename, float(version))
item = TreeItem(filename, name, self.icon)
treeBranch.addChild(item)
if treeBranch != self.notinstalledItem:
item.setCheckState(0, Qt.Checked)
self.grabHTTP(self.urlBase + 'list.txt', self.treeLoaded)

def treeLoaded(self, reply):
"""
update the tree of scripts/models whenever
HTTP request is finished
"""
QApplication.restoreOverrideCursor()
if reply.error() != QNetworkReply.NoError:
self.popupError(reply.error(), reply.request().url().toString())
else:
resources = unicode(reply.readAll()).splitlines()
resources = [r.split(',') for r in resources]
self.resources = {f: (v, n) for f, v, n in resources}
for filename, version, name in sorted(resources, key=lambda kv: kv[2].lower()):
treeBranch = self.getTreeBranchForState(filename, float(version))
item = TreeItem(filename, name, self.icon)
treeBranch.addChild(item)
if treeBranch != self.notinstalledItem:
item.setCheckState(0, Qt.Checked)

reply.deleteLater()
self.tree.addTopLevelItem(self.toupdateItem)
self.tree.addTopLevelItem(self.notinstalledItem)
self.tree.addTopLevelItem(self.uptodateItem)

self.webView.setHtml(self.HELP_TEXT)

def setHelp(self, reply, item):
"""Change the webview HTML content"""
QApplication.restoreOverrideCursor()
if reply.error() != QNetworkReply.NoError:
html = self.tr('<h2>No detailed description available for this script</h2>')
else:
content = unicode(reply.readAll())
descriptions = json.loads(content)
html = '<h2>%s</h2>' % item.name
html += self.tr('<p><b>Description:</b> %s</p>') % getDescription(ALG_DESC, descriptions)
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
reply.deleteLater()
self.webView.setHtml(html)

def currentItemChanged(self, item, prev):
if isinstance(item, TreeItem):
try:
url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
helpContent = readUrl(url)
descriptions = json.loads(helpContent)
html = '<h2>%s</h2>' % item.name
html += self.tr('<p><b>Description:</b> %s</p>') % getDescription(ALG_DESC, descriptions)
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
except HTTPError as e:
html = self.tr('<h2>No detailed description available for this script</h2>')
self.webView.setHtml(html)
url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
self.grabHTTP(url, self.setHelp, item)
else:
self.webView.setHtml(self.HELP_TEXT)

Expand All @@ -223,6 +246,26 @@ def getTreeBranchForState(self, filename, version):
def cancelPressed(self):
self.close()

def storeFile(self, reply, filename):
"""store a script/model that has been downloaded"""
QApplication.restoreOverrideCursor()
if reply.error() != QNetworkReply.NoError:
if os.path.splitext(filename)[1].lower() == '.help':
content = '{"ALG_VERSION" : %s}' % self.resources[filename[:-5]][0]
else:
self.popupError(reply.error(), reply.request().url().toString())
content = None
else:
content = reply.readAll()

reply.deleteLater()
if content:
path = os.path.join(self.folder, filename)
with open(path, 'w') as f:
f.write(content)

self.progressBar.setValue(self.progressBar.value() + 1)

def okPressed(self):
toDownload = []
for i in xrange(self.toupdateItem.childCount()):
Expand All @@ -235,39 +278,27 @@ def okPressed(self):
toDownload.append(item.filename)

if toDownload:
self.progressBar.setMaximum(len(toDownload))
self.progressBar.setMaximum(len(toDownload) * 2)
for i, filename in enumerate(toDownload):
QCoreApplication.processEvents()
url = self.urlBase + filename.replace(' ', '%20')
try:
code = readUrl(url)
path = os.path.join(self.folder, filename)
with open(path, 'w') as f:
f.write(code)
except HTTPError:
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem'),
self.tr('Could not download file: %s') % filename)
return
self.grabHTTP(url, self.storeFile, filename)

url += '.help'
try:
html = readUrl(url)
except HTTPError:
html = '{"ALG_VERSION" : %s}' % self.resources[filename][0]

path = os.path.join(self.folder, filename + '.help')
with open(path, 'w') as f:
f.write(html)
self.progressBar.setValue(i + 1)
self.grabHTTP(url, self.storeFile, filename + '.help')

toDelete = []
for i in xrange(self.uptodateItem.childCount()):
item = self.uptodateItem.child(i)
if item.checkState(0) == Qt.Unchecked:
toDelete.append(item.filename)

# Remove py and help files if they exist
for filename in toDelete:
path = os.path.join(self.folder, filename)
os.remove(path)
for pathname in (filename, filename + u".help"):
path = os.path.join(self.folder, pathname)
if os.path.exists(path):
os.remove(path)

self.updateToolbox = len(toDownload) + len(toDelete) > 0
self.close()
Expand Down

0 comments on commit f2e527f

Please sign in to comment.