changeset 61:e81d034b28a5

more permission handling and table metadata
author casties
date Tue, 26 Oct 2010 21:23:19 +0200
parents 9fdadb60529f
children 3905385c8854
files RestDbGisApi.py RestDbInterface.py zpt/HTML_schema_usertables.zpt
diffstat 3 files changed, 95 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/RestDbGisApi.py	Mon Oct 25 23:24:19 2010 +0200
+++ b/RestDbGisApi.py	Tue Oct 26 21:23:19 2010 +0200
@@ -9,8 +9,8 @@
 from Products.ZSQLExtend import ZSQLExtend
 import logging
 import re
-import json
 import time
+import datetime
 import urllib
 
 from RestDbInterface import *
@@ -46,6 +46,7 @@
     # data templates
     GIS_schema_table = PageTemplateFile('zpt/GIS_schema_table', globals())
     KML_schema_table = PageTemplateFile('zpt/KML_schema_table', globals())
+    HTML_schema_usertables = PageTemplateFile('zpt/HTML_schema_usertables', globals())
     
     # and scripts
     def KML_URL_schema_table(self,schema,table):
@@ -55,10 +56,16 @@
         doc = self.REQUEST.get('doc',None)
         return self.getLiveKmlUrl(schema=schema,table=table)
 
-
+    #
+    # database methods
+    #
     def getTableOwner(self,schema,table):
         """returns the owner of the table"""
-        # TODO: look up in metadata
+        # what do we do with the schema?
+        sql = 'select table_owner from public.gis_table_meta where table_name = %s'
+        res = self.executeSQL(sql,(table,))
+        if len(res['rows']) > 0:
+            return res['rows'][0][0]
         return None
 
     def isAllowed(self,action,schema,table,user=None,owner=None):
@@ -77,6 +84,7 @@
         if action == "update":
             if owner is None:
                 owner = self.getTableOwner(schema,table)
+            logging.debug("isAllowed user=%s owner=%s"%(user,owner))
             if user is not None and str(user) == str(owner):
                 # update only your own table
                 return True
@@ -85,88 +93,54 @@
         
         return True
 
-    def setTableMetaTypes(self,schema,table,fields):
+    def setTableMetaTypes(self,schema,table,fields,user=None):
         """sets the GIS meta information for table"""
-        logging.debug("settablemetatypes schema=%s, table=%s, fields=%s"%(schema,table,fields))
-        gisIdField = None
-        latField = None
-        lonField = None
-        for f in fields:
-            t = f['type']
-            if t == 'gis_id':
-                gisIdField = f['name']
-            elif t == 'coord_lat':
-                latField = f['name']
-            elif t == 'coord_lon':
-                lonField = f['name']
+        if user is None:
+            user = self.REQUEST.get('AUTHENTICATED_USER',None)
+
+        logging.debug("settablemetatypes schema=%s, table=%s, fields=%s user=%s"%(schema,table,fields,user))
                 
-        res = self.executeSQL("select * from public.metadata where tablename=%s", (table,))
+        today = datetime.date.today().isoformat()
+
+        res = self.executeSQL('select * from public.gis_table_meta where table_name = %s', (table,))
         if len(res['rows']) > 0:
             # meta record exists
-            if gisIdField is not None:
-                self.executeSQL('update public.metadata set "attribute with gis_id" = %s where tablename = %s', (gisIdField,table), hasResult=False)
-                
+            sql = 'update public.gis_table_meta set table_owner=%s, table_modified=%s where table_name=%s'
+            self.executeSQL(sql, (str(user),today,table), hasResult=False)
         else:
             # new meta record
-            if gisIdField is not None:
-                self.executeSQL('insert into public.metadata ("tablename", "attribute with gis_id") values (%s, %s)', (table,gisIdField), hasResult=False)
-    
-        
-    def showTable(self,resultFormat='XML',schema='public',table=None,REQUEST=None,RESPONSE=None):
-        """returns PageTemplate with tables"""
-        logging.debug("showtable")
-        if REQUEST is None:
-            REQUEST = self.REQUEST
+            sql = 'insert into public.gis_table_meta (table_name,table_owner,table_created) values (%s,%s,%s)'
+            self.executeSQL(sql, (table,str(user),today), hasResult=False)
+
+        # update row info
+        sql = 'delete from public.gis_table_meta_rows where table_name=%s'
+        self.executeSQL(sql,(table,),hasResult=False)
+        sql = 'insert into public.gis_table_meta_rows (table_name,field_name,gis_type) values (%s,%s,%s)'
+        for f in fields:
+            t = f['type']
+            fn = f['name']
+            self.executeSQL(sql, (table,fn,t), hasResult=False)
             
-        # should be cross-site accessible 
-        if RESPONSE is None:
-            RESPONSE = self.REQUEST.RESPONSE
-        RESPONSE.setHeader('Access-Control-Allow-Origin', '*')
         
