Merge github.com:web2py/web2py
This commit is contained in:
@@ -54,20 +54,19 @@ src:
|
|||||||
### build web2py_src.zip
|
### build web2py_src.zip
|
||||||
echo '' > NEWINSTALL
|
echo '' > NEWINSTALL
|
||||||
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
|
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
|
||||||
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py_exe.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run startweb2py web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
|
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
|
||||||
|
|
||||||
mdp:
|
mdp:
|
||||||
make src
|
make src
|
||||||
make app
|
make app
|
||||||
make win
|
make win
|
||||||
app:
|
app:
|
||||||
echo 'did you uncomment import_all in gluon/main.py?'
|
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||||
python2.5 -c 'import compileall; compileall.compile_dir("gluon/")'
|
|
||||||
#python web2py.py -S welcome -R __exit__.py
|
#python web2py.py -S welcome -R __exit__.py
|
||||||
#cd ../web2py_osx/site-packages/; unzip ../site-packages.zip
|
#cd ../web2py_osx/site-packages/; unzip ../site-packages.zip
|
||||||
find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \;
|
find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \;
|
||||||
cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip *
|
cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip *
|
||||||
mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5
|
mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.7
|
||||||
cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources
|
cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||||
cp NEWINSTALL ../web2py_osx/web2py/web2py.app/Contents/Resources
|
cp NEWINSTALL ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||||
cp LICENSE ../web2py_osx/web2py/web2py.app/Contents/Resources
|
cp LICENSE ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||||
@@ -86,7 +85,6 @@ app:
|
|||||||
cd ../web2py_osx; zip -r web2py_osx.zip web2py
|
cd ../web2py_osx; zip -r web2py_osx.zip web2py
|
||||||
mv ../web2py_osx/web2py_osx.zip .
|
mv ../web2py_osx/web2py_osx.zip .
|
||||||
win:
|
win:
|
||||||
echo 'did you uncomment import_all in gluon/main.py?'
|
|
||||||
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
|
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||||
#cd ../web2py_win/library/; unzip ../library.zip
|
#cd ../web2py_win/library/; unzip ../library.zip
|
||||||
find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \;
|
find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \;
|
||||||
@@ -110,7 +108,7 @@ win:
|
|||||||
cd ../web2py_win; zip -r web2py_win.zip web2py
|
cd ../web2py_win; zip -r web2py_win.zip web2py
|
||||||
mv ../web2py_win/web2py_win.zip .
|
mv ../web2py_win/web2py_win.zip .
|
||||||
run:
|
run:
|
||||||
python2.5 web2py.py -a hello
|
python2.7 web2py.py -a hello
|
||||||
commit:
|
commit:
|
||||||
python web2py.py --run_system_tests
|
python web2py.py --run_system_tests
|
||||||
make src
|
make src
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Version 2.4.1-alpha.2+timestamp.2013.01.23.08.48.16
|
Version 2.4.1-alpha.2+timestamp.2013.02.08.12.41.08
|
||||||
|
|||||||
@@ -469,10 +469,10 @@ def ccache():
|
|||||||
|
|
||||||
def table_template(table):
|
def table_template(table):
|
||||||
from gluon.html import TR, TD, TABLE, TAG
|
from gluon.html import TR, TD, TABLE, TAG
|
||||||
|
|
||||||
def FONT(*args, **kwargs):
|
def FONT(*args, **kwargs):
|
||||||
return TAG.font(*args, **kwargs)
|
return TAG.font(*args, **kwargs)
|
||||||
|
|
||||||
def types(field):
|
def types(field):
|
||||||
f_type = field.type
|
f_type = field.type
|
||||||
if not isinstance(f_type,str):
|
if not isinstance(f_type,str):
|
||||||
@@ -480,7 +480,7 @@ def table_template(table):
|
|||||||
elif f_type == 'string':
|
elif f_type == 'string':
|
||||||
return field.length
|
return field.length
|
||||||
elif f_type == 'id':
|
elif f_type == 'id':
|
||||||
return B('pk')
|
return B('pk')
|
||||||
elif f_type.startswith('reference') or \
|
elif f_type.startswith('reference') or \
|
||||||
f_type.startswith('list:reference'):
|
f_type.startswith('list:reference'):
|
||||||
return B('fk')
|
return B('fk')
|
||||||
@@ -495,7 +495,7 @@ def table_template(table):
|
|||||||
face = "Helvetica"
|
face = "Helvetica"
|
||||||
face_bold = "Helvetica Bold"
|
face_bold = "Helvetica Bold"
|
||||||
border = 0
|
border = 0
|
||||||
|
|
||||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||||
_colspan=3, _cellpadding=cellpadding,
|
_colspan=3, _cellpadding=cellpadding,
|
||||||
_align="center", _bgcolor=color)))
|
_align="center", _bgcolor=color)))
|
||||||
@@ -515,10 +515,31 @@ def table_template(table):
|
|||||||
|
|
||||||
|
|
||||||
def bg_graph_model():
|
def bg_graph_model():
|
||||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||||
|
|
||||||
|
subgraphs = dict()
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||||
|
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||||
|
else:
|
||||||
|
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||||
|
|
||||||
|
group = meta_graphmodel['group'].replace(' ', '')
|
||||||
|
if not subgraphs.has_key(group):
|
||||||
|
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
else:
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
|
||||||
|
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||||
label=table_template(tablename))
|
label=table_template(tablename))
|
||||||
|
|
||||||
|
for n, key in enumerate(subgraphs.iterkeys()):
|
||||||
|
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||||
|
name='cluster%d' % n,
|
||||||
|
style='filled',
|
||||||
|
color=subgraphs[key]['meta']['color'],
|
||||||
|
label=subgraphs[key]['meta']['group'])
|
||||||
|
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
for field in db[tablename]:
|
for field in db[tablename]:
|
||||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
|||||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||||
|
|
||||||
graph.layout()
|
graph.layout()
|
||||||
return graph.draw(format='png', prog='dot')
|
#return graph.draw(format='png', prog='dot')
|
||||||
|
if not request.args:
|
||||||
|
return graph.draw(format='png', prog='dot')
|
||||||
|
else:
|
||||||
|
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||||
|
if request.args(0) == 'dot':
|
||||||
|
return graph.string()
|
||||||
|
else:
|
||||||
|
return graph.draw(format=request.args(0), prog='dot')
|
||||||
|
|
||||||
def graph_model():
|
def graph_model():
|
||||||
return dict(databases=databases, pgv=pgv)
|
return dict(databases=databases, pgv=pgv)
|
||||||
|
|||||||
@@ -565,6 +565,7 @@ def edit():
|
|||||||
# Load json only if it is ajax edited...
|
# Load json only if it is ajax edited...
|
||||||
app = get_app(request.vars.app)
|
app = get_app(request.vars.app)
|
||||||
filename = '/'.join(request.args)
|
filename = '/'.join(request.args)
|
||||||
|
response.title = request.args[-1]
|
||||||
if request.vars.app:
|
if request.vars.app:
|
||||||
path = abspath(filename)
|
path = abspath(filename)
|
||||||
else:
|
else:
|
||||||
@@ -645,6 +646,7 @@ def edit():
|
|||||||
code = request.vars.data.rstrip().replace('\r\n', '\n') + '\n'
|
code = request.vars.data.rstrip().replace('\r\n', '\n') + '\n'
|
||||||
compile(code, path, "exec", _ast.PyCF_ONLY_AST)
|
compile(code, path, "exec", _ast.PyCF_ONLY_AST)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
# offset calculation is only used for textarea (start/stop)
|
||||||
start = sum([len(line) + 1 for l, line
|
start = sum([len(line) + 1 for l, line
|
||||||
in enumerate(request.vars.data.split("\n"))
|
in enumerate(request.vars.data.split("\n"))
|
||||||
if l < e.lineno - 1])
|
if l < e.lineno - 1])
|
||||||
@@ -654,7 +656,7 @@ def edit():
|
|||||||
else:
|
else:
|
||||||
offset = 0
|
offset = 0
|
||||||
highlight = {'start': start, 'end': start +
|
highlight = {'start': start, 'end': start +
|
||||||
offset + 1, 'lineno': e.lineno}
|
offset + 1, 'lineno': e.lineno, 'offset': offset}
|
||||||
try:
|
try:
|
||||||
ex_name = e.__class__.__name__
|
ex_name = e.__class__.__name__
|
||||||
except:
|
except:
|
||||||
@@ -811,6 +813,7 @@ def edit_language():
|
|||||||
""" Edit language file """
|
""" Edit language file """
|
||||||
app = get_app()
|
app = get_app()
|
||||||
filename = '/'.join(request.args)
|
filename = '/'.join(request.args)
|
||||||
|
response.title = request.args[-1]
|
||||||
strings = read_dict(apath(filename, r=request))
|
strings = read_dict(apath(filename, r=request))
|
||||||
|
|
||||||
if '__corrupted__' in strings:
|
if '__corrupted__' in strings:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
'Admin language': 'Admin language',
|
'Admin language': 'Admin language',
|
||||||
'administrative interface': 'administrative interface',
|
'administrative interface': 'administrative interface',
|
||||||
'Administrator Password:': 'Password Amministratore:',
|
'Administrator Password:': 'Password Amministratore:',
|
||||||
|
'An error occured, please %s the page': 'An error occured, please %s the page',
|
||||||
'and rename it (required):': 'e rinominala (obbligatorio):',
|
'and rename it (required):': 'e rinominala (obbligatorio):',
|
||||||
'and rename it:': 'e rinominala:',
|
'and rename it:': 'e rinominala:',
|
||||||
'appadmin': 'appadmin ',
|
'appadmin': 'appadmin ',
|
||||||
@@ -144,8 +145,10 @@
|
|||||||
'Get from URL:': 'Get from URL:',
|
'Get from URL:': 'Get from URL:',
|
||||||
'Git Pull': 'Git Pull',
|
'Git Pull': 'Git Pull',
|
||||||
'Git Push': 'Git Push',
|
'Git Push': 'Git Push',
|
||||||
|
'graph model': 'graph model',
|
||||||
'Hello World': 'Salve Mondo',
|
'Hello World': 'Salve Mondo',
|
||||||
'Help': 'aiuto',
|
'Help': 'aiuto',
|
||||||
|
'Hide/Show Translated strings': 'Hide/Show Translated strings',
|
||||||
'htmledit': 'modifica come html',
|
'htmledit': 'modifica come html',
|
||||||
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
|
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
|
||||||
'Import/Export': 'Importa/Esporta',
|
'Import/Export': 'Importa/Esporta',
|
||||||
@@ -195,6 +198,8 @@
|
|||||||
'NO': 'NO',
|
'NO': 'NO',
|
||||||
'No databases in this application': 'Nessun database presente in questa applicazione',
|
'No databases in this application': 'Nessun database presente in questa applicazione',
|
||||||
'no match': 'nessuna corrispondenza',
|
'no match': 'nessuna corrispondenza',
|
||||||
|
'or alternatively': 'or alternatively',
|
||||||
|
'Or Get from URL:': 'Or Get from URL:',
|
||||||
'or import from csv file': 'oppure importa da file CSV',
|
'or import from csv file': 'oppure importa da file CSV',
|
||||||
'or provide app url:': "oppure fornisci url dell'applicazione:",
|
'or provide app url:': "oppure fornisci url dell'applicazione:",
|
||||||
'Original/Translation': 'Originale/Traduzione',
|
'Original/Translation': 'Originale/Traduzione',
|
||||||
@@ -219,6 +224,7 @@
|
|||||||
'record does not exist': 'il record non esiste',
|
'record does not exist': 'il record non esiste',
|
||||||
'record id': 'ID del record',
|
'record id': 'ID del record',
|
||||||
'register': 'registrazione',
|
'register': 'registrazione',
|
||||||
|
'reload': 'reload',
|
||||||
'Remove compiled': 'rimozione codice compilato',
|
'Remove compiled': 'rimozione codice compilato',
|
||||||
'request': 'request',
|
'request': 'request',
|
||||||
'Resolve Conflict file': 'File di risoluzione conflitto',
|
'Resolve Conflict file': 'File di risoluzione conflitto',
|
||||||
@@ -242,6 +248,7 @@
|
|||||||
'Start wizard': 'start wizard',
|
'Start wizard': 'start wizard',
|
||||||
'state': 'stato',
|
'state': 'stato',
|
||||||
'static': 'statico',
|
'static': 'statico',
|
||||||
|
'Static': 'Static',
|
||||||
'Static files': 'Files statici',
|
'Static files': 'Files statici',
|
||||||
'Stylesheet': 'Foglio di stile (stylesheet)',
|
'Stylesheet': 'Foglio di stile (stylesheet)',
|
||||||
'submit': 'invia',
|
'submit': 'invia',
|
||||||
@@ -261,6 +268,7 @@
|
|||||||
'There are no models': 'Non ci sono modelli',
|
'There are no models': 'Non ci sono modelli',
|
||||||
'There are no modules': 'Non ci sono moduli',
|
'There are no modules': 'Non ci sono moduli',
|
||||||
'There are no plugins': 'There are no plugins',
|
'There are no plugins': 'There are no plugins',
|
||||||
|
'There are no private files': 'There are no private files',
|
||||||
'There are no static files': 'Non ci sono file statici',
|
'There are no static files': 'Non ci sono file statici',
|
||||||
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
|
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
|
||||||
'There are no views': 'Non ci sono viste ("view")',
|
'There are no views': 'Non ci sono viste ("view")',
|
||||||
@@ -280,6 +288,7 @@
|
|||||||
'Translation strings for the application': 'Translation strings for the application',
|
'Translation strings for the application': 'Translation strings for the application',
|
||||||
'try': 'prova',
|
'try': 'prova',
|
||||||
'try something like': 'prova qualcosa come',
|
'try something like': 'prova qualcosa come',
|
||||||
|
'Try the mobile interface': 'Try the mobile interface',
|
||||||
'try view': 'try view',
|
'try view': 'try view',
|
||||||
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
|
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
|
||||||
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
|
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
|
||||||
@@ -298,6 +307,7 @@
|
|||||||
'Update:': 'Aggiorna:',
|
'Update:': 'Aggiorna:',
|
||||||
'upgrade web2py now': 'upgrade web2py now',
|
'upgrade web2py now': 'upgrade web2py now',
|
||||||
'upload': 'upload',
|
'upload': 'upload',
|
||||||
|
'Upload': 'Upload',
|
||||||
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
|
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
|
||||||
'Upload a package:': 'Upload a package:',
|
'Upload a package:': 'Upload a package:',
|
||||||
'Upload and install packed application': 'Upload and install packed application',
|
'Upload and install packed application': 'Upload and install packed application',
|
||||||
@@ -308,6 +318,7 @@
|
|||||||
'Use an url:': 'Use an url:',
|
'Use an url:': 'Use an url:',
|
||||||
'variables': 'variables',
|
'variables': 'variables',
|
||||||
'Version': 'Versione',
|
'Version': 'Versione',
|
||||||
|
'Version %s.%s.%s %s (%s)': 'Version %s.%s.%s %s (%s)',
|
||||||
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
|
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
|
||||||
'versioning': 'sistema di versioni',
|
'versioning': 'sistema di versioni',
|
||||||
'Versioning': 'Versioning',
|
'Versioning': 'Versioning',
|
||||||
|
|||||||
@@ -43,8 +43,9 @@ function doHighlight(highlight) {
|
|||||||
if (window.ace_editor) {
|
if (window.ace_editor) {
|
||||||
window.ace_editor.gotoLine(highlight.lineno);
|
window.ace_editor.gotoLine(highlight.lineno);
|
||||||
} else if (window.mirror) {
|
} else if (window.mirror) {
|
||||||
window.mirror.setSelection({line:highlight.lineno,ch:0},
|
// Put the cursor at the offending line:
|
||||||
{line:highlight.end,ch:0});
|
window.mirror.setCursor({line:highlight.lineno-1,
|
||||||
|
ch:highlight.offset+1});
|
||||||
} else if (window.eamy) {
|
} else if (window.eamy) {
|
||||||
// not implemented
|
// not implemented
|
||||||
} else if (window.textarea) {
|
} else if (window.textarea) {
|
||||||
|
|||||||
@@ -229,7 +229,21 @@
|
|||||||
{{=T('pygraphviz library not found')}}
|
{{=T('pygraphviz library not found')}}
|
||||||
{{elif not databases:}}
|
{{elif not databases:}}
|
||||||
{{=T("No databases in this application")}}
|
{{=T("No databases in this application")}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ function hideShowTranslated(){
|
|||||||
<div class="languageform">
|
<div class="languageform">
|
||||||
{{=form}}
|
{{=form}}
|
||||||
</div>
|
</div>
|
||||||
<!-- end "edit_language" block -->
|
<button class="btn" style="position:fixed; right:10px; bottom: 10px;" onclick="jQuery('form input[type=submit]').click()">Save Changes</button>
|
||||||
|
<!-- end "edit_language" block -->
|
||||||
|
|||||||
@@ -469,10 +469,10 @@ def ccache():
|
|||||||
|
|
||||||
def table_template(table):
|
def table_template(table):
|
||||||
from gluon.html import TR, TD, TABLE, TAG
|
from gluon.html import TR, TD, TABLE, TAG
|
||||||
|
|
||||||
def FONT(*args, **kwargs):
|
def FONT(*args, **kwargs):
|
||||||
return TAG.font(*args, **kwargs)
|
return TAG.font(*args, **kwargs)
|
||||||
|
|
||||||
def types(field):
|
def types(field):
|
||||||
f_type = field.type
|
f_type = field.type
|
||||||
if not isinstance(f_type,str):
|
if not isinstance(f_type,str):
|
||||||
@@ -480,7 +480,7 @@ def table_template(table):
|
|||||||
elif f_type == 'string':
|
elif f_type == 'string':
|
||||||
return field.length
|
return field.length
|
||||||
elif f_type == 'id':
|
elif f_type == 'id':
|
||||||
return B('pk')
|
return B('pk')
|
||||||
elif f_type.startswith('reference') or \
|
elif f_type.startswith('reference') or \
|
||||||
f_type.startswith('list:reference'):
|
f_type.startswith('list:reference'):
|
||||||
return B('fk')
|
return B('fk')
|
||||||
@@ -495,7 +495,7 @@ def table_template(table):
|
|||||||
face = "Helvetica"
|
face = "Helvetica"
|
||||||
face_bold = "Helvetica Bold"
|
face_bold = "Helvetica Bold"
|
||||||
border = 0
|
border = 0
|
||||||
|
|
||||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||||
_colspan=3, _cellpadding=cellpadding,
|
_colspan=3, _cellpadding=cellpadding,
|
||||||
_align="center", _bgcolor=color)))
|
_align="center", _bgcolor=color)))
|
||||||
@@ -515,10 +515,31 @@ def table_template(table):
|
|||||||
|
|
||||||
|
|
||||||
def bg_graph_model():
|
def bg_graph_model():
|
||||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||||
|
|
||||||
|
subgraphs = dict()
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||||
|
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||||
|
else:
|
||||||
|
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||||
|
|
||||||
|
group = meta_graphmodel['group'].replace(' ', '')
|
||||||
|
if not subgraphs.has_key(group):
|
||||||
|
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
else:
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
|
||||||
|
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||||
label=table_template(tablename))
|
label=table_template(tablename))
|
||||||
|
|
||||||
|
for n, key in enumerate(subgraphs.iterkeys()):
|
||||||
|
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||||
|
name='cluster%d' % n,
|
||||||
|
style='filled',
|
||||||
|
color=subgraphs[key]['meta']['color'],
|
||||||
|
label=subgraphs[key]['meta']['group'])
|
||||||
|
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
for field in db[tablename]:
|
for field in db[tablename]:
|
||||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
|||||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||||
|
|
||||||
graph.layout()
|
graph.layout()
|
||||||
return graph.draw(format='png', prog='dot')
|
#return graph.draw(format='png', prog='dot')
|
||||||
|
if not request.args:
|
||||||
|
return graph.draw(format='png', prog='dot')
|
||||||
|
else:
|
||||||
|
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||||
|
if request.args(0) == 'dot':
|
||||||
|
return graph.string()
|
||||||
|
else:
|
||||||
|
return graph.draw(format=request.args(0), prog='dot')
|
||||||
|
|
||||||
def graph_model():
|
def graph_model():
|
||||||
return dict(databases=databases, pgv=pgv)
|
return dict(databases=databases, pgv=pgv)
|
||||||
|
|||||||
@@ -229,7 +229,21 @@
|
|||||||
{{=T('pygraphviz library not found')}}
|
{{=T('pygraphviz library not found')}}
|
||||||
{{elif not databases:}}
|
{{elif not databases:}}
|
||||||
{{=T("No databases in this application")}}
|
{{=T("No databases in this application")}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
|||||||
@@ -469,10 +469,10 @@ def ccache():
|
|||||||
|
|
||||||
def table_template(table):
|
def table_template(table):
|
||||||
from gluon.html import TR, TD, TABLE, TAG
|
from gluon.html import TR, TD, TABLE, TAG
|
||||||
|
|
||||||
def FONT(*args, **kwargs):
|
def FONT(*args, **kwargs):
|
||||||
return TAG.font(*args, **kwargs)
|
return TAG.font(*args, **kwargs)
|
||||||
|
|
||||||
def types(field):
|
def types(field):
|
||||||
f_type = field.type
|
f_type = field.type
|
||||||
if not isinstance(f_type,str):
|
if not isinstance(f_type,str):
|
||||||
@@ -480,7 +480,7 @@ def table_template(table):
|
|||||||
elif f_type == 'string':
|
elif f_type == 'string':
|
||||||
return field.length
|
return field.length
|
||||||
elif f_type == 'id':
|
elif f_type == 'id':
|
||||||
return B('pk')
|
return B('pk')
|
||||||
elif f_type.startswith('reference') or \
|
elif f_type.startswith('reference') or \
|
||||||
f_type.startswith('list:reference'):
|
f_type.startswith('list:reference'):
|
||||||
return B('fk')
|
return B('fk')
|
||||||
@@ -495,7 +495,7 @@ def table_template(table):
|
|||||||
face = "Helvetica"
|
face = "Helvetica"
|
||||||
face_bold = "Helvetica Bold"
|
face_bold = "Helvetica Bold"
|
||||||
border = 0
|
border = 0
|
||||||
|
|
||||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||||
_colspan=3, _cellpadding=cellpadding,
|
_colspan=3, _cellpadding=cellpadding,
|
||||||
_align="center", _bgcolor=color)))
|
_align="center", _bgcolor=color)))
|
||||||
@@ -515,10 +515,31 @@ def table_template(table):
|
|||||||
|
|
||||||
|
|
||||||
def bg_graph_model():
|
def bg_graph_model():
|
||||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||||
|
|
||||||
|
subgraphs = dict()
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||||
|
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||||
|
else:
|
||||||
|
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||||
|
|
||||||
|
group = meta_graphmodel['group'].replace(' ', '')
|
||||||
|
if not subgraphs.has_key(group):
|
||||||
|
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
else:
|
||||||
|
subgraphs[group]['tables'].append(tablename)
|
||||||
|
|
||||||
|
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||||
label=table_template(tablename))
|
label=table_template(tablename))
|
||||||
|
|
||||||
|
for n, key in enumerate(subgraphs.iterkeys()):
|
||||||
|
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||||
|
name='cluster%d' % n,
|
||||||
|
style='filled',
|
||||||
|
color=subgraphs[key]['meta']['color'],
|
||||||
|
label=subgraphs[key]['meta']['group'])
|
||||||
|
|
||||||
for tablename in db.tables:
|
for tablename in db.tables:
|
||||||
for field in db[tablename]:
|
for field in db[tablename]:
|
||||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
|||||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||||
|
|
||||||
graph.layout()
|
graph.layout()
|
||||||
return graph.draw(format='png', prog='dot')
|
#return graph.draw(format='png', prog='dot')
|
||||||
|
if not request.args:
|
||||||
|
return graph.draw(format='png', prog='dot')
|
||||||
|
else:
|
||||||
|
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||||
|
if request.args(0) == 'dot':
|
||||||
|
return graph.string()
|
||||||
|
else:
|
||||||
|
return graph.draw(format=request.args(0), prog='dot')
|
||||||
|
|
||||||
def graph_model():
|
def graph_model():
|
||||||
return dict(databases=databases, pgv=pgv)
|
return dict(databases=databases, pgv=pgv)
|
||||||
|
|||||||
@@ -229,7 +229,21 @@
|
|||||||
{{=T('pygraphviz library not found')}}
|
{{=T('pygraphviz library not found')}}
|
||||||
{{elif not databases:}}
|
{{elif not databases:}}
|
||||||
{{=T("No databases in this application")}}
|
{{=T("No databases in this application")}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||||
|
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<!--[if HTML5]><![endif]-->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
|
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
|
||||||
<!--[if lt IE 7]><html class="ie ie6 ie-lte9 ie-lte8 ie-lte7 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
<!--[if lt IE 7]><html class="ie ie6 ie-lte9 ie-lte8 ie-lte7 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
||||||
@@ -6,16 +7,16 @@
|
|||||||
<!--[if IE 9]><html class="ie9 ie-lte9 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
<!--[if IE 9]><html class="ie9 ie-lte9 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
||||||
<!--[if (gt IE 9)|!(IE)]><!--> <html class="no-js" lang="{{=T.accepted_language or 'en'}}"> <!--<![endif]-->
|
<!--[if (gt IE 9)|!(IE)]><!--> <html class="no-js" lang="{{=T.accepted_language or 'en'}}"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<title>{{=response.title or request.application}}</title>
|
||||||
|
<!--[if !HTML5]>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge{{=not request.is_local and ',chrome=1' or ''}}">
|
||||||
|
<![endif]-->
|
||||||
<!-- www.phpied.com/conditional-comments-block-downloads/ -->
|
<!-- www.phpied.com/conditional-comments-block-downloads/ -->
|
||||||
<!-- Always force latest IE rendering engine
|
<!-- Always force latest IE rendering engine
|
||||||
(even in intranet) & Chrome Frame
|
(even in intranet) & Chrome Frame
|
||||||
Remove this if you use the .htaccess -->
|
Remove this if you use the .htaccess -->
|
||||||
<!--[if IE]>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge{{=not request.is_local and ',chrome=1' or ''}}">
|
<meta charset="utf-8" />
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<title>{{=response.title or request.application}}</title>
|
|
||||||
|
|
||||||
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
|
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
|
||||||
<meta name="application-name" content="{{=request.application}}" />
|
<meta name="application-name" content="{{=request.application}}" />
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ def protolinks_simple(proto, url):
|
|||||||
#elif proto == 'embed': # NOTE: embed is a synonym to iframe now
|
#elif proto == 'embed': # NOTE: embed is a synonym to iframe now
|
||||||
# return '<a href="%s" class="%sembed">%s></a>'%(url,class_prefix,url)
|
# return '<a href="%s" class="%sembed">%s></a>'%(url,class_prefix,url)
|
||||||
elif proto == 'qr':
|
elif proto == 'qr':
|
||||||
return '<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&d=%s" alt="qr code" />'%url
|
return '<img style="width:80px" src="http://qrcode.kaywa.com/img.php?s=8&d=%s" alt="qr code" />'%url
|
||||||
return proto+':'+url
|
return proto+':'+url
|
||||||
|
|
||||||
def render(text,
|
def render(text,
|
||||||
@@ -701,7 +701,7 @@ def render(text,
|
|||||||
'<p><img src="http://example.com" alt="this is an image" style="float:left" /></p>'
|
'<p><img src="http://example.com" alt="this is an image" style="float:left" /></p>'
|
||||||
|
|
||||||
>>> render('[[this is an image http://example.com left 200px]]')
|
>>> render('[[this is an image http://example.com left 200px]]')
|
||||||
'<p><img src="http://example.com" alt="this is an image" style="float:left" width="200px" /></p>'
|
'<p><img src="http://example.com" alt="this is an image" style="float:left;width:200px" /></p>'
|
||||||
|
|
||||||
>>> render("[[Your browser doesn't support <video> HTML5 tag http://example.com video]]")
|
>>> render("[[Your browser doesn't support <video> HTML5 tag http://example.com video]]")
|
||||||
'<p><video controls="controls"><source src="http://example.com" />Your browser doesn\\'t support <video> HTML5 tag</video></p>'
|
'<p><video controls="controls"><source src="http://example.com" />Your browser doesn\\'t support <video> HTML5 tag</video></p>'
|
||||||
@@ -740,7 +740,7 @@ def render(text,
|
|||||||
'<p>auto-image: (<img src="http://example.com/image.jpeg" controls />)</p>'
|
'<p>auto-image: (<img src="http://example.com/image.jpeg" controls />)</p>'
|
||||||
|
|
||||||
>>> render("qr: (qr:http://example.com/image.jpeg)")
|
>>> render("qr: (qr:http://example.com/image.jpeg)")
|
||||||
'<p>qr: (<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&d=http://example.com/image.jpeg" alt="qr code" />)</p>'
|
'<p>qr: (<img style="width:80px" src="http://qrcode.kaywa.com/img.php?s=8&d=http://example.com/image.jpeg" alt="qr code" />)</p>'
|
||||||
|
|
||||||
>>> render("embed: (embed:http://example.com/page)")
|
>>> render("embed: (embed:http://example.com/page)")
|
||||||
'<p>embed: (<iframe src="http://example.com/page" frameborder="0" allowfullscreen></iframe>)</p>'
|
'<p>embed: (<iframe src="http://example.com/page" frameborder="0" allowfullscreen></iframe>)</p>'
|
||||||
@@ -776,10 +776,10 @@ def render(text,
|
|||||||
'<p>title9: <img src="http://example.com" alt="test message" title="title" style="float:left" /></p>'
|
'<p>title9: <img src="http://example.com" alt="test message" title="title" style="float:left" /></p>'
|
||||||
|
|
||||||
>>> render("title10: [[test message [title] http://example.com right 100px]]")
|
>>> render("title10: [[test message [title] http://example.com right 100px]]")
|
||||||
'<p>title10: <img src="http://example.com" alt="test message" title="title" style="float:right" width="100px" /></p>'
|
'<p>title10: <img src="http://example.com" alt="test message" title="title" style="float:right;width:100px" /></p>'
|
||||||
|
|
||||||
>>> render("title11: [[test message [title] http://example.com center 200px]]")
|
>>> render("title11: [[test message [title] http://example.com center 200px]]")
|
||||||
'<p>title11: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" width="200px" /></p></p>'
|
'<p>title11: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" style="width:200px" /></p></p>'
|
||||||
|
|
||||||
>>> render(r"\\[[probe]]")
|
>>> render(r"\\[[probe]]")
|
||||||
'<p>[[probe]]</p>'
|
'<p>[[probe]]</p>'
|
||||||
@@ -1236,23 +1236,24 @@ def render(text,
|
|||||||
return m.group(0)
|
return m.group(0)
|
||||||
k = escape(k)
|
k = escape(k)
|
||||||
t = t or ''
|
t = t or ''
|
||||||
width = ' width="%s"' % w if w else ''
|
style = 'width:%s' % w if w else ''
|
||||||
title = ' title="%s"' % escape(a).replace(META, DISABLED_META) if a else ''
|
title = ' title="%s"' % escape(a).replace(META, DISABLED_META) if a else ''
|
||||||
style = p_begin = p_end = ''
|
p_begin = p_end = ''
|
||||||
if p == 'center':
|
if p == 'center':
|
||||||
p_begin = '<p style="text-align:center">'
|
p_begin = '<p style="text-align:center">'
|
||||||
p_end = '</p>'+pp
|
p_end = '</p>'+pp
|
||||||
elif p in ('left','right'):
|
elif p in ('left','right'):
|
||||||
style = ' style="float:%s"' % p
|
style = ('float:%s' % p)+(';%s' % style if style else '')
|
||||||
|
if style:
|
||||||
|
style = ' style="%s"' % style
|
||||||
if p in ('video','audio'):
|
if p in ('video','audio'):
|
||||||
t = render(t, {}, {}, 'br', URL, environment, latex,
|
t = render(t, {}, {}, 'br', URL, environment, latex,
|
||||||
autolinks, protolinks, class_prefix, id_prefix, pretty_print)
|
autolinks, protolinks, class_prefix, id_prefix, pretty_print)
|
||||||
return '<%(p)s controls="controls"%(title)s%(width)s><source src="%(k)s" />%(t)s</%(p)s>' \
|
return '<%(p)s controls="controls"%(title)s%(style)s><source src="%(k)s" />%(t)s</%(p)s>' \
|
||||||
% dict(p=p, title=title, width=width, k=k, t=t)
|
% dict(p=p, title=title, style=style, k=k, t=t)
|
||||||
alt = ' alt="%s"'%escape(t).replace(META, DISABLED_META) if t else ''
|
alt = ' alt="%s"'%escape(t).replace(META, DISABLED_META) if t else ''
|
||||||
return '%(begin)s<img src="%(k)s"%(alt)s%(title)s%(style)s%(width)s />%(end)s' \
|
return '%(begin)s<img src="%(k)s"%(alt)s%(title)s%(style)s />%(end)s' \
|
||||||
% dict(begin=p_begin, k=k, alt=alt, title=title,
|
% dict(begin=p_begin, k=k, alt=alt, title=title, style=style, end=p_end)
|
||||||
style=style, width=width, end=p_end)
|
|
||||||
|
|
||||||
def sub_link(m):
|
def sub_link(m):
|
||||||
t,a,k,p = m.group('t','a','k','p')
|
t,a,k,p = m.group('t','a','k','p')
|
||||||
|
|||||||
+24
-20
@@ -61,26 +61,30 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
|||||||
and isinstance(globals, dict):
|
and isinstance(globals, dict):
|
||||||
import_tb = None
|
import_tb = None
|
||||||
try:
|
try:
|
||||||
items = current.request.folder.split(os.path.sep)
|
try:
|
||||||
if not items[-1]:
|
oname = name if not name.startswith('.') else '.'+name
|
||||||
items = items[:-1]
|
return NATIVE_IMPORTER(oname, globals, locals, fromlist, level)
|
||||||
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
except ImportError:
|
||||||
if not fromlist:
|
items = current.request.folder.split(os.path.sep)
|
||||||
# import like "import x" or "import x.y"
|
if not items[-1]:
|
||||||
result = None
|
items = items[:-1]
|
||||||
for itemname in name.split("."):
|
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
||||||
new_mod = base_importer(
|
if not fromlist:
|
||||||
modules_prefix, globals, locals, [itemname], level)
|
# import like "import x" or "import x.y"
|
||||||
try:
|
result = None
|
||||||
result = result or new_mod.__dict__[itemname]
|
for itemname in name.split("."):
|
||||||
except KeyError, e:
|
new_mod = base_importer(
|
||||||
raise ImportError, 'Cannot import module %s' % str(e)
|
modules_prefix, globals, locals, [itemname], level)
|
||||||
modules_prefix += "." + itemname
|
try:
|
||||||
return result
|
result = result or new_mod.__dict__[itemname]
|
||||||
else:
|
except KeyError, e:
|
||||||
# import like "from x import a, b, ..."
|
raise ImportError, 'Cannot import module %s' % str(e)
|
||||||
pname = modules_prefix + "." + name
|
modules_prefix += "." + itemname
|
||||||
return base_importer(pname, globals, locals, fromlist, level)
|
return result
|
||||||
|
else:
|
||||||
|
# import like "from x import a, b, ..."
|
||||||
|
pname = modules_prefix + "." + name
|
||||||
|
return base_importer(pname, globals, locals, fromlist, level)
|
||||||
except ImportError, e1:
|
except ImportError, e1:
|
||||||
import_tb = sys.exc_info()[2]
|
import_tb = sys.exc_info()[2]
|
||||||
try:
|
try:
|
||||||
|
|||||||
+445
-63
@@ -339,7 +339,10 @@ if not 'google' in DRIVERS:
|
|||||||
LOGGER.debug('no Oracle driver cx_Oracle')
|
LOGGER.debug('no Oracle driver cx_Oracle')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
#try:
|
||||||
import pyodbc
|
import pyodbc
|
||||||
|
#except ImportError:
|
||||||
|
# from contrib.pypyodbc import pypyodbc as pyodbc
|
||||||
DRIVERS.append('MSSQL(pyodbc)')
|
DRIVERS.append('MSSQL(pyodbc)')
|
||||||
DRIVERS.append('DB2(pyodbc)')
|
DRIVERS.append('DB2(pyodbc)')
|
||||||
DRIVERS.append('Teradata(pyodbc)')
|
DRIVERS.append('Teradata(pyodbc)')
|
||||||
@@ -876,7 +879,7 @@ class BaseAdapter(ConnectionPool):
|
|||||||
# The reason is that we do not want to trigger
|
# The reason is that we do not want to trigger
|
||||||
# a migration simply because a default value changes.
|
# a migration simply because a default value changes.
|
||||||
not_null = self.NOT_NULL(field.default, field_type)
|
not_null = self.NOT_NULL(field.default, field_type)
|
||||||
ftype = ftype.replace('NOT NULL', not_null)
|
ftype = ftype.replace('NOT NULL', not_null)
|
||||||
sql_fields_aux[field_name] = dict(sql=ftype)
|
sql_fields_aux[field_name] = dict(sql=ftype)
|
||||||
# Postgres - PostGIS:
|
# Postgres - PostGIS:
|
||||||
# geometry fields are added after the table has been created, not now
|
# geometry fields are added after the table has been created, not now
|
||||||
@@ -1233,12 +1236,24 @@ class BaseAdapter(ConnectionPool):
|
|||||||
return '(%s LIKE %s)' % (self.expand(first),
|
return '(%s LIKE %s)' % (self.expand(first),
|
||||||
self.expand('%'+second, 'string'))
|
self.expand('%'+second, 'string'))
|
||||||
|
|
||||||
def CONTAINS(self, first, second):
|
def CONTAINS(self, first, second, case_sensitive=False):
|
||||||
if first.type in ('string', 'text', 'json'):
|
if isinstance(second,Expression):
|
||||||
key = '%'+str(second).replace('%','%%')+'%'
|
field = self.expand(first)
|
||||||
elif first.type.startswith('list:'):
|
expr = self.expand(second,'string')
|
||||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
if first.type.startswith('list:'):
|
||||||
return '(%s LIKE %s)' % (self.expand(first),self.expand(key,'string'))
|
expr = 'CONCAT("|", %s, "|")' % expr
|
||||||
|
elif not first.type in ('string', 'text', 'json'):
|
||||||
|
raise RuntimeError("Expression Not Supported")
|
||||||
|
return 'INSTR(%s,%s)' % (field, expr)
|
||||||
|
else:
|
||||||
|
if first.type in ('string', 'text', 'json'):
|
||||||
|
key = '%'+str(second).replace('%','%%')+'%'
|
||||||
|
elif first.type.startswith('list:'):
|
||||||
|
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Expression Not Supported")
|
||||||
|
op = case_sensitive and self.LIKE or self.ILIKE
|
||||||
|
return op(first,key)
|
||||||
|
|
||||||
def EQ(self, first, second=None):
|
def EQ(self, first, second=None):
|
||||||
if second is None:
|
if second is None:
|
||||||
@@ -1317,10 +1332,11 @@ class BaseAdapter(ConnectionPool):
|
|||||||
first = expression.first
|
first = expression.first
|
||||||
second = expression.second
|
second = expression.second
|
||||||
op = expression.op
|
op = expression.op
|
||||||
|
optional_args = expression.optional_args or {}
|
||||||
if not second is None:
|
if not second is None:
|
||||||
return op(first, second)
|
return op(first, second, **optional_args)
|
||||||
elif not first is None:
|
elif not first is None:
|
||||||
return op(first)
|
return op(first,**optional_args)
|
||||||
elif isinstance(op, str):
|
elif isinstance(op, str):
|
||||||
if op.endswith(';'):
|
if op.endswith(';'):
|
||||||
op=op[:-1]
|
op=op[:-1]
|
||||||
@@ -2569,11 +2585,25 @@ class PostgreSQLAdapter(BaseAdapter):
|
|||||||
def after_connection(self):
|
def after_connection(self):
|
||||||
self.connection.set_client_encoding('UTF8')
|
self.connection.set_client_encoding('UTF8')
|
||||||
self.execute("SET standard_conforming_strings=on;")
|
self.execute("SET standard_conforming_strings=on;")
|
||||||
|
self.try_json()
|
||||||
|
|
||||||
def lastrowid(self,table):
|
def lastrowid(self,table):
|
||||||
self.execute("select currval('%s')" % table._sequence_name)
|
self.execute("select currval('%s')" % table._sequence_name)
|
||||||
return int(self.cursor.fetchone()[0])
|
return int(self.cursor.fetchone()[0])
|
||||||
|
|
||||||
|
def try_json(self):
|
||||||
|
# check JSON data type support
|
||||||
|
# (to be added to after_connection)
|
||||||
|
if self.driver_name == "pg8000":
|
||||||
|
supports_json = self.connection.server_version >= "9.2.0"
|
||||||
|
elif (self.driver_name == "psycopg2") and \
|
||||||
|
(self.driver.__version__ >= "2.0.12"):
|
||||||
|
supports_json = self.connection.server_version >= 90200
|
||||||
|
elif self.driver_name == "zxJDBC":
|
||||||
|
supports_json = self.connection.dbversion >= "9.2.0"
|
||||||
|
else: supports_json = None
|
||||||
|
if supports_json: self.types["json"] = "JSON"
|
||||||
|
else: LOGGER.debug("Your database version does not support the JSON data type (using TEXT instead)")
|
||||||
|
|
||||||
def LIKE(self,first,second):
|
def LIKE(self,first,second):
|
||||||
args = (self.expand(first), self.expand(second,'string'))
|
args = (self.expand(first), self.expand(second,'string'))
|
||||||
@@ -2601,12 +2631,13 @@ class PostgreSQLAdapter(BaseAdapter):
|
|||||||
return '(%s ILIKE %s)' % (self.expand(first),
|
return '(%s ILIKE %s)' % (self.expand(first),
|
||||||
self.expand('%'+second,'string'))
|
self.expand('%'+second,'string'))
|
||||||
|
|
||||||
def CONTAINS(self,first,second):
|
def CONTAINS(self,first,second,case_sensitive=False):
|
||||||
if first.type in ('string','text', 'json'):
|
if first.type in ('string','text', 'json'):
|
||||||
key = '%'+str(second).replace('%','%%')+'%'
|
key = '%'+str(second).replace('%','%%')+'%'
|
||||||
elif first.type.startswith('list:'):
|
elif first.type.startswith('list:'):
|
||||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||||
return '(%s ILIKE %s)' % (self.expand(first),self.expand(key,'string'))
|
op = case_sensitive and self.LIKE or self.ILIKE
|
||||||
|
return op(first,key)
|
||||||
|
|
||||||
# GIS functions
|
# GIS functions
|
||||||
|
|
||||||
@@ -2797,6 +2828,7 @@ class JDBCPostgreSQLAdapter(PostgreSQLAdapter):
|
|||||||
self.connection.set_client_encoding('UTF8')
|
self.connection.set_client_encoding('UTF8')
|
||||||
self.execute('BEGIN;')
|
self.execute('BEGIN;')
|
||||||
self.execute("SET CLIENT_ENCODING TO 'UNICODE';")
|
self.execute("SET CLIENT_ENCODING TO 'UNICODE';")
|
||||||
|
self.try_json()
|
||||||
|
|
||||||
|
|
||||||
class OracleAdapter(BaseAdapter):
|
class OracleAdapter(BaseAdapter):
|
||||||
@@ -3037,6 +3069,8 @@ class MSSQLAdapter(BaseAdapter):
|
|||||||
if limitby:
|
if limitby:
|
||||||
(lmin, lmax) = limitby
|
(lmin, lmax) = limitby
|
||||||
sql_s += ' TOP %i' % lmax
|
sql_s += ' TOP %i' % lmax
|
||||||
|
if 'GROUP BY' in sql_o:
|
||||||
|
sql_o = sql_o[:sql_o.find('ORDER BY ')]
|
||||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||||
|
|
||||||
TRUE = 1
|
TRUE = 1
|
||||||
@@ -3179,7 +3213,18 @@ class MSSQL3Adapter(MSSQLAdapter):
|
|||||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||||
if limitby:
|
if limitby:
|
||||||
(lmin, lmax) = limitby
|
(lmin, lmax) = limitby
|
||||||
return 'SELECT %s FROM (SELECT %s ROW_NUMBER() over (order by id) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_f,sql_s,sql_f,sql_t,sql_w,sql_o,lmin,lmax)
|
if lmin == 0:
|
||||||
|
sql_s += ' TOP %i' % lmax
|
||||||
|
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||||
|
lmin += 1
|
||||||
|
sql_o_inner = sql_o[sql_o.find('ORDER BY ')+9:]
|
||||||
|
sql_g_inner = sql_o[:sql_o.find('ORDER BY ')]
|
||||||
|
sql_f_outer = ['f_%s' % f for f in range(len(sql_f.split(',')))]
|
||||||
|
sql_f_inner = [f for f in sql_f.split(',')]
|
||||||
|
sql_f_iproxy = ['%s AS %s' % (o, n) for (o, n) in zip(sql_f_inner, sql_f_outer)]
|
||||||
|
sql_f_iproxy = ', '.join(sql_f_iproxy)
|
||||||
|
sql_f_oproxy = ', '.join(sql_f_outer)
|
||||||
|
return 'SELECT %s %s FROM (SELECT %s ROW_NUMBER() OVER (ORDER BY %s) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_s,sql_f_oproxy,sql_s,sql_f,sql_f_iproxy,sql_t,sql_w,sql_g_inner,lmin,lmax)
|
||||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s,sql_f,sql_t,sql_w,sql_o)
|
return 'SELECT %s %s FROM %s%s%s;' % (sql_s,sql_f,sql_t,sql_w,sql_o)
|
||||||
def rowslice(self,rows,minimum=0,maximum=None):
|
def rowslice(self,rows,minimum=0,maximum=None):
|
||||||
return rows
|
return rows
|
||||||
@@ -3224,6 +3269,7 @@ class MSSQL2Adapter(MSSQLAdapter):
|
|||||||
def execute(self,a):
|
def execute(self,a):
|
||||||
return self.log_execute(a.decode('utf8'))
|
return self.log_execute(a.decode('utf8'))
|
||||||
|
|
||||||
|
|
||||||
class SybaseAdapter(MSSQLAdapter):
|
class SybaseAdapter(MSSQLAdapter):
|
||||||
drivers = ('Sybase',)
|
drivers = ('Sybase',)
|
||||||
|
|
||||||
@@ -3365,13 +3411,17 @@ class FireBirdAdapter(BaseAdapter):
|
|||||||
def SUBSTRING(self,field,parameters):
|
def SUBSTRING(self,field,parameters):
|
||||||
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
|
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
|
||||||
|
|
||||||
def CONTAINS(self, first, second):
|
def CONTAINING(self,first,second):
|
||||||
|
"case in-sensitive like operator"
|
||||||
|
return '(%s CONTAINING %s)' % (self.expand(first),
|
||||||
|
self.expand(second, 'string'))
|
||||||
|
|
||||||
|
def CONTAINS(self, first, second, case_sensitive=False):
|
||||||
if first.type in ('string','text'):
|
if first.type in ('string','text'):
|
||||||
key = str(second).replace('%','%%')
|
key = str(second).replace('%','%%')
|
||||||
elif first.type.startswith('list:'):
|
elif first.type.startswith('list:'):
|
||||||
key = '|'+str(second).replace('|','||').replace('%','%%')+'|'
|
key = '|'+str(second).replace('|','||').replace('%','%%')+'|'
|
||||||
return '(%s CONTAINING %s)' % (self.expand(first),
|
return self.CONTAINING(first,second)
|
||||||
self.expand(key,'string'))
|
|
||||||
|
|
||||||
def _drop(self,table,mode):
|
def _drop(self,table,mode):
|
||||||
sequence_name = table._sequence_name
|
sequence_name = table._sequence_name
|
||||||
@@ -4531,7 +4581,8 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
|||||||
second = [Key.from_path(first._tablename, int(i)) for i in second]
|
second = [Key.from_path(first._tablename, int(i)) for i in second]
|
||||||
return [GAEF(first.name,'in',second,lambda a,b:a in b)]
|
return [GAEF(first.name,'in',second,lambda a,b:a in b)]
|
||||||
|
|
||||||
def CONTAINS(self,first,second):
|
def CONTAINS(self,first,second,case_sensitive=False):
|
||||||
|
# silently ignoring: GAE can only do case sensitive matches!
|
||||||
if not first.type.startswith('list:'):
|
if not first.type.startswith('list:'):
|
||||||
raise SyntaxError("Not supported")
|
raise SyntaxError("Not supported")
|
||||||
return [GAEF(first.name,'=',self.expand(second,first.type[5:]),lambda a,b:b in a)]
|
return [GAEF(first.name,'=',self.expand(second,first.type[5:]),lambda a,b:b in a)]
|
||||||
@@ -4989,7 +5040,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
'list:reference': list,
|
'list:reference': list,
|
||||||
}
|
}
|
||||||
|
|
||||||
error_messages = {"javascript_needed": "This must yet be replaced" +
|
error_messages = {"javascript_needed": "This must yet be replaced" +
|
||||||
" with javascript in order to work."}
|
" with javascript in order to work."}
|
||||||
|
|
||||||
def __init__(self,db,uri='mongodb://127.0.0.1:5984/db',
|
def __init__(self,db,uri='mongodb://127.0.0.1:5984/db',
|
||||||
@@ -5068,12 +5119,12 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
"invalid objectid argument string: %s" % e)
|
"invalid objectid argument string: %s" % e)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid objectid argument string. " +
|
raise ValueError("Invalid objectid argument string. " +
|
||||||
"Requires an integer or base 16 value")
|
"Requires an integer or base 16 value")
|
||||||
elif isinstance(arg, self.ObjectId):
|
elif isinstance(arg, self.ObjectId):
|
||||||
return arg
|
return arg
|
||||||
if not isinstance(arg, (int, long)):
|
if not isinstance(arg, (int, long)):
|
||||||
raise TypeError("object_id argument must be of type " +
|
raise TypeError("object_id argument must be of type " +
|
||||||
"ObjectId or an objectid representable integer")
|
"ObjectId or an objectid representable integer")
|
||||||
if arg == 0:
|
if arg == 0:
|
||||||
hexvalue = "".zfill(24)
|
hexvalue = "".zfill(24)
|
||||||
@@ -5105,7 +5156,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
return value
|
return value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Safe determines whether a asynchronious request is done or a
|
# Safe determines whether a asynchronious request is done or a
|
||||||
# synchronious action is done
|
# synchronious action is done
|
||||||
# For safety, we use by default synchronious requests
|
# For safety, we use by default synchronious requests
|
||||||
def insert(self, table, fields, safe=None):
|
def insert(self, table, fields, safe=None):
|
||||||
@@ -5225,7 +5276,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
elif len(fields) != 0:
|
elif len(fields) != 0:
|
||||||
tablename = fields[0].tablename
|
tablename = fields[0].tablename
|
||||||
else:
|
else:
|
||||||
raise SyntaxError("The table name could not be found in " +
|
raise SyntaxError("The table name could not be found in " +
|
||||||
"the query nor from the select statement.")
|
"the query nor from the select statement.")
|
||||||
|
|
||||||
mongoqry_dict = self.expand(query)
|
mongoqry_dict = self.expand(query)
|
||||||
@@ -5251,7 +5302,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
sort=mongosort_list, snapshot=snapshot).count()}
|
sort=mongosort_list, snapshot=snapshot).count()}
|
||||||
else:
|
else:
|
||||||
# pymongo cursor object
|
# pymongo cursor object
|
||||||
mongo_list_dicts = ctable.find(mongoqry_dict,
|
mongo_list_dicts = ctable.find(mongoqry_dict,
|
||||||
mongofields_dict, skip=limitby_skip,
|
mongofields_dict, skip=limitby_skip,
|
||||||
limit=limitby_limit, sort=mongosort_list,
|
limit=limitby_limit, sort=mongosort_list,
|
||||||
snapshot=snapshot)
|
snapshot=snapshot)
|
||||||
@@ -5348,7 +5399,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
if safe is None:
|
if safe is None:
|
||||||
safe = self.safe
|
safe = self.safe
|
||||||
amount = 0
|
amount = 0
|
||||||
amount = self.count(query, False)
|
amount = self.count(query, False)
|
||||||
if not isinstance(query, Query):
|
if not isinstance(query, Query):
|
||||||
raise RuntimeError("query type %s is not supported" % \
|
raise RuntimeError("query type %s is not supported" % \
|
||||||
type(query))
|
type(query))
|
||||||
@@ -5484,8 +5535,9 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
return {self.expand(first): ('/%s^/' % \
|
return {self.expand(first): ('/%s^/' % \
|
||||||
self.expand(second, 'string'))}
|
self.expand(second, 'string'))}
|
||||||
|
|
||||||
def CONTAINS(self, first, second):
|
def CONTAINS(self, first, second, case_sensitive=False):
|
||||||
#There is a technical difference, but mongodb doesn't support
|
# silently ignore, only case sensitive
|
||||||
|
# There is a technical difference, but mongodb doesn't support
|
||||||
# that, but the result will be the same
|
# that, but the result will be the same
|
||||||
return {self.expand(first) : ('/%s/' % \
|
return {self.expand(first) : ('/%s/' % \
|
||||||
self.expand(second, 'string'))}
|
self.expand(second, 'string'))}
|
||||||
@@ -5515,7 +5567,8 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
re.escape(self.expand(second, 'string')) + '$'}}
|
re.escape(self.expand(second, 'string')) + '$'}}
|
||||||
|
|
||||||
#TODO verify full compatibilty with official oracle contains operator
|
#TODO verify full compatibilty with official oracle contains operator
|
||||||
def CONTAINS(self, first, second):
|
def CONTAINS(self, first, second, case_sensitive=False):
|
||||||
|
# silently ignore, only case sensitive
|
||||||
#There is a technical difference, but mongodb doesn't support
|
#There is a technical difference, but mongodb doesn't support
|
||||||
# that, but the result will be the same
|
# that, but the result will be the same
|
||||||
#TODO contains operators need to be transformed to Regex
|
#TODO contains operators need to be transformed to Regex
|
||||||
@@ -5645,7 +5698,7 @@ class IMAPAdapter(NoSQLAdapter):
|
|||||||
# This avoids the extra server names retrieval
|
# This avoids the extra server names retrieval
|
||||||
|
|
||||||
imapdb.define_tables({"inbox": "INBOX"})
|
imapdb.define_tables({"inbox": "INBOX"})
|
||||||
|
|
||||||
# Selects without content/attachments/email columns will only
|
# Selects without content/attachments/email columns will only
|
||||||
# fetch header and flags
|
# fetch header and flags
|
||||||
|
|
||||||
@@ -6038,7 +6091,7 @@ class IMAPAdapter(NoSQLAdapter):
|
|||||||
|
|
||||||
# keep the requests small for header/flags
|
# keep the requests small for header/flags
|
||||||
if any([(field.name in ["content", "size",
|
if any([(field.name in ["content", "size",
|
||||||
"attachments", "email"]) for
|
"attachments", "email"]) for
|
||||||
field in fields]):
|
field in fields]):
|
||||||
imap_fields = "(RFC822 FLAGS)"
|
imap_fields = "(RFC822 FLAGS)"
|
||||||
else:
|
else:
|
||||||
@@ -6305,7 +6358,8 @@ class IMAPAdapter(NoSQLAdapter):
|
|||||||
# result = "(%s %s)" % (self.expand(first), self.expand(second))
|
# result = "(%s %s)" % (self.expand(first), self.expand(second))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def CONTAINS(self, first, second):
|
def CONTAINS(self, first, second, case_sensitive=False):
|
||||||
|
# silently ignore, only case sensitive
|
||||||
result = None
|
result = None
|
||||||
name = self.search_fields[first.name]
|
name = self.search_fields[first.name]
|
||||||
|
|
||||||
@@ -6482,6 +6536,7 @@ ADAPTERS = {
|
|||||||
'oracle': OracleAdapter,
|
'oracle': OracleAdapter,
|
||||||
'mssql': MSSQLAdapter,
|
'mssql': MSSQLAdapter,
|
||||||
'mssql2': MSSQL2Adapter,
|
'mssql2': MSSQL2Adapter,
|
||||||
|
'mssql3': MSSQL3Adapter,
|
||||||
'sybase': SybaseAdapter,
|
'sybase': SybaseAdapter,
|
||||||
'db2': DB2Adapter,
|
'db2': DB2Adapter,
|
||||||
'teradata': TeradataAdapter,
|
'teradata': TeradataAdapter,
|
||||||
@@ -6644,6 +6699,8 @@ class Row(object):
|
|||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
setattr(self, str(key), value)
|
setattr(self, str(key), value)
|
||||||
|
|
||||||
|
__delitem__ = delattr
|
||||||
|
|
||||||
__copy__ = lambda self: Row(self)
|
__copy__ = lambda self: Row(self)
|
||||||
|
|
||||||
__call__ = __getitem__
|
__call__ = __getitem__
|
||||||
@@ -7081,8 +7138,12 @@ class DAL(object):
|
|||||||
:attempts (defaults to 5). Number of times to attempt connecting
|
:attempts (defaults to 5). Number of times to attempt connecting
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
dbdict = None
|
||||||
if uri == '<zombie>' and db_uid is not None: return
|
if uri == '<zombie>' and db_uid is not None: return
|
||||||
|
elif isinstance(uri, dict):
|
||||||
|
dbdict = uri
|
||||||
|
uri = dbdict["uri"]
|
||||||
|
codec = dbdict["codec"] or codec
|
||||||
if not decode_credentials:
|
if not decode_credentials:
|
||||||
credential_decoder = lambda cred: cred
|
credential_decoder = lambda cred: cred
|
||||||
else:
|
else:
|
||||||
@@ -7164,32 +7225,44 @@ 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:
|
if auto_import or dbdict:
|
||||||
self.import_table_definitions(adapter.folder)
|
self.import_table_definitions(adapter.folder,
|
||||||
|
items=dbdict["items"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tables(self):
|
def tables(self):
|
||||||
return self._tables
|
return self._tables
|
||||||
|
|
||||||
def import_table_definitions(self,path,migrate=False,fake_migrate=False):
|
def import_table_definitions(self, path, migrate=False,
|
||||||
|
fake_migrate=False, items=None):
|
||||||
pattern = pjoin(path,self._uri_hash+'_*.table')
|
pattern = pjoin(path,self._uri_hash+'_*.table')
|
||||||
for filename in glob.glob(pattern):
|
if items:
|
||||||
tfile = self._adapter.file_open(filename, 'r')
|
for tablename, table in items.iteritems():
|
||||||
try:
|
# TODO: read all field/table options
|
||||||
sql_fields = pickle.load(tfile)
|
fields = []
|
||||||
name = filename[len(pattern)-7:-6]
|
for fieldname, field in table["items"].iteritems():
|
||||||
mf = [(value['sortable'],
|
type = field["type"]
|
||||||
Field(key,
|
fields.append(Field(fieldname, type))
|
||||||
type=value['type'],
|
self.define_table(tablename, *fields)
|
||||||
length=value.get('length',None),
|
else:
|
||||||
notnull=value.get('notnull',False),
|
for filename in glob.glob(pattern):
|
||||||
unique=value.get('unique',False))) \
|
tfile = self._adapter.file_open(filename, 'r')
|
||||||
for key, value in sql_fields.iteritems()]
|
try:
|
||||||
mf.sort(lambda a,b: cmp(a[0],b[0]))
|
sql_fields = pickle.load(tfile)
|
||||||
self.define_table(name,*[item[1] for item in mf],
|
name = filename[len(pattern)-7:-6]
|
||||||
**dict(migrate=migrate,fake_migrate=fake_migrate))
|
mf = [(value['sortable'],
|
||||||
finally:
|
Field(key,
|
||||||
self._adapter.file_close(tfile)
|
type=value['type'],
|
||||||
|
length=value.get('length',None),
|
||||||
|
notnull=value.get('notnull',False),
|
||||||
|
unique=value.get('unique',False))) \
|
||||||
|
for key, value in sql_fields.iteritems()]
|
||||||
|
mf.sort(lambda a,b: cmp(a[0],b[0]))
|
||||||
|
self.define_table(name,*[item[1] for item in mf],
|
||||||
|
**dict(migrate=migrate,
|
||||||
|
fake_migrate=fake_migrate))
|
||||||
|
finally:
|
||||||
|
self._adapter.file_close(tfile)
|
||||||
|
|
||||||
def check_reserved_keyword(self, name):
|
def check_reserved_keyword(self, name):
|
||||||
"""
|
"""
|
||||||
@@ -7223,14 +7296,14 @@ def index():
|
|||||||
"/{person.name}/pets[pet.ownedby]/{pet.name}",
|
"/{person.name}/pets[pet.ownedby]/{pet.name}",
|
||||||
"/{person.name}/pets[pet.ownedby]/{pet.name}/:field",
|
"/{person.name}/pets[pet.ownedby]/{pet.name}/:field",
|
||||||
("/dogs[pet]", db.pet.info=='dog'),
|
("/dogs[pet]", db.pet.info=='dog'),
|
||||||
("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
|
("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
|
||||||
]
|
]
|
||||||
parser = db.parse_as_rest(patterns,args,vars)
|
parser = db.parse_as_rest(patterns,args,vars)
|
||||||
if parser.status == 200:
|
if parser.status == 200:
|
||||||
return dict(content=parser.response)
|
return dict(content=parser.response)
|
||||||
else:
|
else:
|
||||||
raise HTTP(parser.status,parser.error)
|
raise HTTP(parser.status,parser.error)
|
||||||
|
|
||||||
def POST(table_name,**vars):
|
def POST(table_name,**vars):
|
||||||
if table_name == 'person':
|
if table_name == 'person':
|
||||||
return db.person.validate_and_insert(**vars)
|
return db.person.validate_and_insert(**vars)
|
||||||
@@ -7321,10 +7394,12 @@ def index():
|
|||||||
return Row({'status':200,'pattern':'list',
|
return Row({'status':200,'pattern':'list',
|
||||||
'error':None,'response':patterns})
|
'error':None,'response':patterns})
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
|
basequery, exposedfields = None, []
|
||||||
if isinstance(pattern,tuple):
|
if isinstance(pattern,tuple):
|
||||||
pattern, basequery = pattern
|
if len(pattern)==2:
|
||||||
else:
|
pattern, basequery = pattern
|
||||||
basequery = None
|
elif len(pattern)>2:
|
||||||
|
pattern, basequery, exposedfields = pattern[0:3]
|
||||||
otable=table=None
|
otable=table=None
|
||||||
if not isinstance(queries,dict):
|
if not isinstance(queries,dict):
|
||||||
dbset=db(queries)
|
dbset=db(queries)
|
||||||
@@ -7437,7 +7512,10 @@ def index():
|
|||||||
orderby = [db[table][f] if not f.startswith('~') else ~db[table][f[1:]] for f in ofields]
|
orderby = [db[table][f] if not f.startswith('~') else ~db[table][f[1:]] for f in ofields]
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
return Row({'status':400,'error':'invalid orderby','response':None})
|
return Row({'status':400,'error':'invalid orderby','response':None})
|
||||||
fields = [field for field in db[table] if field.readable]
|
if exposedfields:
|
||||||
|
fields = [field for field in db[table] if str(field).split('.')[-1] in exposedfields and field.readable]
|
||||||
|
else:
|
||||||
|
fields = [field for field in db[table] if field.readable]
|
||||||
count = dbset.count()
|
count = dbset.count()
|
||||||
try:
|
try:
|
||||||
offset = int(vars.get('offset',None) or 0)
|
offset = int(vars.get('offset',None) or 0)
|
||||||
@@ -7525,6 +7603,38 @@ 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):
|
||||||
|
dbname = codec = uid = uri = None
|
||||||
|
if not sanitize:
|
||||||
|
uri, dbname, codec, uid = (self._uri, self._dbname,
|
||||||
|
self._db_codec, self._db_uid)
|
||||||
|
db_as_dict = dict(items={}, tables=[], uri=uri, dbname=dbname,
|
||||||
|
codec=codec, uid=uid)
|
||||||
|
for table in self:
|
||||||
|
tablename = str(table)
|
||||||
|
db_as_dict["tables"].append(tablename)
|
||||||
|
db_as_dict["items"][tablename] = table.as_dict(flat=flat,
|
||||||
|
sanitize=sanitize)
|
||||||
|
return db_as_dict
|
||||||
|
|
||||||
|
def as_xml(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No xml serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.xml(d)
|
||||||
|
|
||||||
|
def as_json(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No json serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.json(d)
|
||||||
|
|
||||||
|
def as_yaml(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No YAML serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.yaml(d)
|
||||||
|
|
||||||
def __contains__(self, tablename):
|
def __contains__(self, tablename):
|
||||||
try:
|
try:
|
||||||
return tablename in self.tables
|
return tablename in self.tables
|
||||||
@@ -7576,6 +7686,9 @@ def index():
|
|||||||
query = self._adapter.id_query(query)
|
query = self._adapter.id_query(query)
|
||||||
elif isinstance(query,Field):
|
elif isinstance(query,Field):
|
||||||
query = query!=None
|
query = query!=None
|
||||||
|
elif isinstance(query, dict):
|
||||||
|
icf = query.get("ignore_common_filters")
|
||||||
|
if icf: ignore_common_filters = icf
|
||||||
return Set(self, query, ignore_common_filters=ignore_common_filters)
|
return Set(self, query, ignore_common_filters=ignore_common_filters)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
@@ -8411,6 +8524,34 @@ class Table(object):
|
|||||||
if id_map and cid is not None:
|
if id_map and cid is not None:
|
||||||
id_map_self[int(line[cid])] = new_id
|
id_map_self[int(line[cid])] = new_id
|
||||||
|
|
||||||
|
def as_dict(self, flat=False, sanitize=True):
|
||||||
|
tablename = str(self)
|
||||||
|
table_as_dict = dict(name=tablename, items={}, fields=[])
|
||||||
|
for field in self:
|
||||||
|
if (field.readable or field.writable) or (not sanitize):
|
||||||
|
table_as_dict["fields"].append(field.name)
|
||||||
|
table_as_dict["items"][field.name] = \
|
||||||
|
field.as_dict(flat=flat, sanitize=sanitize)
|
||||||
|
return table_as_dict
|
||||||
|
|
||||||
|
def as_xml(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No xml serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.xml(d)
|
||||||
|
|
||||||
|
def as_json(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No json serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.json(d)
|
||||||
|
|
||||||
|
def as_yaml(self, sanitize=True):
|
||||||
|
if not have_serializers:
|
||||||
|
raise ImportError("No YAML serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.yaml(d)
|
||||||
|
|
||||||
def with_alias(self, alias):
|
def with_alias(self, alias):
|
||||||
return self._db._adapter.alias(self,alias)
|
return self._db._adapter.alias(self,alias)
|
||||||
|
|
||||||
@@ -8438,6 +8579,7 @@ class Expression(object):
|
|||||||
first=None,
|
first=None,
|
||||||
second=None,
|
second=None,
|
||||||
type=None,
|
type=None,
|
||||||
|
**optional_args
|
||||||
):
|
):
|
||||||
|
|
||||||
self.db = db
|
self.db = db
|
||||||
@@ -8450,6 +8592,7 @@ class Expression(object):
|
|||||||
self.type = first.type
|
self.type = first.type
|
||||||
else:
|
else:
|
||||||
self.type = type
|
self.type = type
|
||||||
|
self.optional_args = optional_args
|
||||||
|
|
||||||
def sum(self):
|
def sum(self):
|
||||||
db = self.db
|
db = self.db
|
||||||
@@ -8639,24 +8782,30 @@ class Expression(object):
|
|||||||
raise SyntaxError("endswith used with incompatible field type")
|
raise SyntaxError("endswith used with incompatible field type")
|
||||||
return Query(db, db._adapter.ENDSWITH, self, value)
|
return Query(db, db._adapter.ENDSWITH, self, value)
|
||||||
|
|
||||||
def contains(self, value, all=False):
|
def contains(self, value, all=False, case_sensitive=False):
|
||||||
|
"""
|
||||||
|
The case_sensitive parameters is only useful for PostgreSQL
|
||||||
|
For other RDMBs it is ignored and contains is always case in-sensitive
|
||||||
|
For MongoDB and GAE contains is always case sensitive
|
||||||
|
"""
|
||||||
db = self.db
|
db = self.db
|
||||||
if isinstance(value,(list, tuple)):
|
if isinstance(value,(list, tuple)):
|
||||||
subqueries = [self.contains(str(v).strip()) for v in value if str(v).strip()]
|
subqueries = [self.contains(str(v).strip(),case_sensitive=case_sensitive)
|
||||||
|
for v in value if str(v).strip()]
|
||||||
if not subqueries:
|
if not subqueries:
|
||||||
return self.contains('')
|
return self.contains('')
|
||||||
else:
|
else:
|
||||||
return reduce(all and AND or OR,subqueries)
|
return reduce(all and AND or OR,subqueries)
|
||||||
if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'):
|
if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'):
|
||||||
raise SyntaxError("contains used with incompatible field type")
|
raise SyntaxError("contains used with incompatible field type")
|
||||||
return Query(db, db._adapter.CONTAINS, self, value)
|
return Query(db, db._adapter.CONTAINS, self, value, case_sensitive=case_sensitive)
|
||||||
|
|
||||||
def with_alias(self, alias):
|
def with_alias(self, alias):
|
||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db, db._adapter.AS, self, alias, self.type)
|
return Expression(db, db._adapter.AS, self, alias, self.type)
|
||||||
|
|
||||||
# GIS expressions
|
# GIS expressions
|
||||||
|
|
||||||
def st_asgeojson(self, precision=15, options=0, version=1):
|
def st_asgeojson(self, precision=15, options=0, version=1):
|
||||||
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
|
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
|
||||||
dict(precision=precision, options=options,
|
dict(precision=precision, options=options,
|
||||||
@@ -8990,7 +9139,7 @@ class Field(Expression):
|
|||||||
stream = self.uploadfs.open(name, 'rb')
|
stream = self.uploadfs.open(name, 'rb')
|
||||||
else:
|
else:
|
||||||
# ## if file is on regular filesystem
|
# ## if file is on regular filesystem
|
||||||
stream = open(pjoin(file_properties['path'], name), 'rb')
|
stream = pjoin(file_properties['path'], name)
|
||||||
return (filename, stream)
|
return (filename, stream)
|
||||||
|
|
||||||
def retrieve_file_properties(self, name, path=None):
|
def retrieve_file_properties(self, name, path=None):
|
||||||
@@ -9056,6 +9205,76 @@ 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):
|
||||||
|
attrs = ("readable", "writable", "label", "default", "name",
|
||||||
|
"type", "represent", "compute")
|
||||||
|
SERIALIZABLE_TYPES = (int, long, basestring, dict, list,
|
||||||
|
float, tuple, bool, type(None))
|
||||||
|
def flatten(obj):
|
||||||
|
if flat:
|
||||||
|
if isinstance(obj, flatten.__class__):
|
||||||
|
return str(type(obj))
|
||||||
|
elif isinstance(obj, type):
|
||||||
|
try:
|
||||||
|
obj = str(obj).split("'")[1]
|
||||||
|
except IndexError:
|
||||||
|
obj = str(obj)
|
||||||
|
elif not isinstance(obj, SERIALIZABLE_TYPES):
|
||||||
|
obj = str(obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def filter_requires(t, r):
|
||||||
|
if sanitize and any([keyword in str(t).upper() for
|
||||||
|
keyword in ("CRYPT", "IS_STRONG")]):
|
||||||
|
return None
|
||||||
|
for k, v in r.items():
|
||||||
|
if k == "other":
|
||||||
|
if not isinstance(v, dict):
|
||||||
|
other = v.__dict__
|
||||||
|
else: other = v
|
||||||
|
r[k] = {flatten(type(v)):
|
||||||
|
filter_requires(type(v), other)}
|
||||||
|
elif flat and (not isinstance(v, SERIALIZABLE_TYPES)):
|
||||||
|
r[k] = str(v)
|
||||||
|
return r
|
||||||
|
|
||||||
|
if isinstance(self.requires, (tuple, list, set)):
|
||||||
|
requires = dict([(flatten(type(r)),
|
||||||
|
filter_requires(type(r), r.__dict__)) for
|
||||||
|
r in self.requires])
|
||||||
|
else:
|
||||||
|
requires = {flatten(type(self.requires)):
|
||||||
|
filter_requires(type(self.requires),
|
||||||
|
self.requires.__dict__)}
|
||||||
|
|
||||||
|
d = dict(colname="%s.%s" % (self.tablename, self.name),
|
||||||
|
requires=requires)
|
||||||
|
d.update([(attr, flatten(getattr(self, attr))) for attr in attrs])
|
||||||
|
return d
|
||||||
|
|
||||||
|
def as_xml(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
xml = serializers.xml
|
||||||
|
else:
|
||||||
|
raise ImportError("No xml serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return xml(d)
|
||||||
|
|
||||||
|
def as_json(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
json = serializers.json
|
||||||
|
else:
|
||||||
|
raise ImportError("No json serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return json(d)
|
||||||
|
|
||||||
|
def as_yaml(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return serializers.yaml(d)
|
||||||
|
else:
|
||||||
|
raise ImportError("No YAML serializers available")
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -9087,12 +9306,14 @@ class Query(object):
|
|||||||
first=None,
|
first=None,
|
||||||
second=None,
|
second=None,
|
||||||
ignore_common_filters = False,
|
ignore_common_filters = False,
|
||||||
|
**optional_args
|
||||||
):
|
):
|
||||||
self.db = self._db = db
|
self.db = self._db = db
|
||||||
self.op = op
|
self.op = op
|
||||||
self.first = first
|
self.first = first
|
||||||
self.second = second
|
self.second = second
|
||||||
self.ignore_common_filters = ignore_common_filters
|
self.ignore_common_filters = ignore_common_filters
|
||||||
|
self.optional_args = optional_args
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Query %s>' % BaseAdapter.expand(self.db._adapter,self)
|
return '<Query %s>' % BaseAdapter.expand(self.db._adapter,self)
|
||||||
@@ -9111,10 +9332,77 @@ class Query(object):
|
|||||||
return self.first
|
return self.first
|
||||||
return Query(self.db,self.db._adapter.NOT,self)
|
return Query(self.db,self.db._adapter.NOT,self)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return repr(self) == repr(other)
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not (self == other)
|
||||||
|
|
||||||
def case(self,t=1,f=0):
|
def case(self,t=1,f=0):
|
||||||
return self.db._adapter.CASE(self,t,f)
|
return self.db._adapter.CASE(self,t,f)
|
||||||
|
|
||||||
|
def as_dict(self, flat=False, sanitize=True):
|
||||||
|
"""Experimental stuff
|
||||||
|
|
||||||
|
This allows to return a plain dictionary with the basic
|
||||||
|
query representation. Can be used with json/xml services
|
||||||
|
for client-side db I/O
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> q = db.auth_user.id != 0
|
||||||
|
>>> q.as_dict(flat=True)
|
||||||
|
{"op": "NE", "first":{"tablename": "auth_user",
|
||||||
|
"fieldname": "id"},
|
||||||
|
"second":0}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SERIALIZABLE_TYPES = (tuple, dict, list, int, long, float,
|
||||||
|
basestring, type(None), bool)
|
||||||
|
def loop(d):
|
||||||
|
newd = dict()
|
||||||
|
for k, v in d.items():
|
||||||
|
if k in ("first", "second"):
|
||||||
|
if isinstance(v, self.__class__):
|
||||||
|
newd[k] = loop(v.__dict__)
|
||||||
|
elif isinstance(v, Field):
|
||||||
|
newd[k] = {"tablename": v._tablename,
|
||||||
|
"fieldname": v.name}
|
||||||
|
elif isinstance(v, Expression):
|
||||||
|
newd[k] = loop(v.__dict__)
|
||||||
|
elif isinstance(v, SERIALIZABLE_TYPES):
|
||||||
|
newd[k] = v
|
||||||
|
elif k == "op":
|
||||||
|
if callable(v):
|
||||||
|
newd[k] = v.__name__
|
||||||
|
elif isinstance(v, basestring):
|
||||||
|
newd[k] = v
|
||||||
|
else: pass # not callable or string
|
||||||
|
elif isinstance(v, SERIALIZABLE_TYPES):
|
||||||
|
if isinstance(v, dict):
|
||||||
|
newd[k] = loop(v)
|
||||||
|
else: newd[k] = v
|
||||||
|
return newd
|
||||||
|
|
||||||
|
if flat:
|
||||||
|
return loop(self.__dict__)
|
||||||
|
else: return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
def as_xml(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
xml = serializers.xml
|
||||||
|
else:
|
||||||
|
raise ImportError("No xml serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return xml(d)
|
||||||
|
|
||||||
|
def as_json(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
json = serializers.json
|
||||||
|
else:
|
||||||
|
raise ImportError("No json serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return json(d)
|
||||||
|
|
||||||
def xorify(orderby):
|
def xorify(orderby):
|
||||||
if not orderby:
|
if not orderby:
|
||||||
@@ -9148,6 +9436,12 @@ class Set(object):
|
|||||||
def __init__(self, db, query, ignore_common_filters = None):
|
def __init__(self, db, query, ignore_common_filters = None):
|
||||||
self.db = db
|
self.db = db
|
||||||
self._db = db # for backward compatibility
|
self._db = db # for backward compatibility
|
||||||
|
self.dquery = None
|
||||||
|
|
||||||
|
# if query is a dict, parse it
|
||||||
|
if isinstance(query, dict):
|
||||||
|
query = self.parse(query)
|
||||||
|
|
||||||
if not ignore_common_filters is None and \
|
if not ignore_common_filters is None and \
|
||||||
use_common_filters(query) == ignore_common_filters:
|
use_common_filters(query) == ignore_common_filters:
|
||||||
query = copy.copy(query)
|
query = copy.copy(query)
|
||||||
@@ -9166,10 +9460,10 @@ class Set(object):
|
|||||||
query = query!=None
|
query = query!=None
|
||||||
if self.query:
|
if self.query:
|
||||||
return Set(self.db, self.query & query,
|
return Set(self.db, self.query & query,
|
||||||
ignore_common_filters = ignore_common_filters)
|
ignore_common_filters=ignore_common_filters)
|
||||||
else:
|
else:
|
||||||
return Set(self.db, query,
|
return Set(self.db, query,
|
||||||
ignore_common_filters = ignore_common_filters)
|
ignore_common_filters=ignore_common_filters)
|
||||||
|
|
||||||
def _count(self,distinct=None):
|
def _count(self,distinct=None):
|
||||||
return self.db._adapter._count(self.query,distinct)
|
return self.db._adapter._count(self.query,distinct)
|
||||||
@@ -9195,6 +9489,94 @@ class Set(object):
|
|||||||
fields = db[tablename]._listify(update_fields,update=True)
|
fields = db[tablename]._listify(update_fields,update=True)
|
||||||
return db._adapter._update(tablename,self.query,fields)
|
return db._adapter._update(tablename,self.query,fields)
|
||||||
|
|
||||||
|
def as_dict(self, flat=False, sanitize=True):
|
||||||
|
if flat:
|
||||||
|
uid = dbname = uri = None
|
||||||
|
codec = self.db._db_codec
|
||||||
|
if not sanitize:
|
||||||
|
uri, dbname, uid = (self.db._dbname, str(self.db),
|
||||||
|
self.db._db_uid)
|
||||||
|
d = {"query": self.query.as_dict(flat=flat)}
|
||||||
|
d["db"] = {"uid": uid, "codec": codec,
|
||||||
|
"name": dbname, "uri": uri}
|
||||||
|
return d
|
||||||
|
else: return self.__dict__
|
||||||
|
|
||||||
|
def as_xml(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
xml = serializers.xml
|
||||||
|
else:
|
||||||
|
raise ImportError("No xml serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return xml(d)
|
||||||
|
|
||||||
|
def as_json(self, sanitize=True):
|
||||||
|
if have_serializers:
|
||||||
|
json = serializers.json
|
||||||
|
else:
|
||||||
|
raise ImportError("No json serializers available")
|
||||||
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
|
return json(d)
|
||||||
|
|
||||||
|
def parse(self, dquery):
|
||||||
|
"Experimental: Turn a dictionary into a Query object"
|
||||||
|
self.dquery = dquery
|
||||||
|
return self.build(self.dquery)
|
||||||
|
|
||||||
|
def build(self, d):
|
||||||
|
"Experimental: see .parse()"
|
||||||
|
op, first, second = (d["op"], d["first"],
|
||||||
|
d.get("second", None))
|
||||||
|
left = right = built = None
|
||||||
|
|
||||||
|
if op in ("AND", "OR"):
|
||||||
|
if not (type(first), type(second)) == (dict, dict):
|
||||||
|
raise SyntaxError("Invalid AND/OR query")
|
||||||
|
if op == "AND":
|
||||||
|
built = self.build(first) & self.build(second)
|
||||||
|
else: built = self.build(first) | self.build(second)
|
||||||
|
|
||||||
|
elif op == "NOT":
|
||||||
|
if first is None:
|
||||||
|
raise SyntaxError("Invalid NOT query")
|
||||||
|
built = ~self.build(first)
|
||||||
|
else:
|
||||||
|
# normal operation (GT, EQ, LT, ...)
|
||||||
|
for k, v in {"left": first, "right": second}.items():
|
||||||
|
if isinstance(v, dict) and v.get("op"):
|
||||||
|
v = self.build(v)
|
||||||
|
if isinstance(v, dict) and ("tablename" in v):
|
||||||
|
v = self.db[v["tablename"]][v["fieldname"]]
|
||||||
|
if k == "left": left = v
|
||||||
|
else: right = v
|
||||||
|
|
||||||
|
if hasattr(self.db._adapter, op):
|
||||||
|
opm = getattr(self.db._adapter, op)
|
||||||
|
|
||||||
|
if op == "EQ": built = left == right
|
||||||
|
elif op == "NE": built = left != right
|
||||||
|
elif op == "GT": built = left > right
|
||||||
|
elif op == "GE": built = left >= right
|
||||||
|
elif op == "LT": built = left < right
|
||||||
|
elif op == "LE": built = left <= right
|
||||||
|
elif op in ("JOIN", "LEFT_JOIN", "RANDOM", "ALLOW_NULL"):
|
||||||
|
built = Expression(self.db, opm)
|
||||||
|
elif op in ("LOWER", "UPPER", "EPOCH", "PRIMARY_KEY",
|
||||||
|
"COALESCE_ZERO", "RAW", "INVERT"):
|
||||||
|
built = Expression(self.db, opm, left)
|
||||||
|
elif op in ("COUNT", "EXTRACT", "AGGREGATE", "SUBSTRING",
|
||||||
|
"REGEXP", "LIKE", "ILIKE", "STARTSWITH",
|
||||||
|
"ENDSWITH", "ADD", "SUB", "MUL", "DIV",
|
||||||
|
"MOD", "AS", "ON", "COMMA", "NOT_NULL",
|
||||||
|
"COALESCE", "CONTAINS", "BELONGS"):
|
||||||
|
built = Expression(self.db, opm, left, right)
|
||||||
|
# expression as string
|
||||||
|
elif not (left or right): built = Expression(self.db, op)
|
||||||
|
else:
|
||||||
|
raise SyntaxError("Operator not supported: %s" % op)
|
||||||
|
|
||||||
|
return built
|
||||||
|
|
||||||
def isempty(self):
|
def isempty(self):
|
||||||
return not self.select(limitby=(0,1))
|
return not self.select(limitby=(0,1))
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -308,8 +308,8 @@ class Response(Storage):
|
|||||||
chunk_size=DEFAULT_CHUNK_SIZE,
|
chunk_size=DEFAULT_CHUNK_SIZE,
|
||||||
request=None,
|
request=None,
|
||||||
attachment=False,
|
attachment=False,
|
||||||
filename=None,
|
filename=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
if a controller function::
|
if a controller function::
|
||||||
|
|
||||||
@@ -385,6 +385,8 @@ class Response(Storage):
|
|||||||
downloads from http://..../download/filename
|
downloads from http://..../download/filename
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
current.session.forget(current.response)
|
||||||
|
|
||||||
if not request.args:
|
if not request.args:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
name = request.args[-1]
|
name = request.args[-1]
|
||||||
@@ -403,8 +405,8 @@ class Response(Storage):
|
|||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
headers = self.headers
|
headers = self.headers
|
||||||
headers['Content-Type'] = contenttype(name)
|
headers['Content-Type'] = contenttype(name)
|
||||||
if download_filename == None:
|
if download_filename == None:
|
||||||
download_filename = filename
|
download_filename = filename
|
||||||
if attachment:
|
if attachment:
|
||||||
headers['Content-Disposition'] = \
|
headers['Content-Disposition'] = \
|
||||||
'attachment; filename="%s"' % download_filename.replace('"','\"')
|
'attachment; filename="%s"' % download_filename.replace('"','\"')
|
||||||
@@ -689,7 +691,7 @@ class Session(Storage):
|
|||||||
|
|
||||||
def forget(self, response=None):
|
def forget(self, response=None):
|
||||||
self._close(response)
|
self._close(response)
|
||||||
self._forget = True
|
self._forget = True
|
||||||
|
|
||||||
def _try_store_in_cookie(self, request, response):
|
def _try_store_in_cookie(self, request, response):
|
||||||
if response.session_storage_type != 'cookie':
|
if response.session_storage_type != 'cookie':
|
||||||
|
|||||||
+1
-1
@@ -553,7 +553,7 @@ class XML(XmlComponent):
|
|||||||
'img/',
|
'img/',
|
||||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||||
'table', 'tr', 'td', 'div',
|
'table', 'tr', 'td', 'div',
|
||||||
'strong',
|
'strong','span',
|
||||||
],
|
],
|
||||||
allowed_attributes={
|
allowed_attributes={
|
||||||
'a': ['href', 'title', 'target'],
|
'a': ['href', 'title', 'target'],
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Contains:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
|
||||||
import gc
|
import gc
|
||||||
import cgi
|
import cgi
|
||||||
import cStringIO
|
import cStringIO
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ def sanitize(text, permitted_tags=[
|
|||||||
'img/',
|
'img/',
|
||||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||||
'table', 'tr', 'td', 'div',
|
'table', 'tr', 'td', 'div',
|
||||||
|
'strong', 'span',
|
||||||
],
|
],
|
||||||
allowed_attributes={
|
allowed_attributes={
|
||||||
'a': ['href', 'title'],
|
'a': ['href', 'title'],
|
||||||
|
|||||||
+22
-2
@@ -19,6 +19,11 @@ except ImportError:
|
|||||||
except:
|
except:
|
||||||
import contrib.simplejson as json_parser # fallback to pure-Python module
|
import contrib.simplejson as json_parser # fallback to pure-Python module
|
||||||
|
|
||||||
|
have_yaml = True
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
have_yaml = False
|
||||||
|
|
||||||
def loads_json(o):
|
def loads_json(o):
|
||||||
# deserialize a json string
|
# deserialize a json string
|
||||||
@@ -68,8 +73,12 @@ def xml(value, encoding='UTF-8', key='document', quote=True):
|
|||||||
|
|
||||||
|
|
||||||
def json(value, default=custom_json):
|
def json(value, default=custom_json):
|
||||||
return json_parser.dumps(value, default=default)
|
# replace JavaScript incompatible spacing
|
||||||
|
# http://timelessrepo.com/json-isnt-a-javascript-subset
|
||||||
|
return json_parser.dumps(value,
|
||||||
|
default=default).replace(ur'\u2028',
|
||||||
|
'\\u2028').replace(ur'\2029',
|
||||||
|
'\\u2029')
|
||||||
|
|
||||||
def csv(value):
|
def csv(value):
|
||||||
return ''
|
return ''
|
||||||
@@ -119,3 +128,14 @@ def rss(feed):
|
|||||||
pubDate=entry.get('created_on', now)
|
pubDate=entry.get('created_on', now)
|
||||||
) for entry in feed.get('entries', [])])
|
) for entry in feed.get('entries', [])])
|
||||||
return rss.to_xml(encoding='utf-8')
|
return rss.to_xml(encoding='utf-8')
|
||||||
|
|
||||||
|
def yaml(data):
|
||||||
|
if have_yaml:
|
||||||
|
return yaml.dump(data)
|
||||||
|
else: raise ImportError("No YAML serializer available")
|
||||||
|
|
||||||
|
def loads_yaml(data):
|
||||||
|
if have_yaml:
|
||||||
|
return yaml.load(data)
|
||||||
|
else: raise ImportError("No YAML serializer available")
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -1867,7 +1867,10 @@ class SQLFORM(FORM):
|
|||||||
dbset = db(query)
|
dbset = db(query)
|
||||||
tablenames = db._adapter.tables(dbset.query)
|
tablenames = db._adapter.tables(dbset.query)
|
||||||
if left is not None:
|
if left is not None:
|
||||||
tablenames += db._adapter.tables(left)
|
if not isinstance(left, (list, tuple)):
|
||||||
|
left = [left]
|
||||||
|
for join in left:
|
||||||
|
tablenames += db._adapter.tables(join)
|
||||||
tables = [db[tablename] for tablename in tablenames]
|
tables = [db[tablename] for tablename in tablenames]
|
||||||
if not fields:
|
if not fields:
|
||||||
fields = reduce(lambda a, b: a + b,
|
fields = reduce(lambda a, b: a + b,
|
||||||
@@ -2511,7 +2514,7 @@ class SQLFORM(FORM):
|
|||||||
except (KeyError, ValueError, TypeError):
|
except (KeyError, ValueError, TypeError):
|
||||||
redirect(URL(args=table._tablename))
|
redirect(URL(args=table._tablename))
|
||||||
if nargs == len(args) + 1:
|
if nargs == len(args) + 1:
|
||||||
query = table._id > 0
|
query = table._id != None
|
||||||
|
|
||||||
# filter out data info for displayed table
|
# filter out data info for displayed table
|
||||||
if table._tablename in constraints:
|
if table._tablename in constraints:
|
||||||
@@ -2549,7 +2552,7 @@ class SQLFORM(FORM):
|
|||||||
links_in_grid=links_in_grid,
|
links_in_grid=links_in_grid,
|
||||||
user_signature=user_signature, **kwargs)
|
user_signature=user_signature, **kwargs)
|
||||||
if isinstance(grid, DIV):
|
if isinstance(grid, DIV):
|
||||||
header = table._plural + (field and ' for ' + field.name or '')
|
header = table._plural + (field and ' for ' + field.label or '')
|
||||||
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
|
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
|
||||||
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
|
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
|
||||||
grid.insert(
|
grid.insert(
|
||||||
|
|||||||
+2
-2
@@ -46,8 +46,8 @@ def stream_file_or_304_or_206(
|
|||||||
request=None,
|
request=None,
|
||||||
headers={},
|
headers={},
|
||||||
status=200,
|
status=200,
|
||||||
error_message=None,
|
error_message=None
|
||||||
):
|
):
|
||||||
if error_message is None:
|
if error_message is None:
|
||||||
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
||||||
try:
|
try:
|
||||||
|
|||||||
+6
-1
@@ -5092,7 +5092,12 @@ class Wiki(object):
|
|||||||
url = URL(args=('_edit', slug))
|
url = URL(args=('_edit', slug))
|
||||||
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
|
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
|
||||||
else:
|
else:
|
||||||
return dict(content=XML(self.fix_hostname(page.html)))
|
return dict(title=page.title,
|
||||||
|
slug=page.slug,
|
||||||
|
content=XML(self.fix_hostname(page.html)),
|
||||||
|
tags=page.tags,
|
||||||
|
created_on=page.created_on,
|
||||||
|
modified_on=page.modified_on)
|
||||||
elif current.request.extension == 'load':
|
elif current.request.extension == 'load':
|
||||||
return self.fix_hostname(page.html) if page else ''
|
return self.fix_hostname(page.html) if page else ''
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,19 +1,49 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Script for installing Web2py with Nginx and Uwsgi on Centos 5
|
# -------------------------------------------------------------------
|
||||||
# Created By Hutchinson
|
# Description : Installation and basic configuration of web2py,
|
||||||
# Modified by spametki
|
# uWSGI, andNGINX.
|
||||||
# License: BSD
|
# in CentOS 5.x GNU/Linux
|
||||||
|
# Usage : Copy the script in /home/username and run it as root,
|
||||||
# It was originally posted in this web2py-users group thread:
|
# you may need to allow execution (chmod +x)
|
||||||
# https://groups.google.com/forum/?fromgroups#!topic/web2py/O4c4Jfr18tM
|
#
|
||||||
|
# WARNING: This script was modified to install compiled
|
||||||
# There are lots of subtleties of ownership, and one has to take care
|
# versions of Python and other packages that may be
|
||||||
# when installing python 2.6 not to stop the systems python2.4 from working.
|
# available at your Centos package repository.
|
||||||
|
# This change was made in order to get the latest
|
||||||
# NOTE: The only thing that should need changing for
|
# stable libraries available for avoiding compatibility
|
||||||
# each installation is the $BASEARCH (base architecture) of the machine.
|
#
|
||||||
# This is determined by doing uname -i. This is needed for the nginx installation.
|
# A bug was reported for the 2.7.3 version of python
|
||||||
|
# here http://bugs.python.org/issue14572
|
||||||
|
# in case you choose to change to VERSION=2.7
|
||||||
|
# (see below) mind that the automatic patch applied
|
||||||
|
# could not work for your environment. By default,
|
||||||
|
# Python 2.6 is installed.
|
||||||
|
#
|
||||||
|
# It was originally posted in this web2py-users group
|
||||||
|
# thread: https://groups.google.com/forum/?fromgroups#
|
||||||
|
# !topic/web2py/O4c4Jfr18tM
|
||||||
|
#
|
||||||
|
# There are lots of subtleties of ownership, and one
|
||||||
|
# has to take care when installing python 2.6 not to
|
||||||
|
# stop the systems python2.4 from working.
|
||||||
|
#
|
||||||
|
# NOTE: The only thing that should need changing for
|
||||||
|
# each installation is the $BASEARCH (base
|
||||||
|
# architecture)
|
||||||
|
# of the machine. This is determined by doing uname -i.
|
||||||
|
# This is needed for the nginx installation.
|
||||||
|
#
|
||||||
|
# File : setup-web2py-nginx-uwsgi-on-centos.sh
|
||||||
|
# Author : Peter Hutchinson
|
||||||
|
# Modified by : Alan Etkin
|
||||||
|
# Email : spametki@gmail.com
|
||||||
|
# Copyright : web2py
|
||||||
|
# Date : 2013-01-28
|
||||||
|
# Disclaimers : This script is provided "as is", without warranty of
|
||||||
|
# any kind.
|
||||||
|
# Licence : BSD
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
# Retrieve base architecture
|
# Retrieve base architecture
|
||||||
BASEARCH=$(uname -i)
|
BASEARCH=$(uname -i)
|
||||||
@@ -32,8 +62,8 @@ bzip2-devel sqlite-devel db4-devel openssl-devel tk-devel bluez-libs-devel
|
|||||||
# to fit your deployment needs.
|
# to fit your deployment needs.
|
||||||
|
|
||||||
# Python options
|
# Python options
|
||||||
PREFIX=2.7
|
PREFIX=2.6
|
||||||
VERSION=2.7.3
|
VERSION=2.6.8
|
||||||
|
|
||||||
# uWSGI options
|
# uWSGI options
|
||||||
version=uwsgi-1.2.4
|
version=uwsgi-1.2.4
|
||||||
@@ -47,8 +77,10 @@ wget http://www.python.org/ftp/python/$VERSION/Python-$VERSION.tgz
|
|||||||
tar xvfz Python-$VERSION.tgz
|
tar xvfz Python-$VERSION.tgz
|
||||||
cd Python-$VERSION
|
cd Python-$VERSION
|
||||||
|
|
||||||
echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
|
if [ "$VERSION" == "2.7.3" ]; then
|
||||||
curl -sk https://raw.github.com/gist/2727063/ | patch -p1
|
echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
|
||||||
|
curl -sk https://raw.github.com/gist/2727063/ | patch -p1
|
||||||
|
fi
|
||||||
|
|
||||||
./configure --prefix=/opt/python$PREFIX --with-threads --enable-shared
|
./configure --prefix=/opt/python$PREFIX --with-threads --enable-shared
|
||||||
make
|
make
|
||||||
|
|||||||
@@ -19,19 +19,50 @@ apt-get -y install build-essential python-dev libxml2-dev python-pip unzip
|
|||||||
pip install --upgrade pip
|
pip install --upgrade pip
|
||||||
PIPPATH=`which pip`
|
PIPPATH=`which pip`
|
||||||
$PIPPATH install --upgrade uwsgi
|
$PIPPATH install --upgrade uwsgi
|
||||||
|
# Create common nginx sections
|
||||||
|
mkdir /etc/nginx/conf.d/web2py
|
||||||
|
echo '
|
||||||
|
gzip_static on;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
|
gzip_disable "MSIE [1-6]\.";
|
||||||
|
gzip_vary on;
|
||||||
|
' > /etc/nginx/conf.d/web2py/gzip_static.conf
|
||||||
|
echo '
|
||||||
|
gzip on;
|
||||||
|
gzip_disable "msie6";
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_buffers 16 8k;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
' > /etc/nginx/conf.d/web2py/gzip.conf
|
||||||
# Create configuration file /etc/nginx/sites-available/web2py
|
# Create configuration file /etc/nginx/sites-available/web2py
|
||||||
echo 'server {
|
echo 'server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name $hostname;
|
server_name $hostname;
|
||||||
#to enable correct use of response.static_version
|
###to enable correct use of response.static_version
|
||||||
#location ~* /(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ {
|
#location ~* /(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ {
|
||||||
# alias /home/www-data/web2py/applications/$1/static/$2;
|
# alias /home/www-data/web2py/applications/$1/static/$2;
|
||||||
# expires max;
|
# expires max;
|
||||||
#}
|
#}
|
||||||
|
###
|
||||||
|
|
||||||
|
###if you use something like myapp = dict(languages=['en', 'it', 'jp'], default_language='en') in your routes.py
|
||||||
|
#location ~* /(\w+)/(en|it|jp)/static/(.*)$ {
|
||||||
|
# alias /home/www-data/web2py/applications/$1/;
|
||||||
|
# try_files static/$2/$3 static/$3 =404;
|
||||||
|
#}
|
||||||
|
###
|
||||||
location ~* /(\w+)/static/ {
|
location ~* /(\w+)/static/ {
|
||||||
root /home/www-data/web2py/applications/;
|
root /home/www-data/web2py/applications/;
|
||||||
#remove next comment on production
|
#remove next comment on production
|
||||||
#expires max;
|
#expires max;
|
||||||
|
### if you want to use pre-gzipped static files (recommended)
|
||||||
|
### check scripts/zip_static_files.py and remove the comments
|
||||||
|
# include /etc/nginx/conf.d/web2py/gzip_static.conf;
|
||||||
|
###
|
||||||
}
|
}
|
||||||
location / {
|
location / {
|
||||||
#uwsgi_pass 127.0.0.1:9001;
|
#uwsgi_pass 127.0.0.1:9001;
|
||||||
@@ -39,6 +70,14 @@ echo 'server {
|
|||||||
include uwsgi_params;
|
include uwsgi_params;
|
||||||
uwsgi_param UWSGI_SCHEME $scheme;
|
uwsgi_param UWSGI_SCHEME $scheme;
|
||||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||||
|
|
||||||
|
###remove the comments to turn on if you want gzip compression of your pages
|
||||||
|
# include /etc/nginx/conf.d/web2py/gzip.conf;
|
||||||
|
### end gzip section
|
||||||
|
|
||||||
|
### remove the comments if you use uploads (max 10 MB)
|
||||||
|
#client_max_body_size 10m;
|
||||||
|
###
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server {
|
server {
|
||||||
@@ -58,7 +97,15 @@ server {
|
|||||||
include uwsgi_params;
|
include uwsgi_params;
|
||||||
uwsgi_param UWSGI_SCHEME $scheme;
|
uwsgi_param UWSGI_SCHEME $scheme;
|
||||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||||
|
###remove the comments to turn on if you want gzip compression of your pages
|
||||||
|
# include /etc/nginx/conf.d/web2py/gzip.conf;
|
||||||
|
### end gzip section
|
||||||
|
### remove the comments if you want to enable uploads (max 10 MB)
|
||||||
|
#client_max_body_size 10m;
|
||||||
|
###
|
||||||
}
|
}
|
||||||
|
## if you serve static files through https, copy here the section
|
||||||
|
## from the previous server instance to manage static files
|
||||||
|
|
||||||
}' >/etc/nginx/sites-available/web2py
|
}' >/etc/nginx/sites-available/web2py
|
||||||
|
|
||||||
@@ -78,25 +125,26 @@ sudo mkdir /etc/uwsgi
|
|||||||
sudo mkdir /var/log/uwsgi
|
sudo mkdir /var/log/uwsgi
|
||||||
|
|
||||||
# Create configuration file /etc/uwsgi/web2py.xml
|
# Create configuration file /etc/uwsgi/web2py.xml
|
||||||
echo '<uwsgi>
|
echo '[uwsgi]
|
||||||
<socket>/tmp/web2py.socket</socket>
|
|
||||||
<pythonpath>/home/www-data/web2py/</pythonpath>
|
socket = /tmp/web2py.socket
|
||||||
<mount>/=wsgihandler:application</mount>
|
pythonpath = /home/www-data/web2py/
|
||||||
<master/>
|
mount = /=wsgihandler:application
|
||||||
<processes>4</processes>
|
processes = 4
|
||||||
<harakiri>60</harakiri>
|
master = true
|
||||||
<reload-mercy>8</reload-mercy>
|
harakiri = 60
|
||||||
<cpu-affinity>1</cpu-affinity>
|
reload-mercy = 8
|
||||||
<stats>/tmp/stats.socket</stats>
|
cpu-affinity = 1
|
||||||
<max-requests>2000</max-requests>
|
stats = /tmp/stats.socket
|
||||||
<limit-as>512</limit-as>
|
max-requests = 2000
|
||||||
<reload-on-as>256</reload-on-as>
|
limit-as = 512
|
||||||
<reload-on-rss>192</reload-on-rss>
|
reload-on-as = 256
|
||||||
<uid>www-data</uid>
|
reload-on-rss = 192
|
||||||
<gid>www-data</gid>
|
uid = www-data
|
||||||
<cron>0 0 -1 -1 -1 python /home/www-data/web2py/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o</cron>
|
gid = www-data
|
||||||
<no-orphans/>
|
cron = 0 0 -1 -1 -1 python /home/www-data/web2py/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o
|
||||||
</uwsgi>' >/etc/uwsgi/web2py.xml
|
no-orphans = true
|
||||||
|
' >/etc/uwsgi/web2py.ini
|
||||||
|
|
||||||
#Create a configuration file for uwsgi in emperor-mode
|
#Create a configuration file for uwsgi in emperor-mode
|
||||||
#for Upstart in /etc/init/uwsgi-emperor.conf
|
#for Upstart in /etc/init/uwsgi-emperor.conf
|
||||||
@@ -135,4 +183,4 @@ start uwsgi-emperor
|
|||||||
## and stop it with
|
## and stop it with
|
||||||
# stop uwsgi-emperor
|
# stop uwsgi-emperor
|
||||||
## to reload web2py only (without restarting uwsgi)
|
## to reload web2py only (without restarting uwsgi)
|
||||||
# touch /etc/uwsgi/web2py.xml
|
# touch /etc/uwsgi/web2py.xml
|
||||||
|
|||||||
Reference in New Issue
Block a user