diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ef0a7..12fd6e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ + +### 0.2.2 (2015-09-02) + +#### Enhancements + +* Change privacy for maps (public checkbox). +* Change some texts. + ### 0.2.1 (2015-08-19) diff --git a/cartodb/cartodbapi.py b/cartodb/cartodbapi.py index 9b0ec9c..745ccb0 100644 --- a/cartodb/cartodbapi.py +++ b/cartodb/cartodbapi.py @@ -23,6 +23,7 @@ def __init__(self, cartodbUser, apiKey, multiuser=False, hostname='cartodb.com') self.cartodbUser = cartodbUser self.hostname = hostname self.apiUrl = "https://{}.{}/api/v1/".format(cartodbUser, hostname) + self.returnDict = True self.manager = QNetworkAccessManager() self.manager.finished.connect(self.returnFetchContent) @@ -180,6 +181,18 @@ def createVizFromTable(self, table, name, description='', returnDict=True): reply.finished.connect(loop.exit) loop.exec_() + def updateViz(self, viz, returnDict=True): + self.returnDict = returnDict + url = QUrl(self.apiUrl + "viz/{}?api_key={}".format(viz['id'], self.apiKey)) + request = self._getRequest(url) + + reply = self.manager.put(request, json.dumps(viz)) + loop = QEventLoop() + reply.downloadProgress.connect(self.progressCB) + reply.error.connect(self._error) + reply.finished.connect(loop.exit) + loop.exec_() + def addLayerToMap(self, mapId, layer, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "maps/{}/layers?api_key={}".format(mapId, self.apiKey)) diff --git a/dialogs/CreateViz.py b/dialogs/CreateViz.py index c01479a..993504b 100644 --- a/dialogs/CreateViz.py +++ b/dialogs/CreateViz.py @@ -18,32 +18,35 @@ * * ***************************************************************************/ """ -from PyQt4.QtCore import Qt, QFile, QFileInfo, pyqtSlot, qDebug, QPyNullVariant -from PyQt4.QtGui import QApplication, QAbstractItemView, QDialog, QListWidgetItem, QLabel, QPixmap, QPushButton, QSizePolicy -from PyQt4.QtGui import QClipboard, QPainter, QColor +# pylint: disable-msg=E0611 +from PyQt4.QtCore import Qt, qDebug, QPyNullVariant +from PyQt4.QtGui import QApplication, QListWidgetItem, QPushButton, QSizePolicy +from PyQt4.QtGui import QPainter, QColor -from qgis.core import QGis, QgsMapLayerRegistry, QgsMapLayer, QgsPalLayerSettings, NULL +from qgis.core import QGis, QgsMapLayer, QgsPalLayerSettings from qgis.gui import QgsMessageBar +# pylint: disable-msg=F0401 import QgisCartoDB.CartoDBPlugin from QgisCartoDB.cartodb import CartoDBApi from QgisCartoDB.dialogs.Basic import CartoDBPluginUserDialog from QgisCartoDB.layers import CartoDBLayer from QgisCartoDB.ui.CreateViz import Ui_CreateViz -from QgisCartoDB.utils import randomColor -from QgisCartoDB.widgets import CartoDBLayersListWidget, CartoDBLayerListItem +from QgisCartoDB.utils import randomColor, getSize, getLineJoin, getLineDasharray +from QgisCartoDB.widgets import CartoDBLayerListItem from string import Template import copy -import os import webbrowser class CartoDBPluginCreateViz(CartoDBPluginUserDialog): + """Dialog for create map""" def __init__(self, toolbar, iface, parent=None): CartoDBPluginUserDialog.__init__(self, toolbar, parent) + self.currentViz = None self.iface = iface self.ui = Ui_CreateViz() @@ -77,6 +80,7 @@ def __init__(self, toolbar, iface, parent=None): self.ui.mapNameTX.textChanged.connect(self.validateButtons) # self.ui.mapList.itemSelectionChanged.connect(self.validateButtons) + # pylint: disable-msg=E1101 self.ui.cancelBT.clicked.connect(self.reject) self.ui.saveBT.clicked.connect(self.createViz) self.ui.cartoCssBT.clicked.connect(self.createCartoCss) @@ -97,19 +101,21 @@ def __init__(self, toolbar, iface, parent=None): self.ui.availableList.clear() self.cartoDBLayers = [] - cartoDBLayersCount = 0 + cartodb_layers_count = 0 for ly in layers: if ly.type() == QgsMapLayer.VectorLayer and isinstance(ly, CartoDBLayer): - cartoDBLayersCount = cartoDBLayersCount + 1 + cartodb_layers_count = cartodb_layers_count + 1 + # pylint: disable-msg=E1101 if ly.user == self.currentUser: self.cartoDBLayers.append(ly) item = QListWidgetItem(self.ui.availableList) - widget = CartoDBLayerListItem(ly.name(), ly, self.getSize(ly), ly.dataProvider().featureCount()) + widget = CartoDBLayerListItem(ly.name(), ly, getSize(ly), ly.dataProvider().featureCount()) item.setSizeHint(widget.sizeHint()) self.ui.availableList.setItemWidget(item, widget) - if cartoDBLayersCount > 0 and len(self.cartoDBLayers) == 0: + if cartodb_layers_count > 0 and len(self.cartoDBLayers) == 0: self.ui.bar.clearWidgets() + # pylint: disable-msg=E1101 self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Warning') + '!!', QApplication.translate('CartoDBPlugin', 'At least one CartoDB layer should belong or be visible to {}').format(self.currentUser), @@ -117,7 +123,7 @@ def __init__(self, toolbar, iface, parent=None): self.ui.mapNameTX.setEnabled(False) self.ui.descriptionTX.setEnabled(False) self.ui.publicCH.setEnabled(False) - elif cartoDBLayersCount == 0: + elif cartodb_layers_count == 0: self.ui.bar.clearWidgets() self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Warning') + '!!', QApplication.translate('CartoDBPlugin', @@ -129,7 +135,9 @@ def __init__(self, toolbar, iface, parent=None): else: self.cartoDBLayers.reverse() + ''' def copyItem(self, source, dest, item): + """Copy Item from source to dest""" itemWidget = source.itemWidget(item) newItemWidget = CartoDBLayerListItem(itemWidget.tableName, itemWidget.layer, itemWidget.size, itemWidget.rows) newItem = source.takeItem(source.row(item)) @@ -142,7 +150,6 @@ def addAllItems(self): self.ui.availableList.selectAll() self.addItems() - ''' def addItems(self): if len(self.ui.availableList.selectedItems()) > 0: for item in self.ui.availableList.selectedItems(): @@ -161,38 +168,18 @@ def removeItems(self): self.validateButtons() ''' - def getSize(self, layer): - filePath = layer.dataProvider().dataSourceUri() - if filePath.find('|') != -1: - filePath = filePath[0:filePath.find('|')] - - file = QFile(filePath) - fileInfo = QFileInfo(file) - - dirName = fileInfo.dir().absolutePath() - fileName = fileInfo.completeBaseName() - - size = 0 - if layer.storageType() == 'ESRI Shapefile': - for suffix in ['.shp', '.dbf', '.prj', '.shx']: - file = QFile(os.path.join(dirName, fileName + suffix)) - fileInfo = QFileInfo(file) - size = size + fileInfo.size() - elif layer.storageType() in ['GPX', 'GeoJSON', 'LIBKML']: - size = size + fileInfo.size() - - return size - def createCartoCss(self): + """Create CartoCSS for selected layer""" item = self.ui.availableList.currentItem() if item is not None: widget = self.ui.availableList.itemWidget(item) layer = widget.layer - cartoCSS = self.convert2CartoCSS(layer) - qDebug('CartoCSS: {}'.format(cartoCSS.encode('utf8', 'ignore'))) + carto_css = self.convert2CartoCSS(layer) + qDebug('CartoCSS: {}'.format(carto_css.encode('utf8', 'ignore'))) def createViz(self): + """Create Map in CartoDB""" self.ui.bar.clearWidgets() self.ui.bar.pushMessage("Info", QApplication.translate('CartoDBPlugin', 'Creating Map'), level=QgsMessageBar.INFO) self.withWarnings = False @@ -205,9 +192,10 @@ def createViz(self): layer = None if layer is not None: - cartoDBApi = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) - cartoDBApi.fetchContent.connect(self.cbCreateViz) - cartoDBApi.createVizFromTable(layer.fullTableName(), self.ui.mapNameTX.text(), self.ui.descriptionTX.toPlainText()) + # pylint: disable-msg=E1101 + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + cartodb_api.fetchContent.connect(self.cbCreateViz) + cartodb_api.createVizFromTable(layer.fullTableName(), self.ui.mapNameTX.text(), self.ui.descriptionTX.toPlainText()) else: self.ui.bar.clearWidgets() widget = self.ui.bar.createMessage(QApplication.translate('CartoDBPlugin', 'Error!!'), @@ -215,16 +203,24 @@ def createViz(self): self.ui.bar.pushWidget(widget, QgsMessageBar.CRITICAL) def cbCreateViz(self, data): + """Callback for create map, get created map data""" self.currentViz = data - cartoDBApi = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) - cartoDBApi.fetchContent.connect(self.cbGetLayers) - cartoDBApi.getLayersMap(data['map_id']) + # pylint: disable-msg=E1101 + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + cartodb_api.fetchContent.connect(self.cbGetLayers) + cartodb_api.getLayersMap(data['map_id']) + if self.ui.publicCH.isChecked(): + data['privacy'] = 'PUBLIC' + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + cartodb_api.updateViz(data) def cbGetLayers(self, data): + """Callback for getLayers, update cartoCSS to map layers""" layer = self.cartoDBLayers[0] - cartoCSS = self.convert2CartoCSS(layer) - cartoDBApi = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + carto_css = self.convert2CartoCSS(layer) + # pylint: disable-msg=E1101 + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) layer1 = data['layers'][1] if layer.isSQL: layer1["options"]["query"] = layer.sql @@ -232,39 +228,44 @@ def cbGetLayers(self, data): layer1["options"]["query"] = "" qDebug('Layer {}'.format(layer.fullTableName())) layer1["options"]["table_name"] = layer.fullTableName() - layer1['options']['tile_style'] = cartoCSS + layer1['options']['tile_style'] = carto_css layer1["options"]["legend"] = None layer1["options"]["order"] = 1 layer1["order"] = 1 - cartoDBApi.fetchContent.connect(self.showMessage) - cartoDBApi.updateLayerInMap(self.currentViz['map_id'], layer1) + cartodb_api.fetchContent.connect(self.showMessage) + cartodb_api.updateLayerInMap(self.currentViz['map_id'], layer1) for i, layer in enumerate(self.cartoDBLayers[1:len(self.cartoDBLayers)]): order = i + 2 qDebug('Agregando: {} en pos: {}'.format(layer.tableName(), order)) - cartoCSS = self.convert2CartoCSS(layer) - # cartoDBApi.fetchContent.connect(self.cbCreateViz) - newLayer = copy.deepcopy(layer1) - newLayer["options"]["tile_style"] = cartoCSS - newLayer["options"]["order"] = order - newLayer["options"]["legend"] = None - newLayer["order"] = order - newLayer["id"] = None + carto_css = self.convert2CartoCSS(layer) + # cartodb_api.fetchContent.connect(self.cbCreateViz) + new_layer = copy.deepcopy(layer1) + new_layer["options"]["tile_style"] = carto_css + new_layer["options"]["order"] = order + new_layer["options"]["legend"] = None + new_layer["order"] = order + new_layer["id"] = None if layer.isSQL: - newLayer["options"]["query"] = layer.sql + new_layer["options"]["query"] = layer.sql else: qDebug('Layer {}'.format(layer.fullTableName())) - newLayer["options"]["query"] = "" - newLayer["options"]["table_name"] = layer.fullTableName() - cartoDBApi.addLayerToMap(self.currentViz['map_id'], newLayer) + new_layer["options"]["query"] = "" + new_layer["options"]["table_name"] = layer.fullTableName() + cartodb_api.addLayerToMap(self.currentViz['map_id'], new_layer) + # pylint: disable-msg=W0613 def showMessage(self, data): + """Show message to user""" + # pylint: disable-msg=E1101 url = '{}/viz/{}/public_map'.format(self.currentUserData['base_url'], self.currentViz['id']) def openVis(): + """Open map in default browser""" webbrowser.open(url) def copyURL(): + """Copy map URL to clipboard""" QApplication.clipboard().setText(url) @@ -288,29 +289,30 @@ def copyURL(): self.ui.bar.pushWidget(widget, QgsMessageBar.INFO if not self.withWarnings else QgsMessageBar.WARNING, duration=10) def convert2CartoCSS(self, layer): + """Convert layer symbology to CartoCSS""" renderer = layer.rendererV2() - cartoCSS = '' - labelCSS = '' + carto_css = '' + label_css = '' - labelSettings = QgsPalLayerSettings() - labelSettings.readFromLayer(layer) - if labelSettings.enabled: + label_settings = QgsPalLayerSettings() + label_settings.readFromLayer(layer) + if label_settings.enabled: d = { 'layername': '#' + layer.tableName(), - 'field': labelSettings.getLabelExpression().dump(), + 'field': label_settings.getLabelExpression().dump(), # TODO Get font size 'size': 11, - 'color': labelSettings.textColor.name() + 'color': label_settings.textColor.name() } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/labels.less') - labelCSS = Template(filein.read()) - labelCSS = labelCSS.substitute(d) - # qDebug('Label CSS: ' + labelCSS) + label_css = Template(filein.read()) + label_css = label_css.substitute(d) + # qDebug('Label CSS: ' + label_css) # CSS for single symbols if renderer.type() == 'singleSymbol': symbol = renderer.symbol() - cartoCSS = self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName()) + carto_css = self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName()) # CSS for categorized symbols elif renderer.type() == 'categorizedSymbol': # qDebug('Categorized: ' + renderer.classAttribute()) @@ -325,15 +327,16 @@ def convert2CartoCSS(self, layer): value = str(value.encode('utf8', 'ignore')) # qDebug('Value {}'.format(value)) - styleName = '#{}[{}={}]'.format(layer.tableName(), renderer.classAttribute(), value).decode('utf8') - cartoCSS = cartoCSS + \ - self.symbol2CartoCSS(layer, symbol, styleName) + style_name = '#{}[{}={}]'.format(layer.tableName(), renderer.classAttribute(), value).decode('utf8') + carto_css = carto_css + \ + self.symbol2CartoCSS(layer, symbol, style_name) else: - cartoCSS = self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName()) + cartoCSS + carto_css = self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName()) + carto_css # CSS for graduated symbols elif renderer.type() == 'graduatedSymbol': # qDebug('Graduated') def upperValue(ran): + """Get upper value from range""" return ran.upperValue() ranges = sorted(renderer.ranges(), key=upperValue, reverse=True) @@ -346,41 +349,44 @@ def upperValue(ran): ran.label() )) ''' - cartoCSS = cartoCSS + \ - self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName() + '[' + renderer.classAttribute() + '<=' + str(ran.upperValue()) + ']') + carto_css = carto_css + \ + self.symbol2CartoCSS(layer, symbol, '#' + layer.tableName() + \ + '[' + renderer.classAttribute() + '<=' + str(ran.upperValue()) + ']') - return '/** Styles designed from QGISCartoDB Plugin */\n\n' + cartoCSS + '\n' + labelCSS + return '/** Styles designed from QGISCartoDB Plugin */\n\n' + carto_css + '\n' + label_css def symbol2CartoCSS(self, layer, symbol, styleName): - cartoCSS = '' - layerOpacity = str(float((100.0 - layer.layerTransparency())/100.0)) - - blendMode = layer.featureBlendMode() - compositionMode = 'src-over' - if blendMode == QPainter.CompositionMode_Lighten: - compositionMode = 'lighten' - elif blendMode == QPainter.CompositionMode_Screen: - compositionMode = 'screen' - elif blendMode == QPainter.CompositionMode_ColorDodge: - compositionMode = 'color-dodge' - elif blendMode == QPainter.CompositionMode_Plus: - compositionMode = 'plus' - elif blendMode == QPainter.CompositionMode_Darken: - compositionMode = 'darken' - elif blendMode == QPainter.CompositionMode_Multiply: - compositionMode = 'multiply' - elif blendMode == QPainter.CompositionMode_ColorBurn: - compositionMode = 'color-burn' - elif blendMode == QPainter.CompositionMode_Overlay: - compositionMode = 'overlay' - elif blendMode == QPainter.CompositionMode_SoftLight: - compositionMode = 'soft-light' - elif blendMode == QPainter.CompositionMode_HardLight: - compositionMode = 'hard-light' - elif blendMode == QPainter.CompositionMode_Difference: - compositionMode = 'difference' - elif blendMode == QPainter.CompositionMode_Exclusion: - compositionMode = 'exclusion' + # pylint: disable-msg=R0914,R0912,R0915 + """Convert QGIS symbol to cartoCSS""" + carto_css = '' + layer_opacity = str(float((100.0 - layer.layerTransparency())/100.0)) + + blend_mode = layer.featureBlendMode() + composition_mode = 'src-over' + if blend_mode == QPainter.CompositionMode_Lighten: + composition_mode = 'lighten' + elif blend_mode == QPainter.CompositionMode_Screen: + composition_mode = 'screen' + elif blend_mode == QPainter.CompositionMode_ColorDodge: + composition_mode = 'color-dodge' + elif blend_mode == QPainter.CompositionMode_Plus: + composition_mode = 'plus' + elif blend_mode == QPainter.CompositionMode_Darken: + composition_mode = 'darken' + elif blend_mode == QPainter.CompositionMode_Multiply: + composition_mode = 'multiply' + elif blend_mode == QPainter.CompositionMode_ColorBurn: + composition_mode = 'color-burn' + elif blend_mode == QPainter.CompositionMode_Overlay: + composition_mode = 'overlay' + elif blend_mode == QPainter.CompositionMode_SoftLight: + composition_mode = 'soft-light' + elif blend_mode == QPainter.CompositionMode_HardLight: + composition_mode = 'hard-light' + elif blend_mode == QPainter.CompositionMode_Difference: + composition_mode = 'difference' + elif blend_mode == QPainter.CompositionMode_Exclusion: + composition_mode = 'exclusion' if symbol.symbolLayerCount() > 0: lyr = None @@ -400,41 +406,41 @@ def symbol2CartoCSS(self, layer, symbol, styleName): 'fillColor': lyr.fillColor().name(), # 96 ppi = 3.7795275552 mm 'width': round(3.7795275552 * lyr.size(), 0), - 'opacity': layerOpacity, + 'opacity': layer_opacity, 'borderColor': lyr.outlineColor().name(), 'borderWidth': round(3.7795275552 * lyr.outlineWidth(), 0), - 'markerCompOp': compositionMode + 'markerCompOp': composition_mode } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/simplepoint.less') elif layer.geometryType() == QGis.Line: - lineWidth = round(3.7795275552 * lyr.width(), 0) + line_width = round(3.7795275552 * lyr.width(), 0) if lyr.penStyle() == Qt.NoPen: - lineWidth = 0 + line_width = 0 d = { 'layername': styleName, 'lineColor': lyr.color().name(), - 'lineWidth': lineWidth, - 'opacity': layerOpacity, - 'lineCompOp': compositionMode, - 'lineJoin': self._getLineJoin(lyr), - 'lineDasharray': self._getLineDasharray(lyr.penStyle(), lineWidth) + 'lineWidth': line_width, + 'opacity': layer_opacity, + 'lineCompOp': composition_mode, + 'lineJoin': getLineJoin(lyr), + 'lineDasharray': getLineDasharray(lyr.penStyle(), line_width) } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/simpleline.less') elif layer.geometryType() == QGis.Polygon: - borderWidth = round(3.7795275552 * lyr.borderWidth(), 0) + border_width = round(3.7795275552 * lyr.borderWidth(), 0) if lyr.borderStyle() == Qt.NoPen: - borderWidth = 0 + border_width = 0 d = { 'layername': styleName, 'fillColor': lyr.fillColor().name(), - 'opacity': layerOpacity, + 'opacity': layer_opacity, 'borderColor': lyr.outlineColor().name(), - 'borderWidth': borderWidth, - 'polygonCompOp': compositionMode, - 'lineJoin': self._getLineJoin(lyr), - 'lineDasharray': self._getLineDasharray(lyr.borderStyle(), borderWidth) + 'borderWidth': border_width, + 'polygonCompOp': composition_mode, + 'lineJoin': getLineJoin(lyr), + 'lineDasharray': getLineDasharray(lyr.borderStyle(), border_width) } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/simplepolygon.less') @@ -452,51 +458,34 @@ def symbol2CartoCSS(self, layer, symbol, styleName): d = { 'layername': styleName, 'fillColor': QColor(r, g, b, 255).name(), - 'markerCompOp': compositionMode + 'markerCompOp': composition_mode } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/defaultpoint.less') elif layer.geometryType() == QGis.Line: d = { 'layername': styleName, 'lineColor': QColor(r, g, b, 255).name(), - 'lineCompOp': compositionMode, + 'lineCompOp': composition_mode, } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/defaultline.less') elif layer.geometryType() == QGis.Polygon: d = { 'layername': styleName, 'fillColor': QColor(r, g, b, 255).name(), - 'polygonCompOp': compositionMode, + 'polygonCompOp': composition_mode, } filein = open(QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/templates/defaultpolygon.less') - cartoCSS = Template(filein.read()) - cartoCSS = cartoCSS.substitute(d, - input_encoding='utf-8', - output_encoding='utf-8', - encoding_errors='replace') - return cartoCSS + carto_css = Template(filein.read()) + carto_css = carto_css.substitute( + d, + input_encoding='utf-8', + output_encoding='utf-8', + encoding_errors='replace' + ) + return carto_css def validateButtons(self): + """Validate save button""" enabled = self.ui.mapNameTX.text() != '' # and self.ui.mapList.count() > 0 self.ui.saveBT.setEnabled(enabled) - - def _getLineJoin(self, lyr): - joinStyle = 'miter' - if lyr.penJoinStyle() == Qt.BevelJoin: - joinStyle = 'bevel' - elif lyr.penJoinStyle() == Qt.RoundJoin: - joinStyle = 'round' - return joinStyle - - def _getLineDasharray(self, lineStyle, lineWidth): - lineDasharray = '0' - if lineStyle == Qt.DashLine: - lineDasharray = '5,5' - elif lineStyle == Qt.DotLine: - lineDasharray = '{},{}'.format(lineWidth, lineWidth*5) - elif lineStyle == Qt.DashDotLine: - lineDasharray = '{},{},{},{}'.format(lineWidth*10, lineWidth*10, lineWidth, lineWidth*10) - elif lineStyle == Qt.DashDotDotLine: - lineDasharray = '{},{},{},{},{},{}'.format(lineWidth*5, lineWidth*5, lineWidth, lineWidth*5, lineWidth, lineWidth*5) - return lineDasharray diff --git a/dialogs/Upload.py b/dialogs/Upload.py index 53ee4d7..b61d07e 100644 --- a/dialogs/Upload.py +++ b/dialogs/Upload.py @@ -1,7 +1,6 @@ """ /*************************************************************************** CartoDB Plugin -A QGIS plugin ---------------------------------------------------------------------------- begin : 2014-09-08 @@ -18,25 +17,26 @@ * * ***************************************************************************/ """ -from PyQt4.QtCore import Qt, QSettings, QFile, QFileInfo, QTimer, QVariant, pyqtSignal, pyqtSlot, qDebug -from PyQt4.QtGui import QApplication, QDialog, QPixmap, QListWidgetItem, QLabel, QSizePolicy +from PyQt4.QtCore import QTimer, pyqtSignal, qDebug +from PyQt4.QtGui import QApplication, QDialog, QListWidgetItem, QSizePolicy -from qgis.core import QgsMapLayerRegistry, QgsMapLayer, QgsVectorFileWriter, QgsField, QgsVectorLayer +from qgis.core import QgsMapLayerRegistry, QgsMapLayer, QgsVectorFileWriter from qgis.gui import QgsMessageBar +# pylint: disable-msg=F0401 from QgisCartoDB.cartodb import CartoDBApi from QgisCartoDB.layers import CartoDBLayer from QgisCartoDB.dialogs.Basic import CartoDBPluginUserDialog from QgisCartoDB.ui.Upload import Ui_Upload +from QgisCartoDB.utils import getSize, checkTempDir, zipLayer, checkCartoDBId from QgisCartoDB.widgets import CartoDBLayerListItem import math -import os import tempfile -import zipfile class CartoDBPluginUpload(CartoDBPluginUserDialog): + """Dialog for Upload data to CartoDB""" addedLayer = pyqtSignal(str, str) def __init__(self, iface, toolbar, parent=None): @@ -53,6 +53,7 @@ def __init__(self, iface, toolbar, parent=None): self.ui.uploadBT.clicked.connect(self.upload) self.ui.cancelBT.clicked.connect(self.reject) + self.ui.layersList.itemSelectionChanged.connect(self.validateButtons) layers = QgsMapLayerRegistry.instance().mapLayers() @@ -64,24 +65,27 @@ def __init__(self, iface, toolbar, parent=None): self.ui.uploadBar.setValue(0) self.ui.uploadBar.hide() self.ui.uploadingLB.hide() - for id, ly in layers.iteritems(): + for id_ly, ly in layers.iteritems(): + qDebug('Layer id {}'.format(id_ly)) if ly.type() == QgsMapLayer.VectorLayer and not isinstance(ly, CartoDBLayer): item = QListWidgetItem(self.ui.layersList) - widget = CartoDBLayerListItem(ly.name(), ly, self.getSize(ly), ly.dataProvider().featureCount()) + widget = CartoDBLayerListItem(ly.name(), ly, getSize(ly), ly.dataProvider().featureCount()) item.setSizeHint(widget.sizeHint()) self.ui.layersList.setItemWidget(item, widget) def upload(self): - registry = QgsMapLayerRegistry.instance() - for layerItem in self.ui.layersList.selectedItems(): - widget = self.ui.layersList.itemWidget(layerItem) + """Init upload proccess""" + for layer_item in self.ui.layersList.selectedItems(): + widget = self.ui.layersList.itemWidget(layer_item) qDebug('Layer: ' + str(widget.layer.storageType())) - layer = self.checkCartoDBId(widget.layer, self.ui.convertCH.isChecked()) - zipPath = self.zipLayer(layer) - self.uploadZip(zipPath, widget, layer, self.ui.convertCH.isChecked()) + layer = checkCartoDBId(widget.layer, self.ui.convertCH.isChecked()) + zip_path = zipLayer(layer) + self.uploadZip(zip_path, widget, layer, self.ui.convertCH.isChecked()) - def uploadZip(self, zipPath, widget, convertLayer=None, convert=False): + def uploadZip(self, zip_path, widget, convertLayer=None, convert=False): + """Upload Zipfile""" def completeUpload(data): + """On complete upload""" timer = QTimer(self) self.ui.uploadBar.hide() @@ -91,155 +95,72 @@ def completeUpload(data): self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Upload Complete'), level=QgsMessageBar.INFO, duration=5) - def statusComplete(d): - if d['state'] == 'complete': + def statusComplete(res): + """On CartoDB import proccess complete o fail""" + if res['state'] == 'complete': timer.stop() self.ui.statusLB.setText(QApplication.translate('CartoDBPlugin', 'Ready')) - widget.setStatus(d['state'], 100) + widget.setStatus(res['state'], 100) self.ui.bar.clearWidgets() - self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Table {} created').format(d['table_name']), + self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Table {} created').format(res['table_name']), level=QgsMessageBar.INFO, duration=5) if convert: - self.convert2CartoDB(convertLayer if convertLayer is not None else widget.layer, d['table_name']) - elif d['state'] == 'failure': + self.convert2CartoDB(convertLayer if convertLayer is not None else widget.layer, res['table_name']) + elif res['state'] == 'failure': timer.stop() self.ui.statusLB.setText(QApplication.translate('CartoDBPlugin', '{} failed, {}').format( - widget.layer.name(), d['get_error_text']['title'])) - widget.setStatus(d['state']) + widget.layer.name(), res['get_error_text']['title'])) + widget.setStatus(res['state']) self.ui.bar.clearWidgets() self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Error uploading {}').format(widget.layer.name()), level=QgsMessageBar.WARNING, duration=5) else: - widget.setStatus(d['state']) + widget.setStatus(res['state']) def timerComplete(): - cartodbApi = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) - cartodbApi.fetchContent.connect(statusComplete) - cartodbApi.checkUploadStatus(data['item_queue_id']) + """On timer complete, check import process in CartoDB Servers""" + # pylint: disable-msg=E1101 + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + cartodb_api.fetchContent.connect(statusComplete) + cartodb_api.checkUploadStatus(data['item_queue_id']) timer.timeout.connect(timerComplete) timer.start(500) - cartodbApi = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) - cartodbApi.fetchContent.connect(completeUpload) - cartodbApi.progress.connect(self.progressUpload) + + # pylint: disable-msg=E1101 + cartodb_api = CartoDBApi(self.currentUser, self.currentApiKey, self.currentMultiuser) + cartodb_api.fetchContent.connect(completeUpload) + cartodb_api.progress.connect(self.progressUpload) self.ui.uploadBar.show() self.ui.uploadBT.setEnabled(False) self.ui.uploadingLB.setText(QApplication.translate('CartoDBPlugin', 'Uploading {}').format(widget.layer.name())) self.ui.uploadingLB.show() self.ui.bar.clearWidgets() - cartodbApi.upload(zipPath) + cartodb_api.upload(zip_path) def convert2CartoDB(self, layer, tableName): - self.checkTempDir() - tf = tempfile.NamedTemporaryFile() - qDebug('New file {}'.format(tf.name)) - error = QgsVectorFileWriter.writeAsVectorFormat(layer, tf.name, "utf-8", None, "SQLite") + """Convert QGIS styles to CartoCSS""" + checkTempDir() + temp = tempfile.NamedTemporaryFile() + qDebug('New file {}'.format(temp.name)) + error = QgsVectorFileWriter.writeAsVectorFormat(layer, temp.name, "utf-8", None, "SQLite") if error == QgsVectorFileWriter.NoError: - self.addedLayer.emit(tf.name + '.sqlite', tableName) + self.addedLayer.emit(temp.name + '.sqlite', tableName) else: self.ui.bar.pushMessage(QApplication.translate('CartoDBPlugin', 'Error loading CartoDB layer {}').format(tableName), level=QgsMessageBar.WARNING, duration=5) def progressUpload(self, current, total): + """Check upload proccess""" self.ui.uploadBar.setValue(math.ceil(float(current)/float(total)*100)) - def getSize(self, layer): - isZipFile = False - filePath = layer.dataProvider().dataSourceUri() - if filePath.find('|') != -1: - filePath = filePath[0:filePath.find('|')] - - if filePath.startswith('/vsizip/'): - filePath = filePath.replace('/vsizip/', '') - isZipFile = True - - file = QFile(filePath) - fileInfo = QFileInfo(file) - - dirName = fileInfo.dir().absolutePath() - fileName = fileInfo.completeBaseName() - - size = 0 - if layer.storageType() == 'ESRI Shapefile' and not isZipFile: - for suffix in ['.shp', '.dbf', '.prj', '.shx']: - file = QFile(os.path.join(dirName, fileName + suffix)) - fileInfo = QFileInfo(file) - size = size + fileInfo.size() - elif layer.storageType() in ['GPX', 'GeoJSON', 'LIBKML'] or isZipFile: - size = size + fileInfo.size() - - return size - - def checkCartoDBId(self, layer, convert = False): - ly = layer - - if convert and layer.fieldNameIndex('cartodb_id') == -1: - self.checkTempDir() - tf = tempfile.NamedTemporaryFile() - error = QgsVectorFileWriter.writeAsVectorFormat(layer, tf.name, 'utf-8', None, 'ESRI Shapefile') - if error == QgsVectorFileWriter.NoError: - ly = QgsVectorLayer(tf.name + '.shp', layer.name(), 'ogr') - res = ly.dataProvider().addAttributes([QgsField('cartodb_id', QVariant.Int)]) - ly.updateFields() - features = ly.getFeatures() - i = 1 - for f in features: - fid = f.id() - aid = ly.fieldNameIndex('cartodb_id') - attrs = { aid: i } - ly.dataProvider().changeAttributeValues({ fid : attrs }) - i = i + 1 - ly.updateFeature(f) - return ly - - def zipLayer(self, layer): - filePath = layer.dataProvider().dataSourceUri() - if filePath.find('|') != -1: - filePath = filePath[0:filePath.find('|')] - - if filePath.startswith('/vsizip/'): - filePath = filePath.replace('/vsizip/', '') - if layer.storageType() in ['ESRI Shapefile', 'GPX', 'GeoJSON', 'LIBKML']: - return filePath - - file = QFile(filePath) - fileInfo = QFileInfo(file) - - dirName = fileInfo.dir().absolutePath() - fileName = fileInfo.completeBaseName() - - tempdir = self.checkTempDir() - - zipPath = os.path.join(tempdir, layer.name() + '.zip') - zipFile = zipfile.ZipFile(zipPath, 'w') - - - if layer.storageType() == 'ESRI Shapefile': - for suffix in ['.shp', '.dbf', '.prj', '.shx']: - if os.path.exists(os.path.join(dirName, fileName + suffix)): - zipFile.write(os.path.join(dirName, fileName + suffix), layer.name() + suffix, zipfile.ZIP_DEFLATED) - elif layer.storageType() == 'GeoJSON': - zipFile.write(filePath, layer.name() + '.geojson', zipfile.ZIP_DEFLATED) - elif layer.storageType() == 'GPX': - zipFile.write(filePath, layer.name() + '.gpx', zipfile.ZIP_DEFLATED) - elif layer.storageType() == 'LIBKML': - zipFile.write(filePath, layer.name() + '.kml', zipfile.ZIP_DEFLATED) - else: - geoJsonName = os.path.join(tempfile.tempdir, layer.name()) - error = QgsVectorFileWriter.writeAsVectorFormat(layer, geoJsonName, "utf-8", None, "GeoJSON") - if error == QgsVectorFileWriter.NoError: - zipFile.write(geoJsonName + '.geojson', layer.name() + '.geojson', zipfile.ZIP_DEFLATED) - zipFile.close() - return zipPath - def reject(self): - # Back out of dialogue + """Back out of dialogue""" QDialog.reject(self) - def checkTempDir(self): - tempdir = tempfile.tempdir - if tempdir is None: - tempdir = tempfile.mkdtemp() - return tempdir + def validateButtons(self): + """Validate upload button""" + enabled = self.ui.layersList.count() > 0 + self.ui.uploadBT.setEnabled(enabled) diff --git a/i18n/en.ts b/i18n/en.ts index 3081cff..7922c01 100644 --- a/i18n/en.ts +++ b/i18n/en.ts @@ -29,8 +29,8 @@ Filter Tables - Import data by current extent - Import data by current extent + Download data only within the current view + Download data only within the current view Name @@ -214,8 +214,8 @@ Replace if exists - Add CartoDB layer to project (this could truncate the column's names) - Add CartoDB layer to project (this could truncate the column's names) + Add CartoDB layer to project + Add CartoDB layer to project Cancel @@ -227,7 +227,7 @@ Ready - Listo + Ready @@ -277,8 +277,8 @@ Create - Make Public - Make Public + Public + Public diff --git a/i18n/es.ts b/i18n/es.ts index 31b2281..98a49e5 100644 --- a/i18n/es.ts +++ b/i18n/es.ts @@ -29,8 +29,8 @@ Filtrar Tablas - Import data by current extent - Importar datos por la extensión actual + Download data only within the current view + Descargar datos únicamente dentro de la vista actual Name @@ -214,8 +214,8 @@ Reemplazar si existe en CartoDB - Add CartoDB layer to project (this could truncate the column's names) - Agregar capa CartoDB al proyecto (esto podría truncar los nombres de las columnas) + Add CartoDB layer to project + Agregar capa CartoDB al proyecto Cancel @@ -277,8 +277,8 @@ Crear - Make Public - Hacer público + Public + Público diff --git a/metadata.txt b/metadata.txt index 0458a88..5d5801b 100644 --- a/metadata.txt +++ b/metadata.txt @@ -2,7 +2,7 @@ name=QGISCartoDB description=CartoDB Plugin for QGis. It allows to view, create, edit or delete data from your CartoDB account using your favorite opensource desktop GIS: QGIS. category=Web -version=0.2.1 +version=0.2.2 qgisMinimumVersion=2.4 icon=images/icon.png author=Kudos Ltda. and contributors @@ -23,7 +23,10 @@ about= - simplejson - certifi email=michaelsalgado@gkudos.com -changelog=0.2.1 +changelog=0.2.2 + - Change privacy for maps, (public checkbox). + - Change some texts. + 0.2.1 - Create maps without select layers. - Fix order layer in maps. - Fix when you push a new layer to CartoDB, it doesn't register the layer with your current project as being a CartoDB layer. diff --git a/pylintrc b/pylintrc index b416d32..b85e6a6 100644 --- a/pylintrc +++ b/pylintrc @@ -1,3 +1,25 @@ +[MASTER] +extension-pkg-whitelist=PyQt4,qgis + +[BASIC] +# Good names for variables +good-names = i,j,k,r,g,b,d,ui,e,ex + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{,30} + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + [FORMAT] # Maximum number of characters on a single line. max-line-length = 150 diff --git a/test/cartodbapi.py b/test/cartodbapi.py index 88a3e33..0a4a405 100644 --- a/test/cartodbapi.py +++ b/test/cartodbapi.py @@ -1,10 +1,13 @@ +""" +Unit Test for CartoDB api +""" + from config import * import logging import unittest -import sys -from PyQt4.QtCore import QObject, QEventLoop, pyqtSlot +from PyQt4.QtCore import QObject, pyqtSlot from PyQt4.QtGui import QApplication from cartodb.cartodbapi import CartoDBApi @@ -137,7 +140,6 @@ def test_show_user_data(self): cartodbApi.fetchContent.connect(self.signalsObject.cb_show_user_data) cartodbApi.getUserDetails() - @unittest.skip("testing skipping") def test_show_user_tables(self): self.logger.debug("\n*******************************************************************************") self.logger.debug('\nTest get user tables for: ' + cartodb_user) @@ -177,6 +179,7 @@ def test_upload_file(self): # path = '/home/elesdoar/Documents/QGIS CartoDB/lotes_simplificados.zip' cartodbApi.upload(path) + @unittest.skip("testing skipping") def test_upload_file_status(self): self.logger.debug("\n*******************************************************************************") self.logger.debug('\nTest upload file status for shape: constituencies.zip') @@ -192,7 +195,7 @@ def test_create_viz_from_table(self): self.logger.debug("\n*******************************************************************************") cartodbApi = CartoDBApi(cartodb_user, api_key, is_multiuser) cartodbApi.fetchContent.connect(self.signalsObject.cb_show_create_viz_result) - cartodbApi.createVizFromTable('test_ideca_manzanas', 'Test map from QGISCartoDB', 'Test map from Description') + cartodbApi.createVizFromTable('test_ideca_cuerpos_agua', 'Test map from QGISCartoDB', 'Test map from Description') @unittest.skip("testing skipping") def test_add_layer_to_map(self): diff --git a/ui/CreateViz.ui b/ui/CreateViz.ui index a73e48e..085678c 100644 --- a/ui/CreateViz.ui +++ b/ui/CreateViz.ui @@ -78,7 +78,7 @@ - Make Public + Public diff --git a/ui/UI_CartoDBPlugin.ui b/ui/UI_CartoDBPlugin.ui index c57034a..d4cc139 100644 --- a/ui/UI_CartoDBPlugin.ui +++ b/ui/UI_CartoDBPlugin.ui @@ -63,7 +63,7 @@ - Import data by current extent + Download data only within the current view @@ -144,7 +144,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Close|QDialogButtonBox::Ok diff --git a/ui/Upload.ui b/ui/Upload.ui index be47884..9664318 100644 --- a/ui/Upload.ui +++ b/ui/Upload.ui @@ -153,7 +153,7 @@ true - Add CartoDB layer to project (this could truncate the column's names) + Add CartoDB layer to project true @@ -191,6 +191,9 @@ + + false + Upload diff --git a/utils/Utils.py b/utils/Utils.py index 255ee26..6dbe257 100644 --- a/utils/Utils.py +++ b/utils/Utils.py @@ -1,7 +1,6 @@ """ /*************************************************************************** CartoDB Plugin -A QGIS plugin ---------------------------------------------------------------------------- begin : 2014-09-08 @@ -18,10 +17,18 @@ * * ***************************************************************************/ """ +from PyQt4.QtCore import Qt, QFile, QFileInfo, QVariant + +from qgis.core import QgsVectorFileWriter, QgsVectorLayer, QgsField + +import os import random +import tempfile +import zipfile def randomColor(mix=(255, 255, 255)): + """Generate Random Color""" red = random.randrange(0, 256) blue = random.randrange(0, 256) green = random.randrange(0, 256) @@ -32,3 +39,124 @@ def randomColor(mix=(255, 255, 255)): blue = (blue + b)/2 return (red, blue, green) + +def getSize(layer): + """Get layer size on disk""" + is_zip_file = False + file_path = layer.dataProvider().dataSourceUri() + if file_path.find('|') != -1: + file_path = file_path[0:file_path.find('|')] + + if file_path.startswith('/vsizip/'): + file_path = file_path.replace('/vsizip/', '') + is_zip_file = True + + _file = QFile(file_path) + file_info = QFileInfo(_file) + + dirname = file_info.dir().absolutePath() + filename = file_info.completeBaseName() + + size = 0 + if layer.storageType() == 'ESRI Shapefile' and not is_zip_file: + for suffix in ['.shp', '.dbf', '.prj', '.shx']: + _file = QFile(os.path.join(dirname, filename + suffix)) + file_info = QFileInfo(_file) + size = size + file_info.size() + elif layer.storageType() in ['GPX', 'GeoJSON', 'LIBKML'] or is_zip_file: + size = size + file_info.size() + + return size + +def zipLayer(layer): + """Compress layer to zip file""" + file_path = layer.dataProvider().dataSourceUri() + if file_path.find('|') != -1: + file_path = file_path[0:file_path.find('|')] + + if file_path.startswith('/vsizip/'): + file_path = file_path.replace('/vsizip/', '') + if layer.storageType() in ['ESRI Shapefile', 'GPX', 'GeoJSON', 'LIBKML']: + return file_path + + _file = QFile(file_path) + file_info = QFileInfo(_file) + + dirname = file_info.dir().absolutePath() + filename = file_info.completeBaseName() + + tempdir = checkTempDir() + + zip_path = os.path.join(tempdir, layer.name() + '.zip') + zip_file = zipfile.ZipFile(zip_path, 'w') + + + if layer.storageType() == 'ESRI Shapefile': + for suffix in ['.shp', '.dbf', '.prj', '.shx']: + if os.path.exists(os.path.join(dirname, filename + suffix)): + zip_file.write(os.path.join(dirname, filename + suffix), layer.name() + suffix, zipfile.ZIP_DEFLATED) + elif layer.storageType() == 'GeoJSON': + zip_file.write(file_path, layer.name() + '.geojson', zipfile.ZIP_DEFLATED) + elif layer.storageType() == 'GPX': + zip_file.write(file_path, layer.name() + '.gpx', zipfile.ZIP_DEFLATED) + elif layer.storageType() == 'LIBKML': + zip_file.write(file_path, layer.name() + '.kml', zipfile.ZIP_DEFLATED) + else: + geo_json_name = os.path.join(tempfile.tempdir, layer.name()) + error = QgsVectorFileWriter.writeAsVectorFormat(layer, geo_json_name, "utf-8", None, "GeoJSON") + if error == QgsVectorFileWriter.NoError: + zip_file.write(geo_json_name + '.geojson', layer.name() + '.geojson', zipfile.ZIP_DEFLATED) + zip_file.close() + return zip_path + +def checkTempDir(): + """Check temporar dir""" + tempdir = tempfile.tempdir + if tempdir is None: + tempdir = tempfile.mkdtemp() + return tempdir + +def checkCartoDBId(layer, convert=False): + """Check if layer has cartodb_id field""" + new_layer = layer + + if convert and layer.fieldNameIndex('cartodb_id') == -1: + checkTempDir() + temp = tempfile.NamedTemporaryFile() + error = QgsVectorFileWriter.writeAsVectorFormat(layer, temp.name, 'utf-8', None, 'ESRI Shapefile') + if error == QgsVectorFileWriter.NoError: + new_layer = QgsVectorLayer(temp.name + '.shp', layer.name(), 'ogr') + new_layer.dataProvider().addAttributes([QgsField('cartodb_id', QVariant.Int)]) + new_layer.updateFields() + features = new_layer.getFeatures() + i = 1 + for feature in features: + fid = feature.id() + aid = new_layer.fieldNameIndex('cartodb_id') + attrs = {aid: i} + new_layer.dataProvider().changeAttributeValues({fid : attrs}) + i = i + 1 + new_layer.updateFeature(feature) + return new_layer + +def getLineJoin(lyr): + """Get line join from symbol layer""" + join_style = 'miter' + if lyr.penJoinStyle() == Qt.BevelJoin: + join_style = 'bevel' + elif lyr.penJoinStyle() == Qt.RoundJoin: + join_style = 'round' + return join_style + +def getLineDasharray(lineStyle, lineWidth): + """Get line dash array from line style""" + line_dash_array = '0' + if lineStyle == Qt.DashLine: + line_dash_array = '5,5' + elif lineStyle == Qt.DotLine: + line_dash_array = '{},{}'.format(lineWidth, lineWidth*5) + elif lineStyle == Qt.DashDotLine: + line_dash_array = '{},{},{},{}'.format(lineWidth*10, lineWidth*10, lineWidth, lineWidth*10) + elif lineStyle == Qt.DashDotDotLine: + line_dash_array = '{},{},{},{},{},{}'.format(lineWidth*5, lineWidth*5, lineWidth, lineWidth*5, lineWidth, lineWidth*5) + return line_dash_array