-        user = self.REQUEST.get('AUTHENTICATED_USER',None)
-        logging.debug("user=%s"%user)
-                
-        # everything else has its own template
-        pt = getattr(self.template, '%s_schema_table'%resultFormat, None)
-        if pt is None:
-            return "ERROR!! template %s_schema_table not found"%resultFormat
+    def getListOfUserTables(self,schema='public',username='guest'):
+        """return list of tables"""
+        logging.debug("getlistofusertables")
+        # get list of db tables
+        qstr = """SELECT t.table_name FROM information_schema.tables t, public.gis_table_meta m WHERE t.table_type = 'BASE TABLE' 
+                    AND t.table_schema = %s AND t.table_name = m.table_name ORDER BY 1"""
         
-        #data = self.getTable(schema,table)
-        # templates have to get their own data
-        return pt(schema=schema,table=table)
- 
+        data=self.executeSQL(qstr,(schema,))
+        return data
+
 
     def createEmptyTable(self,schema,table,fields):
         """create a table with the given fields
            returns list of created fields"""
-        logging.debug("createEmptyTable")
-        sqlFields = []
-        for f in fields:
-            if isinstance(f,dict):
-                # {name: XX, type: YY}
-                name = sqlName(f['name'])
-                type = f['type']
-                sqltype = gisToSqlTypeMap[type]
-            else:
-                # name only
-                name = sqlName(f)
-                type = 'text'
-                sqltype = 'text'
-                
-            sqlFields.append({'name':name, 'type':type, 'sqltype':sqltype})
-            
-        if self.isAllowed("create", schema, table):
-            self.executeSQL('drop table if exists "%s"."%s"'%(schema,table),hasResult=False)
-            fieldString = ", ".join(['"%s" %s'%(f['name'],f['sqltype']) for f in sqlFields])
-            sqlString = 'create table "%s"."%s" (%s)'%(schema,table,fieldString)
-            logging.debug("createemptytable: SQL=%s"%sqlString)
-            self.executeSQL(sqlString,hasResult=False)
+        sqlFields = RestDbInterface.createEmptyTable(self,schema,table,fields)
+        if sqlFields is not None:
             self.setTableMetaTypes(schema,table,sqlFields)
-            return sqlFields
-        else:
-            logging.warning("create table not allowed!")
-            # throw exception?
-            return None
+
+        return sqlFields
     
     
     def getLiveKmlUrl(self,schema,table,useTimestamp=True,REQUEST=None):
@@ -216,8 +190,9 @@
         if (gisIdField is None) and (latField is None or lonField is None):
             # no fields given - choose automagically
             # gis id in metadata first
-            SQL='SELECT "attribute with gis_id" FROM public.metadata WHERE tablename = %s'
-            res = self.executeSQL(SQL, (table,))
+            #SQL='SELECT "attribute with gis_id" FROM public.metadata WHERE tablename = %s'
+            sql = 'SELECT field_name FROM public.gis_table_meta_rows WHERE table_name = %s and gis_type = %s'
+            res = self.executeSQL(sql, (table,'gis_id'))
             if len(res['rows']) > 0:
                 gisIdField = res['rows'][0][0]
             else:
--- a/RestDbInterface.py	Mon Oct 25 23:24:19 2010 +0200
+++ b/RestDbInterface.py	Tue Oct 26 21:23:19 2010 +0200
@@ -248,17 +248,17 @@
             
         if len(path) == 1:
             # list of schemas
-            return self.showListOfSchemas(resultFormat=resultFormat)
+            return self.showListOfSchemas(format=resultFormat)
         elif len(path) == 2:
             # list of tables
-            return self.showListOfTables(resultFormat=resultFormat,schema=path[1])
+            return self.showListOfTables(format=resultFormat,schema=path[1])
         elif len(path) == 3:
             # table
             if REQUEST.get("method") == "POST" and REQUEST.get("create_table_file",None) is not None:
                 # POST to table to check
-                return self.checkTable(resultFormat=resultFormat,schema=path[1],table=path[2])
+                return self.checkTable(format=resultFormat,schema=path[1],table=path[2])
             # else show table
-            return self.showTable(resultFormat=resultFormat,schema=path[1],table=path[2])
+            return self.showTable(format=resultFormat,schema=path[1],table=path[2])
         
         # don't know what to do
         return str(REQUEST)
@@ -306,7 +306,7 @@
             RESPONSE.setStatus(400)
             return
         
-    def showTable(self,resultFormat='XML',schema='public',table=None,REQUEST=None,RESPONSE=None):
+    def showTable(self,format='XML',schema='public',table=None,REQUEST=None,RESPONSE=None):
         """returns PageTemplate with tables"""
         logging.debug("showtable")
         if REQUEST is None:
@@ -319,9 +319,9 @@
         RESPONSE.setHeader('Access-Control-Allow-Origin', '*')
         
         # everything else has its own template
-        pt = getattr(self.template, '%s_schema_table'%resultFormat, None)
+        pt = getattr(self.template, '%s_schema_table'%format, None)
         if pt is None:
-            return "ERROR!! template %s_schema_table not found"%resultFormat
+            return "ERROR!! template %s_schema_table not found"%format
         
         #data = self.getTable(schema,table)
         return pt(schema=schema,table=table)
