diff --git a/CHANGELOG.md b/CHANGELOG.md
index 335317d..247fe6e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,13 @@
* Default symbology for not supported symbols.
* Add messages for not supported symbols.
-* Add uploaded message
+* Add uploaded message.
+* Change icons.
+
+#### Fixes
+
+* Fix #29 Error updating layer single user account.
+* Fix #27 Error getting columns in SQL dialog.
@@ -13,7 +19,7 @@
#### Enhancements
-* Remove connection controls in SQL Editor
+* Remove connection controls in SQL Editor.
* Add composite mode in create maps.
* Add dash style to lines in create maps.
* Add join style to lines in create maps.
@@ -23,9 +29,9 @@
#### Fixes
-* Fix #19 Do not allow to edit read-only layers
-* Show owner in shared layers, issue #18
-* Fix #26 Encoding error creating map
+* Fix #19 Do not allow to edit read-only layers.
+* Show owner in shared layers, issue #18.
+* Fix #26 Encoding error creating map.
@@ -39,7 +45,7 @@
#### Fixes
-* Fix #23 Error conecting multiuser account
+* Fix #23 Error conecting multiuser account.
### 0.1.5 (2015-06-16)
@@ -48,9 +54,9 @@
* Upload layers to CartoDB (More formats).
* Create basic visualization. Support:
- * Borders
- * Fill
- * Label
+ * Borders.
+ * Fill.
+ * Label.
### 0.1.4 (2015-06-05)
@@ -65,7 +71,7 @@
#### Fixes
-* Fix avatar size
+* Fix avatar size.
* Fix #21 Insert data without complete fields.
* Enable buttons only if there is at least one created connection.
@@ -101,6 +107,7 @@
### 0.1.1 (2014-11-27)
+* Fix error when repeat layer name at spatialite database.
#### New Features
* Load cartodb layers from SQL Queries.
@@ -108,7 +115,6 @@
#### Fixes.
* New connection dialog is now a modal window.
-* Fix error when repeat layer name at spatialite database.
### 0.1.0 (2014-09-23)
diff --git a/CartoDBPlugin.py b/CartoDBPlugin.py
index efa7d44..e6bdd2c 100644
--- a/CartoDBPlugin.py
+++ b/CartoDBPlugin.py
@@ -81,11 +81,11 @@ def initGui(self):
self._mainAction = QAction(self.tr('Add CartoDB Layer'), self.iface.mainWindow())
self._mainAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/add.png"))
self._loadDataAction = QAction(self.tr('Upload layers to CartoDB'), self.iface.mainWindow())
- self._loadDataAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/polygon.png"))
+ self._loadDataAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/upload.png"))
self._createVizAction = QAction(self.tr('Create New Map'), self.iface.mainWindow())
- self._createVizAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/rectangle.png"))
+ self._createVizAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/map.png"))
self._addSQLAction = QAction(self.tr('Add SQL CartoDB Layer'), self.iface.mainWindow())
- self._addSQLAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/add_sql.png"))
+ self._addSQLAction.setIcon(QIcon(":/plugins/qgis-cartodb/images/icons/sql.png"))
self.toolbar = CartoDBToolbar()
self.toolbar.setClick(self.connectionManager)
@@ -182,7 +182,8 @@ def run(self):
for i, table in enumerate(selectedItems):
widget = dlg.getItemWidget(table)
worker = CartoDBLayerWorker(self.iface, widget.tableName, widget.tableOwner, dlg,
- filterByExtent=dlg.filterByExtent(), readonly=widget.readonly)
+ filterByExtent=dlg.filterByExtent(), readonly=widget.readonly,
+ multiuser=widget.multiuser)
worker.finished.connect(self.addLayer)
self.worker = worker
worker.load()
diff --git a/README.md b/README.md
index ed088c5..6394d36 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,7 @@ Voilá !!!!
#### Adding SQL CartoDB layer
-Click on the icon: ![Icon](images/add_sql.png?raw=true "Icon") or on the web menu item "CartoDB Plugin" => "Add SQL CartoDB Layer"
+Click on the icon: ![Icon](images/icons/sql.png?raw=true "Icon") or on the web menu item "CartoDB Plugin" => "Add SQL CartoDB Layer"
![Dialog 3](images/sql_dialog.png?raw=true "Adding SQL layer")
diff --git a/dialogs/Main.py b/dialogs/Main.py
index 422548f..b3f7fda 100644
--- a/dialogs/Main.py
+++ b/dialogs/Main.py
@@ -86,7 +86,7 @@ def updateList(self, visualizations):
widget = CartoDBDatasetsListItem(
visualization['name'], owner, visualization['table']['size'], visualization['table']['row_count'],
- shared=(owner != self.currentUser))
+ shared=(owner != self.currentUser), multiuser=self.currentMultiuser)
# item.setText(visualization['name'])
readonly = False
# qDebug('Vis:' + json.dumps(visualization, sort_keys=True, indent=2, separators=(',', ': ')))
diff --git a/dialogs/NewSQL.py b/dialogs/NewSQL.py
index 3f1cdc1..901af85 100644
--- a/dialogs/NewSQL.py
+++ b/dialogs/NewSQL.py
@@ -115,13 +115,12 @@ def findTables(self):
if not str(self.currentMultiuser) in ['true', '1', 'True']:
sqlTables = "SELECT CDB_UserTables() table_name"
res = cl.sql(
- "SELECT *, CDB_ColumnType(table_name, column_name) column_type \
- FROM ( \
- SELECT *, CDB_ColumnNames(table_name) column_name \
- FROM (" + sqlTables + ") t1 \
- ) t2 \
- WHERE CDB_ColumnType(table_name, column_name) != 'USER-DEFINED' \
- ORDER BY table_name, column_name")
+ "WITH usertables AS (" + sqlTables + ") \
+ SELECT ut.table_name, c.column_name, c.data_type column_type \
+ FROM usertables ut \
+ JOIN information_schema.columns c ON c.table_name = ut.table_name \
+ WHERE c.data_type != 'USER-DEFINED' \
+ ORDER BY ut.table_name, c.column_name")
else:
sqlTables = "SELECT string_agg(privilege_type, ', ') AS privileges, table_schema, table_name \
FROM information_schema.role_table_grants tg \
@@ -208,3 +207,12 @@ def getQuery(self):
def showEvent(self, event):
worker = CartoDBPluginWorker(self, 'findTables')
worker.start()
+
+'''
+WITH usertables AS (SELECT CDB_UserTables() table_name)
+SELECT ut.table_name, c.column_name, c.data_type column_type, ut.privileges
+ FROM usertables ut
+ JOIN information_schema.columns c ON c.table_name = ut.table_name
+WHERE c.data_type != 'USER-DEFINED'
+ORDER BY ut.table_name, c.column_name
+'''
diff --git a/images/icons/add.png b/images/icons/add.png
index 898afe9..2c1d66f 100644
Binary files a/images/icons/add.png and b/images/icons/add.png differ
diff --git a/images/icons/add_sql.png b/images/icons/add_sql.png
deleted file mode 100644
index e5d394f..0000000
Binary files a/images/icons/add_sql.png and /dev/null differ
diff --git a/images/icons/avatar.png b/images/icons/avatar.png
new file mode 100644
index 0000000..3e079e2
Binary files /dev/null and b/images/icons/avatar.png differ
diff --git a/images/icons/error.png b/images/icons/error.png
new file mode 100644
index 0000000..cc07f30
Binary files /dev/null and b/images/icons/error.png differ
diff --git a/images/icons/map.png b/images/icons/map.png
new file mode 100644
index 0000000..78994f2
Binary files /dev/null and b/images/icons/map.png differ
diff --git a/images/icons/sql.png b/images/icons/sql.png
index 29a3bd0..8b2c422 100644
Binary files a/images/icons/sql.png and b/images/icons/sql.png differ
diff --git a/images/icons/upload.png b/images/icons/upload.png
new file mode 100644
index 0000000..de70c73
Binary files /dev/null and b/images/icons/upload.png differ
diff --git a/layers/CartoDBLayer.py b/layers/CartoDBLayer.py
index 6a5d87c..9608fce 100644
--- a/layers/CartoDBLayer.py
+++ b/layers/CartoDBLayer.py
@@ -44,13 +44,15 @@ class CartoDBLayer(QgsVectorLayer):
LAYER_TNAME_PROPERTY = 'tableName'
LAYER_SQL_PROPERTY = 'cartoSQL'
- def __init__(self, iface, tableName, user, apiKey, owner=None, sql=None, geoJSON=None, filterByExtent=False, spatiaLite=None, readonly=False):
+ def __init__(self, iface, tableName, user, apiKey, owner=None, sql=None, geoJSON=None,
+ filterByExtent=False, spatiaLite=None, readonly=False, multiuser=False):
# SQLite available?
self.iface = iface
self.user = user
self._apiKey = apiKey
self.layerType = 'ogr'
self.owner = owner
+ self.multiuser = multiuser
driverName = "SQLite"
sqLiteDrv = ogr.GetDriverByName(driverName)
self.databasePath = QgisCartoDB.CartoDBPlugin.PLUGIN_DIR + '/db/database.sqlite'
@@ -61,7 +63,7 @@ def __init__(self, iface, tableName, user, apiKey, owner=None, sql=None, geoJSON
self.forceReadOnly = False or readonly
if sql is None:
- sql = 'SELECT * FROM ' + ((owner + '.') if owner is not None else '') + self.cartoTable
+ sql = 'SELECT * FROM ' + self._schema() + self.cartoTable
if filterByExtent:
extent = self.iface.mapCanvas().extent()
sql = sql + " WHERE ST_Intersects(ST_GeometryFromText('{}', 4326), the_geom)".format(extent.asWktPolygon())
@@ -169,7 +171,7 @@ def _loadData(self, sql, geoJSON=None, spatiaLite=None):
self.setCustomProperty(CartoDBLayer.LAYER_SQL_PROPERTY, sql)
def _uneditableFields(self):
- schema = 'public' if self.owner is None else self.owner
+ schema = 'public' if not self.multiuser else self.owner
sql = "SELECT table_name, column_name, column_default, is_nullable, data_type, table_schema \
FROM information_schema.columns \
WHERE data_type != 'USER-DEFINED' AND table_schema = '" + schema + "' AND table_name = '" + self.cartoTable + "' \
@@ -224,11 +226,10 @@ def _beforeCommitChanges(self):
self._deleteFeatures(editBuffer.deletedFeatureIds())
def _updateAttributes(self, changedAttributeValues):
- schema = '' if self.owner is None else (self.owner + '.')
provider = self.dataProvider()
for featureID, v in changedAttributeValues.iteritems():
QgsMessageLog.logMessage('Update attributes for feature ID: ' + str(featureID), 'CartoDB Plugin', QgsMessageLog.INFO)
- sql = "UPDATE " + schema + self.cartoTable + " SET "
+ sql = "UPDATE " + self._schema() + self.cartoTable + " SET "
request = QgsFeatureRequest().setFilterFid(featureID)
try:
feature = self.getFeatures(request).next()
@@ -263,12 +264,11 @@ def _updateAttributes(self, changedAttributeValues):
level=self.iface.messageBar().WARNING, duration=10)
def _updateGeometries(self, changedGeometries):
- schema = '' if self.owner is None else (self.owner + '.')
for featureID, geom in changedGeometries.iteritems():
QgsMessageLog.logMessage('Update geometry for feature ID: ' + str(featureID), 'CartoDB Plugin', QgsMessageLog.INFO)
request = QgsFeatureRequest().setFilterFid(featureID)
try:
- sql = "UPDATE " + schema + self.cartoTable + " SET the_geom = "
+ sql = "UPDATE " + self._schema() + self.cartoTable + " SET the_geom = "
feature = self.getFeatures(request).next()
sql = sql + "ST_GeomFromText('" + geom.exportToWkt() + "', ST_SRID(the_geom)) WHERE cartodb_id = " + unicode(feature['cartodb_id'])
sql = sql.encode('utf-8')
@@ -285,11 +285,10 @@ def _updateGeometries(self, changedGeometries):
level=self.iface.messageBar().WARNING, duration=10)
def _addFeatures(self, addedFeatures):
- schema = '' if self.owner is None else (self.owner + '.')
provider = self.dataProvider()
for featureID, feature in addedFeatures.iteritems():
QgsMessageLog.logMessage('Add feature with feature ID: ' + str(featureID), 'CartoDB Plugin', QgsMessageLog.INFO)
- sql = "INSERT INTO " + schema + self.cartoTable + " ("
+ sql = "INSERT INTO " + self._schema() + self.cartoTable + " ("
addComma = False
for field in feature.fields():
if unicode(feature[field.name()]) == 'NULL' or feature[field.name()] is None:
@@ -311,26 +310,29 @@ def _addFeatures(self, addedFeatures):
addComma = True
if addComma:
sql = sql + ", "
- sql = sql + "ST_GeomFromText('" + feature.geometry().exportToWkt() + "', 4326)) RETURNING cartodb_id"
+ sql = sql + "ST_GeomFromText('" + feature.geometry().exportToWkt() + "', 4326)) RETURNING cartodb_id, created_at, updated_at"
sql = sql.encode('utf-8')
res = self._updateSQL(sql, 'Some error ocurred inserting feature')
if isinstance(res, dict) and res['total_rows'] == 1:
self.iface.messageBar().pushMessage('Info',
'Feature inserted at CartoDB servers',
level=self.iface.messageBar().INFO, duration=10)
- self.setFieldEditable(self.fieldNameIndex('cartodb_id'), True)
- self.editBuffer().changeAttributeValue(featureID, self.fieldNameIndex('cartodb_id'), res['rows'][0]['cartodb_id'], None)
- self.setFieldEditable(self.fieldNameIndex('cartodb_id'), False)
+ for f in ['cartodb_id', 'created_at', 'updated_at']:
+ self._updateNullableFields(featureID, f, res['rows'][0][f])
+
+ def _updateNullableFields(self, featureID, fieldName, value):
+ self.setFieldEditable(self.fieldNameIndex(fieldName), True)
+ self.editBuffer().changeAttributeValue(featureID, self.fieldNameIndex(fieldName), value, None)
+ self.setFieldEditable(self.fieldNameIndex(fieldName), False)
def _deleteFeatures(self, deletedFeatureIds):
- schema = '' if self.owner is None else (self.owner + '.')
provider = self.dataProvider()
for featureID in deletedFeatureIds:
QgsMessageLog.logMessage('Delete feature with feature ID: ' + str(featureID), 'CartoDB Plugin', QgsMessageLog.INFO)
request = QgsFeatureRequest().setFilterFid(featureID)
try:
feature = provider.getFeatures(request).next()
- sql = "DELETE FROM " + schema + self.cartoTable + " WHERE cartodb_id = " + unicode(feature['cartodb_id'])
+ sql = "DELETE FROM " + self._schema() + self.cartoTable + " WHERE cartodb_id = " + unicode(feature['cartodb_id'])
res = self._updateSQL(sql, 'Some error ocurred deleting feature')
if isinstance(res, dict) and res['total_rows'] == 1:
self.iface.messageBar().pushMessage('Info',
@@ -354,6 +356,13 @@ def _updateSQL(self, sql, errorMsg):
self.iface.messageBar().pushMessage('Error!!', errorMsg, level=self.iface.messageBar().CRITICAL, duration=10)
return e
+ def _schema(self):
+ schema = self.schema()
+ return schema + ('.' if schema != '' else '')
+
+ def schema(self):
+ return '' if not self.multiuser else self.owner
+
def tableName(self):
return self.cartoTable
@@ -377,12 +386,13 @@ class CartoDBLayerWorker(QObject):
finished = pyqtSignal(CartoDBLayer)
error = pyqtSignal(Exception, basestring)
- def __init__(self, iface, tableName, owner=None, dlg=None, sql=None, filterByExtent=False, readonly=False):
+ def __init__(self, iface, tableName, owner=None, dlg=None, sql=None, filterByExtent=False, readonly=False, multiuser=False):
QObject.__init__(self)
self.iface = iface
self.owner = owner
self.tableName = tableName
self.readonly = readonly
+ self.multiuser = multiuser
self.dlg = dlg
self.sql = sql
self.filterByExtent = filterByExtent
@@ -398,7 +408,7 @@ def load(self):
@pyqtSlot(str)
def _loadData(self, spatiaLite):
layer = CartoDBLayer(self.iface, self.tableName, self.dlg.currentUser, self.dlg.currentApiKey,
- self.owner, self.sql, spatiaLite=spatiaLite, readonly=self.readonly)
+ self.owner, self.sql, spatiaLite=spatiaLite, readonly=self.readonly, multiuser=self.multiuser)
self.finished.emit(layer)
@pyqtSlot()
diff --git a/metadata.txt b/metadata.txt
index da50bdd..74bcbb6 100644
--- a/metadata.txt
+++ b/metadata.txt
@@ -26,7 +26,10 @@ email=michaelsalgado@gkudos.com
changelog=0.1.9
- Default symbology for not supported symbols.
- Add messages for not supported symbols.
- - Add uploaded message
+ - Add uploaded message.
+ - Change icons.
+ - Fix error updating layer single user account.
+ - Fix error getting columns in SQL dialog.
0.1.8
- Remove connection controls in SQL Editor.
- Add composite mode in create maps.
diff --git a/resources.qrc b/resources.qrc
index 05b973d..980d6ba 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -12,7 +12,7 @@
images/icons/rectangle.png
- images/icons/add_sql.png
+ images/icons/sql.png
images/icons/text.png
@@ -29,4 +29,10 @@
images/icons/layers.png
+
+ images/icons/upload.png
+
+
+ images/icons/map.png
+
diff --git a/toolbars/CartoDBToolbar.py b/toolbars/CartoDBToolbar.py
index 1ef0890..65740bb 100644
--- a/toolbars/CartoDBToolbar.py
+++ b/toolbars/CartoDBToolbar.py
@@ -39,6 +39,7 @@ def __init__(self, parent=None, flags=Qt.WindowFlags(0)):
if self.currentUser:
self.currentApiKey = self.settings.value('/CartoDBPlugin/%s/api' % self.currentUser)
self.currentMultiuser = self.settings.value('/CartoDBPlugin/%s/multiuser' % self.currentUser, False)
+ self.currentMultiuser = self.currentMultiuser in ['True', 'true', True]
else:
self.currentApiKey = None
self.currentMultiuser = None
diff --git a/widgets/ListItemWidgets.py b/widgets/ListItemWidgets.py
index ee5dc9e..1b4ecc1 100644
--- a/widgets/ListItemWidgets.py
+++ b/widgets/ListItemWidgets.py
@@ -26,12 +26,13 @@
class CartoDBDatasetsListItem(QWidget):
- def __init__(self, tableName=None, tableOwner=None, size=None, rows=None, shared=False):
+ def __init__(self, tableName=None, tableOwner=None, size=None, rows=None, multiuser=False, shared=False):
QWidget.__init__(self)
self.ui = Ui_ListItem()
self.ui.setupUi(self)
self.shared = shared
+ self.multiuser = multiuser
self.readonly = False
self.tableOwner = tableOwner