Merge ssh://github.com/web2py/web2py
This commit is contained in:
@@ -1 +1 @@
|
|||||||
Version 2.6.0-development+timestamp.2013.08.21.04.28.27
|
Version 2.6.0-development+timestamp.2013.08.22.03.46.07
|
||||||
|
|||||||
@@ -302,8 +302,6 @@ class Table(DALStorage):
|
|||||||
def __call__(self, id):
|
def __call__(self, id):
|
||||||
return self.get(id)
|
return self.get(id)
|
||||||
|
|
||||||
def __getitem__(self,id):
|
|
||||||
return self.get(id)
|
|
||||||
|
|
||||||
class Expression(object):
|
class Expression(object):
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
"""
|
||||||
|
BSD license - created by Massimo Di Pierro
|
||||||
|
"""
|
||||||
|
from reportlab.pdfgen.canvas import Canvas
|
||||||
|
from reportlab.platypus import Table
|
||||||
|
from reportlab.lib.pagesizes import A4
|
||||||
|
from reportlab.lib.units import cm
|
||||||
|
from decimal import Decimal
|
||||||
|
import cStringIO
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
def listify(item):
|
||||||
|
if isinstance(item,basestring):
|
||||||
|
item = item.split('\n')
|
||||||
|
return item
|
||||||
|
|
||||||
|
class PDF(object):
|
||||||
|
def __init__(self, page_size=A4, font_face='Helvetica'):
|
||||||
|
self.page_size = page_size
|
||||||
|
self.font_face = font_face
|
||||||
|
self.logo = None
|
||||||
|
def format_currency(self,value):
|
||||||
|
a = list(str(int(value)))
|
||||||
|
for k in range(len(a)-3,0,-3):
|
||||||
|
a.insert(k,',')
|
||||||
|
a = ''.join(a)
|
||||||
|
b = ("%.2f" % (value-int(value)))[2:]
|
||||||
|
return "%s.%s" % (a,b)
|
||||||
|
def draw(self, invoice, items_page=10):
|
||||||
|
""" Draws the invoice """
|
||||||
|
buffer = cStringIO.StringIO()
|
||||||
|
invoice_items = invoice['items']
|
||||||
|
pages = max((len(invoice_items)-2)/items_page+1,1)
|
||||||
|
canvas = Canvas(buffer, pagesize=self.page_size)
|
||||||
|
for page in range(pages):
|
||||||
|
canvas.translate(0, 29.7 * cm)
|
||||||
|
canvas.setFont(self.font_face, 10)
|
||||||
|
|
||||||
|
canvas.saveState()
|
||||||
|
canvas.setStrokeColorRGB(0.9, 0.5, 0.2)
|
||||||
|
canvas.setFillColorRGB(0.2, 0.2, 0.2)
|
||||||
|
canvas.setFont(self.font_face, 16)
|
||||||
|
canvas.drawString(1 * cm, -1 * cm, invoice.get('title',''))
|
||||||
|
if self.logo:
|
||||||
|
canvas.drawInlineImage(self.logo, 1 * cm, -1 * cm, 250, 16)
|
||||||
|
canvas.setLineWidth(4)
|
||||||
|
canvas.line(0, -1.25 * cm, 21.7 * cm, -1.25 * cm)
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
canvas.saveState()
|
||||||
|
notes = listify(invoice.get('notes',''))
|
||||||
|
textobject = canvas.beginText(1 * cm, -25 * cm)
|
||||||
|
for line in notes:
|
||||||
|
textobject.textLine(line)
|
||||||
|
canvas.drawText(textobject)
|
||||||
|
textobject = canvas.beginText(18 * cm, -28 * cm)
|
||||||
|
textobject.textLine('Pag.%s/%s' % (page+1,pages))
|
||||||
|
canvas.drawText(textobject)
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
canvas.saveState()
|
||||||
|
business_details = listify(invoice.get('from','FROM:'))
|
||||||
|
canvas.setFont(self.font_face, 9)
|
||||||
|
textobject = canvas.beginText(13 * cm, -2.5 * cm)
|
||||||
|
for line in business_details:
|
||||||
|
textobject.textLine(line)
|
||||||
|
canvas.drawText(textobject)
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
canvas.saveState()
|
||||||
|
client_info = listify(invoice.get('to','TO:'))
|
||||||
|
textobject = canvas.beginText(1.5 * cm, -2.5 * cm)
|
||||||
|
for line in client_info:
|
||||||
|
textobject.textLine(line)
|
||||||
|
canvas.drawText(textobject)
|
||||||
|
canvas.restoreState()
|
||||||
|
|
||||||
|
textobject = canvas.beginText(1.5 * cm, -6.75 * cm)
|
||||||
|
textobject.textLine(u'Invoice ID: %s' % invoice.get('id','<invoice id>'))
|
||||||
|
textobject.textLine(u'Invoice Date: %s' % invoice.get('date',datetime.date.today()))
|
||||||
|
textobject.textLine(u'Client: %s' % invoice.get('client_name','<invoice client>'))
|
||||||
|
canvas.drawText(textobject)
|
||||||
|
|
||||||
|
items = invoice_items[1:][page*items_page:(page+1)*items_page]
|
||||||
|
if items:
|
||||||
|
data = [invoice_items[0]]
|
||||||
|
for item in items:
|
||||||
|
data.append([
|
||||||
|
self.format_currency(x)
|
||||||
|
if isinstance(x,float) else x
|
||||||
|
for x in item])
|
||||||
|
righta = [k for k,v in enumerate(items[0])
|
||||||
|
if isinstance(v,(int,float,Decimal))]
|
||||||
|
if page == pages-1:
|
||||||
|
total = self.format_currency(invoice['total'])
|
||||||
|
else:
|
||||||
|
total = ''
|
||||||
|
data.append(['']*(len(items[0])-1)+[total])
|
||||||
|
colWidths = [2.5*cm]*len(items[0])
|
||||||
|
colWidths[1] = (21.5-2.5*len(items[0]))*cm
|
||||||
|
table = Table(data, colWidths=colWidths)
|
||||||
|
table.setStyle([
|
||||||
|
('FONT', (0, 0), (-1, -1), self.font_face),
|
||||||
|
('FONTSIZE', (0, 0), (-1, -1), 8),
|
||||||
|
('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)),
|
||||||
|
('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)),
|
||||||
|
('GRID', (-1, -1), (-1, -1), 1, (0.7, 0.7, 0.7)),
|
||||||
|
('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)),
|
||||||
|
]+[('ALIGN',(k,0),(k,-1),'RIGHT') for k in righta])
|
||||||
|
tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm)
|
||||||
|
table.drawOn(canvas, 1 * cm, -8 * cm - th)
|
||||||
|
|
||||||
|
if page == pages-1:
|
||||||
|
items = invoice['totals'][1:]
|
||||||
|
if items:
|
||||||
|
data = [invoice['totals'][0]]
|
||||||
|
for item in items:
|
||||||
|
data.append([
|
||||||
|
self.format_currency(x)
|
||||||
|
if isinstance(x,float) else x
|
||||||
|
for x in item])
|
||||||
|
righta = [k for k,v in enumerate(items[0])
|
||||||
|
if isinstance(v,(int,float,Decimal))]
|
||||||
|
total = self.format_currency(invoice['total'])
|
||||||
|
data.append(['']*(len(items[0])-1)+[total])
|
||||||
|
colWidths = [2.5*cm]*len(items[0])
|
||||||
|
colWidths[1] = (21.5-2.5*len(items[0]))*cm
|
||||||
|
table = Table(data, colWidths=colWidths)
|
||||||
|
table.setStyle([
|
||||||
|
('FONT', (0, 0), (-1, -1), self.font_face),
|
||||||
|
('FONTSIZE', (0, 0), (-1, -1), 8),
|
||||||
|
('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)),
|
||||||
|
('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)),
|
||||||
|
('GRID', (-1, -1), (-1, -1), 1, (0.7, 0.7, 0.7)),
|
||||||
|
('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)),
|
||||||
|
]+[('ALIGN',(k,0),(k,-1),'RIGHT') for k in righta])
|
||||||
|
tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm)
|
||||||
|
table.drawOn(canvas, 1 * cm, -18 * cm - th)
|
||||||
|
canvas.showPage()
|
||||||
|
canvas.save()
|
||||||
|
return buffer.getvalue()
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
invoice = {
|
||||||
|
'title': 'Invoice - web2py.com',
|
||||||
|
'id': '00001',
|
||||||
|
'date': '10/10/2013',
|
||||||
|
'client_name': 'Nobody',
|
||||||
|
'from': 'FROM:\nweb2py.com\nWabash ave\nChicago',
|
||||||
|
'to': 'TO:\nNobody\nHis address',
|
||||||
|
'notes': 'no comment!',
|
||||||
|
'total': 650.00,
|
||||||
|
'items': [
|
||||||
|
['Codice','Desc','Quantity','Unit price','Total']]+[
|
||||||
|
['000001','Chair',2,10.0,20.0] for k in range(30)],
|
||||||
|
'totals': [
|
||||||
|
['Codice','Desc','Total']]+[
|
||||||
|
['000001','Chairs',600.0],
|
||||||
|
['','Tax',50.0]],
|
||||||
|
}
|
||||||
|
print PDF().draw(invoice,items_page=20)
|
||||||
+192
-90
@@ -6932,7 +6932,7 @@ def sqlhtml_validators(field):
|
|||||||
refs = reduce(lambda a,b:a&b, [count(ids[i:i+30]) for i in rx])
|
refs = reduce(lambda a,b:a&b, [count(ids[i:i+30]) for i in rx])
|
||||||
else:
|
else:
|
||||||
refs = db(id.belongs(ids)).select(id)
|
refs = db(id.belongs(ids)).select(id)
|
||||||
return (refs and ', '.join(str(f(r,x.id)) for x in refs) or '')
|
return (refs and ', '.join(f(r,x.id) for x in refs) or '')
|
||||||
field.represent = field.represent or list_ref_repr
|
field.represent = field.represent or list_ref_repr
|
||||||
if hasattr(referenced, '_format') and referenced._format:
|
if hasattr(referenced, '_format') and referenced._format:
|
||||||
requires = validators.IS_IN_DB(db,referenced._id,
|
requires = validators.IS_IN_DB(db,referenced._id,
|
||||||
@@ -7252,7 +7252,7 @@ class DAL(object):
|
|||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
db = DAL(**{"uri": ..., "tables": [...]...}) # experimental
|
db = DAL({"uri": ..., "items": ...}) # experimental
|
||||||
|
|
||||||
db.define_table('tablename', Field('fieldname1'),
|
db.define_table('tablename', Field('fieldname1'),
|
||||||
Field('fieldname2'))
|
Field('fieldname2'))
|
||||||
@@ -7367,9 +7367,8 @@ class DAL(object):
|
|||||||
migrate_enabled=True, fake_migrate_all=False,
|
migrate_enabled=True, fake_migrate_all=False,
|
||||||
decode_credentials=False, driver_args=None,
|
decode_credentials=False, driver_args=None,
|
||||||
adapter_args=None, attempts=5, auto_import=False,
|
adapter_args=None, attempts=5, auto_import=False,
|
||||||
bigint_id=False, debug=False, lazy_tables=False,
|
bigint_id=False,debug=False,lazy_tables=False,
|
||||||
db_uid=None, do_connect=True,
|
db_uid=None, do_connect=True, after_connection=None):
|
||||||
after_connection=None, tables=None):
|
|
||||||
"""
|
"""
|
||||||
Creates a new Database Abstraction Layer instance.
|
Creates a new Database Abstraction Layer instance.
|
||||||
|
|
||||||
@@ -7381,7 +7380,7 @@ class DAL(object):
|
|||||||
experimental: you can specify a dictionary as uri
|
experimental: you can specify a dictionary as uri
|
||||||
parameter i.e. with
|
parameter i.e. with
|
||||||
db = DAL({"uri": "sqlite://storage.sqlite",
|
db = DAL({"uri": "sqlite://storage.sqlite",
|
||||||
"tables": {...}, ...})
|
"items": {...}, ...})
|
||||||
|
|
||||||
for an example of dict input you can check the output
|
for an example of dict input you can check the output
|
||||||
of the scaffolding db model with
|
of the scaffolding db model with
|
||||||
@@ -7425,6 +7424,18 @@ class DAL(object):
|
|||||||
:lazy_tables (defaults to False): delay table definition until table access
|
:lazy_tables (defaults to False): delay table definition until table access
|
||||||
:after_connection (defaults to None): a callable that will be execute after the connection
|
:after_connection (defaults to None): a callable that will be execute after the connection
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
items = None
|
||||||
|
if isinstance(uri, dict):
|
||||||
|
if "items" in uri:
|
||||||
|
items = uri.pop("items")
|
||||||
|
try:
|
||||||
|
newuri = uri.pop("uri")
|
||||||
|
except KeyError:
|
||||||
|
newuri = DEFAULT_URI
|
||||||
|
locals().update(uri)
|
||||||
|
uri = newuri
|
||||||
|
|
||||||
if uri == '<zombie>' and db_uid is not None: return
|
if uri == '<zombie>' and db_uid is not None: return
|
||||||
if not decode_credentials:
|
if not decode_credentials:
|
||||||
credential_decoder = lambda cred: cred
|
credential_decoder = lambda cred: cred
|
||||||
@@ -7517,20 +7528,31 @@ class DAL(object):
|
|||||||
self._fake_migrate = fake_migrate
|
self._fake_migrate = fake_migrate
|
||||||
self._migrate_enabled = migrate_enabled
|
self._migrate_enabled = migrate_enabled
|
||||||
self._fake_migrate_all = fake_migrate_all
|
self._fake_migrate_all = fake_migrate_all
|
||||||
if auto_import or tables:
|
if auto_import or items:
|
||||||
self.import_table_definitions(adapter.folder,
|
self.import_table_definitions(adapter.folder,
|
||||||
tables=tables)
|
items=items)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tables(self):
|
def tables(self):
|
||||||
return self._tables
|
return self._tables
|
||||||
|
|
||||||
def import_table_definitions(self, path, migrate=False,
|
def import_table_definitions(self, path, migrate=False,
|
||||||
fake_migrate=False, tables=None):
|
fake_migrate=False, items=None):
|
||||||
pattern = pjoin(path,self._uri_hash+'_*.table')
|
pattern = pjoin(path,self._uri_hash+'_*.table')
|
||||||
if tables:
|
if items:
|
||||||
for table in tables:
|
for tablename, table in items.iteritems():
|
||||||
self.define_table(**table)
|
# TODO: read all field/table options
|
||||||
|
fields = []
|
||||||
|
# remove unsupported/illegal Table arguments
|
||||||
|
[table.pop(name) for name in ("name", "fields") if
|
||||||
|
name in table]
|
||||||
|
if "items" in table:
|
||||||
|
for fieldname, field in table.pop("items").iteritems():
|
||||||
|
# remove unsupported/illegal Field arguments
|
||||||
|
[field.pop(key) for key in ("requires", "name",
|
||||||
|
"compute", "colname") if key in field]
|
||||||
|
fields.append(Field(str(fieldname), **field))
|
||||||
|
self.define_table(str(tablename), *fields, **table)
|
||||||
else:
|
else:
|
||||||
for filename in glob.glob(pattern):
|
for filename in glob.glob(pattern):
|
||||||
tfile = self._adapter.file_open(filename, 'r')
|
tfile = self._adapter.file_open(filename, 'r')
|
||||||
@@ -7828,14 +7850,8 @@ def index():
|
|||||||
):
|
):
|
||||||
if not fields and 'fields' in args:
|
if not fields and 'fields' in args:
|
||||||
fields = args.get('fields',())
|
fields = args.get('fields',())
|
||||||
if not isinstance(tablename, str):
|
if not isinstance(tablename,str):
|
||||||
if isinstance(tablename, unicode):
|
raise SyntaxError("missing table name")
|
||||||
try:
|
|
||||||
tablename = str(tablename)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
raise SyntaxError("invalid unicode table name")
|
|
||||||
else:
|
|
||||||
raise SyntaxError("missing table name")
|
|
||||||
elif hasattr(self,tablename) or tablename in self.tables:
|
elif hasattr(self,tablename) or tablename in self.tables:
|
||||||
if not args.get('redefine',False):
|
if not args.get('redefine',False):
|
||||||
raise SyntaxError('table already defined: %s' % tablename)
|
raise SyntaxError('table already defined: %s' % tablename)
|
||||||
@@ -7899,40 +7915,48 @@ def index():
|
|||||||
if on_define: on_define(table)
|
if on_define: on_define(table)
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def as_dict(self, flat=False, sanitize=True):
|
def as_dict(self, flat=False, sanitize=True, field_options=True):
|
||||||
db_uid = uri = None
|
dbname = db_uid = uri = None
|
||||||
if not sanitize:
|
if not sanitize:
|
||||||
uri, db_uid = (self._uri, self._db_uid)
|
uri, dbname, db_uid = (self._uri, self._dbname, self._db_uid)
|
||||||
db_as_dict = dict(tables=[], uri=uri, db_uid=db_uid,
|
db_as_dict = dict(items={}, tables=[], uri=uri, dbname=dbname,
|
||||||
**dict([(k, getattr(self, "_" + k, None))
|
db_uid=db_uid,
|
||||||
for k in 'pool_size','folder','db_codec',
|
**dict([(k, getattr(self, "_" + k)) for
|
||||||
|
k in 'pool_size','folder','db_codec',
|
||||||
'check_reserved','migrate','fake_migrate',
|
'check_reserved','migrate','fake_migrate',
|
||||||
'migrate_enabled','fake_migrate_all',
|
'migrate_enabled','fake_migrate_all',
|
||||||
'decode_credentials','driver_args',
|
'decode_credentials','driver_args',
|
||||||
'adapter_args', 'attempts',
|
'adapter_args', 'attempts',
|
||||||
'bigint_id','debug','lazy_tables',
|
'bigint_id','debug','lazy_tables',
|
||||||
'do_connect']))
|
'do_connect']))
|
||||||
|
|
||||||
for table in self:
|
for table in self:
|
||||||
db_as_dict["tables"].append(table.as_dict(flat=flat,
|
tablename = str(table)
|
||||||
sanitize=sanitize))
|
db_as_dict["tables"].append(tablename)
|
||||||
|
db_as_dict["items"][tablename] = table.as_dict(flat=flat,
|
||||||
|
sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return db_as_dict
|
return db_as_dict
|
||||||
|
|
||||||
def as_xml(self, sanitize=True):
|
def as_xml(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No xml serializers available")
|
raise ImportError("No xml serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.xml(d)
|
return serializers.xml(d)
|
||||||
|
|
||||||
def as_json(self, sanitize=True):
|
def as_json(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No json serializers available")
|
raise ImportError("No json serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.json(d)
|
return serializers.json(d)
|
||||||
|
|
||||||
def as_yaml(self, sanitize=True):
|
def as_yaml(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No YAML serializers available")
|
raise ImportError("No YAML serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.yaml(d)
|
return serializers.yaml(d)
|
||||||
|
|
||||||
def __contains__(self, tablename):
|
def __contains__(self, tablename):
|
||||||
@@ -8300,9 +8324,7 @@ class Table(object):
|
|||||||
if len(self._primarykey)==1:
|
if len(self._primarykey)==1:
|
||||||
self._id = [f for f in fields if isinstance(f,Field) \
|
self._id = [f for f in fields if isinstance(f,Field) \
|
||||||
and f.name==self._primarykey[0]][0]
|
and f.name==self._primarykey[0]][0]
|
||||||
elif not [f for f in fields if (isinstance(f,Field) and
|
elif not [f for f in fields if isinstance(f,Field) and f.type=='id']:
|
||||||
f.type=='id') or (isinstance(f, dict) and
|
|
||||||
f.get("type", None)=="id")]:
|
|
||||||
field = Field('id', 'id')
|
field = Field('id', 'id')
|
||||||
newfields.append(field)
|
newfields.append(field)
|
||||||
fieldnames.add('id')
|
fieldnames.add('id')
|
||||||
@@ -8320,7 +8342,8 @@ class Table(object):
|
|||||||
if field.db is not None:
|
if field.db is not None:
|
||||||
field = copy.copy(field)
|
field = copy.copy(field)
|
||||||
include_new(field)
|
include_new(field)
|
||||||
elif isinstance(field, dict) and not field['fieldname'] in fieldnames:
|
elif isinstance(field, dict) and 'fieldname' and \
|
||||||
|
not field['fieldname'] in fieldnames:
|
||||||
include_new(Field(**field))
|
include_new(Field(**field))
|
||||||
elif isinstance(field, Table):
|
elif isinstance(field, Table):
|
||||||
table = field
|
table = field
|
||||||
@@ -8709,6 +8732,32 @@ class Table(object):
|
|||||||
response.id = None
|
response.id = None
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def validate_and_update(self, _key=DEFAULT, **fields):
|
||||||
|
response = Row()
|
||||||
|
response.errors = Row()
|
||||||
|
new_fields = copy.copy(fields)
|
||||||
|
|
||||||
|
for key,value in fields.iteritems():
|
||||||
|
value,error = self[key].validate(value)
|
||||||
|
if error:
|
||||||
|
response.errors[key] = "%s" % error
|
||||||
|
else:
|
||||||
|
new_fields[key] = value
|
||||||
|
|
||||||
|
if _key is DEFAULT:
|
||||||
|
record = self(**values)
|
||||||
|
elif isinstance(_key,dict):
|
||||||
|
record = self(**_key)
|
||||||
|
else:
|
||||||
|
record = self(_key)
|
||||||
|
|
||||||
|
if not response.errors and record:
|
||||||
|
row = self._db(self._id==_key)
|
||||||
|
response.id = row.update(**fields)
|
||||||
|
else:
|
||||||
|
response.id = None
|
||||||
|
return response
|
||||||
|
|
||||||
def update_or_insert(self, _key=DEFAULT, **values):
|
def update_or_insert(self, _key=DEFAULT, **values):
|
||||||
if _key is DEFAULT:
|
if _key is DEFAULT:
|
||||||
record = self(**values)
|
record = self(**values)
|
||||||
@@ -8875,8 +8924,9 @@ class Table(object):
|
|||||||
if id_map and cid is not None:
|
if id_map and cid is not None:
|
||||||
id_map_self[long(line[cid])] = new_id
|
id_map_self[long(line[cid])] = new_id
|
||||||
|
|
||||||
def as_dict(self, flat=False, sanitize=True):
|
def as_dict(self, flat=False, sanitize=True, field_options=True):
|
||||||
table_as_dict = dict(tablename=str(self), fields=[],
|
tablename = str(self)
|
||||||
|
table_as_dict = dict(name=tablename, items={}, fields=[],
|
||||||
sequence_name=self._sequence_name,
|
sequence_name=self._sequence_name,
|
||||||
trigger_name=self._trigger_name,
|
trigger_name=self._trigger_name,
|
||||||
common_filter=self._common_filter, format=self._format,
|
common_filter=self._common_filter, format=self._format,
|
||||||
@@ -8884,26 +8934,31 @@ class Table(object):
|
|||||||
|
|
||||||
for field in self:
|
for field in self:
|
||||||
if (field.readable or field.writable) or (not sanitize):
|
if (field.readable or field.writable) or (not sanitize):
|
||||||
table_as_dict["fields"].append(field.as_dict(
|
table_as_dict["fields"].append(field.name)
|
||||||
flat=flat, sanitize=sanitize))
|
table_as_dict["items"][field.name] = \
|
||||||
|
field.as_dict(flat=flat, sanitize=sanitize,
|
||||||
|
options=field_options)
|
||||||
return table_as_dict
|
return table_as_dict
|
||||||
|
|
||||||
def as_xml(self, sanitize=True):
|
def as_xml(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No xml serializers available")
|
raise ImportError("No xml serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.xml(d)
|
return serializers.xml(d)
|
||||||
|
|
||||||
def as_json(self, sanitize=True):
|
def as_json(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No json serializers available")
|
raise ImportError("No json serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.json(d)
|
return serializers.json(d)
|
||||||
|
|
||||||
def as_yaml(self, sanitize=True):
|
def as_yaml(self, sanitize=True, field_options=True):
|
||||||
if not have_serializers:
|
if not have_serializers:
|
||||||
raise ImportError("No YAML serializers available")
|
raise ImportError("No YAML serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
field_options=field_options)
|
||||||
return serializers.yaml(d)
|
return serializers.yaml(d)
|
||||||
|
|
||||||
def with_alias(self, alias):
|
def with_alias(self, alias):
|
||||||
@@ -9384,13 +9439,8 @@ class Field(Expression):
|
|||||||
self.op = None
|
self.op = None
|
||||||
self.first = None
|
self.first = None
|
||||||
self.second = None
|
self.second = None
|
||||||
if isinstance(fieldname, unicode):
|
|
||||||
try:
|
|
||||||
fieldname = str(fieldname)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
raise SyntaxError('Field: invalid unicode field name')
|
|
||||||
self.name = fieldname = cleanup(fieldname)
|
self.name = fieldname = cleanup(fieldname)
|
||||||
if not isinstance(fieldname, str) or hasattr(Table, fieldname) or \
|
if not isinstance(fieldname,str) or hasattr(Table,fieldname) or \
|
||||||
fieldname[0] == '_' or REGEX_PYTHON_KEYWORDS.match(fieldname):
|
fieldname[0] == '_' or REGEX_PYTHON_KEYWORDS.match(fieldname):
|
||||||
raise SyntaxError('Field: invalid field name: %s' % fieldname)
|
raise SyntaxError('Field: invalid field name: %s' % fieldname)
|
||||||
self.type = type if not isinstance(type, (Table,Field)) else 'reference %s' % type
|
self.type = type if not isinstance(type, (Table,Field)) else 'reference %s' % type
|
||||||
@@ -9590,61 +9640,113 @@ class Field(Expression):
|
|||||||
def count(self, distinct=None):
|
def count(self, distinct=None):
|
||||||
return Expression(self.db, self.db._adapter.COUNT, self, distinct, 'integer')
|
return Expression(self.db, self.db._adapter.COUNT, self, distinct, 'integer')
|
||||||
|
|
||||||
def as_dict(self, flat=False, sanitize=True):
|
def as_dict(self, flat=False, sanitize=True, options=True):
|
||||||
attrs = ("name", 'authorize', 'represent', 'ondelete',
|
|
||||||
'custom_store', 'autodelete', 'custom_retrieve',
|
attrs = ('type', 'length', 'default', 'required',
|
||||||
'filter_out', 'uploadseparate', 'widget', 'uploadfs',
|
'ondelete', 'notnull', 'unique', 'uploadfield',
|
||||||
'update', 'custom_delete', 'uploadfield', 'uploadfolder',
|
'widget', 'label', 'comment', 'writable', 'readable',
|
||||||
'custom_qualifier', 'unique', 'writable', 'compute',
|
'update', 'authorize', 'autodelete', 'represent',
|
||||||
'map_none', 'default', 'type', 'required', 'readable',
|
'uploadfolder', 'uploadseparate', 'uploadfs',
|
||||||
'requires', 'comment', 'label', 'length', 'notnull',
|
'compute', 'custom_store', 'custom_retrieve',
|
||||||
'custom_retrieve_file_properties', 'filter_in')
|
'custom_retrieve_file_properties', 'custom_delete',
|
||||||
serializable = (int, long, basestring, float, tuple,
|
'filter_in', 'filter_out', 'custom_qualifier',
|
||||||
bool, type(None))
|
'map_none', 'name')
|
||||||
|
|
||||||
|
SERIALIZABLE_TYPES = (int, long, basestring, dict, list,
|
||||||
|
float, tuple, bool, type(None))
|
||||||
|
|
||||||
def flatten(obj):
|
def flatten(obj):
|
||||||
if isinstance(obj, dict):
|
if flat:
|
||||||
return dict((flatten(k), flatten(v)) for k, v in
|
if isinstance(obj, flatten.__class__):
|
||||||
obj.items())
|
return str(type(obj))
|
||||||
elif isinstance(obj, (tuple, list, set)):
|
elif isinstance(obj, type):
|
||||||
return [flatten(v) for v in obj]
|
try:
|
||||||
elif isinstance(obj, serializable):
|
return str(obj).split("'")[1]
|
||||||
return obj
|
except IndexError:
|
||||||
elif isinstance(obj, (datetime.datetime,
|
return str(obj)
|
||||||
datetime.date, datetime.time)):
|
elif not isinstance(obj, SERIALIZABLE_TYPES):
|
||||||
return str(obj)
|
return str(obj)
|
||||||
else:
|
elif isinstance(obj, dict):
|
||||||
|
newobj = dict()
|
||||||
|
for k, v in obj.items():
|
||||||
|
newobj[k] = flatten(v)
|
||||||
|
return newobj
|
||||||
|
elif isinstance(obj, (list, tuple, set)):
|
||||||
|
return [flatten(v) for v in obj]
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
elif isinstance(obj, (dict, set)):
|
||||||
|
return obj.copy()
|
||||||
|
else: return obj
|
||||||
|
|
||||||
|
def filter_requires(t, r, options=True):
|
||||||
|
if sanitize and any([keyword in str(t).upper() for
|
||||||
|
keyword in ("CRYPT", "IS_STRONG")]):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
d = dict()
|
if not isinstance(r, dict):
|
||||||
if not (sanitize and not (self.readable or self.writable)):
|
if options and hasattr(r, "options"):
|
||||||
for attr in attrs:
|
if callable(r.options):
|
||||||
if flat:
|
r.options()
|
||||||
d.update({attr: flatten(getattr(self, attr))})
|
newr = r.__dict__.copy()
|
||||||
else:
|
else:
|
||||||
d.update({attr: getattr(self, attr)})
|
newr = r.copy()
|
||||||
d["fieldname"] = d.pop("name")
|
|
||||||
|
# remove options if not required
|
||||||
|
if not options and newr.has_key("labels"):
|
||||||
|
[newr.update({key:None}) for key in
|
||||||
|
("labels", "theset") if (key in newr)]
|
||||||
|
|
||||||
|
for k, v in newr.items():
|
||||||
|
if k == "other":
|
||||||
|
if isinstance(v, dict):
|
||||||
|
otype, other = v.popitem()
|
||||||
|
else:
|
||||||
|
otype = flatten(type(v))
|
||||||
|
other = v
|
||||||
|
newr[k] = {otype: filter_requires(otype, other,
|
||||||
|
options=options)}
|
||||||
|
else:
|
||||||
|
newr[k] = flatten(v)
|
||||||
|
return newr
|
||||||
|
|
||||||
|
if isinstance(self.requires, (tuple, list, set)):
|
||||||
|
requires = dict([(flatten(type(r)),
|
||||||
|
filter_requires(type(r), r,
|
||||||
|
options=options)) for
|
||||||
|
r in self.requires])
|
||||||
|
else:
|
||||||
|
requires = {flatten(type(self.requires)):
|
||||||
|
filter_requires(type(self.requires),
|
||||||
|
self.requires, options=options)}
|
||||||
|
|
||||||
|
d = dict(colname="%s.%s" % (self.tablename, self.name),
|
||||||
|
requires=requires)
|
||||||
|
d.update([(attr, flatten(getattr(self, attr))) for attr in attrs])
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def as_xml(self, sanitize=True):
|
def as_xml(self, sanitize=True, options=True):
|
||||||
if have_serializers:
|
if have_serializers:
|
||||||
xml = serializers.xml
|
xml = serializers.xml
|
||||||
else:
|
else:
|
||||||
raise ImportError("No xml serializers available")
|
raise ImportError("No xml serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
options=options)
|
||||||
return xml(d)
|
return xml(d)
|
||||||
|
|
||||||
def as_json(self, sanitize=True):
|
def as_json(self, sanitize=True, options=True):
|
||||||
if have_serializers:
|
if have_serializers:
|
||||||
json = serializers.json
|
json = serializers.json
|
||||||
else:
|
else:
|
||||||
raise ImportError("No json serializers available")
|
raise ImportError("No json serializers available")
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
options=options)
|
||||||
return json(d)
|
return json(d)
|
||||||
|
|
||||||
def as_yaml(self, sanitize=True):
|
def as_yaml(self, sanitize=True, options=True):
|
||||||
if have_serializers:
|
if have_serializers:
|
||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize,
|
||||||
|
options=options)
|
||||||
return serializers.yaml(d)
|
return serializers.yaml(d)
|
||||||
else:
|
else:
|
||||||
raise ImportError("No YAML serializers available")
|
raise ImportError("No YAML serializers available")
|
||||||
|
|||||||
+10
-2
@@ -196,8 +196,11 @@ class Request(Storage):
|
|||||||
if (body and
|
if (body and
|
||||||
env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH') and
|
env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH') and
|
||||||
not is_json):
|
not is_json):
|
||||||
|
query_string = env.pop('QUERY_STRING') if 'QUERY_STRING' in env else None
|
||||||
dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
|
dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
|
||||||
post_vars.update(dpost)
|
post_vars.update(dpost)
|
||||||
|
if query_string is not None:
|
||||||
|
env['QUERY_STRING'] = query_string
|
||||||
# The same detection used by FieldStorage to detect multipart POSTs
|
# The same detection used by FieldStorage to detect multipart POSTs
|
||||||
is_multipart = dpost.type[:10] == 'multipart/'
|
is_multipart = dpost.type[:10] == 'multipart/'
|
||||||
body.seek(0)
|
body.seek(0)
|
||||||
@@ -880,7 +883,9 @@ class Session(Storage):
|
|||||||
if response.session_new:
|
if response.session_new:
|
||||||
return
|
return
|
||||||
# Get session data out of the database
|
# Get session data out of the database
|
||||||
(record_id, unique_key) = response.session_id.split(':')
|
if response.session_id is None:
|
||||||
|
return
|
||||||
|
(record_id, sep, unique_key) = response.session_id.partition(':')
|
||||||
|
|
||||||
if record_id.isdigit() and long(record_id)>1:
|
if record_id.isdigit() and long(record_id)>1:
|
||||||
new_unique_key = web2py_uuid()
|
new_unique_key = web2py_uuid()
|
||||||
@@ -912,9 +917,12 @@ class Session(Storage):
|
|||||||
if response.session_id:
|
if response.session_id:
|
||||||
rcookies[response.session_id_name] = response.session_id
|
rcookies[response.session_id_name] = response.session_id
|
||||||
rcookies[response.session_id_name]['path'] = '/'
|
rcookies[response.session_id_name]['path'] = '/'
|
||||||
if response.session_cookie_expires:
|
if isinstance(response.session_cookie_expires,datetime.datetime):
|
||||||
rcookies[response.session_id_name]['expires'] = \
|
rcookies[response.session_id_name]['expires'] = \
|
||||||
response.session_cookie_expires.strftime(FMT)
|
response.session_cookie_expires.strftime(FMT)
|
||||||
|
elif isinstance(response.session_cookie_expires,str):
|
||||||
|
rcookies[response.session_id_name]['expires'] = \
|
||||||
|
response.session_cookie_expires
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
previous_session_hash = self.pop('_session_hash', None)
|
previous_session_hash = self.pop('_session_hash', None)
|
||||||
|
|||||||
+1
-4
@@ -3095,10 +3095,7 @@ class ExporterHTML(ExportClass):
|
|||||||
ExportClass.__init__(self, rows)
|
ExportClass.__init__(self, rows)
|
||||||
|
|
||||||
def export(self):
|
def export(self):
|
||||||
if self.rows:
|
return '<html>\n<head>\n<meta http-equiv="content-type" content="text/html; charset=UTF-8" />\n</head>\n<body>\n%s\n</body>\n</html>' % (self.rows.xml() or '')
|
||||||
return self.rows.xml()
|
|
||||||
else:
|
|
||||||
return '<html>\n<body>\n<table>\n</table>\n</body>\n</html>'
|
|
||||||
|
|
||||||
class ExporterXML(ExportClass):
|
class ExporterXML(ExportClass):
|
||||||
label = 'XML'
|
label = 'XML'
|
||||||
|
|||||||
+33
-29
@@ -701,12 +701,13 @@ class TestDALDictImportExport(unittest.TestCase):
|
|||||||
assert isinstance(dbdict, dict)
|
assert isinstance(dbdict, dict)
|
||||||
uri = dbdict["uri"]
|
uri = dbdict["uri"]
|
||||||
assert isinstance(uri, basestring) and uri
|
assert isinstance(uri, basestring) and uri
|
||||||
assert len(dbdict["tables"]) == 2
|
assert len(dbdict["items"]) == 2
|
||||||
assert len(dbdict["tables"][0]["fields"]) == 3
|
assert len(dbdict["items"]["person"]["items"]) == 3
|
||||||
assert dbdict["tables"][0]["fields"][1]["type"] == db.person.name.type
|
assert dbdict["items"]["person"]["items"]["name"]["type"] == db.person.name.type
|
||||||
assert dbdict["tables"][0]["fields"][1]["default"] == db.person.name.default
|
assert dbdict["items"]["person"]["items"]["name"]["default"] == db.person.name.default
|
||||||
|
assert dbdict
|
||||||
|
|
||||||
db2 = DAL(**dbdict)
|
db2 = DAL(dbdict, check_reserved=['all'])
|
||||||
assert len(db.tables) == len(db2.tables)
|
assert len(db.tables) == len(db2.tables)
|
||||||
assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
|
assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
|
||||||
assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
|
assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
|
||||||
@@ -724,7 +725,7 @@ class TestDALDictImportExport(unittest.TestCase):
|
|||||||
unicode_keys = True
|
unicode_keys = True
|
||||||
if sys.version < "2.6.5":
|
if sys.version < "2.6.5":
|
||||||
unicode_keys = False
|
unicode_keys = False
|
||||||
db3 = DAL(**serializers.loads_json(dbjson,
|
db3 = DAL(serializers.loads_json(dbjson,
|
||||||
unicode_keys=unicode_keys))
|
unicode_keys=unicode_keys))
|
||||||
assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
|
assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
|
||||||
db3.person.uuid.type == db.person.uuid.type
|
db3.person.uuid.type == db.person.uuid.type
|
||||||
@@ -735,19 +736,18 @@ class TestDALDictImportExport(unittest.TestCase):
|
|||||||
|
|
||||||
mpfc = "Monty Python's Flying Circus"
|
mpfc = "Monty Python's Flying Circus"
|
||||||
dbdict4 = {"uri": DEFAULT_URI,
|
dbdict4 = {"uri": DEFAULT_URI,
|
||||||
"tables":[{"tablename": "staff",
|
"items":{"staff":{"items": {"name":
|
||||||
"fields": [{"fieldname": "name",
|
{"default":"Michael"},
|
||||||
"default":"Michael"},
|
"food":
|
||||||
{"fieldname": "food",
|
{"default":"Spam"},
|
||||||
"default":"Spam"},
|
"tvshow":
|
||||||
{"fieldname": "tvshow",
|
{"type": "reference tvshow"}
|
||||||
"type": "reference tvshow"}]},
|
}},
|
||||||
{"tablename": "tvshow",
|
"tvshow":{"items": {"name":
|
||||||
"fields": [{"fieldname": "name",
|
{"default":mpfc},
|
||||||
"default":mpfc},
|
"rating":
|
||||||
{"fieldname": "rating",
|
{"type":"double"}}}}}
|
||||||
"type":"double"}]}]}
|
db4 = DAL(dbdict4, check_reserved=['all'])
|
||||||
db4 = DAL(**dbdict4)
|
|
||||||
assert "staff" in db4.tables
|
assert "staff" in db4.tables
|
||||||
assert "name" in db4.staff
|
assert "name" in db4.staff
|
||||||
assert db4.tvshow.rating.type == "double"
|
assert db4.tvshow.rating.type == "double"
|
||||||
@@ -761,19 +761,20 @@ class TestDALDictImportExport(unittest.TestCase):
|
|||||||
db4.commit()
|
db4.commit()
|
||||||
|
|
||||||
dbdict5 = {"uri": DEFAULT_URI}
|
dbdict5 = {"uri": DEFAULT_URI}
|
||||||
db5 = DAL(**dbdict5)
|
db5 = DAL(dbdict5, check_reserved=['all'])
|
||||||
assert db5.tables in ([], None)
|
assert db5.tables in ([], None)
|
||||||
assert not (str(db5) in ("", None))
|
assert not (str(db5) in ("", None))
|
||||||
|
|
||||||
dbdict6 = {"uri": DEFAULT_URI,
|
dbdict6 = {"uri": DEFAULT_URI,
|
||||||
"tables":[{"tablename": "staff"},
|
"items":{"staff":{},
|
||||||
{"tablename": "tvshow",
|
"tvshow":{"items": {"name": {},
|
||||||
"fields": [{"fieldname": "name"},
|
"rating":
|
||||||
{"fieldname": "rating", "type":"double"}
|
{"type":"double"}
|
||||||
]
|
}
|
||||||
}]
|
}
|
||||||
}
|
}
|
||||||
db6 = DAL(**dbdict6)
|
}
|
||||||
|
db6 = DAL(dbdict6, check_reserved=['all'])
|
||||||
|
|
||||||
assert len(db6["staff"].fields) == 1
|
assert len(db6["staff"].fields) == 1
|
||||||
assert "name" in db6["tvshow"].fields
|
assert "name" in db6["tvshow"].fields
|
||||||
@@ -781,12 +782,15 @@ class TestDALDictImportExport(unittest.TestCase):
|
|||||||
assert db6.staff.insert() is not None
|
assert db6.staff.insert() is not None
|
||||||
assert db6(db6.staff).select().first().id == 1
|
assert db6(db6.staff).select().first().id == 1
|
||||||
|
|
||||||
|
|
||||||
db6.staff.drop()
|
db6.staff.drop()
|
||||||
db6.tvshow.drop()
|
db6.tvshow.drop()
|
||||||
db6.commit()
|
db6.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1225,8 +1225,7 @@ class Auth(object):
|
|||||||
response = current.response
|
response = current.response
|
||||||
if auth and auth.remember:
|
if auth and auth.remember:
|
||||||
# when user wants to be logged in for longer
|
# when user wants to be logged in for longer
|
||||||
response.cookies[response.session_id_name]["expires"] = \
|
response.session_cookie_expires = auth.expiration
|
||||||
auth.expiration
|
|
||||||
if signature:
|
if signature:
|
||||||
self.define_signature()
|
self.define_signature()
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user