@@ -342,7 +342,7 @@
         ret = bool(data['rows'])
         return ret
 
-    def showListOfTables(self,resultFormat='XML',schema='public',REQUEST=None,RESPONSE=None):
+    def showListOfTables(self,format='XML',schema='public',REQUEST=None,RESPONSE=None):
         """returns PageTemplate with list of tables"""
         logging.debug("showlistoftables")
         # should be cross-site accessible 
@@ -350,9 +350,9 @@
             RESPONSE = self.REQUEST.RESPONSE
         RESPONSE.setHeader('Access-Control-Allow-Origin', '*')
 
-        pt = getattr(self.template, '%s_schema'%resultFormat, None)
+        pt = getattr(self.template, '%s_schema'%format, None)
         if pt is None:
-            return "ERROR!! template %s_schema not found"%resultFormat
+            return "ERROR!! template %s_schema not found"%format
         
         #data = self.getListOfTables(schema)
         return pt(schema=schema)
@@ -361,15 +361,17 @@
         """return list of tables"""
         logging.debug("getlistoftables")
         # get list of fields and types of db table
-        qstr="""SELECT c.relname AS tablename FROM pg_catalog.pg_class c
-            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            WHERE c.relkind IN ('r','') AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
-            AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1"""
-        #qstr="select attname, format_type(pg_attribute.atttypid, pg_attribute.atttypmod) from pg_attribute, pg_class where attrelid = pg_class.oid and pg_attribute.attnum > 0"
-        data=self.executeSQL(qstr)
+        #qstr="""SELECT c.relname AS tablename FROM pg_catalog.pg_class c
+        #    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+        #    WHERE c.relkind IN ('r','') AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
+        #    AND pg_catalog.pg_table_is_visible(c.oid) 
+        #    AND c.relname ORDER BY 1"""
+        qstr = """SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' 
+                        AND table_schema = %s ORDER BY 1"""
+        data=self.executeSQL(qstr,(schema,))
         return data
 
-    def showListOfSchemas(self,resultFormat='XML',REQUEST=None,RESPONSE=None):
+    def showListOfSchemas(self,format='XML',REQUEST=None,RESPONSE=None):
         """returns PageTemplate with list of schemas"""
         logging.debug("showlistofschemas")
         # should be cross-site accessible 
@@ -377,9 +379,9 @@
             RESPONSE = self.REQUEST.RESPONSE
         RESPONSE.setHeader('Access-Control-Allow-Origin', '*')
 
-        pt = getattr(self.template, '%s_index'%resultFormat, None)
+        pt = getattr(self.template, '%s_index'%format, None)
         if pt is None:
-            return "ERROR!! template %s_index not found"%resultFormat
+            return "ERROR!! template %s_index not found"%format
         
         #data = self.getListOfSchemas()
         return pt()
@@ -391,7 +393,7 @@
         data={'fields': (('schemas',),), 'rows': [('public',),]}
         return data
     
-    def checkTable(self,resultFormat,schema,table,REQUEST=None,RESPONSE=None):
+    def checkTable(self,format,schema,table,REQUEST=None,RESPONSE=None):
         """check the table.
            returns valid data fields and table name."""
         if REQUEST is None:
@@ -402,18 +404,18 @@
         res = self.checkTableFromXML(schema, table, file)
         logging.debug("checkTable result=%s"%repr(res))
         # return the result as JSON
-        if resultFormat == "JSON":
+        if format == "JSON":
             RESPONSE.setHeader("Content-Type", "application/json")
             json.dump(res, RESPONSE)
             
-        elif resultFormat == "JSONHTML":
+        elif format == "JSONHTML":
             RESPONSE.setHeader("Content-Type", "text/html")
             RESPONSE.write("<html>\n<body>\n<pre>")
             json.dump(res, RESPONSE)
             RESPONSE.write("</pre>\n</body>\n</html>")
             
         else:
-            return "ERROR: invalid resultFormat"
+            return "ERROR: invalid format"
 
     def checkTableFromXML(self,schema,table,data,REQUEST=None,RESPONSE=None):
         """check the table with the given XML data.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zpt/HTML_schema_usertables.zpt	Tue Oct 26 21:23:19 2010 +0200
@@ -0,0 +1,22 @@
+<html tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
+  schema python:options.get('schema','public'); data python:here.getListOfUserTables(schema)">
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title tal:content="template/title">The title</title>
+  </head>
+  <body>
+    <h2>List of tables for schema <span tal:replace="schema"/></h2>
+    <tal:block tal:condition="python:layout=='select'">
+    <select tal:attributes="id element_id">
+      <option tal:repeat="tbl data/rows" tal:content="python:tbl[0]"/>
+    </select>
+    </tal:block>
+    <tal:block tal:condition="python:layout=='table'">
+    <table tal:attributes="id element_id">
+      <tr tal:repeat="row data/rows">
+        <td><a tal:define="tbl python:row[0]" tal:attributes="href tbl" tal:content="tbl"/></td>
+      </tr>
+    </table>
+    </tal:block>
+  </body>
+</html>
\ No newline at end of file