From cd9bd799ed0ddeafc997dadc0717c00989f5a807 Mon Sep 17 00:00:00 2001
From: Renaud Le Gac <legac@cppm.in2p3.fr>
Date: Tue, 23 Oct 2012 20:14:55 +0200
Subject: [PATCH] Improved version of the Selector with many more
 fonctionalities.

---
 modules/plugin_dbui/report.py | 129 +++++++++++++++++++++++++++-------
 1 file changed, 103 insertions(+), 26 deletions(-)

diff --git a/modules/plugin_dbui/report.py b/modules/plugin_dbui/report.py
index 7220f5e7..9027eb63 100644
--- a/modules/plugin_dbui/report.py
+++ b/modules/plugin_dbui/report.py
@@ -1,19 +1,24 @@
 """ Base tools to build report
 
-A database report relies on four main components:
-    - A selector form allowing the user to defined conditions.
-      It can be build using a virtual database,
-    - The Selector class to decode the selected values and to build query.
-    - A controller to extract value from the selector and 
-      to interrogate the database.
+A database report relies on three main components:
+    - A selector table and its form to allow the user to defined conditions.
+      It can be build using a virtual database.
+    - The Selector running in a controller to decode the user requirement
+      and to select records in the database.
     - A view to render the selected records.
 
 A powerful mechanism is also in place to render the report in different format
 like HTML, CSV, .... It relies on the Selector class, on the IFRAME and 
-on the view.
+on the view:
+
+    selector = Selector(virtdb.myselector, extfield='format')
+    iframe = selector.download()
+    if iframe:
+        return iframe
 
 """
 
+from datetime import date, datetime, time
 from gluon import current
 from gluon.html import IFRAME, URL
 from gluon.storage import Storage
@@ -28,25 +33,27 @@ class Selector(Storage):
     
     Decode the data send by the selector widget. 
     Build the query for a given table of the database.
+    Select Rows for a given table of the database.
+    
+    This class handle table and its foreign keys.
+    The basic query handles the selector constraints
+    and the inner join for foreign keys. Additional
+    constraints can be added via the method add_query.
 
     All fields of the selector are attributes of this class.
     They can be accessed via myselector.myfield or myselector['myfield'].
+    There type are those defined in the model (virtdb.myselector)
     
     A mechanism is provide to download the report in different format
     which can be selected by the user. It relies on the extfield and
-    on the method download. Inside a controller:
-    
-        selector = Selector(db, extfield='format')
-        iframe = selector.download()
-        if iframe:
-            return iframe
+    on the method download.
             
     """    
-    def __init__(self, db, extfield='format'):
+    def __init__(self, table, extfield='format'):
 
-        self._db = db
         self._ext_field = extfield
         self._extension = None
+        self._extra_queries = []
         
         # Decode the current request
         for key in current.request.vars:
@@ -55,14 +62,50 @@ class Selector(Storage):
             if len(t) != 2 :
                 continue
             
-            field = t[1]
-            if field == self._ext_field:
+            fieldname = t[1]
+            if fieldname == self._ext_field:
                 self._extension = current.request.vars[key]
+
+            # ignore field id
+            elif fieldname == 'id':
+                continue
+            
+            val = current.request.vars[key]
+            
+            if not val:
+                self[fieldname] = val
+                continue
+            
+            # convert the selector value according to field type
+            fieldtype = table[fieldname].type
+
+            if fieldtype == 'boolean':
+                val = val == 'true' or val == 'True'
+                
+            elif fieldtype == 'date':
+                ds = datetime.strptime(val, '%Y-%m-%dT%H:%M:%S')
+                val = date(ds.year, ds.month, ds.day)
+            
+            elif fieldtype == 'datetime':
+                val = datetime.strptime(val, '%Y-%m-%dT%H:%M:%S')
+            
+            elif fieldtype == 'double':
+                val = float(val)
                 
-            elif field != 'id':
-                self[field] = current.request.vars[key]
+            elif fieldtype == 'integer':
+                val = int(val)
                 
+            elif fieldtype == 'time':
+                ds = datetime.strptime(val, '%H:%M:%S')
+                val = time(ds.hour, ds.minute, ds.second)
+                
+            self[fieldname] = val
+
 
+    def append_query(self, query):
+        self._extra_queries.append(query)
+        
+        
     def as_dict(self):
         """Return the selector fields and their values as a dictionary.
         
@@ -128,17 +171,18 @@ class Selector(Storage):
         return li
 
 
-    def query(self, tablename, exclude_fields=()):
-        """Build the database query for the table tablename
-        including inner join for foreign keys and selector constraints.
+    def query(self, table, exclude_fields=()):
+        """Build the database query for the database table
+        including inner join for foreign keys, selector constraints
+        and extra queries. The latter can be add via the append_query
+        method.
         
         The extfield and fields in the exclude_fields tuple are 
         not take into account.
         
         """
-        db = self._db
+        db = table._db
         query = None
-        table = self._db[tablename]
     
         # inner join for foreign keys
         if is_table_with_foreign_fields(table):
@@ -159,11 +203,44 @@ class Selector(Storage):
                fieldname == self._ext_field:
                 continue
             
-            q = db[tablename][fieldname] == self[fieldname]
+            q = table[fieldname] == self[fieldname]
             
             if query:
                 query = (query) & (q)
             else:
                 query = q
 
-        return query
\ No newline at end of file
+        # append the extra queries
+        for q in self._extra_queries:
+            query = (query) & (q)
+            
+        return query
+
+
+    def select(self, table, exclude_fields=(), **kwargs):
+        """Select the record of the table according to the selector constraints
+        and to the extra queries.
+
+        The extfield and fields in the exclude_fields tuple are 
+        not take into account.
+        
+        It returns Rows which contains foreign tables columns. 
+        
+        The keyword arguments are those of the select orderby, groupby, ....
+        
+        """
+        db = table._db
+        columns = [table.ALL]
+
+        # columns for foreign tables
+        if is_table_with_foreign_fields(table):
+            for field in table:
+                if is_foreign_field(field):
+                    k_table, k_field, k_id = get_foreign_field(field)
+                    columns.append(db[k_table].ALL)
+                    
+        # query handling selector constraints
+        query = self.query(table, exclude_fields=exclude_fields)
+        
+        # retrieve the selected records
+        return db(query).select(*columns, **kwargs)
-- 
GitLab