diff --git a/VERSION b/VERSION
index 28fef7d1..49828b3f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 1.99.4 (2012-02-21 16:25:39) stable
+Version 1.99.4 (2012-02-22 11:32:05) stable
diff --git a/applications/examples/views/default/who.html b/applications/examples/views/default/who.html
index 4c9c218a..485be8cc 100644
--- a/applications/examples/views/default/who.html
+++ b/applications/examples/views/default/who.html
@@ -120,9 +120,9 @@
Thadeus Burgess (validators)
Tim Michelsen (Sphinx documentation)
Timothy Farrell (python 2.6 compliance, windows support)
-Yair Eshel (internationalizaiton)
+Yair Eshel (internationalizaiton, DAL improvement)
Yannis Aribaud (CAS compliance)
-Yarko Tymciurak (design, Sphinx documentation)
+Yarko Tymciurak (design)
Younghyun Jo (internationalization)
Vidul Nikolaev Petrov (captcha)
Vinicius Assef
diff --git a/gluon/dal.py b/gluon/dal.py
index aa0958ec..31640901 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -930,7 +930,7 @@ class BaseAdapter(ConnectionPool):
self.execute(query)
except Exception, e:
if isinstance(e,self.integrity_error_class()):
- return None
+ return None
raise e
if hasattr(table,'_primarykey'):
return dict([(k[0].name, k[1]) for k in fields \
@@ -1190,7 +1190,7 @@ class BaseAdapter(ConnectionPool):
for tablename in self.tables(field):
if not tablename in tablenames:
tablenames.append(tablename)
-
+
if use_common_filters(query):
query = self.common_filter(query,tablenames)
@@ -1470,7 +1470,7 @@ class BaseAdapter(ConnectionPool):
try:
value = value.decode(self.db._db_codec)
except Exception:
- pass
+ pass
if isinstance(value, unicode):
value = value.encode('utf-8')
elif isinstance(field_type, SQLCustomType):
@@ -1478,7 +1478,7 @@ class BaseAdapter(ConnectionPool):
if not isinstance(field_type, str) or value is None:
return value
elif field_type in ('string', 'text', 'password', 'upload'):
- return value
+ return value
else:
key = regex_type.match(field_type).group(0)
return self.parsemap[key](value,field_type)
@@ -1522,7 +1522,7 @@ class BaseAdapter(ConnectionPool):
def parse_blob(self, value, field_type):
return base64.b64decode(str(value))
-
+
def parse_decimal(self, value, field_type):
decimals = int(field_type[8:-1].split(',')[-1])
if self.dbengine == 'sqlite':
@@ -1536,7 +1536,7 @@ class BaseAdapter(ConnectionPool):
value = bar_decode_integer(value)
return value
- def parse_list_references(self, value, field_type):
+ def parse_list_references(self, value, field_type):
if not self.dbengine=='google:datastore':
value = bar_decode_integer(value)
return [self.parse_reference(r, field_type[5:]) for r in value]
@@ -1568,7 +1568,7 @@ class BaseAdapter(ConnectionPool):
'blob':self.parse_blob,
'decimal':self.parse_decimal,
'list:integer':self.parse_list_integers,
- 'list:reference':self.parse_list_references,
+ 'list:reference':self.parse_list_references,
'list:string':self.parse_list_strings,
}
@@ -1974,10 +1974,10 @@ class PostgreSQLAdapter(BaseAdapter):
elif library == "postgres:psycopg2":
self.driver = self.drivers.get('psycopg2')
elif library == "postgres:pg8000":
- self.driver = self.drivers.get('pg8000')
+ self.driver = self.drivers.get('pg8000')
if not self.driver:
raise RuntimeError, "%s is not available" % library
-
+
self.__version__ = "%s %s" % (self.driver.__name__, self.driver.__version__)
def connect(msg=msg,driver_args=driver_args):
return self.driver.connect(msg,**driver_args)
@@ -2073,8 +2073,8 @@ class OracleAdapter(BaseAdapter):
'datetime': 'DATE',
'id': 'NUMBER PRIMARY KEY',
'reference': 'NUMBER, CONSTRAINT %(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
- 'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
- 'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
+ 'reference FK': ', CONSTRAINT FK_%(constraint_name)s FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
+ 'reference TFK': ' CONSTRAINT FK_%(foreign_table)s_PK FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_table)s (%(foreign_key)s) ON DELETE %(on_delete_action)s',
'list:integer': 'CLOB',
'list:string': 'CLOB',
'list:reference': 'CLOB',
@@ -3911,7 +3911,7 @@ class MongoDBAdapter(NoSQLAdapter):
elif fieldtype == 'list:string' or fieldtype == 'list:integer' or fieldtype == 'list:reference':
return value #raise SyntaxError, "Not Supported"
return value
-
+
#Safe determines whether a asynchronious request is done or a synchronious action is done
#For safety, we use by default synchronious requests
def insert(self,table,fields,safe=None):
@@ -3920,14 +3920,14 @@ class MongoDBAdapter(NoSQLAdapter):
ctable = self.connection[table._tablename]
values = dict((k.name,self.represent(v,table[k.name].type)) for k,v in fields)
ctable.insert(values,safe=safe)
- return int(str(values['_id']), 16)
-
+ return int(str(values['_id']), 16)
+
def create_table(self, table, migrate=True, fake_migrate=False, polymodel=None, isCapped=False):
if isCapped:
raise RuntimeError, "Not implemented"
else:
pass
-
+
def count(self,query,distinct=None,snapshot=True):
if distinct:
raise RuntimeError, "COUNT DISTINCT not supported"
@@ -3966,7 +3966,7 @@ class MongoDBAdapter(NoSQLAdapter):
raise SyntaxError, 'second argument must be of type bson.objectid.ObjectId or an objectid representable integer'
elif expression.second == 0:
expression.second = pymongo.objectid.ObjectId('000000000000000000000000')
- return expression.op(expression.first, expression.second)
+ return expression.op(expression.first, expression.second)
if isinstance(expression, Field):
if expression.type=='id':
return "_id"
@@ -3988,16 +3988,16 @@ class MongoDBAdapter(NoSQLAdapter):
return ','.join(self.represent(item,field_type) for item in expression)
else:
return expression
-
+
def _select(self,query,fields,attributes):
from pymongo import son
for key in set(attributes.keys())-set(('limitby','orderby')):
raise SyntaxError, 'invalid select attribute: %s' % key
-
+
new_fields=[]
mongosort_list = []
-
+
# try an orderby attribute
orderby = attributes.get('orderby', False)
limitby = attributes.get('limitby', False)
@@ -4007,7 +4007,7 @@ class MongoDBAdapter(NoSQLAdapter):
if isinstance(orderby, (list, tuple)):
print "in xorify"
orderby = xorify(orderby)
-
+
# !!!! need to add 'random'
for f in self.expand(orderby).split(','):
@@ -4015,10 +4015,10 @@ class MongoDBAdapter(NoSQLAdapter):
mongosort_list.append((f[1:],-1))
else:
mongosort_list.append((f,1))
- print "mongosort_list = %s" % mongosort_list
-
+ print "mongosort_list = %s" % mongosort_list
+
if limitby:
- # a tuple
+ # a tuple
limitby_skip,limitby_limit = limitby
else:
limitby_skip = 0
@@ -4029,7 +4029,7 @@ class MongoDBAdapter(NoSQLAdapter):
#if distinct:
#print "in distinct %s" % distinct
-
+
mongofields_dict = son.SON()
mongoqry_dict = {}
for item in fields:
@@ -4049,7 +4049,7 @@ class MongoDBAdapter(NoSQLAdapter):
for f in fieldnames:
mongofields_dict[f.name] = 1 # ie field=1
return tablename, mongoqry_dict, mongofields_dict, mongosort_list, limitby_limit, limitby_skip
-
+
# need to define all the 'sql' methods gt,lt etc....
def select(self,query,fields,attributes,count=False,snapshot=False):
@@ -4069,10 +4069,10 @@ class MongoDBAdapter(NoSQLAdapter):
return {'count' : ctable.find(mongoqry_dict,mongofields_dict,skip=limitby_skip, limit=limitby_limit, sort=mongosort_list,snapshot=snapshot).count()}
else:
mongo_list_dicts = ctable.find(mongoqry_dict,mongofields_dict,skip=limitby_skip, limit=limitby_limit, sort=mongosort_list,snapshot=snapshot) # pymongo cursor object
- print "mongo_list_dicts=%s" % mongo_list_dicts
+ print "mongo_list_dicts=%s" % mongo_list_dicts
#if mongo_list_dicts.count() > 0: #
#colnames = mongo_list_dicts[0].keys() # assuming all docs have same "shape", grab colnames from first dictionary (aka row)
- #else:
+ #else:
#colnames = mongofields_dict.keys()
#print "colnames = %s" % colnames
#rows = [row.values() for row in mongo_list_dicts]
@@ -4095,7 +4095,7 @@ class MongoDBAdapter(NoSQLAdapter):
def INVERT(self,first):
#print "in invert first=%s" % first
- return '-%s' % self.expand(first)
+ return '-%s' % self.expand(first)
def drop(self, table, mode=''):
ctable = self.connection[table._tablename]
@@ -4208,7 +4208,7 @@ class MongoDBAdapter(NoSQLAdapter):
#return '(%s == %s)' % (self.expand(first),self.expand(second,first.type))
result[self.expand(first)] = self.expand(second)
return result
-
+
def NE(self, first, second=None):
print "in NE"
result = {}
@@ -4345,7 +4345,7 @@ class MongoDBAdapter(NoSQLAdapter):
#return '(%s == %s)' % (self.expand(first),self.expand(second,first.type))
result[self.expand(first)] = self.expand(second)
return result
-
+
def NE(self, first, second=None):
print "in NE"
result = {}
@@ -4453,14 +4453,14 @@ class IMAPAdapter(NoSQLAdapter):
IMAP server mailbox list information.
Here is a list of supported fields:
-
+
Field Type Description
################################################################
- uid string
+ uid string
answered boolean Flag
- created date
+ created date
content list:string A list of text or html parts
- to string
+ to string
cc string
bcc string
size integer the amount of octets of the message*
@@ -4470,7 +4470,7 @@ class IMAPAdapter(NoSQLAdapter):
sender string
recent boolean Flag
seen boolean Flag
- subject string
+ subject string
mime string The mime header declaration
email string The complete RFC822 message**
attachments list:string Each non text decoded part as string
@@ -4498,7 +4498,7 @@ class IMAPAdapter(NoSQLAdapter):
# Count today's unseen messages
# smaller than 6000 octets from the
# inbox mailbox
-
+
q = imapdb.INBOX.seen == False
q &= imapdb.INBOX.created == datetime.date.today()
q &= imapdb.INBOX.size < 6000
@@ -4524,7 +4524,7 @@ class IMAPAdapter(NoSQLAdapter):
# It is possible also to mark messages for deletion instead of ereasing them
# directly with set.update(deleted=True)
-
+
"""
types = {
@@ -6384,7 +6384,7 @@ class Table(dict):
if not field.name in fieldnames and not field.type=='id':
field = copy.copy(field)
# correct self references
- if not table._actual and field.type == 'reference '+table._tablename:
+ if not table._actual and field.type == 'reference '+table._tablename:
field.type = 'reference '+self._tablename
newfields.append(field)
fieldnames.add(field.name)
@@ -7373,7 +7373,7 @@ class Set(object):
def count(self,distinct=None):
return self.db._adapter.count(self.query,distinct)
- def select(self, *fields, **attributes):
+ def select(self, *fields, **attributes):
adapter = self.db._adapter
fields = adapter.expand_all(fields, adapter.tables(self.query))
return adapter.select(self.query,fields,attributes)
@@ -7624,6 +7624,23 @@ class Rows(object):
"""
return Rows(self.db,sorted(self,key=f,reverse=reverse),self.colnames)
+ def group_by_value(self, field):
+ """
+ regroups the rows, by one of the fields
+ """
+ if not self.records:
+ return {}
+ key = str(field)
+ grouped_row_group = dict()
+
+ for row in self:
+ value = row[key]
+ if not value in grouped_row_group:
+ grouped_row_group[value] = [row]
+ else:
+ grouped_row_group[value].append(row)
+ return grouped_row_group
+
def as_list(self,
compact=True,
storage_to_dict=True,
@@ -8000,3 +8017,4 @@ if __name__ == '__main__':
import doctest
doctest.testmod()
+